bsod_
bsod_

Reputation: 941

Knockout add to array of an array

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 -

enter image description here

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)

Upvotes: 5

Views: 179

Answers (1)

MKougiouris
MKougiouris

Reputation: 2861

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())

Upvotes: 2

Related Questions