[Vue warn]: Duplicate keys detected: - vue.js

[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">

Related

Problem with indexing children while running v-for directive

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.

"v-on with no argument expects an object value" facing this error in quasar selector when focus into the field

here is my code, options array updated in created hook based on api response and response of api is array of object and v-model value is also updated in created hook. this selector is of input type and also filter the data based on input type from options array.
hope so this chunk of code is enough to explain.
<q-select
ref="registeredCountry"
for="registeredCountry"
color="olab-brand-blue"
v-model="registeredAddress.country"
use-input
fill-input
hide-selected
:options="countryOptions"
#filter="filterCountryList"
emit-value
option-label="countryName"
option-value="countryName"
#update:model-value="resetStateAndCityFields('registeredAddress')"
map-options
>
</q-select>
I got exactly the same Vue warning with one of my q-selects, after migrating it (unchanged) from Vue 2/Quasar 1 to Vue 3/Quasar 2. The same q-select code worked without warnings on the older levels. The warning is very unspecific from a Quasar point of view.
However, I found a hint on https://github.com/quasarframework/quasar/issues/8898 to eliminate this warning, which helped to resolve the issue in my case.
The reason for the warning was in my case due to the use of q-chips with q-items for the options in the q-select. Those q-items for the q-select options used "v-on='scope.itemEvents'", together with "v-bind='scope.itemProps'", which was the recommended combination in Quasar 1/Vue 2.
In Quasar 2/Vue 3 the "v-on='scope.itemEvents' is no longer necessary (if used, it causes this Vue warning). Just search for all "v-on='scope.itemEvents'" in your template code, then drop (i.e. delete) those "v-on=...", but keep the "v-bind=...".
This eliminates the above Vue warnings (which otherwise come up for every selectable option in your q-select).

Initially hide first group in Vue-Formulate repeatable group

I'm using Vue-Formulate's Repeatable Groups. For my requirements:
A group is optional to add
If a button is pressed to add a group, then the fields in the group must be validated
The form should not initially show the group; it should show the button to add a group
For example, the desired initial appearance is in the following screenshot, which I generated after clicking the "remove" / X button in the linked codesandbox:
I've mocked this up at codesandbox here: Vue Formulate Group with Button to Start
Is this possible? If so, how?
May 2021 UPDATED WORKAROUND
In #braid/vue-formulate#2.5.2, the workaround below (in Research: A hack that seems to UPDATE: USED TO work) no longer works, using a slot to override the close button, save a ref, and trigger a click does. See also the related feature request at https://github.com/wearebraid/vue-formulate/issues/425.
<script>
export default {
// ... fluff omitted
async mounted() {
await this.$nextTick();
if (!this.hasMessages) {
// See also feature request at https://github.com/wearebraid/vue-formulate/issues/425
this.$refs.closeButton.click();
}
},
};
</script>
<template>
<FormulateInput
type="group"
name="rangeMessages"
:minimum="0"
repeatable>
<!-- See https://vueformulate.com/guide/inputs/types/group/#slots -->
<template #remove="{removeItem}">
<button ref="closeButton" #click.prevent="removeItem"/>
</template>
</FormulateInput>
</template>
Research - Vue-Formulate's Docs
In Vue-Formulate's docs on input with type="group"'s props and slots, there is a minimum prop. I've set that to zero, but that doesn't change the initial appearance. I do see multiple slots, but I'm not quite sure which one to use or if I could use any of them to achieve what I want; it seems like default, grouping, and repeatable might be useful in preventing the initial display of the first group.
Research - Vue-Formulate's Tests
I see that FormulateInputGroup.test.js tests that it('repeats the default slot when adding more', so the default slot is the content that gets repeated. According to the docs, the default slot also receives the index as a slot prop, so that could be useful.
Research - Vue Debugger
The item which I want to initially remove is at FormulateInputGroup > FormulateGrouping > FormulateRepeatableProvider > FormulateRepeatable > FormulateInput:
When I remove the initial item to match the desired initial layout, the group hierarchy changes to:
<FormulateInput><!-- the input type="group" -->
<FormulateInputGroup>
<FormulateGrouping/>
<FormulateAddMore>...</FormulateAddMore>
</FormulateInputGroup>
</FormulateInput>
Based on this change, I would expect that I need to modify FormulateGrouping to get the desired initial appearance, but I haven't found in the source what items are available to me there.
Research: A hack that seems to UPDATE: USED TO work
This hack worked in v2.4.5, but as of 2.5.2, it no longer works. See top of post for an updated workaround.
In the mounted hook, when I first render the form, I can introspect
into the formValues passed to v-model to see if the group lacks an
initial elements that is filled out. If so, then I can make use of a
ref msgs on the FormulateInput of type group to then call
this.$refs.msgs.$children[0].$children[0].removeItem(), which
triggers an initial remove of the (empty) item. This is super hacky,
so I'd prefer a better way, but it kind of works. The only problem is
that it validates the fields when clicking on the button, before any
input has been entered.
This is a fair question, and Vue Formulate used to support the behavior of just using an empty array to begin with, however it became clear that it was confusing to users that their fields would not show up without an empty object [{}] when they bound the model, so a change was made to consider an initial value of an empty array an "empty" field and pre-hydrate it with a value. Once that initial hydration is completed however, you can easily re-assign it back to an empty array and you're good to go. This is easily done in the mounted lifecycle hook:
...
async mounted () {
await this.$nextTick()
this.formData.groupData = []
}
...
Here's a fork of your code sandbox: https://codesandbox.io/s/vue-formulate-group-with-button-to-start-forked-32jly?file=/src/components/Reproduction.vue
Provided solutions weren't working for me but thanks to previous answer I've managed to find this one:
mounted(){
Vue.set(this.formData, "groupData", [])
},
which does same effect as
data(){
formData: {
groupData: [],
},
},
mounted(){
this.formData.groupData = []
},

Vue: My initial data value is not accessible when used as a function param

I am having an issue when triggering the click handler in my button here. The error comes back as TypeError: Cannot read property 'discountProduct' of null when I click the button. I know for a fact that this.discountProduct.id has a value by inspecting it from the Vue Tools and by knowing that the button is rendering to begin with since it only conditionally shows if the id > 1.
I alternatively tried to remove this but it still throws the same error.
I also tried manually just inserting the product id as the param and that works so I am not sure what the issue is at this point.
<button v-if="this.discountProduct.id >= 1"
type="button"
v-on:click="addToCart(this.discountProduct.id, true)"
Is you button in the <template> tags?
If so, you do not need to use this delete from the click argument and v-if. However that should only throw a warning.
Can you also post the code for the method addToCart?
If you are not setting the full discountProduct object you will need to make sure setting the id is reactive. Like the example below:
this.$set(this.discountProduct, 'id', 1)
Nested Object/keys are not reactive without using $set. See https://v2.vuejs.org/v2/guide/reactivity.html for more details.
Hope this helps

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.