Polymer, adding new item in nested Array in not getting rendered by dom-repeat

63 views Asked by At

I have a nested array and I need to add new subject for multiple persons, but for keeping it simple I will add new subject to Jon only.

[
      {
        gender: "male",
        persons: [
          {
            name: "Jon",
            subjects: [
              { subName: "maths", marks: "45" },
              { subName: "phy", marks: "47" },
            ],
          },
        ],
      },
      {
        gender: "female",
        persons: [
          {
            name: "Amily",
            subjects: [{ subName: "bio", marks: "43" }],
          },
        ],
      },
    ]

I will add subject on button click.

addSubject() {
    /* This doesn't work */
    let newData = [...this.data];
    newData[0].persons[0].subjects.push({ subName: "chem", marks: "50" });
    this.data = newData;
}

It works if I use this.push("data.0.persons.0.subjects", { subName: "chem", marks: "50" }); but if I need to add subjects to multiple persons,this method will update property every time. Above could accumulate all changes in temporary array only update once.

Render

<template is="dom-repeat" items="[[data]]">
  <div>Gender: [[item.gender]]</div>
  <template is="dom-repeat" items="[[item.persons]]">
    <div>Name: [[item.name]]</div>
    <template is="dom-repeat" items="[[item.subjects]]">
        <div>Subject: [[item.subName]], [[item.marks]]</div>
    </template>
  </template>
</template>

If I try rendering simpler data, then its working fine

<template is="dom-repeat" items="[[simpleData]]">
   <div>Subject: [[item.subName]], [[item.marks]]</div>
</template>

static get properties() {
  return {
     simpleData: {
        type: Array,
        notify: true,
        value: () => [
          { subName: "math", marks: "11" },
          { subName: "phy", marks: "22" },
        ],
     },
}
addSubjectInSimple() {
    let newData = [...this.simpleData];
    newData.push({ subName: "chem", marks: "50" });
    this.simpleData = newData;
}

2

There are 2 answers

2
Tanner Stern On

Try adding the mutable-data attribute on your dom-repeat. This tells polymer to look out for deep changes any time the object changes. (You still need to call this.set or this.notifyPathto trigger the dom update.)

Polymer tries very hard (sometimes too hard) to not update the dom when it doesn't think it needs to, which is why it does dirty checking on dom-repeats. You are using a more modern approach when you create your newData object and change it, which modern browsers can usually handle, but Polymer needs "permission" to use that approach.

Note: Object spread ... is doing a shallow copy of your object, not a deep clone. (See this answer for more on that.)

0
Metal Paw On

You should be able to do the following for the line this.data = newData;: this.set("data", newData);.

It could still fail to update based on Tanner Stern's response that you are working on a shallow copy as it is technically still the same object and Polymer may see it as not being updated. You can quickly clone the array with: let newData = JSON.parse(JSON.stringify(this.data));

This should force Polymer to set the entire array to the new values as well as notify the dom that it has been changed. Usually (in my personal experience in the past 5 years), Polymer doesn't like to acknowledge notifications on anything further in than one child access, eg. obj.obj but not obj.obj.obj or further down, so I've found that the best is to set the entire array/object with the Polymer set function.