Is it possible to inherit ko.computed to a prototype class?

173 Views Asked by At

Let's say that I wish to have the behaviour a ko.computed has with:

  • read
  • write

but I want to have an internal observable inside that ko.computed property, something like:

this.a = ko.computed(function() {
    value: ko.observable(null),

    read: function() {
        return value;
    },

    write: function(newValue) { 
        if (newValue) {
            value(newValue);
        }
    }
});

Is this possible in some manner? The above doesn't work.

3

There are 3 best solutions below

2
On BEST ANSWER

It looks like what you want is an observable that doesn't allow assignments that fail validation. You can do that with an extender on an ordinary observable:

ko.extenders.constrain = function (target, option) {
    var lastValidValue = target();
    target.subscribe(function (newValue) {
        if (option(newValue)) {
            lastValidValue = newValue;
        } else {
            target(lastValidValue);
        }
    });
    return target;
};

Call it like so to prevent assignment of falsy values:

var notFalsy = ko.observable("Bob").extend({
    constrain: function (newValue) {
        return newValue;
    }
});

Update: It's possibly significant that the above method does allow the assignment before correcting it. If it is important that the observable never take an invalid value, a computed (structured much like Anders' solution) can be used.

function constrainedObservable(constraint) {
    var value = ko.observable();
    return ko.computed({
        read: value,
        write: function (newValue) {
            if (constraint(newValue)) {
                value(newValue);
            }
        }
    });
}

Demo http://jsfiddle.net/o641v37L/1/ (uncomment the assignment to use the computed)

4
On

You can just do,

http://jsfiddle.net/b5stgh3L/

var myComputed = function() {
    var value = ko.observable();

    return ko.computed({         
        read: function() {
            return value();
        },
        write: function(newValue) { 
            if (newValue) {
                value(newValue);
            }
        }
    })    
};

var vm = function() {
    this.my = myComputed();
};

var instance = new vm();

instance.my("value");
instance.my(null);

console.log(instance.my());

You can also extend the computed using prototype, like ko use it internaly

function setPrototypeOf(obj, proto) {
    obj.__proto__ = proto;
    return obj;
} 

Used liked (ko.utils.setPrototypeOf is not exported use above code)

ko.utils.setPrototypeOf(ko.observableArray['fn'], ko.observable['fn']);

My old answer found here can probaly help you how to use the proto approuch

How can I create a typed collection class in Knockout mapping?

http://jsfiddle.net/xop2go2z/

2
On

It's not exactly a duplicate of that question answered over here https://stackoverflow.com/a/30506199/18771, but it's very close.

Bottom line is, no, you can't practically have a computed in your prototype chain because that computed would be shared between all your viewmodel instances.

If sharing a single computed between mutiple object instances is what you intend to do, go ahead.

If you want to create some kind of class hierarchy but still want to that your your individual viewmodel instances have their own individual observables, then you can't do it through the prototype.


Background: JavaScript does neither have classes (abstract object descriptions) nor class-based inheritance (hierarchical abstract object descriptions). JavaScript has objects (produced by constructor functions) and the prototype chain (a singly linked list of objects). Everything in the prototype chain is physically shared between all instances created by an object constructor.