How to access a prop inside a v-for? - javascript

When a prop has the same name as a sub-element in a v-for directive. How to access it?
<template>
<ul>
<li v-for="{ id, name } in data" :key="id">
{{name}} and {{props.name}} // Doesn't work, and props name is shadowed here.
</li>
</ul>
</template>
<script setup lang="ts">
const props = defineProps({ data: Array, name: String });
</script>

Don't destructure it:
<template>
<ul>
<li v-for="item in data" :key="item.id">
{{ item.name }}
</li>
</ul>
</template>

Related

I don't know how to create a changeable text for a reusable component in Vue Js

I was creating a reusable tabs component by watching tutorial. Having watched it, I figured out how it works. But, as for my project, I need to create tabs with heading containing a title that can be changed because this is a reusable component, so I have to change each heading's title for each new tab, but I haven't figured out how to do it. I need somehow to get title from component TabsWrapper that I added to my page
<div class="tab_header">{{title}}</div>
and then make this title change a text inside this div that is the main header of a TabsWrapper component.
<div class="tab_header">{{title}}</div>
My code:
The first one is the code out of component that I added to my homepage of the website.
<TabsWrapper>
<Tab title="Tab 1">Hello 1</Tab>
<Tab title="Tab 2">Hello 2 </Tab>
<Tab title="Tab 3">Hello 3</Tab>
<Tab title="Tab 4">Hello 4</Tab>
</TabsWrapper>
The second one is the code inside the component that is responsible for TabsWrapper
<template>
<div class="tabs">
<div class="tab_header"></div>
<ul class="tabs_header">
<li
v-for="title in tabTitles"
:key="title"
#click="selectedTitle = title"
:class=" {selected: title ==selectedTitle}"
>
{{ title }}
</li>
</ul>
<slot />
</div>
</template>
<script>
import { ref} from 'vue';
import { provide } from 'vue';
export default{
setup(props,{slots}){
const tabTitles=ref(slots.default().map((tab)=> tab.props.title))
const selectedTitle=ref(tabTitles.value[0])
provide("selectedTitle", selectedTitle)
return{
selectedTitle,
tabTitles,
}
}
}
</script>
This chunk of code takes each title from Tab
<Tab title="Tab 1">Hello 1</Tab>
And this chunk of code renders it out
<li
v-for="title in tabTitles"
:key="title"
#click="selectedTitle = title"
:class=" {selected: title ==selectedTitle}"
>
{{ title }}
</li>
I've tried to repeat the same technique, but it worked out, but I think that there's the better way to do it
<div class="tabs">
<div class="tab_header" v-for="headtitle in headTitles" :key="headtitle">{{headtitle}}</div>
<ul class="tabs_header">
<li
v-for="title in tabTitles"
:key="title"
#click="selectedTitle = title"
:class=" {selected: title ==selectedTitle}"
>
{{ title }}
</li>
</ul>
<slot />
</div>
</template>
<script>
import { ref} from 'vue';
import { provide } from 'vue';
export default{
setup(props,{slots}){
const tabTitles=ref(slots.default().map((tab)=> tab.props.title))
const headTitles=ref(slots.default().map((tab)=>tab.props.headtitle))
const selectedTitle=ref(tabTitles.value[0])
provide("selectedTitle", selectedTitle)
return{
selectedTitle,
tabTitles,
headTitles,
}
}
}
</script>
You can simply pass props in the script tag and directly access them using this keyword and prop name.
export default {
props: ['foo'],
created() {
// props are exposed on `this`
console.log(this.foo)
}
}
In the template tag like this
<span>{{ foo }}</span>
you don't need to use ref you can directly use v-for and loop over the array elements.
<li v-for="(item, index) in foo">
{{item}}
</li>

How to use <parent> and <child> component in template recursion in angular

I have 2 components <parent> and <child>. I want to use them in recursive <ng-template>
let say for the sake of simplicity
<parent> component will contain <ul>
<child> component will contain <li>
here is what I have referenced https://gist.github.com/arniebradfo/5cf89c362cc216df6fc1d9ca4d536b72
here is how I have tried
<div>
<ng-template #recursiveList let-list>
<span *ngFor="let item of list">
<!-- commented {{item.title}} -->
<child [item]="item"/>
<!-- commented <ul *ngIf="item.children.length > 0"> -->
<template>
<parent [item]="item"/>
<ng-container *ngTemplateOutlet="recursiveList; context:{ $implicit: item.children }"></ng-container>
</template>
<!-- commented </ul> -->
</span>
</ng-template>
<ng-container *ngTemplateOutlet="recursiveList; context:{ $implicit: list }"></ng-container>
</div>
but above code does not work properly
my expected result should look like below shown, for a parent with 2 childs
<span>
<parent>
<child/> // <-- child 1
<child/> // <-- child 2
</span>
with html it look like below
<span>
<ul>Parent</ul>
<li>child 1</li> // <-- child 1
<li>child 1</li> // <-- child 2
</span>
it must follow same nesting with any level.
please help me with a demo from https://gist.github.com/arniebradfo/5cf89c362cc216df6fc1d9ca4d536b72
Please help me thanks in advance !!!
main.component.html
<div>
<parent *ngFor="let item of list" [item]="item">
</div>
parent.component.html
<span>{{item.name}}</span>
<ul>
<child *ngFor="let child of item.children" [item]="child">
</ul>
child.component.html
<li>
<parent [item]="item">
</li>

Get path/route/namespace for current/parent component/view in vue.js

I've been working on a sub menu system for vue.js that gets populated by the current route's children. I recently posted a question about it and it was answered here.
Now I'm trying to improve upon that but I'm having trouble finding out how to get a component's path or namespace (not sure what word to use). Currently I see what I want in the Vue Dev tools I just don't now how to get those properties.
I've tried {{$route.path}} but that only gives me the full path.
Another thing I've tried which kind of helps is storing the current path the first time I load the menu. Which preserves the path I want to be appending to. The only issue is when i navigate directly to the page it loads the menu with the pages url which then breaks the functionality.
Here is the code:
<template>
<nav id="sidebar">
<div class="sidebar-header">
<h3>Bootstrap Sidebar</h3>
</div>
<h2>Route: {{ }}</h2>
<ul class="list-unstyled components" v-for="(route, index) in $router.options.routes.filter(x=>x.path==path)">
<li v-for="child in route.children">
<a class="nav-item" :key="index">
<router-link :to="{path: path+'/'+child.path}" exact-active-class="active">
<icon :icon="route.icon" class="mr-2" /><span>{{ child.path }}</span>
</router-link>
</a>
</li>
</ul>
</nav>
</template>
<script>
export default {
data() {
return {
path: this.$route.path
}
},
methods: {
},
}
</script>
I really want something closer to this though, where instead of using $route.path to return the full path like /traveler/Create I want something to just return /traveler or whatever the path for it's router-view is:
<template>
<nav id="sidebar">
<div class="sidebar-header">
<h3>Bootstrap Sidebar</h3>
</div>
<ul class="list-unstyled components" v-for="(route, index) in $router.options.routes.filter(x=>x.path==$route.path)">
<li v-for="child in route.children">
<a class="nav-item" :key="index">
<router-link :to="{path: $route.path+'/'+child.path, params: { idk: 1 }}" exact-active-class="active">
<icon :icon="route.icon" class="mr-2" /><span>{{ child.path }}</span>
</router-link>
</a>
</li>
</ul>
</nav>
</template>
<script>
import { travelerroutes } from '../../router/travelerroutes'
export default {
data() {
console.log(travelerroutes);
return {
travelerroutes,
collapsed: true
}
},
methods: {
toggleCollapsed: function (event) {
this.collapsed = !this.collapsed
}
}
}
</script>
To get the path of the current component I had to just use the $Route.matched property. In my case because I didn't want to include the childrens' paths I used the first match like this $Route.matched[0].path
you can learn more about it here
I also used it to update my other question/answer
Essentially you can use it in a template like this:
<template>
<nav id="sidebar">
<div class="sidebar-header">
<h3>Bootstrap Sidebar</h3>
</div>
<ul class="list-unstyled components" v-for="(route, index) in $router.options.routes.filter(x=>x.path==$route.matched[0].path)">
<li v-for="child in route.children">
<a class="nav-item" :key="index">
<router-link :to="route.path+'/'+child.path" exact-active-class="active">
<icon :icon="route.icon" class="mr-2" /><span>{{ child.name }}</span>
</router-link>
</a>
</li>
</ul>
</nav>
</template>
<script>
export default {
data() {
return {
}
}
}
</script>

How to make a components prop available to its slotted elements?

I have a structure like this:
<childs>
<child>
<ul>
<li v-for="item in currentData">#{{ item.name }}</li>
</ul>
</child>
</childs>
In the child component, I have a data property currentData.
// Child.vue
data: {
currentData: {}
}
For some reason, I am assigning value to this currentData prop, from the childs component (not from child).
// Childs.vue
child.currentData = data;
How do I make this currentData available to the slotted elements of <child>:
<ul>
<li v-for="item in currentData">#{{ item.name }}</li>
</ul>
The template for Child.vue is like this:
<template> <div><slot></slot></div> </template>
I tried something like this:
<template> <div><slot :current-data="currentData"></slot></div> </template>
I belive what you need is Scoped Slots.
For that you should explicitly pass (in the slot declaration at the template) what props you want to make available to the <slot> "user".
E.g. say you want to make a foo property available to the slot users of <child> (assuming childData property existi in <child>). You would do:
<!-- This is <child>'s template -->
<template> <div><slot :foo="childData"></slot></div> </template>
From that point on, whoever uses that <child> component can access that foo property by declaring slot-scope:
<child>
<ul slot-scope="slotProps">
<li>{{ slotProps.foo }}</li>
</ul>
</child>
Notice that the slot-scope is declared in the element that takes the place where the <slot> was.
Full demo:
Vue.component('children', {
template: '#children'
})
Vue.component('child', {
template: '#child',
data() {
return {
childData: "I am childData"
}
}
})
new Vue({
el: '#app'
})
<script src="https://unpkg.com/vue#2"></script>
<div id="app">
<children>
<child>
<ul slot-scope="slotProps">
<li>{{ slotProps.foo }}</li>
<!-- <li v-for="item in currentData">#{{ item.name }}</li> -->
</ul>
</child>
</children>
</div>
<template id="children">
<div><slot></slot></div>
</template>
<template id="child">
<div><slot :foo="childData"></slot></div>
</template>
What if I wanted to add another element outside the <ul> element? Vue simply discards anything outside slot-scope.
This is not due to slot-scope, but to <slot>s in general.
Since child has only one <slot>, the first element you place within <child> is the one that will take the slot.
If you want to have multiple elements take the slot, you'll have to wrap them. E.g. in a <div>. But, if you don't want this wrapper element to be rendered, use a <template>. See demo below.
Vue.component('children', {
template: '#children'
})
Vue.component('child', {
template: '#child',
data() {
return {
childData: "I am childData"
}
}
})
new Vue({
el: '#app'
})
.child { border: 1px solid red }
<script src="https://unpkg.com/vue"></script>
<div id="app">
<children>
<child>
<template slot-scope="slotProps">
<ul>
<li>{{ slotProps.foo }}</li>
<!-- <li v-for="item in currentData">#{{ item.name }}</li> -->
</ul>
<span>howdy</span>
</template>
</child>
</children>
</div>
<template id="children">
<div><slot></slot></div>
</template>
<template id="child">
<div class="child"><slot :foo="childData"></slot></div>
</template>

Vue-component emitting

This is my first Vue.js component, I try to emit event from it. But my browser doesnt see 'updateValue' method... Whats wrong?
<template>
<div>
<ul class="nav flex-column">
<li v-for="item in items">
<div class="nav-link" onclick="updateValue(1)">
{{ item.name }}
</div>
</li>
</ul>
</div>
</template>
<script>
export default {
props: ['items'],
methods: {
updateValue: function (value){
this.$emit('change', value);
}
}
}
</script>
In Vue you should use #click or v-on:click instead of onclick.

Categories

Resources