3
votes

I'm using knockout binding along with knockout validation plugin (https://github.com/Knockout-Contrib/Knockout-Validation)

I want to show error messages only on form submit and not in input blur, any ideas?

HTML

<div id="formErrorMessages" data-bind="visible: errors().length != 0">
           <p data-bind="validationMessage : email"></p>
           <p data-bind="validationMessage: firstName"></p>
           <span class="arrow-down"></span> </div>

<form id="short-reg-form" method="post" data-bind="validationOptions: { insertMessages:false,decorateInputElement:true,errorElementClass:'input-error' },submit:submit">   
    <input type="text" name="email" data-bind="value: email" class="email" data-placeholder="Email"/>   
    <input type="text" name="firstName" data-bind="value: firstName" class="right-input" data-placeholder="First Name"/>

</form>

JS

function viewModel() {

var self = this;

self.showErrorsDiv = ko.observable(false);
//Email
var emailPlaceholder = getPlaceholder("email");
self.email = ko.observable("")
    .extend({
        ng_required: {
            params: emailPlaceholder,
            message: notValidValueReturnString(emailString)
        },
        ng_email: {
            params:emailPlaceholder,
            message:notValidValueReturnString(emailString)
        }
    }).clearError(); // invoke clearError to prevent validation on page load.
//--Email
//FirstName
var firstNamePlaceholder = getPlaceholder("firstName");
self.firstName = ko.observable("").extend({
    ng_required: {
        params: firstNamePlaceholder,
        message : notValidValueReturnString(firstNameString)
    }
});
//--FirstName
self.submit = function (e) {

    if (self.errors().length == 0) { // check form validation.
        alert(true);
    } else {
        alert(false);
    }

    //prevent form submit default behavior.
    return false;
};

// utilities 
function getPlaceholder(nameAttributeVal) {
    return $('#short-reg-form input[name="' + nameAttributeVal + '"]').attr("data-placeholder");
}//--utilities  

}

$(function () {

// attach view model to the DOM
var pageModel = new viewModel();
pageModel.errors = ko.validation.group([pageModel.email,pageModel.firstName]);

ko.applyBindings(pageModel);

});

thanks.

3
And I want a million dollar!GôTô
Seriously, you need to show that you tried something before coming here and just say what you want. This is not a please code for me siteGôTô
the main question is obvious I thought and doesn't need code explanation - is there a way to invoke validation using knockout validation plugin on form submit only and not in blur event?Samih A
I understand the question and the answer is I believe quite simple, which leads to: have you tried/searched?GôTô
yes I searched Google before - no results, beside I put question on this site only if I really had searched :)Samih A

3 Answers

2
votes

I also has this requirement - the client didn't want the user to see 'red' before actually trying to submit the form. Check out this codepen - http://codepen.io/dmoojunk/pen/zxqYbb

Html -

<div class="container no-padding">
    <div class="col-md-12 col-sm-12 col-xs-12">
        <div class="col-md-4 col-sm-4 col-xs-12">
            <div>
                <div>
                    <p>Phone: <a href="tel:0123456789">0123456789</a></p>
                    <p>Email: <a href="mailto:[email protected]" target="_top">[email protected]</a></p>
                    <pre data-bind="text: ko.toJSON($data, null, 2)"></pre>
                </div>
            </div>
        </div>

        <div class="col-md-8 col-sm-8 col-xs-12">
            <div>
                <div>
                    <form role="form">
                        <div class="row">
                            <div class="col-md-6">
                                <div class="form-group" data-bind="validationElement: firstName">
                                    <label class="control-label" for="contact-name">First Name</label>
                                    <input type="text" class="form-control" id="contact-name" placeholder="i.e Joe" data-bind="textInput: firstName"/>
                      <span class="help-block" data-bind="validationMessage: firstName"></span>
                                </div>
                            </div>
                            <div class="col-md-6">
                                <div class="form-group" data-bind="validationElement: email">
                                    <label class="control-label" for="contact-email">Email</label>
                                    <input type="email" class="form-control" id="contact-email" placeholder="i.e [email protected]" data-bind="textInput: email"/>
                      <span class="help-block" data-bind="validationMessage: email"></span>
                                </div>
                            </div>
                        </div>
                        <div class="row">
                            <div class="col-md-6">
                                <div class="form-group" data-bind="validationElement: surname">
                                    <label class="control-label" for="contact-surname"> Surname</label>
                                    <input type="text" class="form-control" id="contact-surname" placeholder="i.e Bloggs" data-bind="textInput: surname"/>
                      <span class="help-block" data-bind="validationMessage: surname"></span>
                                </div>
                            </div>
                            <div class="col-md-6">
                                <div class="form-group" data-bind="validationElement: category">
                                    <label class="control-label" for="contact-topic">Please select a category</label>
                                    <select class="form-control" id="contact-topic" data-bind="value: category">
                                        <option value="">Select category</option>
                                        <option value="general">General contact</option>
                                        <option value="issue">Report an issue</option>
                                        <option value="help">Help enquiry</option>
                                    </select>
                                </div>
                            </div>
                        </div>
                    </div>
                    <div class="row">
                        <div class="col-md-12">
                            <div class="form-group" data-bind="validationElement: message">
                                <label class="control-label" for="contact-textarea">Write your message here, the more detail the better</label>
                              <textarea style="width:100%" id="contact-textarea" placeholder="Place message here" rows="10" data-bind="textInput:message"></textarea>
                   <span class="help-block" data-bind="validationMessage: message"></span>
                            </div>
                            <button type="button" class="btn btn-green" data-bind="click: submit">Get in touch</button>
                        </div>
                    </div>
                </div>
            </form>
        </div>
    </div>
</div>

And JS -

var mynamespace = mynamespace || {};

ko.validation.init({
  errorElementClass: 'has-error',
  errorMessageClass: 'help-block',
  decorateElement: true,
  insertMessages: false
});

//Viewmodel
mynamespace.ContactUsViewModel = function(){
  var self = this;
  self.validationEnabled = ko.observable(false);
  self.isValidationEnabled = function() {
    return self.validationEnabled();
  }
  self.firstName = ko.observable('').extend({
    required: {
      onlyIf: self.isValidationEnabled
    },
    minLength: {
      onlyIf: self.isValidationEnabled,
      params: 5
    } 
  });
  self.email = ko.observable('').extend({
    required: {
      onlyIf: self.isValidationEnabled
    },
    email: {
      onlyIf: self.isValidationEnabled
    }
  });
  self.surname = ko.observable('').extend({
    required: {
      onlyIf: self.isValidationEnabled
    },
    minLength: {
      onlyIf: self.isValidationEnabled,
      params: 5
    } 
  });
  self.category = ko.observable('').extend({
    required: {
      onlyIf: self.isValidationEnabled
    },
    minLength: {
      onlyIf: self.isValidationEnabled,
      params: 1
    } 
  });
  self.message = ko.observable('').extend({
    required: {
      onlyIf: self.isValidationEnabled
    },
    minLength: {
      onlyIf: self.isValidationEnabled,
      params: 5
    } 
  });
  self.submit = function() {
    self.validationEnabled(true);
    if (!this.isValid()) {
      this.errors.showAllMessages();
    } else {
      alert('Form Valid');
    };
  }
};

var viewmodel = ko.validatedObservable(new mynamespace.ContactUsViewModel())();
ko.applyBindings(viewmodel);
0
votes

Try this:

var viewmodel = function () {
    var self = this;
    //...
    this.errors = ko.validation.group(this, { deep: true });

    this.submitViewModel = function () {
        if (self.errors().length > 0) {
            self.errors.showAllMessages(true);
            return;
        }
        //...
    }
}
0
votes

There is a way to achieve this, but you need to use the init Options to set insertMessages to false.

ko.validation.init({
    insertMessages: false,
    messagesOnModified: true,
    parseInputAttributes: false
});

This code should be called before your validatedObservable or extend{required: true} etc logic is loaded. I call this at the top of my code file before any of my viewModel code is loaded.

Next you should add a new observable to your view model called something like "submitted"

var submitted = ko.observable(false);

Now in your submit button click you do something like this:

var saveDraft = function () {
    submitted(true);
    var isValid = poll.isValid();
    if (!isValid) {
        console.log('not valid!');
        poll.errors.showAllMessages(true);
        return;
    }
}
model.saveDraft = saveDraft;

Then you'll need to manually place your validation messages in your markup, like so:

    <div class="form-group">
        <div class="col-md-12">
            <label for="txtPollName" data-bind="">POLL NAME</label>
            <input type="text" class="form-control" id="txtPollName" placeholder="Enter a unique name for the poll." data-bind="value: poll().name">
            <div class="invalid-feedback" data-bind="visible: submitted(), validationMessage: poll().name">
            </div>
        </div>
    </div>

Notice that in the markup I've added an extra check on the validationMessage binding by adding a visible binding. So the validation message will only show up if the form has been submitted.

Also don't forget that if your validatedObservable has nested objects that need validated that you can instantiate the validatedObservable with grouping like so:

this.createPoll = function (parent, options) {
    var poll = new pollObject(parent, options);
    if (options && options.plain)
        return poll;
    var obs = ko.validatedObservable(null, { deep: true, live: true });
    obs(poll);
    return obs;
}

This snippet is from my object factory that creates my objects for me and auto wraps them in grouped validatedObservables. The deep: true options means the isValid() will call all of it's child validation objects isValid methods and they all have to be true for the object to be valid. Deep false means only the validatedObservable's value is checked for isValid and since it always has a value it'll always be valid. Live: true means the validation logic will rerun if any changes to dependent observable's being validated are made, in real time.