简体   繁体   中英

How to mutate the array which has been computed based on Vue "prop"?

From the Vue documentation:

The prop is passed in as a raw value that needs to be transformed. In this case, it's best to define a computed property using the prop's value.

Source

What if this "prop" is array of the objects? I tried to transform it to computed array according documentation. But how to mutate in then?

In below example, component accepts array of items Array<{ ID: string, lettering: string; }> Array<{ ID: string, lettering: string; }> . Component renders buttons for each item.

  1. When we click the button, it must change the color.
  2. If we click same button again, it must return to initial state.

在此处输入图像描述

Fiddle

<template>
  <div>
    <button  
      v-for="(item, index) in selectableItems"
      :key="`BUTTON-${item.ID}`"
      :class="{ selected: item.selected }"
       @click="() => {onButtonClicked(item, index) }"
    > {{ item.lettering }}
    </button>
  </div>  
</template>

I compute the selectableItems from items . selectableItems has property selected in addition to item 's properties.

export default {
  name: "HelloWorld",
  props: {
    items: Array
  },
  computed: {
    selectableItems: function () {
      const selectableItems = [];
      this.items.forEach((item) => {
        selectableItems.push({
          ID: item.ID,
          lettering: item.lettering,
          selected: false
        });
      });
      return selectableItems;
    }
  },
  methods: {
    onButtonClicked: function(item, index) {
      if (item.selected) {
        this.selectableItems[index].selected = false;
      } else {
        this.selectableItems[index].selected = true;
      }
      console.log(this.selectableItems);
    }
  }
};

Currently, Vue dues not re-render buttons.

I know that the mutate the getter is improper usage. But how I should to mutate the array?

Forbidden solution

Passing the { ID: string, lettering: string; selected: boolean; } { ID: string, lettering: string; selected: boolean; } { ID: string, lettering: string; selected: boolean; } via props instead of { ID: string, lettering: string; } { ID: string, lettering: string; } is not allowed. { ID: string, lettering: string; } { ID: string, lettering: string; } is pure model, and it must not know about UI. selected: boolean; is for UI only.

Here's a quick example in a jsfiddle https://jsfiddle.net/hgvajn5t/

As I'm short on time I've skipped creating two components and actually reach down the data as props.

Key is to define your selected buttons in your data

data () {
    return {
      selectedItems: []
    }
},

Second part is to store which items are selected and which aren't

onButtonClicked: function(item){
  let index = this.selectedItems.indexOf(item.ID)
  if (index > -1) {
    this.selectedItems.splice(index, 1)
  } else {
    this.selectedItems.push(item.ID)
  }
}

The onButtonClicked recives the item as input and checks if it exists in your selectedItems - if so it will be removed, otherwise it will be added

Final part is to change your binding to set the selected class

:class="{ selected: selectedItems.indexOf(item.ID) > -1 }"

Here again - simply check if it is part of the selectItems

You can then remove your computed property and simply use the props to loop through the elements.

IMPORTANT: This will only work if your item ID values are unique.

Some general thoughts on this: The concept of this can be a bit hard to wrap your head around when you're new to vue, as it seems to be very complicated for simple select states. This might be true for the solution I gave - but overall it highly depends on how you structure your UI in general. The main reason for all this is that you should not manipulate props directly. Using computed props to enhance them is good - manipulating computed props is just as bad. If you need the select state outside your current component you will need to think about using events to emit the click on a button to the parent (where the data comes from) and change the select state directly there. It will then be passed down via props directly. This is the more "natural" way as you manipulate the single source of truth instead of holding additional information about the items in another screen.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM