Revealing module pattern with Knockout-es5
Asked Answered
S

3

7

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);
});
Sexist answered 1/6, 2013 at 1:20 Comment(0)
H
1

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.

Himation answered 2/6, 2013 at 0:8 Comment(1)
Thanks for the answer. The project is already using this pattern so I was hoping to use the new plugin to simplifying the models going forward. The more I look into it more I realize that the plugin is designed to be used with the way models are defined in the above mentioned blog post and many other examples. It is fine, I just wanted to make sure that I did not miss something obvious. Sometimes the answer "It can't be done this way" is the correct answer. I will leave it open for a couple of days and accept this.Sexist
A
3

It did not work because you linked JS directly from github. See updated fiddle that works: http://jsfiddle.net/tkirda/Wznkm/1/

The reason for that not working, is because Github indicates that content type is "text/plain" and not "application/x-javascript".

Content-Type:text/plain; charset=utf-8
Access-Control-Allow-Origin:https://render.github.com

Therefore browsers do not execute this code. I guess they did it so that people stop hotlinking files from GitHub.

UPDATE: In your case lastName is changed, add console.log and you will see it's new value.

        doSomething = function (n) {
            // Doesn't work
            lastName += " " + n;
            console.log(lastName);
            // Works
            // this.lastName += " " + n;
        }

However it is not used by model because when you did assign them to retObj, you passed only values and not reference. Because string is value type. When you work with functions, they are of reference type. So you just updated local variable, but your model is not bound to that variable.

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

I hope that makes sense.

Ashbaugh answered 1/6, 2013 at 2:53 Comment(6)
Both fiddle work exactly the same at least for me. The issue is between the two methods of defining the models. The observableSection works as expected while withoutObservableSection doesn't. Both sections should display "John 1 Doe 2" after both timeouts fire. I also have the same code locally where both lobs are served locally and I get same behavior.Sexist
Thanks for the update. I understand why it is not working. Is there a way around it or may be I can't use these two together i.e. either use another pattern or continue to use the traditional syntax for the properties.Sexist
I suggest make it as simple as possible. Do you really need to protect those as closures?Ashbaugh
What do you mean simple? It already is. It is the standard revealing module pattern. The project is already using it so I was hoping to use the new plugin to simplifying the models going forward. The more I look into it more I realize that the plugin is designed to be used with the way models are defined in the above mentioned blog post and many other examples. It is fine, I just wanted to make sure that I did not miss something obvious. Sometimes the answer "It can't be done this way" is the correct answer.Sexist
I mean just use as ViewModel1 sample.Ashbaugh
That is what we use now. It is just that having to write properties with parenthesis as those are observable functions is not very natural and this plugin helps in that i.e. write model exactly as one would (without ko).Sexist
H
1

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.

Himation answered 2/6, 2013 at 0:8 Comment(1)
Thanks for the answer. The project is already using this pattern so I was hoping to use the new plugin to simplifying the models going forward. The more I look into it more I realize that the plugin is designed to be used with the way models are defined in the above mentioned blog post and many other examples. It is fine, I just wanted to make sure that I did not miss something obvious. Sometimes the answer "It can't be done this way" is the correct answer. I will leave it open for a couple of days and accept this.Sexist
S
1

As much as I loathe using constructor functions and the broken "this" keyword, the revealing module pattern is almost fully incompatible with the ES5 ko plugin (You can make it work in a very convoluted and fragile way and only in simple scenarios)

A trackable property needs a host object (so unlike your private vars) and cannot be copied over (as you then lose the newly generated getter)

So pick the lesser of the two evils :p (for me it's the ES5 syntax)

Sharpwitted answered 28/8, 2013 at 14:54 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.