Jeremy B.
Jeremy B.

Reputation: 11

Vue.js 2 - Array change detection

Here's a simplified version of my code :

    <template>

        /* ---------------------------------------------------------- 
         *  Displays a list of templates, @click, select the template
        /* ----------------------------------------------------------
        <ul>
            <li 
                v-for="form in forms.forms" 
                @click="selectTemplate(form)" 
                :key="form.id" 
                :class="{selected: templateSelected == form}">
                <h4>{{ form.name }}</h4>
                <p>{{ form.description }}</p>
            </li>
        </ul>

        /* -------------------------------------------------------- 
         *  Displays the "Editable fields" of the selected template
        /* --------------------------------------------------------
        
        <div class="form-group" v-for="(editableField, index) in editableFields" :key="editableField.id">                                       
            <input 
                    type="text" 
                    class="appfield appfield-block data-to-document" 
                    :id="'item_'+index" 
                    :name="editableField.tag" 
                    v-model="editableField.value">
        </div>                    
    </template>
            
    <script> 
        export default {                        
            data: function () {
                return {
                    editableFields: [],
                }
            },
            methods: {
                selectTemplate: function (form) {

                    /* ------------------ 
                    *  My problem is here
                    */ ------------------

                    for (let i = 0; i < form.editable_fields.length; i++) {                                         
                        this.editableFields.push(form.editable_fields[i]);              
                    }
                }
            }
        }
    </script>

Basically I want to update the array EditableFields each time the user clicks on a template. My problem is that Vuejs does not update the display because the detection is not triggered. I've read the documentation here which advise to either $set the array or use Array instance methods only such as splice and push.

The code above (with push) works but the array is never emptied and therefore, "editable fields" keep pilling up, which is not a behavior I desire.

In order to empty the array before filling it again with fresh data, I tried several things with no luck :

this.editableFields.splice(0, this.editableFields.length);

for (let i = 0; i < form.editable_fields.length; i++) {
    this.editableFields.push(form.editable_fields[i]);                   
}

==> Does not update the display

for (let i = 0; i < form.editable_fields.length; i++) {                 
    this.$set(this.editableFields, i, form.editable_fields[i]);
}

==> Does not update the display

this.editableFields = form.editable_fields;

==> Does not update the display

Something I haven't tried yet is setting a whole new array with the fresh data but I can't understand how I can put that in place since I want the user to be able to click (and change the template selection) more than once.

I banged my head on that problem for a few hours now, I'd appreciate any help. Thank you in advance :) !

Upvotes: 1

Views: 3434

Answers (2)

FitzFish
FitzFish

Reputation: 8629

I've got no problem using splice + push. The reactivity should be triggered normally as described in the link you provided.

See my code sample:

new Vue({
  el: '#app',
  data: function() {
    return {
      forms: {
        forms: [{
            id: 'form1',
            editable_fields: [{
                id: 'form1_field1',
                value: 'form1_field1_value'
              },
              {
                id: 'form1_field2',
                value: 'form1_field2_value'
              }
            ]
          },
          {
            id: 'form2',
            editable_fields: [{
                id: 'form2_field1',
                value: 'form2_field1_value'
              },
              {
                id: 'form2_field2',
                value: 'form2_field2_value'
              }
            ]
          }
        ]
      },
      editableFields: []
    }
  },
  methods: {
    selectTemplate(form) {
      this.editableFields.splice(0, this.editableFields.length);
      for (let i = 0; i < form.editable_fields.length; i++) {
        this.editableFields.push(form.editable_fields[i]);
      }
    }
  }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.3.4/vue.min.js"></script>
<div id="app">
    <ul>
        <li v-for="form in forms.forms" 
            @click="selectTemplate(form)" 
            :key="form.id">
            <h4>{{ form.id }}</h4>
        </li>
    </ul>

    <div class="form-group"
        v-for="(editableField, index) in editableFields"
        :key="editableField.id">
            {{ editableField.id }}:
        <input type="text" v-model="editableField.value">
    </div>
</div>

Upvotes: 1

Jeremy B.
Jeremy B.

Reputation: 11

Problem solved... Another remote part of the code was in fact, causing the problem.

For future reference, this solution is the correct one :

this.editableFields.splice(0, this.editableFields.length);

for (let i = 0; i < form.editable_fields.length; i++) {
    this.editableFields.push(form.editable_fields[i]);                   
}

Using only Array instance methods is the way to go with Vuejs.

Upvotes: 0

Related Questions