Search code examples
knockout.jsknockout-mapping-plugin

Why does adding a computed to an observableArray fail?


I think something is very wrong here. I would like to add a ko.computed to a ko.observableArray like this.

Before that my model for clarity

//var job = @Html.Raw(Json.Encode(Model.job));
//var customer = @Html.Raw(Json.Encode(Model.customer));
//var estimateMaterials = @Html.Raw(Json.Encode(Model.estimateMaterials));
//var estimate = @Html.Raw(Json.Encode(Model.estimate));
var estimateTasks = @Html.Raw(Json.Encode(Model.Tasks));

var JobPostViewModel = function(){
    var self = this;
    //self.customer = ko.observable(customer);
    //self.job = ko.observable(job);
    //self.estimateMaterials = ko.observableArray(estimateMaterials);
    //self.estimate = ko.observable(estimate);
    self.estimateTasks = ko.observableArray(estimateTasks);
    self.estimateTasks().estLaborSubTotal = ko.computed(function () {
        return (isNaN(self.EstHr)? 0: +self.EstHr) * (isNaN(self.TaskPerHourCost)? 0: +self.TaskPerHourCost);
    });
};
var model = new JobPostViewModel();
ko.applyBindings(model,  document.getElementById("my_job_form"));

So this is what my model bind is. my_job_form is data-bind="with:jobs" and I am populating a table inside the form that is bound to estimateTasks. The markup is

<tbody data-bind="foreach: $root.estimateTasks">
    <tr>
        <td>
            <input type="text" data-bind="value: EstHr" />
            <input type="hidden" data-bind="value: TaskPerHourCost" />
        </td>
        <td>
            <input type="text" data-bind="value: estLaborSubTotal" disabled />
        </td>
    </tr>
</tbody>

On bind, I get the error

ReferenceError: estLaborSubTotal is not defined

What am I doing wrong here?


Solution

  • You are adding an computed to an array. This array is just the result of the evaluation of estimateTasks observableArray();

    If I understand what you are trying to do.

    You better do it this way. This will add an computed named estLaborSubTotal to item.

    var JobPostViewModel = function(){
        var self = this;
        ko.utils.arrayForEach(estimateTasks, function(et) {
            et.estLaborSubTotal  = ko.computed({
                read: function(){
                    var estHr = this.EstHr();
                    var taskPerHourCost =  this.TaskPerHourCost();
                    if(estHr === null)
                        return 0;
                    return estHr * taskPerHourCost; 
                },
                owner : et // specify the "this" during evaluation 
            });
        });
    
        self.estimateTasks = ko.observableArray(estimateTasks);
    };
    

    See fiddle

    I hope it helps.