Search code examples
knockout.jsknockout-es5-plugin

Revealing module pattern with Knockout-es5


I am trying to put together a demo to use knockout-es5 plugin to simplifying the models that are using revealing module pattern. ViewModel1 is original Knockout model and it works fine. ViewModel2 is an attempt to use knockout-es5 plugin. Running into few things

  • The computed properties don't work as the local variables are not tracked (e.g. fullName1). I can use ko.defineProperty but first it is separated from the other properties, second have to use this.propertyName.
  • The changes made by member functions are not reflected probably for the very same reason (e.g. doSomething). Again using this.propertyName works but the RM pattern gets violated.

JS Fiddle

var NS = NS || {};

$(function () {

    NS.ViewModel1 = function (first, last) {
        var
            firstName = ko.observable(first),
            lastName = ko.observable(last),
            fullName = ko.computed(function () {
                return firstName() + " " + lastName();
            }),
            doSomething = function (n) {
                lastName(lastName() + " " + n);
            }
        ;

        return {
            firstName: firstName,
            lastName: lastName,
            fullName: fullName,
            doSomething: doSomething
        };
    };

    NS.ViewModel2 = function (first, last) {
        var
            firstName = first,
            lastName = last,
            fullName1 = ko.computed(function () {
                // Changed values are not reflected
                return firstName + " " + lastName;
            }),
            fullName2 = ko.computed(function () {
                // Should not work
                return this.firstName + " " + this.lastName;
            }),
            doSomething = function (n) {
                // Doesn't work
                lastName += " " + n;
                // Works
                // this.lastName += " " + n;
            }
        ;

        var retObj = {
            firstName: firstName,
            lastName: lastName,
            fullName1: fullName1,
            fullName2: fullName2,
            doSomething: doSomething
        };

        ko.track(retObj);
        ko.defineProperty(retObj, 'fullName3', function () {
            // Changed values are not reflected
            return firstName + " " + lastName;
        });
        ko.defineProperty(retObj, 'fullName4', function () {
            // Works
            return this.firstName + " " + this.lastName;
        });

        return retObj;
    };

    var vm1 = new NS.ViewModel1("John", "Doe");
    ko.applyBindings(vm1, document.getElementById("observableSection"));

    var vm2 = new NS.ViewModel2("Jane", "Doe");
    ko.applyBindings(vm2, document.getElementById("withoutObservableSection"));

    setTimeout(function () {
        vm1.firstName("John 1");
        vm2.firstName = "Jane 1";
    }, 2000);

    setTimeout(function () {
        vm1.doSomething(2);
        vm2.doSomething(2);
    }, 4000);
});

Solution

  • I'm not sure what your question is, but yes it won't work with the pattern you are trying to use.

    I like the idea of knockout-es5, but the current API it exposes has some issues that can really get you in trouble if you deviate from their pattern.

    Another issue is, if you pass this.property to some other viewmodel with the goal of that other viewmodel being able to subscribe to the observable, or update the observable, then it obviously won't work. You'll just be passing the current value. You have to remember to get the actual observable for your property in this situation. Using good old knockout, you know you are passing the observable itself when you access the property.

    The project is young, so hopefully these API issues will be resolved. But right now, it requires the developer to remember too many things to get it right.