Reputation: 55210
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?
Upvotes: 0
Views: 475
Reputation: 8987
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);
};
I hope it helps.
Upvotes: 3
Reputation: 14995
I think this is what you are trying to do -
function jobPostEstimateTaskModel (task) {
var self = this;
// Make some properties of this task observable
self.name = ko.observable(task.name);
// Make a computed for each task
self.estLaborSubTotal = ko.computed(function () {
return (isNaN(self.EstHr)? 0: +self.EstHr) * (isNaN(self.TaskPerHourCost)? 0: +self.TaskPerHourCost);
});
}
var JobPostViewModel = function(){
var self = this;
self.estimateTasks = ko.observableArray();
$.each(estimateTasks, function (index, item) {
self.estimateTasks.push(new jobPostEstimateTaskModel(item));
}
};
This will iterate through the estimateTasks plain old JavaScript array that you have created and add a new jobPostEstimateTaskModel object for each task in the array. It will create observable properties on the task such as name in this example, and then create a computed for each item in the array.
By calling it the way you were above you are simply creating a computed onto the observableArray, and not doing what you intended.
One of the confusion points is that your ViewModel has an object that is an observableArray called estimateTasks but you also have a variable at the context 'above' it named the exact same thing. Consider renaming your non-observable to something like estimateTasksJson to clarify.
Edit
Another way to approach this is to use prototyping - you would still need to instantiate an object like jobPostEstimateTaskModel or whatever you want to call it but you could extend an observable onto each instance without having to iterate through that code each time and add it in the model.
Upvotes: 2
Reputation: 7475
Your problem is that inside foreach
current binding content is current array element.
And this element does not have estLaborSubTotal
property.
You can use <input type="text" data-bind="value: $root.estimateTasks().estLaborSubTotal" disabled />
instead.
Or you can change your viewmodel:
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.estLaborSubTotal = ko.computed(function () {
return (isNaN(self.EstHr)? 0: +self.EstHr) * (isNaN(self.TaskPerHourCost)? 0: +self.TaskPerHourCost);
});
};
And use <input type="text" data-bind="value: $root.estLaborSubTotal" disabled />
.
Upvotes: 1