JSFiddle - React, Tailwind, and code Playground

by kougiland

HTML

<script src="http://cdnjs.cloudflare.com/ajax/libs/knockout/2.3.0/knockout-min.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/knockout-validation/1.0.2/knockout.validation.min.js"></script>
<h2>Test with wizard using Knockout.js</h2>
  <div data-bind="template: { name: 'currentTmpl', data: currentStep }"></div> 
<hr/>

<button data-bind="click: goPrevious, visible: canGoPrevious, enable: modelIsValid">Previous</button>
<button data-bind="click: goNext, visible: canGoNext, enable: modelIsValid">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 }">
            <label for"FirstName">First Name</label>
            <input id="FirstName" type="text" data-bind="value: FirstName, valueUpdate: 'afterkeydown'" />
            <p data-bind="validationMessage: FirstName"></p>
        </p>
        <p data-bind="css: { error: LastName.hasError }">
            <label for"LastName">Last Name</label>
            <input id="LastName" type="text" data-bind="value: LastName, valueUpdate: 'afterkeydown'" />
            <p data-bind="validationMessage: LastName"></p>
        </p>
    </fieldset>
    <div style="display: inline-block;" data-bind="text: FirstName"></div>&nbsp;<div style="display: inline-block;" data-bind="text: LastName"></div>
</script>

<script id="addressTmpl" type="text/html">
    <fieldset>
        <legend>Adresgegevens</legend>
            <p data-bind="css: { error: Address.hasError }">
                <label for"Address">Address</label>
                <input id="Address" type="text" data-bind="value: Address, valueUpdate: 'afterkeydown'" />
                <p data-bind="validationMessage: Address"></p>
            </p>
            <p data-bind="css: { error:...

JavaScript

ko.validation.configure({
    insertMessages: false,
    decorateElement: true,
    errorElementClass: 'error'
});

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 = ko.observable(new NameModel(model));    
    self.addressModel = ko.observable(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.computed(function() {
        return self.stepModels.indexOf(self.currentStep());
    });

    self.getTemplate = function(data) {
        return self.currentStep().template();
    };
    
    self.canGoNext = ko.computed(function() {
        return (self.currentIndex() < (self.stepModels().length - 1));
    });    

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

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

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

NameModel = function (model) {
    var self = this;

   ...