Vue remove child component with it state - vue.js

I want delete child component. I use this.rows.splice(index, 1)
When i call this.rows.splice(index, 1) VueJs always remove last component from this.$children and save internal state in component.$data ;
Example is here
`https://jsfiddle.net/abratko/gc7h1r34/3/`
How fix it?

Vue associates each data item with each vnode according to the item's index by default. This results in existing Vue components being reused, but bound to different items, when re-rendering the list after an item was removed from the array.
This is why you should always bind key to a value which uniquely identifies that particular item. In your example, since each item is a unique string you can just bind to that:
<row-component v-for="(row, index) in rows" :key="row">
^^^^^^^^^^

Related

Send template to grandchild component (nested template)

There is this TablePackage component that we use in our project. I saw that to customize how values in a column render, one can pass template named with the specific column name (in this example status) and use slot prop row.
<!--Code Snippet 1-->
<!--In each row of status columns, we will see status buttons instead of plain status text-->
<TablePackage>
<template #status="{ row }">
<button>{{ row.status }}</button>
</template>
</TablePackage>
Now there are two more components, Parent and Dialogue.
Parent uses Dialogue, Dialogue uses TablePackage.
Parent component
<Dialogue title="dialogueTitle">
</Dialogue>
Dialogue component
{{ title }}
<TablePackage rows="rowList" columns="columnObj">
</TablePackage>
Now I want to be able to custom render values in a column as in 'Code Snippet 1' which means I want to render a button not a text in 'status' column. How can I achieve this from Parent component level? I tried nested template but it was not allowed. With the constraint that I can edit Parent and Dialogue components but not TablePackage, what's the recommended away to achieve this?

Using inner component in a loop in Shopware 6 does not persist the values uniquely for each looped component

I am using a v-for over a custom component and passing the item as a prop. But the issue is that each component instance in the loop takes the same item prop. For e.g in the 1st loop a component field has text "abc", then the second looped component also will have the same "abc" text. If I change the text in the 2nd one, it changes in the 1st component too. Is there a way to make the prop unique for each loop ?
For e.g this is the code which calls the inner component:
<template v-for="(businesscase, index) in businessCase.fields">
<custom-case-freetext-field #field-changed="updateFields"
:key="index"
#field-removed="removeFields"
:fields="businessCase.fields"
:index="index">
</custom-case-freetext-field>
</template>
and inside this component I have a basic form
<sw-field :label="$tc('rma.modules.case.freetext.nameLabel')"
:placeholder="$tc('rma.modules.case.freetext.nameLabel')"
required
v-model="fields[index].name">
</sw-field>
<sw-single-select
labelValue="label"
valueProperty="label"
:options="fieldTypes"
:label="$tc('rma.modules.case.freetext.fieldType')"
:placeholder="$tc('rma.modules.case.freetext.fieldType')"
v-model="fields[index].type"
#input="changeType"
required>
</sw-single-select>
If I do :value instead of v-model, the entered value disappears as soon as the element loses focus.
If I use v-model, the data stays there, but then both (or as many are there in the loop) component instances, have data binding between them, so it defeats the purpose of having a loop for multiple components. As seen in the screenshot, I am typing in the 2nd component, but it changes the text for the first one too.
In the above example I am sending the whole array as prop, but I have also tried with individual field element instead of fields
Your are not using the businesscase variable inside your components. And since every component always works on the upper scope property, they will all change the same. Use the innerscope property. If you have problems with reactivity, because you try to mutate props directly work with events emitting the key and the changed value to the upperscope component.

v-for and component external (croppa)

I have a dynamic component with Vue Croppa.
<croppa v-for="(image, key) in images" :key="key" ...props></croppa>
If I add an element in images, it add a new croppa components and work perfect.
The problem is when I try to remove an element.
For example, if I added 3 images:
images: [
'image1.png',
'image2.png',
'image3.png,
]
if I deleted image2 (index: 1) it remove image3.
What I need to do to preserve all config (initial-image, position, flipX, etc...) of croppa by position?
Thanks
Using index argument of v-for directive (in your case key) is reliable way to set item key attribute only if looped array (in your case images) is not going to be mutated.
In example above, when images length changes indexes of rendered items stay the same. Meaning, if second item has been deleted, renderer can notice changes of key indexes
from 0, 1, 2 to 0, 1
Thus, the item with last index 2 is removed.
In order to achieve expected behavior, key attribute should be set to a unique value. For array of objects it is usually an id or any unique property. For array of strings (if values in array are unique) the key attribute can be set to that value. Example:
<croppa
v-for="image in images"
:key="image"
/>
In case if some values in that array are not unique (which should be assumed if values come from external source - API, user input, etc), key attribute should be set to concatenated value and index. Example:
<croppa
v-for="(image, index) in images"
:key="`${image}${index}`"
/>

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.

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

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/