5

I have a fairly simple view model to hold an array of data and take a string which I want to use to filter the data.

I have some very simple mark-up to render it like this:

<section class="task-list">
    <ul data-bind="foreach:filteredRecords">
        <li>
            <label>Task name:</label>
            <span data-bind="text:TaskName"></span>              
        </li>
    </ul>
</section>

and I've set up the model as follows;

var viewModel = {

    searchText : ko.observable(''),
    taskData : ko.observableArray([]),

    searchData : function () {

        // use ko.utils.arrayFilter to limit records by searchText()
        return this.taskData();
    },

    filterRecords: ko.computed(this.searchData, this).extend({ throttle: 500 }),
};

ko.applyBindings(viewModel, $('.task-list')[0]); 

What I get is

Uncaught Error: Pass a function that returns the value of the ko.computed 

moving the function back inside the ko:computed like this:

 filteredRecords: ko.computed(function () {
        return this.taskData();
    },this).extend({ throttle: 500 })

just gives me this error insread:

Uncaught TypeError: Object [object global] has no method 'taskData' 

So tried to define another property to use instead:

var viewModel = {

    _self : this,

    searchText : ko.observable(''),
    taskData : ko.observableArray([]),

    filteredRecords: ko.computed(function () {
        return this.taskData();
    },this).extend({ throttle: 500 })
};

but this now gives the error

Uncaught ReferenceError: _self is not defined 

finally I tried doing this:

var viewModel = {

    searchText : ko.observable(''),
    taskData : ko.observableArray([]),

    filteredRecords: ko.computed(function() {
        var me = this;
        return function () {
            return me.taskData();
        }
    },this).extend({ throttle: 500 })
};

Now this, does not throw any of the previous errors but also does not yield any results in the HTML either...

I've set up a Fiddle at

How do I correctly apply a ko.computed to an object so that I can use the observable and observableArray that I have inside the object?

Code Uniquely
  • 6,356
  • 4
  • 30
  • 40

1 Answers1

7

While you are defining an object literal, you are not able to reference any of the properties of it until after it is created. The value of this will not be your new object.

You can either use a constructor function like:

var ViewModel = function() {
   this.searchText = ko.observableArray();
   this.taskData = ko.observableArray([]);

   this.filteredRecords = ko.computed(function() {
        //filter logic here, can use `this` and reference searchText/taskData
    },this).extend({ throttle: 500 })
};

then, create a new instance of it like:

ko.applyBindings(new ViewModel(), $('.task-list')[0]); 

Otherwise, you could create your viewModel first before adding the computed. Then do:

viewModel.filteredRecords = ko.computed(function() {
  //filter logic here, can use `this` and reference searchText/taskData
}, viewModel);

This answer might help too: Difference between knockout View Models declared as object literals vs functions

Community
  • 1
  • 1
RP Niemeyer
  • 114,592
  • 18
  • 291
  • 211
  • ko.computed() triggered only one time .After that its not at all triggering...How to use with object literal? – ManirajSS Sep 30 '14 at 15:36