Skip to content
Advertisement

How can I reactively remove an object from an array which is passed as a prop so that it is reflected in the DOM?

I have the following blade file:

@extends('layouts.app')

@section('content')
    <div class="container">
        <div class="row">
            <div class="col-xs-12 col-sm-12 col-md-12 col-lg-8 col-xl-7 mx-auto">
                <items :items="{{ $items  }}" />
            </div>
        </div>
    </div>
@endsection

where $items is a JSON array of objects pulled from the controller. Now, the items component looks like this:

<template>
    <div>
        <item v-for="(item, key) in computedItems" :key="key" :item="item"/>
    </div>
</template>

The computedItems property looks like:

computed: {
    computedItems() {
        // referring to the prop that is passed from the blade file
        let items = this.items;

        if (this.filterOptions.type !== 'Clear') {
            items = items.filter(i => i.status === this.filterOptions.type);
        }

        if (this.searchOptions.query.trim() !== '') {
            items = items.filter(
                i => i.name.toLowerCase().indexOf(this.searchOptions.query.toLowerCase()) !== -1
            );
        }

        if (this.sortOptions.type === 'asc') {
            items.sort((a, b) => a.id - b.id);
        } else if (this.sortOptions.type === 'desc') {
            items.sort((a, b) => b.id - a.id);
        }

        return items;
    }
}

In item sub-component you have the ability to delete items through a method as follows:

methods: {
    _delete() {
        const _confirm = confirm('Are you sure you want to delete this item?');

        if (!_confirm) {
            return;
        }

        axios.delete(`/items/${this.item.id}`).then(response => {
            this.$eventBus.$emit('deleted', { id: this.item.id })
        });
    }
}

The event gets picked up by the parent (items component) and is handled:

deleted(item) {
    const i = this.items.findIndex(_i => item.id === _i.id);

    this.items.splice(i, 1);
}

But for some reason, the DOM is not updating and the v-for is still displaying the deleted object, though, if I dump the prop, it shows that it has been amended. My suspicion is that Vue can’t tell that the object has been modified because it is part of props and not a data. I’ve tried the following:

  • Using Vue.delete instead of Array.splice
  • Using this.$delete instead of Array.splice
  • Using Array.splice on the computed prop instead of the prop
  • Setting watchers
  • Expanding the computed properties to accept a getter and setter
  • A whole lot of searching on the Vue forums

Now, I understand you can’t mutate a prop directly because that’s anti-pattern, so I used a computed prop, but still, my changes aren’t reflected in the DOM.

So, why is the DOM not updating?

Advertisement

Answer

Posting my comment as requested and I’ll list the key points of the topic:

  • One Way Data Flow: No prop mutation. If there’s a prop you need to mutate locally in a component, first clone it to data and mutate the clone.

  • Use Vuex instead of an event bus for state/event management

  • Vuex allows for global state which can be mutated via actions, in a controlled fashion, from within a component (or elsewhere). Data flows down from the store and emits up through mapped/called actions.

User contributions licensed under: CC BY-SA
2 People found this is helpful
Advertisement