3

I am using a computed value to dynamically filter an array ("orders").

The computed .filter() function allows the user to dynamically search by order number, name or reference:

data() {
  return {
    orders: [],
    search: ""  // search string from a text input    
  };
},

computed: {
  filtered: 
    return this.orders.filter(order => {
      const s =
        order.order_number + order.reference + order.name;
      const su = s.toUpperCase();
      return su.match(this.search.toUpperCase());
    });
  }

I am using a v-for loop to render the search results as follows:

 <tbody v-for="(order, index) in filtered" :key="order.id">              
   <tr>       
     <td @click="add_events(order, index)>{{order.order_number}}</td>
     <td>{{order.reference}}</td>
     <td>{{order.name}}</td>
      ...
    </tr>
  </tbody>

I want to use the @click to target a specific component (an object) in the filtered array and use $set to append a value ("objEvents") to that object:

methods: {
  add_events (order, index) {
    const objEvents= [ external data from an API ]      
    this.$set(this.orders[index], "events", objEvents)          
  }
}

However the index of the component in the filtered array ("filtered") is not the same as its index in the original array ("orders") and so the add_events method targets the wrong component.

Can I use key to target the correct component? or is there some other way to identify the target component in the filtered array?

2 Answers 2

2

There's no need to track index. filtered is just an array of references to the original objects in orders, so you could modify the order iterator in add_events() to achieve the desired effect:

this.$set(order, 'events', objEvents);

new Vue({
  el: '#app',
  data() {
    return {
      orders: [
        {id: 1, order_number: 111, name: 'John', reference: 'R111'},
        {id: 2, order_number: 222, name: 'Bob', reference: 'R222'},
        {id: 3, order_number: 333, name: 'Bob', reference: 'R333'},
      ],
      search: ''
    };
  },
  computed: {
    filtered() {
      return this.orders.filter(order => {
        const s =
              order.order_number + order.reference + order.name;
        const su = s.toUpperCase();
        return su.match(this.search.toUpperCase());
      });
    }
  },
  methods: {
    add_events(order, index) {
      const objEvents = [
        {id: 1, name: 'Event 1'},
        {id: 2, name: 'Event 2'},
        {id: 3, name: 'Event 3'}
      ];
      this.$set(order, "events", objEvents);
    }
  }
})
<script src="https://unpkg.com/[email protected]"></script>

<div id="app">
  <input type="text" v-model="search" placeholder="Search">
  <table>
    <tbody v-for="(order, index) in filtered" :key="order.id">              
      <tr>
        <td @click="add_events(order, index)">{{order.order_number}}</td>
        <td>{{order.reference}}</td>
        <td>{{order.name}}</td>
        <td>{{order.events}}</td>
      </tr>
    </tbody>
  </table>
  
  <pre>{{orders}}</pre>
</div>

Sign up to request clarification or add additional context in comments.

Comments

1

You could map the original array and add the an origIndex property to each item as follows :

    computed:{
        filtered(){
           let mapped= this.orders.map((item,i)=>{
                             let tmp=item;
                              tmp.origIndex=i;
                             return tmp;
                            });
             return this.mapped.filter(order => {
                    const s =
                      order.order_number + order.reference + order.name;
                       const su = s.toUpperCase();
                    return su.match(this.search.toUpperCase());
             });
            }
        }//end computed

In your template use the origIndex property instead of index

           <tbody v-for="(order, index) in filtered" :key="order.id">              
              <tr>       
                <td @click="add_events(order, order.origIndex)>{{order.order_number}}</td>
               <td>{{order.reference}}</td>
                <td>{{order.name}}</td>
                 ...
               </tr>
            </tbody>

3 Comments

This will work - thank you. But I had hoped there was some way to co-opt the key field that Vue uses internally to target individual components.
@AlexWebster sorry, probably i had misunderstand your use case
No - I think you understood my question perfectly, but I was on the wrong track. Accepted answer from @tony19 shows that index is not actually required for this purpose. Thanks again.

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.