5

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.

1 Answer 1

4

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]);
Sign up to request clarification or add additional context in comments.

2 Comments

What you say makes sense, but it's still not working. Changed my moveUp function to moveUp: function(key) { this.fields[0].value.splice( key - 1, 2, this.fields[0].value[key], this.fields[0].value[key - 1] ); },
@IllesPeter Perhaps the array at this.fields[0].value is not reactive? The easiest way to check is to log it to the console, console.log(this.fields[0].value), and see whether it has the __ob__ property set to an Observer. Vue adds that property when it makes an array reactive. You can also dig into the __proto__, an array that is reactive should have various methods overridden with a function called mutator. If that array is not reactive then you'll need to find where it is being introduced and ensure that $set is being used where required.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.