Sign Up

Sign Up to our social questions and Answers Engine to ask questions, answer people’s questions, and connect with other people.

Have an account? Sign In

Have an account? Sign In Now

Sign In

Login to our social questions & Answers Engine to ask questions answer people’s questions & connect with other people.

Sign Up Here

Forgot Password?

Don't have account, Sign Up Here

Forgot Password

Lost your password? Please enter your email address. You will receive a link and will create a new password via email.

Have an account? Sign In Now

You must login to ask a question.

Forgot Password?

Need An Account, Sign Up Here

Please briefly explain why you feel this question should be reported.

Please briefly explain why you feel this answer should be reported.

Please briefly explain why you feel this user should be reported.

Sign InSign Up

The Archive Base

The Archive Base Logo The Archive Base Logo

The Archive Base Navigation

  • SEARCH
  • Home
  • About Us
  • Blog
  • Contact Us
Search
Ask A Question

Mobile menu

Close
Ask a Question
  • Home
  • Add group
  • Groups page
  • Feed
  • User Profile
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Buy Points
  • Users
  • Help
  • Buy Theme
  • SEARCH
Home/ Questions/Q 8296965
In Process

The Archive Base Latest Questions

Editorial Team
  • 0
Editorial Team
Asked: June 8, 20262026-06-08T15:13:36+00:00 2026-06-08T15:13:36+00:00

I have managed to create a simple wizard based on an answer given by

  • 0

I have managed to create a simple wizard based on an answer given by Niemeyer. This works fine. I want to add validation. I have managed to add a required validion on the field Firstname. Leaving this empty displays an error. But what I could not succeed in is the following:
Validate the model in the current step, and have the go next enabled or disabled based whether there are errors. If it is too difficult to enable or disable the next button, that is ok. I can also live without the button disabled when there are errors. As long as the user is prevented to proceed to the next step when there are errors.

. My view looks like this:

 //model is retrieved from server model
 <script type="text/javascript">
     var serverViewModel = @Html.Raw(Json.Encode(Model));
 </script>


<h2>Test with wizard using Knockout.js</h2>
  <div data-bind="template: { name: 'currentTmpl', data: currentStep }"></div> 
<hr/>

<button data-bind="click: goPrevious, enable: canGoPrevious">Previous</button>
<button data-bind="click: goNext, enable: canGoNext">Next</button>

<script id="currentTmpl" type="text/html">
    <h2 data-bind="text: name"></h2>
    <div data-bind="template: { name: getTemplate, data: model }"></div> 
</script>

<script id="nameTmpl" type="text/html">
    <fieldset>
        <legend>Naamgegevens</legend>
        <p data-bind="css: { error: FirstName.hasError }">
            @Html.LabelFor(model => model.FirstName)
            @Html.TextBoxFor(model => model.FirstName, new { data_bind = "value: FirstName, valueUpdate: 'afterkeydown'"})
            <span data-bind='visible: FirstName.hasError, text: FirstName.validationMessage'> </span>
        </p>
        @Html.LabelFor(model => model.LastName)
        @Html.TextBoxFor(model => model.LastName, new { data_bind = "value: LastName" })
    </fieldset>
</script>

<script id="addressTmpl" type="text/html">
    <fieldset>
        <legend>Adresgegevens</legend>
        @Html.LabelFor(model => model.Address)
        @Html.TextBoxFor(model => model.Address, new { data_bind = "value: Address" })
        @Html.LabelFor(model => model.PostalCode)
        @Html.TextBoxFor(model => model.PostalCode, new { data_bind = "value: PostalCode" })
        @Html.LabelFor(model => model.City)
        @Html.TextBoxFor(model => model.City, new { data_bind = "value: City" })
    </fieldset>
</script>

<script id="confirmTmpl" type="text/html">
        <fieldset>
        <legend>Naamgegevens</legend>
        @Html.LabelFor(model => model.FirstName)
        <b><span data-bind="text:NameModel.FirstName"></span></b>
        <br/>
        @Html.LabelFor(model => model.LastName)
        <b><span data-bind="text:NameModel.LastName"></span></b>
    </fieldset>
    <fieldset>
        <legend>Adresgegevens</legend>
        @Html.LabelFor(model => model.Address)
        <b><span data-bind="text:AddressModel.Address"></span></b>
        <br/>
        @Html.LabelFor(model => model.PostalCode)
        <b><span data-bind="text:AddressModel.PostalCode"></span></b>
        <br/>
        @Html.LabelFor(model => model.City)
        <b><span data-bind="text:AddressModel.City"></span></b>           
    </fieldset>
    <button data-bind="click: confirm">Confirm</button>
</script>

<script type='text/javascript'>
    $(function() {
        if (typeof(ViewModel) != "undefined") {
            ko.applyBindings(new ViewModel(serverViewModel));
        } else {
            alert("Wizard not defined!");
        }
    });
</script>

The knockout.js implementation looks like this:

function Step(id, name, template, model) {
    var self = this;
    self.id = id;
    self.name = ko.observable(name);
    self.template = template;
    self.model = ko.observable(model);

    self.getTemplate = function() {
        return self.template;
    };
}

function ViewModel(model) {
    var self = this;

    self.nameModel = new NameModel(model);
    self.addressModel = new AddressModel(model);

    self.stepModels = ko.observableArray([
            new Step(1, "Step1", "nameTmpl", self.nameModel),
            new Step(2, "Step2", "addressTmpl", self.addressModel),
            new Step(3, "Confirmation", "confirmTmpl", {NameModel: self.nameModel, AddressModel:self.addressModel})]);

    self.currentStep = ko.observable(self.stepModels()[0]);

    self.currentIndex = ko.dependentObservable(function() {
        return self.stepModels.indexOf(self.currentStep());
    });

    self.getTemplate = function(data) {
        return self.currentStep().template();
    };

    self.canGoNext = ko.dependentObservable(function () {
        return self.currentIndex() < self.stepModels().length - 1;
    });

    self.goNext = function() {
        if (self.canGoNext()) {
            self.currentStep(self.stepModels()[self.currentIndex() + 1]);
        }
    };

    self.canGoPrevious = ko.dependentObservable(function() {
        return self.currentIndex() > 0;
    });

    self.goPrevious = function() {
        if (self.canGoPrevious()) {
            self.currentStep(self.stepModels()[self.currentIndex() - 1]);
        }
    };
}

NameModel = function (model) {

    var self = this;

    //Observables
    self.FirstName = ko.observable(model.FirstName).extend({ required: "Please enter a first name" });;
    self.LastName = ko.observable(model.LastName);

    return self;
};

AddressModel = function(model) {

    var self = this;

    //Observables
    self.Address = ko.observable(model.Address);
    self.PostalCode = ko.observable(model.PostalCode);
    self.City = ko.observable(model.City);

    return self;
};

And I have added an extender for the required validation as used in the field Firstname:

ko.extenders.required = function(target, overrideMessage) {
    //add some sub-observables to our observable    
    target.hasError = ko.observable();
    target.validationMessage = ko.observable();
    //define a function to do validation    

    function validate(newValue) {
        target.hasError(newValue ? false : true);
        target.validationMessage(newValue ? "" : overrideMessage || "This field is required");
    }

    //initial validation    
    validate(target());

    //validate whenever the value changes    
    target.subscribe(validate);
    //return the original observable    
    return target;
};
  • 1 1 Answer
  • 0 Views
  • 0 Followers
  • 0
Share
  • Facebook
  • Report

Leave an answer
Cancel reply

You must login to add an answer.

Forgot Password?

Need An Account, Sign Up Here

1 Answer

  • Voted
  • Oldest
  • Recent
  • Random
  1. Editorial Team
    Editorial Team
    2026-06-08T15:13:38+00:00Added an answer on June 8, 2026 at 3:13 pm

    This was a tricky one, but I’ll offer a couple of solutions for you…

    If you simply want to prevent the Next button from proceeding with an invalid model state, then the easiest solution I found is to start by adding a class to each of the <span> tags that are used for displaying the validation messages:

    <span class="validationMessage" 
          data-bind='visible: FirstName.hasError, text: FirstName.validationMessage'>
    

    (odd formatting to prevent horizontal scrolling)

    Next, in the goNext function, change the code to include a check for whether or not any of the validation messages are visible, like this:

    self.goNext = function() {
        if (
            (self.currentIndex() < self.stepModels().length - 1) 
            && 
            ($('.validationMessage:visible').length <= 0)
           ) 
        {
            self.currentStep(self.stepModels()[self.currentIndex() + 1]);
        }
    };
    

    Now, you may be asking “why not put that functionality in the canGoNext dependent observable?”, and the answer is that calling that function wasn’t working like one might thing it would.

    Because canGoNext is a dependentObservable, its value is computed any time the model that it’s a member of changes.

    However, if its model hasn’t changed, canGoNext simply returns the last calculated value, i.e. the model hasn’t changed, so why recalculate it?

    This wasn’t vital when only checking whether or not there were more steps remaining, but when I tried to include validation in that function, this came into play.

    Why? Well, changing First Name, for example, updates the NameModel it belongs to, but in the ViewModel, self.nameModel is not set as an observable, so despite the change in the NameModel, self.nameModel is still the same. Thus, the ViewModel hasn’t changed, so there’s no reason to recompute canGoNext. The end result is that canGoNext always sees the form as valid because it’s always checking self.nameModel, which never changes.

    Confusing, I know, so let me throw a bit more code at you…

    Here’s the beginning of the ViewModel, I ended up with:

    function ViewModel(model) {
        var self = this;
    
        self.nameModel = ko.observable(new NameModel(model));
        self.addressModel = ko.observable(new AddressModel(model));
    
        ...
    

    As I mentioned, the models need to be observable to know what’s happening to them.

    Now the changes to the goNext and goPrevious methods will work without making those models observable, but to get the true real-time validation you’re looking for, where the buttons are disabled when the form is invalid, making the models observable is necessary.

    And while I ended up keeping the canGoNext and canGoPrevious functions, I didn’t use them for validation. I’ll explain that in a bit.

    First, though, here’s the function I added to ViewModel for validation:

    self.modelIsValid = ko.computed(function() {
        var isOK = true;
        var theCurrentIndex = self.currentIndex();
        switch(theCurrentIndex)
        {
            case 0:
                isOK = (!self.nameModel().FirstName.hasError()
                        && !self.nameModel().LastName.hasError());
                break;
            case 1:
                isOK = (!self.addressModel().Address.hasError()
                        && !self.addressModel().PostalCode.hasError()
                        && !self.addressModel().City.hasError());
                break;
            default:
                break;
        };
        return isOK;                
    });
    

    [Yeah, I know… this function couples the ViewModel to the NameModel and AddressModel classes even more than simply referencing an instance of each of those classes, but for now, so be it.]

    And here’s how I bound this function in the HTML:

    <button data-bind="click: goPrevious, 
                       visible: canGoPrevious, 
                       enable: modelIsValid">Previous</button>
    <button data-bind="click: goNext, 
                       visible: canGoNext, 
                       enable: modelIsValid">Next</button>
    

    Notice that I changed canGoNext and canGoPrevious so each is bound to its button’s visible attribute, and I bound the modelIsValid function to the enable attribute.

    The canGoNext and canGoPrevious functions are just as you provided them — no changes there.

    One result of these binding changes is that the Previous button is not visible on the Name step, and the Next button is not visible on the Confirm step.

    In addition, when validation is in place on all of the data properties and their associated form fields, deleting a value from any field instantly disables the Next and/or Previous buttons.

    Whew, that’s a lot to explain!

    I may have left something out, but here’s the link to the fiddle I used to get this working: http://jsfiddle.net/jimmym715/MK39r/

    I’m sure that there’s more work to do and more hurdles to cross before you’re done with this, but hopefully this answer and explanation helps.

    • 0
    • Reply
    • Share
      Share
      • Share on Facebook
      • Share on Twitter
      • Share on LinkedIn
      • Share on WhatsApp
      • Report

Sidebar

Related Questions

I have managed to create a simple app which deletes (bypassing the recycle bin)
This seems like a pretty simple question, but I have not managed to find
Following this tutorial http://netbeans.org/kb/docs/javaee/maven-osgiservice-cdi.html I have managed to create a simple OSGI bundle and
I have been trying to create a simple calculator. Using PHP I managed to
I have created some documents and managed to make some simple queries but I
I have managed to create a custom action in C# using MakeSfxCA which is
What I want to do is create a simple NSCollectionView in my application and
I'm quite new to vb and doing simple basics, I have managed to access
I have a simple .csv file that has that I want to extract data
I have a simple CoreData app which allows you to add items to a

Explore

  • Home
  • Add group
  • Groups page
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Users
  • Help
  • SEARCH

Footer

© 2021 The Archive Base. All Rights Reserved
With Love by The Archive Base

Insert/edit link

Enter the destination URL

Or link to existing content

    No search term specified. Showing recent items. Search or use up and down arrow keys to select an item.