19

I have been using Knockout.js for a lot of projects lately, and I am writing a lot of repetitive code. I would like to be able to define a BaseViewModel class and have my page-specific ViewModels inherit from it. I am a bit confused about how to do this is Javascript. Here is my basic BaseViewModel:

(function (ko, undefined) {
    ko.BaseViewModel = function () {
        var self = this;
        self.items = ko.observable([]);
        self.newItem = {};
        self.dirtyItems = ko.computed(function () {
            return self.items().filter(function (item) {
                return item.dirtyFlag.isDirty();
            });
        });
        self.isDirty = ko.computed(function () {
            return self.dirtyItems().length > 0;
        });
        self.load = function () { }
    };
}(ko));

I would like to be able to list signatures for methods like load in the BaseViewModel and then give them definitions in the inheriting ViewModel. Is any of this possible? I have found a few solutions online but they all rely on defining functions/classes to make the inheritance work.

6
  • why are you passing undefined into the function? Commented May 15, 2013 at 16:03
  • programmers.stackexchange.com/a/158376/90240 Commented May 15, 2013 at 16:07
  • Huh, never seen that before. I've also never run into the case where not doing that causes a bug, and he doesn't explain what that case is. Commented May 15, 2013 at 16:11
  • 4
    It is a defensive technique if you are writing code to be consumed by others. It is possible to re-define the undefined symbol. Declaring a function parameter undefined and then not passing any value for that argument is a technique to capture the actual undefined value even if someone has redefined the symbol. As it happens, it also lets your JS minimizer minimize your usages of undefined since it is now just a local variable. Commented May 15, 2013 at 16:16
  • 1
    stackoverflow.com/questions/8783510/… Commented May 15, 2013 at 16:16

2 Answers 2

18

Since your BaseViewModel is just adding all of the properties/methods to this (and not using prototype) then it is pretty easy:

In your new view models, just call BaseViewModel:

var MyVM = function () {
    var self = this;
    ko.BaseViewModel.call(self);

    self.somethingElse = ko.observable();
    self.itemCount = ko.computed(function() { return self.items().length; });
    self.items([1, 2, 3]); 
};


// ...
var vm = new MyVM();
Sign up to request clarification or add additional context in comments.

6 Comments

Well that is way easier than I was expecting. Now I just need to figure out how that works!
The ko.BaseViewModel.call(self) line is the same as doing :base() in c#, right?
@theyshookhands Yes, it is the same as a :base() call. The ViewModel.prototype = new BaseViewModel() is the same as Class : SubClass in c#.
@theyshookhands - read about Function.call. It allows you to call any function and supply the this value to use. In this example, BaseViewModel is just being used as a helper function to initialize your viewmodel. We are not really doing inheritance. Things gets slightly more interesting when you start using prototype, which is JavaScript's mechanism for doing inheritance.
@Homer The extend technique allocates a new object (BaseViewModel), then copies the properties over to your subclass object and throws away the BaseViewModel object. So a wasted allocation. But more importantly, if BaseViewModel defines some functions or computeds that reference self, they will be referring to the thrown away object instead of your subclass instance, so the extend technique can actually lead to some subtle and hard to identify bugs.
|
10

Javascript inheritance is done in two pieces. The first is in the constructor, and the second is on the prototype (which you aren't using, so you could skip).

var ViewModel = function(data) {
    BaseViewModel.call(this);
};
//you only need to do this if you are adding prototype properties
ViewModel.prototype = new BaseViewModel();

To your last point, about overriding load, its no different that putting a load function on your viewmodel normally. Javascript allows you to override any objects properties with anything, there are no special steps here.

Here is a fiddle demonstrating the inheritance.

2 Comments

If you only need to worry about supporting modern browsers, you can replace that last line with Viewmodel.prototype = Object.create(BaseViewModel.prototype); and avoid some thorny issues with any undesired effects of calling BaseViewModel in this way.
And it's easy to shim Object.create in older browsers: var objCreate = Object.create || function(o) { function F(){}; F.prototype = o; return new F(); };

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.