mutex
mutex

Reputation: 7728

Knockout computed observable in hottowel SPA default viewmodel

I'm trying to create a simple knockout computed observable following the viewmodal pattern in the Hottowel SPA template. What is the best way to do this?

I originally had something like this:

define(['services/logger'], function (logger) {
    var vm = {
        activate: activate,
        title: 'Home View',
        testField: ko.observable("This is a test"),
        testComputedField: ko.computed(getComputed, this)
    };

    return vm;

    //#region Internal Methods
    function getComputed() {
        return "Computed: " + this.testField();
    }

    function activate() {
        logger.log('Home View Activated', null, 'home', true);
        return true;
    }
    //#endregion
});

but this results in an error, though I'm not 100% sure why

TypeError: this.testField is not a function

So with a bit of trial and error I got to this:

define(['services/logger'], function (logger) {
    var vm = {
        activate: activate,
        title: 'Home View',
        testField: ko.observable("This is a test")
    };

    vm.testComputedField = ko.computed(getComputed, vm);

    return vm;

    //#region Internal Methods
    function getComputed() {
        return "Computed: " + this.testField();
    }

    function activate() {
        logger.log('Home View Activated', null, 'home', true);
        return true;
    }
    //#endregion
});

But I'm not sure this is a very pretty way of doing this; I'm obviously not grokking the module pattern used for viewmodels in HotTowel very well. So my questions are:

Why doesn't my original method work? Are there any better or alternate ways to define\structure the viewmodel than my second method?

Upvotes: 1

Views: 1354

Answers (2)

mikekidder
mikekidder

Reputation: 923

Mutex, this is the general pattern I am using, it is working well for me.

I use function expressions and local scoped variables for the KO bindings, and function declarations for the Durandal specific methods (activated, viewAttached, etc.). The vm object needs to be after the function expressions. Eliminates the need to use 'this' everywhere.

define(['services/logger'], function (logger) {

    var testField = ko.observable("This is a test");

    var getComputed = ko.computed(function () {
        return "Computed: " + testField();
    });

    function activate() {
        logger.log('Home View Activated', null, 'home', true);
        return true;
    }

    var vm = {
        activate: activate,
        title: 'Home View',
        testField: testField,
        testComputedField: getComputed
    };

    return vm;
});

NOTE: This is for the HotTowel SPA template

Upvotes: 2

Evan Larsen
Evan Larsen

Reputation: 9965

Your original method:

var vm = {
   activate: activate,
   title: 'Home View',
   testField: ko.observable("This is a test"),
   testComputedField: ko.computed(getComputed, this)
};

When you pass this to the computed.. this does not point to your vm. Instead pass in the vm itself:

var vm = {
   activate: activate,
   title: 'Home View',
   testField: ko.observable("This is a test"),
   testComputedField: ko.computed(getComputed, vm)
};

EDIT ** I don't know why the above didn't work. According to the knockout documentation the second argument should set the this context to the method in the first argument. How about trying this:

function getComputed() {
   return "Computed: " + vm.testField();
}

Upvotes: 0

Related Questions