How to access item in <slot> inside v-for (vue.js) - javascript

How to send item from v-for to slot? In vue.js.
ListComponent:
<ul>
<li v-for="item in list">
<slot></slot>
</li>
</ul>
Page view:
<list-component :list="list">
<span v-text="item.value"></span>
</list-component>
Code <span v-text="item.value"></span> can't access item (from component's scope). Is it a way to send an item from component to code in tag?
P.S. I know, that as a workaround I can send $index from component to parent view, but it's a little bit hacky
UPD: Example of my current workaround (somebody may find this useful):
Declare index in view's data:
data () {
return {
index: 0,
list: [ ... ]
}
Add to index param to the view's template:
<list-component :list="list" :index="index">
<span v-text="list[index].value"></span>
</list-component>
Declare index in component's props:
props: {
index: {
type: Number
}
}
Add index to v-for's element:
<ul>
<li v-for="item in list" index="$index">
<slot></slot>
</li>
</ul>

the bind expression is compiled in the context who define them,so:
v-text="item.value"
can not be compiled because in Page view there is no "item" defined.
maybe you can try to change your code structure like below:
ListComponent:
//content in component
<span>i am text in component,item value is:{{item.value}}</span>
<slot></slot>
Page view:
<ul>
<li v-for="item in list">
<list-component :item="item"></list-component>
</li>
</ul>

Related

2d array in Vue.js

I'm fairly new to Vue and js and I'm trying to create a Todo App with firebase integration.
What i need it to look like is the following: there should be an option to create a new list of tasks and an option to create new tasks within this list.
So i need a list of lists.
My firebase database structure looks like this:
users (collection) -> tasks(collection) -> and inside the followin structure:
I understand that there should be a nested v-for element right?
Something like this:
<div v-for="task in tasks" :key="task.id">
<ul v-for="todo in task(?)">
</ul>
</div>
But i don't understand how to approach it.
Any advice will be GREATLY appreciated!
<template>
<ul>
<li v-for="task in tasks" :key="task.id">
{{task.name}}
<ul>
<li v-for="td in task.todo" :key="td.id">
{{ td.completed }}
<br>
{{ td.date }}
<br>
{{ td.title }}
<br>
{{ tdurgent }}
</li>
</ul>
</li>
</ul>
</template>
<script>
import {db} from './firebase';
export default {
data() {
return {
users: {}
}
},
firebase: {
tasks: {
source: db.ref('tasks'),
// Optional, allows you to handle any errors.
cancelCallback(err) {
console.error(err);
}
}
}
}
</script>
firebase.js
import Firebase from 'firebase'
const firebaseApp = Firebase.initializeApp({
// Populate your firebase configuration data here.
...
});
// Export the database for components to use.
// If you want to get fancy, use mixins or provide / inject to avoid redundant imports.
export const db = firebaseApp.database();
You can write like this
<div v-for="task in tasks" :key="task.id">
<ul v-for="todo in task.keyOfArrayType" :key="todo.id" >
{{todo.name}}
</ul>
</div>
In the json no key id display for both outer and inner if you interested to put key:="task.id" then you have to change the structure like
otherwise remove :key = "task.id" and :key="td.id"
<ul>
<li v-for="task in tasks" :key="task.id">
{{name}}
<ul>
<li v-for="td in task.todo" :key="td.id">
{{ completed }}
<br>
{{ date }}
<br>
{{ title }}
<br>
{{ urgent }}
</li>
</ul>
</li>
</ul>

How to detect which property is selected/clicked

I'm trying to detect which property is selected/clicked out of ngFor , which comes from a REST API.
I want to get which property(broker.username) is selected out of others
<div class="list-group">
<ul *ngFor="let broker of brokers">
<li class="broker_list"> {{broker.username}}</li>
</ul>
</div>
this is the REST call
[
{ id: 1, username: "PersonA"},
{ id: 2, username: "PersonB"}
]
You have to use event handler on anchor tag like this:
<div class="list-group">
<ul *ngFor="let broker of brokers">
<li class="broker_list">
<a (click)="onSelect(broker)" href="#" class="list-group-item list-group-item-action" style="text-align: center;"> {{ broker.username }}</a>
</li>
</ul>
</div>
Within your component add method:
#Component({ })
class XYZ {
// ... some code
public onSelect(broker) {
// Do what you need with broker?
}
}
Pass the broker directly in the method, like this
<div class="list-group">
<ul *ngFor="let broker of brokers">
<li class="broker_list"> {{broker.username}}</li>
</ul>
</div>
There are several ways, one of those would be to do something like this.
1) Add click handler on a tag:
<div class="list-group">
<ul *ngFor="let broker of brokers">
<li class="broker_list"> {{broker.username}}</li>
</ul>
</div>
2) Add method to component:
selected(brokerName: string) {
console.log(brokerName);
}
You could add a (click) property that calls a function, which takes a broker as a parameter. You can then access the username of that broker.
{{broker.username}}
The a tag should be able to know what the current broker is, since it is inside the *ngFor.

How to render a list of static content with Vue named slot?

I have trouble figuring out how to get the following to work:
My parent template
<comp>
link 1
link 2
</comp>
and my component comp template looks like the following:
<ul class="comp">
<li class="comp-item"><slot name="links"></slot></li>
</ul>
currently all my anchors goes to that single li tag (which is expected)
but I would like to be able to produce multiple li for every named slot I inserted like the following:
<ul class="comp">
<li class="comp-item">link 1</li>
<li class="comp-item">link 2</li>
</ul>
Is there any way to achieve what I need without using scoped slot? Because my content is pure HTML so I feel it is unnecessary to put static content inside prop in order to render them.
From what I have seen, most vue UI framework requires you to use another custom component for the list item, which I feel is over killed for the problem. Is there any other way to do this?
This is easily accomplished with a render function.
Vue.component("comp", {
render(h){
let links = this.$slots.links.map(l => h('li', {class: "comp-item"}, [l]))
return h('ul', {class: 'comp'}, links)
}
})
Here is a working example.
console.clear()
Vue.component("comp", {
render(h){
let links = this.$slots.links.map(l => h('li', {class: "comp-item"}, [l]))
return h('ul', {class: 'comp'}, links)
}
})
new Vue({
el: "#app"
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.13/vue.js"></script>
<div id="app">
<comp>
link 1
link 2
</comp>
</div>
Or with the help of a small utility component for rendering vNodes you could do it like this with a template.
Vue.component("vnode", {
functional: true,
render(h, context){
return context.props.node
}
})
Vue.component("comp", {
template: `
<ul class="comp">
<li class="comp-item" v-for="link in $slots.links"><vnode :node="link" /></li>
</ul>
`
})
new Vue({
el: "#app"
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.13/vue.js"></script>
<div id="app">
<comp>
link 1
link 2
</comp>
</div>
You can make use of scoped slots instead of slots
Your comp component receives a prop links which is an array of links(since static initialized as a custom option). Iterate over the links and pass link as data to the slot just like passing props to a component
Vue.component("comp", {
template: `
<ul class="comp">
<li class="comp-item" v-for="link in links">
<slot v-bind="link"></slot>
</li>
</ul>
`,
props: ["links"]
})
new Vue({
el: "#app",
// custom static option , accessed using vm.$options.links
links: [
{text: "link1"},
{text: "link2"},
{text: "lin3"}
]
})
In the parent where the comp component is used make use of a <template> element with a special attribute slot-scope , indicating that it is a template for a scoped slot.
The value of slot-scope will be used as the name of a temporary variable that holds the props object passed from the child:
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.13/vue.js"></script>
<div id="app">
<comp :links="$options.links">
<template slot-scope="link">
{{link.text}}
</template>
</comp>
</div>
Here is the working fiddle
If you don't like to put your data in array, and render list with v-for
You can put all of them in the component, no slot:
<ul class="comp">
<li class="comp-item">link 1</li>
<li class="comp-item">link 2</li>
</ul>
or with slot:
<comp>
<ul class="comp" slot="links">
<li class="comp-item">link 1</li>
<li class="comp-item">link 2</li>
</ul>
</comp>

Vue.js v-for item, bind attribute to current index / key

<li v-for="(value, key) in {facebook: [data], twitter: [data]}">
<i class="icon" :class="key"></i>
....
</li>
Error: [Vue warn]: Trying to access non-existent property "key" while rendering.
How can I bind key in attributes of li's children? It only works in li itself.
It works as expected for me. If you inspect the items in the snippet below, you'll see the class is assigned.
new Vue({el: 'body'});
<script src="//cdnjs.cloudflare.com/ajax/libs/vue/1.0.26/vue.min.js"></script>
<li v-for="(key, value) in {facebook: ['data'], twitter: ['data']}">
<i class="icon" :class="key">{{key}}</i>
</li>

Give dynamic id Angular2 Binds

My JavaScript:
this.items = [
{name: 'Amsterdam1', id: '1'},
{name: 'Amsterdam2', id: '2'},
{name: 'Amsterdam3', id: '3'}
];
My HTML:
<ul>
<li *ngFor="#item of items" id={{item.id}}>
{{ item.name}}
</li>
</ul>
I want to assign a dynamic id to each element, but can't get it to work. The name is showing up but the id is not.
the way you said works, Angular 2.0.0.beta.8.
in this case you can use for example:
[id]="item.id"
[attr.id]="item.id"
id={{item.id}}
<ul>
<li *ngFor="#item of items" #elem [id]="item.id">
{{ item.name}} ID: {{elem.id}}
</li>
</ul>
<ul>
<li *ngFor="#item of items" #elem [attr.id]="item.id">
{{ item.name}} ID: {{elem.id}}
</li>
</ul>
<ul>
<li *ngFor="#item of items" #elem id={{item.id}}>
{{ item.name}} ID: {{elem.id}}
</li>
</ul>
Plunker
You can look here for more information in Template syntax.
https://angular.io/guide/cheatsheet
All the methods provided by #AngelAngel are correct but just to explain why to use [attr.id] posted as answer.
Angular by default uses property binding. To tell Angular explicitly to use attribute binding, we use instead:
<ul>
<li *ngFor="#item of items" #elem [attr.id]="item.id">
{{ item.name}} ID: {{elem.id}}
</li>
</ul>
With attr.id you have to explicitly opt in to attribute binding because attribute binding is expensive. Attributes are reflected in the DOM and changes require for example to check if CSS selectors are registered that match with this attribute set. Property binding is JS only and cheap, therefore the default.
<ul>
<li *ngFor="#item of items" attr.id={{item.name}}>
{{ item.name}}
</li>
</ul>
This should work (worked for me on angular2).
I found the answer. I can use [attr.id] tag on the 'li' tag.

Categories