Problem with indexing children while running v-for directive - vuex

As you will see bellow i'm v-foring on array form Store.
<q-tab-panel v-for="(detailGoal, index) in $store.state.documents[0].document.content.mainGoal.detailGoals" :key="index" :name="detailGoal.title">
<div class="text-h6">{{detailGoal.title}}</div>
<div>{{idx}}</div>
<!-- jj. this is next component -->
<goals-details :thisIndex="index"/>
</q-tab-panel>
I also have button that push new element to this array and it's work fine.
//.jj it's in mutations
ADD_DETAIL_GOAL(store, detailGoal: detailGoal){
if(store.documents){store.documents[0].document.content.mainGoal.detailGoals.push(detailGoal)}
},
On refreshing the site i recived default number of children with correct propsed index'es
but when i start to adding new children they show up but not with good idex'es
Does any one had this problem? please help.

It's not a problem, it's just your lack of knowledge about programming. V-for loop index is completely different index then index in your array. Instead of using .push() method, use .unshift() method, and it should work. This is a quick fix to your problem. Instead, you should create your own ID for data in arrays because you might have problems with deleting correct documents in array. For example, you might have some getter or computed property with will sort the original array creating a new one and give it to v-for loop.

Related

Dynamically updating v-combobox items list

Is it possible somehow to update Vuetify v-combobox items as the user is typing? I want to change the list of available items depending on what the users started typing, to create an address input with suggestions from a geolocation API.
This is what I tried: #update:search-input='fetchAddresses'
And in fetchAddresses: this.items = newListOfItems
However, while #update:search-input fires as expected, the combobox list will only be updated after losing focus. Can I somehow trigger it to be updated? (This may very well be an X/Y problem, so any hints about other approaches are welcome)
My current, ugly, hack is to force the whole combobox component to re-render with the current value set, and then refocus on it. (There is an activateMenu() method on the combobox that I could use to make sure the list reopened:
this.$nextTick(() => {
if (this.$refs.addressCombobox) {
this.$refs.addressCombobox.focus()
this.$refs.addressCombobox.activateMenu()
}
})
I'm using Vue 2.
In my case no-filter solved this problem
<v-combobox no-filter ... >

Vue js - is that right to put all my code in watch?

I Have component A and Component B
in component A i have an API call.
when i passing info to my component b:
<B :structuresMetaData="structureTree"></B>
Inside mounted the variable structuresMetaData the length is 0
and inside the watch the length is 1.
my issue that mounted appear before the watch.
is it would be right to put all my code in watch ? if not what is the solution ?
It looks like structureTree is being changed after <B> is created/mounted.
You have two options depending on your use case:
The first option is to defer the creation of <B> until your data is loaded. This way you don't need <B> to watch for changes to the prop and the prop will be fully populated in the created/mounted hook. This is the simpler approach.
Assuming structureTree is initially an empty array:
<B
v-if="structureTree.length > 0"
:structuresMetaData="structureTree"
/>
created() {
// this.structuresMetaData is a non-empty array here
}
I usually initialize my data with null so that I can distinguish between "not loaded" and "loaded" (where "loaded" can actually be an empty array if the data I fetched from the API was empty).
The second way is using a watcher in <B> to react to changes to the prop. You will need to do this method if the bound prop might change multiple times and you need <B> to update in some way to that change.
watch: {
structuresMetaData(value) {
// React to the change, load secondary data, etc
}
}
What exactly are you doing in <B> that requires a watcher? It's best to avoid explicit watchers if you can avoid them because it usually means you are copying data around and it gets messy when you don't have a single source of truth. Use pure computed properties whenever you can if you just want to transform the data in some way.

[Vue warn]: Duplicate keys detected:

[Vue warn]: Duplicate keys detected: '65535'. This may cause an update error.
found in
---> <Board> at src/components/Board.vue
<App> at src/App.vue
<Root>
this code
<div class="listWrapper" v-for="list in board.lists" :key="list.pos" :data-list-id="list.id">
<List :data="list"></List>
</div>
and
const targetCard = {
id: el.dataset.cardId * 1,
listId: wrapper.dataset.listId * 1,
pos : 65535
}
I got this error. There seems to be an error in this code. How to solve it?
your :key="list.pos" is pos:65535 so you have error
use different key will be ok
the key will make your dom update with data is update, so should make the :key unmatched.
list.pos isn't unique for each item you're looping through. In order for Vue to keep track of your items when board.lists changes internally, each item needs to have a unique identifier. This is very important if you're going to be adding/moving/removing items in the array.
If the items in board.lists come from a database, you could use :key="list.id" (or whichever key the database uses to distinguish the different items). This removes the error, and causes the least rerenders when board.lists updates.
If your item doesn't have an id (or another key that is guaranteed to be unique), you could use the index method provided by the other answers. v-for="(list, listidx) in board.lists" :key="'list-' + listidx".
The index method can cause items to rerender unnecessarily. When the first item
is removed from the array for example, the index (and thus the key)
changes for every item after that, causing the entire list to be
rerendered. Depending on the size of your list and the markup of your
list items, this can be deadly to your app's performance.
You should use unique ids in v-for key. A simple solution is to use index as your v-for key.
<div class="listWrapper" v-for="(list,index) in board.lists" :key="index" :data-list-id="list.id">

possible to dynamically add objects to an array and have Vuejs detect it

edit 2
I think the issue is how I'm referring to an array from a parent component. A fiddle is provided in the comments.
I have an app where we want to be able to add items to a menu_header. I have tried pushing to the bottom of the array but Vuejs doesn't seem to be detecting it.
I have read this section https://v2.vuejs.org/v2/guide/reactivity.html#Change-Detection-Caveats and am trying to make this work but I'm not sure if this is possible.
Something like:
var obj = { name: "my name" }
menu_header.items.push(obj);
Do I need to use this.$set syntax? I really need to add into the middle of an array via splice.
edit 1
So this is a component that is recursive (ie a menu_header can have many menu_headers). I have tried adding a simple button in a menu_item to add to the parent component like this:
methods:{
addItem: function(){
var items = this.$parent.$data.menuHeader.menu_items;
var obj = { header: "my header", detail: "this detail"}
console.log("11 items length: " + items.length);
items.splice(1,0,obj);
console.log("22 items length: " + items.length);
},
The count of the number of items is incremented but the view doesn't rerender. This component is nested 3 levels deep (a Menu component has many MenuHeader components which can have many MenuItem components and also have many MenuHeader components). I'm pretty sure it's a reactivity / array issue - but not sure about exact problem.
Really the issue here was that you should use a key with a list in order for Vue to property render it in all cases and when you are iterating over a component you must use a key. The code in the fiddle is properly adding the elements to the array and Vue is detecting the changes, it just doesn't properly render the list because of it's update strategy. Using a key fixes that.
To that end I modified these lines in the template.
<div v-for="menu_header in menu.menu_headers" :key="menu_header.name">
and
<div v-for="(menu_item, idx) in menuHeader.menu_items" :key="menu_item.header">
The best key for these is some unique property of the object in the list. The above uses name and header, but I expect with real code you could come up with a better key.
It's best to get in the habit of always adding a key whenever you render a list in Vue.

Vue.js $set on array element does not trigger notification

I have a Vue instance whose data contains an array of objects. I display this data in a table without issue.
Upon clicking the table rows, I set a "selected" property on the corresponding data object using:
key.$set('testing', testing);
Upon setting this value, I add a custom css class the the table row, without issue.
However, if I do a simple output of the entire array of objects at the bottom of the page (such as below), it doesn't get updated to reflect the new "testing" attribute I have added.
{{ gridData | json }}
I was able to reproduce my question using some modified vue.js example code in this JSFiddle:
https://jsfiddle.net/wdmatl/531axrtt/
Notice when you click on rows of data, you either see "testing" or "not testing". But this new object data isn't reflected in the json at the bottom of the page. Why not?