Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Vue array updates in developer tools but doesn't re-render

I have a fairly complicated object with nested components. It looks sort of like this (stripped down for easier reading):

<script type="text/x-template" id="fieldset-template">
    <div class="fieldset">
        <div class="fieldset-repetition" v-for="(repetition, key) in repetitions">
            <div class="field-list">
                <field v-for="field in fields" v-bind:key="field.key" v-bind:fieldset="field.fieldset" v-bind:fieldset-key="key" v-bind:field-data="field"></field>
            </div>
            <div class="repetition-meta">
                <a class="move-repetition" v-on:click="moveUp(key)">Up</a>
            </div>
        </div>
    </div>
</script>

<script type="text/x-template" id="field-template">
    <div class="field">
        <div class="form-group">
            <label class="control-label" v-html="name"></label>
            <div class="field-repetition" v-for="(repetition, key) in repetitions">
                <div class="field-text">
                    <input class="form-control" v-model="values[key]" />
                </div>
            </div>
        </div>
    </div>
</script>



<script>
    Vue.component('set', {
        components: {
            field: {
                created: function() {
                    // populate with data
                    this.populateData();
                },
                data: function() {
                    return {
                        repetitions: [],
                        values: [],
                    }
                },
                methods: {
                    populateData: function() {
                        this.repetitions = this.fieldData.repetitions;

                        this.repetitions.forEach(function(repetition, key) {
                            this.values = this.fieldData.value[this.fieldsetKey];
                        }.bind(this));
                    },
                    repeatField: function() {
                        var field = Object.clone(this);
                        delete field.repetitions;
                        this.repetitions.push(field);
                        if (this.widget != 'checkbox') {
                            this.values.push(this.fieldData.default);
                        }
                        else {
                            this.values.push([this.fieldData.default]);
                        }
                    },
                },
                props: {
                    fieldData: {
                        type: Object
                    },
                    fieldset: {
                        type: Object
                    },
                    fieldsetKey: {
                        type: Number
                    }
                },
                template: '#field-template'
            }
        },
        data: function() {
            return {
                fields: [FieldObject1, FieldObject2, FieldObject3],
                repetitions: [RepetitionObject1, RepetitionObject2,  RepetitionObject3,  RepetitionObject4,  RepetitionObject5],
            }
        },
        methods: {
            moveUp: function(key) {
                var field = this.fields[0];
                var value = field.value[key];
                field.value[key] = field.value[key - 1];
                field.value[key - 1] = value;
                this.$set(this.fields, 0, field);
            }
        },
        template: '#fieldset-template'
    });
</script>

Whenever the moveUp method runs, it updates the fields object, but the field component doesn't re-render.

I have suspicion it is because of the secondary (outer) for cycle for the repetitions, but I couldn't figure a way around it.

like image 706
Illes Peter Avatar asked Oct 15 '22 07:10

Illes Peter


1 Answers

this.$set(this.fields, 0, field); won't do anything as this.fields[0] is already equal to field.

Assuming field.value is an array, it's this step that's making a non-reactive change:

field.value[key] = field.value[key - 1];
field.value[key - 1] = value;

See https://v2.vuejs.org/v2/guide/list.html#Caveats

You could write it as:

this.$set(field.value, key, field.value[key - 1]);
this.$set(field.value, key - 1, value);

Or use splice:

field.value.splice(key - 1, 2, field.value[key], field.value[key - 1]);
like image 113
skirtle Avatar answered Nov 15 '22 06:11

skirtle