How does a single file component pass context.data? - vue.js

I loop functional components in the transition-group, and because I didn't pass a key reference to the root element of the tag component to the tag component.
But how does a single file component pass the context.data?
The sample link
https://codesandbox.io/s/rjjmpvwm4n/
https://github.com/vuejs/vue/issues/7777

The <tag> component needs a key to use as its child <span>'s id.
Since <tag> is a functional component, you will have to access to the data via the data. prefix.
So, since you are using <tag v-for="item in list" :key="item"></tag>, inside the <tag>'s template, you can access the key (of context.data) automatically using data.key:
Add :key="data.key" in tag.vue:
<template functional>
<span :key="data.key">tag content</span>
</template>
Demo CodeSandbox: https://codesandbox.io/s/wxmvxnojl?module=%2Fsrc%2Fcomponents%2Ftag.vue

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?

How to use `v-if` and `v-for` on the same element?

Hi I'm trying to figure out how to use v-if on a iterated element which also uses v-for. I need to check if the current element has any of a series of classes, which are numbers.
so the classes of each article would be:
<article class="post-item post-guide 12 22 19 30 55">...
this is the HTML that renders all:
<article v-if="showIfHasClass" :class="'post-item post-guide ' + guide.categories.toString().replace(/,/g, ' ')"
v-for="(guide, index) in guides" :key="index">
<header>
<h1 class="post-title">
{{ guide.title.rendered}}
</h1>
</header>
</article>
I have tried with methods that check the class of all elements, that works, but i'm trying to use a clean Vue built-in solution with v-if without success, i'm not able to retrieve the class of this in a successful way.
Should showIfHasClass be a computed property? I have tried with that too... but it seems, I'm missing something along the way.
my data I have to check against is an array:
data:{
guides: [...]
selectedCategories: [1, 22, 33, 100, 30];
}
or maybe it is better to directly loop over the guides and check if they have the selectedCategory or not, then remove the element from the guides data array?
What is more effective?
Besides the option to create an additional filtered computed (effectively eliminating the need to use v-for and v-if on the same element), you also have a template level way of dealing with such edge-cases: the <template> tag.
The <template> tag allows you to use arbitrary template logic without actually rendering an extra element. Just remember that, because it doesn't render any element, you have to place the keys from the v-for on the actual elements, like this:
<template v-for="(guide, index) in guides">
<article v-if="isGuideVisible(guide)"
:key="index"
class="post-item post-guide"
:class="[guide.categories.toString().replace(/,/g, ' ')]">
<header>
<h1 v-text="guide.title.rendered" />
</header>
</article>
</template>
isGuideVisible should be a method returning whether the item is rendered, so you don't have to write that logic inside your markup. One advantage of this method is that you can follow your v-if element with a fallback v-else element, should you want to replace the missing items with fallback content. Just remember to also :key="index" the fallback element as well.
Apart from the above use-case, <template> tags come in handy when rendering additional wrapper elements is not an option (would result in invalid HTML markup) (i.e: table > tr > td relations or ol/ul > li relations).
It's mentioned here as "invisible wrapper", but it doesn't have a dedicated section in the docs.
Side note: since you haven't actually shown what's inside guide.categories, I can't advise on it, but there's probably a cleaner way to deal with it than .toString().replace(). If guide.categories is an array of strings, you could simply go: :class="guide.categories".
I think the most Vue way is to create a computed property with filtered items from selected categories, then use that in v-for loop (the idea is to move the business logic away from template).
computed: {
filteredItems(){
return this.guides.filter(e => this.selectedCategories.includes(e.category))
}
}
Also, as a note, it is not recommended to use v-if and v-for on the same element, as it may interfere with the rendering and ordering of loop elements. If you don't want to add another level of nesting, you can loop on a 'template' element.
<template v-for="item in items">
// Note the key is defined on real element, and not on template
<item-element v-if='condition' :key="item.key"></item-element>
</template>

Vue.js v-for loop binding items objects in array and update on change

I have a component and am using a v-for loop to loop through and object array at the $root level of my app to populate a list.
In other components in my app I'll EventBus.$emit data to update tables in my database. That means the $root array data updates.
Is there a way to bind or map the v-for list I created to detect and update using the index or another method?
Here is the example loop and example $root data.
I guess I'm hoping there's a way to connect an object in an array by using [index]
<v-list-item-content v-if="this.$root.people">
<div v-for="(item, index) in this.$root.people" :key="item.id">
<div>
<span v-html="this.$root.people[index].value"></span>
</div>
</div>
</v-list-item-content>
My array looks like this.
people:Array[2]
0:Object
event_id:6
submission_values:Array[4]
0:Object
value:"Danny"
1:Object
2:Object
3:Object
I suppose if people is already reactive then you should look at how you update this array. There are some caveats about updating a reactive array in VueJS, please take a look at the official documentation

how to pass a property from parent template to slot in vuejs

i have a component A which wraps the component B using slot
component-A declaration
<component-A>
<slot></slot>
</component-A>
component-B Usage:
<component-A :some-prop="foo">
<component-B></component-B>
</component-A>
when i pass this prop (someProp) to component-A, is there any way to access it in component-B?
if you want to share information between the father and whatever have in your slot, Vue offers a slot props propertie, something like this:
<slot name="icon-order" :someProp="some-prop" :someProp2="some-prop2">
Once you inject something on component slot, you'll be able to access that propertie like:
<component-A :some-prop="foo">
<component-B slot-scope="slotProps"></component-B>
</component-A>
Then, inside component-B you'll have access to slotProps with every declared propertie set on component-A, just use: slotProps.someProp or slotProps.someProp2.
You can have a better look at Vue documentation:
https://v2.vuejs.org/v2/guide/components-slots.html#Scoped-Slots
hope that helps
If component-B needs to be passed some data, then it must receive it through a prop.
What you're asking is for component-B to also receive foo; just pass it via a prop in the same way you did for component-A, the slotting here makes no difference.
<component-A :some-prop="foo">
<component-B :some-prop="foo"></component-B>
</component-A>
However I suspect this isn't exactly your issue; if not please clarify how the slotting here is relevant.

Keep list components alive in Vue

I have a list of components that I render using v-for. Not all the components are shown simultaneously. I page the the array of rendered components by using slice.
These components shouldn't be rerendered, as some of them have user inputted data and some of them do network related tasks.
I tried to use <keep-alive>. However, this renders only the first component.
<keep-alive>
<component :is="component.type" :key="component.id" v-for="component in components">
</keep-alive>
How do I keep a dynamic list of components alive?
<div v-for="comp in compList" :key="'container'+comp.keyId" >
<keep-alive>
<component :is="comp.type" :key="comp.keyId"></component>
</keep-alive>
</div>
this above works for me . Pushing elements to compList correctly creates new instances of their respective components. Moving an element's index within the array , maintaining key, does not call destroy/create and maintains state within each component
Tested the answer above in a fiddle and doesn't work. https://jsfiddle.net/z11fe07p/680/
<div v-for="component in myComponents" :key="component.id" >
<keep-alive>
<component :is="component.type"
:name="component.name">
</component>
</keep-alive>
</div>
Also i would avoid using vue reserved words such as components because there is a components key in the vue instance which tells what components the instance is using.
I read source code of Vue's <keep-alive>, and I created new Component which works very well with list.
package name is vue-keep-alive-global. Here is a link, https://www.npmjs.com/package/vue-keep-alive-global
How to use :
<KeepAliveGlobal>
<YourComponent
:key="uniqueKey"
/>
</KeepAliveGlobal>
With Array,
<template v-for="(item, index) of array">
<KeepAliveGlobal :key="`blah-blah-${index}`">
<YourComponent
:item="item"
:key="`your-component-${index}`"
/>
</KeepAliveGlobal>
</template>
KeepAliveGlobal will cache component by key.
There's note at Vue docs about your use case
Note, <keep-alive> is designed for the case where it has one direct
child component that is being toggled. It does not work if you have
v-for inside it. When there are multiple conditional children, as
above, ` requires that only one child is rendered at a
time.
https://v2.vuejs.org/v2/api/#keep-alive
Try v-once directive instead.
https://v2.vuejs.org/v2/api/#v-once