In VueJS, v-for doesn't create discrete items when new data is assigned - vue.js

Currently, when I have a v-for loop like this:
<li v-for="record in records">
<my-vue-list-item :vue-title="record.title"></my-vue-list-item>
</li>
and I update the data driving the v-for (i.e. assign a new value to records), it doesn't actually create new list items based on the new data. It just updates some of the properties on the list item components components.
Here is a JSFiddle illustrating what I'm talking about. Try expanding one of the buttons (e.g. click "Expand Apple"), then click on "Set 2" to see that even when the list items switch, the component stays expanded.
What's the recommended way of getting around this behavior? I want each new list item (when the data is swapped out) to appear like new. (In the fiddle example, which I load a new set, it should not be already expanded.)

You need to let the Vue components know that the item is different. As far as they know, they're still rendering the same element index from the same list.
You can do this by specifying the key in your v-for iterator...
<li v-for="item in items" :key="item">
https://jsfiddle.net/ahxf44jk/21/

Related

Prevent list display filter from resetting v-list selection

Version info: Vuetify 2.6.3, Vue 2.6.14, Nuxt 2.15.8
I'm making a custom component that is supposed to be somewhat similar to v-autocomplete, except that it's rendered as bottom sheet. If user enters a display filter into v-text-field, the option list (v-list) is supposed to display only those options that match the filter.
In overall it works fine except one use case: let say the list has 5 items (aa, bb, cc, dd, ee) and user selected bb and cc from the list. Now, v-list-item-group's model selectedItems contains the 2 selected items bb and cc, perfect! However, when user enters b into display filter, the already selected item cc will be auto deleted from selectedItems. I can't tell if selectedItems change is caused by filter or by user selection. Is there a way to maintain the selection in model?
I'm considering a hack - if an item is selected, keep it in filteredChoices even if it does not match the filter. This behaviour is bearable but UX wise not as intuitive as the filter of v-autocomplete.
The simplified structure looks like the below:
<template>
<v-bottom-sheet scrollable>
<v-card>
<v-card-text>
<v-list>
<v-list-item-group
v-model="selectedItems"
:mandatory="!optional"
:multiple="multiple"
>
<v-list-item
v-for="item in filteredChoices"
:key="item.value"
:value="item"
>
</v-list-item>
</v-list-item-group>
</v-list>
</v-card-text>
<v-text-field
v-model="filterInput"
placeholder="filter choices..."
hide-details
></v-text-field>
</v-card>
</v-bottom-sheet>
</template>
<script>
...
filteredChoices() {
if (this.filterInput == null) {
return this.allItems
}
return this.allItems.filter((item) => {
return item.label
.toLocaleLowerCase()
.includes(String(this.filterInput).toLocaleLowerCase())
})
},
...
</script>
How I reach the solution:
I was quite new to front-end stuff. Previously, when I was learning how to implement v-model support for custom component, all web resources I came across say the same thing - bind inner component's value props to custom component's value props. However I just discovered that this is merely one of the ways rather than a must. With that new learning, more possibilities pop up in my mind and one of them lead me to below solution.
Solution:
Decouple the custom component value from bottomsheet's list
Bind the model of inner v-autocomplete to an array data, say internalValue. (this inner component is not included in question's template for simplification)
Bind the model of inner v-list-item-group (in bottomsheet) to a separate data, say bottomSheetSelections.
Update the custom component value based on user actions in bottomsheet
Add a watcher to bottomSheetSelections array:
if the array grows, it means the user has selected more item. We should push the additional item to internalValue.
if the array shrinks:
if the missing item is still there in filteredChoices, the removal is triggered by user de-selection. We should remove this item from internalValue.
else, we consider the removal is triggered by list filter. No action is needed.
Restore user selection in bottomsheet on clearing filter
Add a watcher for filteredChoices. Whenever the array grows, if the additional choice exist in internalValue, we should push it to bottomSheetSelections.
Summary
Strictly speaking, this doesn't solve the ask of question's title - the list selection in bottomsheet (bound to v-list-item-group) is still getting reset. However, at least we're able to restore it in bottomsheet on clearing filter.
More importantly, this solution achieved the objective mentioned in question details - retain the user selection value. We kept it in a separate data internalValue.
I haven't test this solution with long list of data. My guts feeling is that there could be more efficient solution. Please share it if you have a better solution.

build a v-checkbox list in Vue

I need to build a to-do list with check boxes but instead of using an array that pulls each items as if it was a separate row these are all separate columns. So the object looks like
[{"InterProgramatic":false,"IntraProgramatic":false,"MultiInstitution":false,"Accepted":true,"Rejected":false,"Cancer_Related":true}]
How can I build the Checkboxes using a v-for loop?
my array is simply
statusItems: []
If each item was in a separate row then I can assign a separate id, but is there an easier way to do this in Vue? To save the data I planned on just sending back to the Web API the statusItems object and parsing it there so I was hoping to bind it if possible.
I can use
v-for="(item, index) in statusItems" :key="index"
<v-checkbox
:key="index"
:label="item"
color="success"
v-bind:id="status"
v-model="statusItems[key].checked"
#change="statusChange(statusItems[key])"
>></v-checkbox>
to build the checkboxes but not sure how to bind it. Thanks for the help
Is that what your trying to do ?
https://codesandbox.io/s/vue-template-tkx60?fontsize=14
To bind the v-model, you need to use the array index or the object key.

get complete Vuetify item from v-autocomplete

I need to get the entire array element that makes up the Autocomplete item, I want the element so I can store it in Veux without store each item, such as first name, MI last name etc. I'm going to build upon another question that was very similar that was posted here Vuetify v-select get item index and with the jsfiddle answer solution
my code that I'm using is:
<v-list-tile>
<v-autocomplete
v-model="data"
:allow-overflow="false"
:items="named_items"
:item-text="getFullName"
:loading="loadingMembers"
:debounce-search="0"
:search-input.sync="searchInput"
class="purple-input search-input"
default
color="purple"
autofocus
placeholder="Search..."
item-value="MemberID"
hide-no-data
no-data-text="Add New Member"
#change="changeMember"
#keyup.enter="hitEnter"
/>
</v-list-tile>
the jsfiddle is at https://jsfiddle.net/Roland1993/fg461d55/1/
and my question is when an item is selected is it possible to get the element that makes up the ID, Question and Answers? so the it can be stored as an array?
It is entirely possible, for example, you have already bound to events, but you can have the name or values of their targets.
See: Method Event handlers

vuejs expression is not working correctly

what is wrong with my expressions? I am making a rest api call, which works fine. Data loads and gets written to console. I am simply trying to get that data on screen. I can but its not correct. It either shows all of the data. I am only trying to get the first item in the array. items.Name does not work, but it does if I do a v-for="item in items". Regardless it loads everything. When I add a [0] to get the first element, it simply removes all the text except the first letter of the object in the array.What do I need to do?
<p>{{items.Name}}</p>
Please, read the docs about list rendering
https://v2.vuejs.org/v2/guide/list.html
If you have an items array of objects, then you can loop over it in your HTML by using v-for, or directly access each entry by specifying the index in the expression: {{ items[0].Name }}
<ul>
<li v-for="item in items" :key="item.ID">{{ item.name }}</li>
</ul>
Notice you're using item, not items, inside the <li> tag, because you instructed v-for to assign the cursor to that variable.

Vue V-for binding array to wrong components

In my Vuex state I have an object containing an array of orderLines, in my model I access that object using a getter and loop over the orderLines creating for each one a component.
Now when I trigger a remove mutation, I request an update of the order object as well and swap the old with the correct new one. So far so good.. all my components get the new order object and update their lists.
But they do not Create/Recreate the components in the loop they just seem to update the indexes -> resulting in the next problem:
If i remove the top item all the next item's data is bound to the 'removed' component and its state :/
<div v-for="orderLine in order.order_lines">
<order-line :order-line="orderLine" ></order-line>
</div>
Use a key.
<div v-for="orderLine in order.order_lines" :key="orderLine">
<order-line :order-line="orderLine" ></order-line>
</div>
If each order_line has an id that would be an even better key.