I am looking for a way to add an item to an array that belongs to an item within another array using knockout and knockout mapping.
I have the following, a Person which has an array of WorkItems that has an array of ActionPlans. Person > WorkItems > ActionPlans
Knockout code is as follows -
var PersonViewModel = function(data) {
var self = this;
ko.mapping.fromJS(data, trainingCourseItemMapping, self);
self.addWorkItem = function() {
var WorkItem = new WorkItemVM({
Id: null,
JobSkillsAndExpDdl: "",
JobSkillsAndExperience: "",
ActionPlans: ko.observableArray(),
PersonId: data.Id
})
self.WorkItems.push(WorkItem)
};
self.addActionPlan = function () {
var actionPlan = new ActionPlanVM({
Id: null,
priorityAreaStage: "",
goal: "",
action: "",
byWho: "",
byWhen: ""
WorkItemId: data.Id
});
self.ActionPlans.push(actionPlan);
};
}
Array mapping
var trainingCourseItemMapping = {
'WorkItem': {
key: function(workitem) {
return ko.utils.unwrapObservable(workitem.Id);
},
create: function(options) {
return new WorkItemVM(options.data);
},
'ActionPlans': {
key: function (actionPlanItem) {
return ko.utils.unwrapObservable(actionPlanItem.id);
},
create: function (options) {
return new ActionPlanVM(options.data);
}
}
}
Array item mapping
var WorkItemVM = function(data) {
var self = this;
ko.mapping.fromJS(data, trainingCourseItemMapping, self);
}
var ActionPlanVM = function(data) {
var self = this;
ko.mapping.fromJS(data, {}, self);
}
And within my view i want to have the following (edited) -
<tbody data-bind="foreach: WorkItems">
//body table html here
</tbody>
<!--ko foreach: WorkItems-->
<tbody data-bind="foreach: ActionPlans">
//body table html here
</tbody>
<!--/ko-->
Error
The error i am currently getting is -
Unable to process binding "click: function(){return addActionPlan }"
How can i push an item to the "nested" action plan array of WorkItems? Thanks
Edit -
Image as requested -
Preceding this is a "add work item" button within the main Form. When Save is pressed the WorkItem is shown within a table row (all working fine)
One thing to note is that you have ActionPlans WITHIN WorkItems, so your binds should reflect that too:
<tbody data-bind="foreach: WorkItems">
//body table html here
<tbody data-bind="foreach: ActionPlans"> /*ActionPlans exists in this context*/
//body table html here
</tbody>
</tbody>
with your current HTML ActionPlans are not defined in their binding context This is where the specific error comes from, ActionPlans are not defined in a "sibling" context, they are properties of each WorkItem
EDIT: You might also try virtual elements, knockout's containerless syntax, allthough this is not advised in general ( bad performance relative to the rest of the framework, some problems may occur with minifier's removing comments etc)
<tbody data-bind="foreach: WorkItems">
//body table html here
</tbody>
<!-- ko foreach: WorkItems -->
<tbody data-bind="foreach: ActionPlans">
</tbody>
<!-- /ko -->
You best option thought is to restruct your VM!
EDIT 2: Try this in your personVM
self.addActionPlanToWorkItem = function (workItem) {
var actionPlan = new ActionPlanVM({
Id: null,
priorityAreaStage: "",
goal: "",
action: "",
byWho: "",
byWhen: ""
WorkItemId: workItem.Id
});
workItem.ActionPlans.push(actionPlan);
};
Call it from anywhere and pass in your current active WorkItem, it will add a new empty ActionPlan to your model.
Or you can maybe try out this way: Replace workItemVM with this
var WorkItemVM = function(data) {
var self = this;
ko.mapping.fromJS(data, trainingCourseItemMapping, self);
self.addActionPlan = function(actionPlanToAdd)
{
self.ActionPlans.push(actionPlanToAdd);
};
}
and call it on your workItems as
someWorkItemInstance.addActionPlan(new ActionPPlanVM())