0

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 Answers2

0

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

Tanner Stern
  • 128
  • 1
  • 8
  • Do I need to add ```mutable-data``` to both the ```dom-repeat``` or which one? Also if top ```dom-repeat``` is in Parent component and inner one is in child component, do I need to extend both parent and child classes with ```Polymer.MutableData``` mixin – Abhigyan Kumar Feb 09 '23 at 17:14
  • I'm not actually sure. I would try it both ways to see, but I think you will at least need it on the top-level `dom-repeat`. That's the one that should be directly observing the object you are re-setting. Sorry I cannot be more exact, even internally we usually have to do a lot of fiddling to get it to work just right. – Tanner Stern Feb 09 '23 at 17:40
0

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.

Metal Paw
  • 112
  • 1
  • 13