I have a Vue (2) component --
Vue.component('item-component', {
props: ['item'],
template: `<div :id="???">item.name</div>`
})
with HTML --
<template v-for="item in items">
<item-component v-bind:item="item"></item-component>
</template>
items are --
items = [{ itemid: 1, name: "Foo" }]
The question is: how can I generate derived id value like --
<div id="my-item-1">Foo</div>
You can just use:
<div :id="'my-item-'+item.itemid">Foo</div>
Related
In my Vue application, I have a list of months in my parent component like this-
When clicking on any month, I would like to pass its id to the child component named timeComponent. Below is the code.
Parent Component-
<template>
<div class="content container-fluid" id="prayer_time">
<div class="col-md-12">
<div class="card">
<div class="card-header p-2">
<ul class="nav nav-pills">
<li class="nav-item" v-for="(month, index) in months" :key="index">
<a class="nav-link" v-on:click="monthId(index + 1)">
{{ month }}
</a>
</li>
</ul>
</div><!-- /.card-header -->
</div>
<!-- /.card -->
</div>
<div v-if="prayertimeshow">
<timeComponent :month_id=month_id :mosque_id=this.mosque_id />
</div>
</div>
</template>
<script>
import timeComponent from './TimeComponent.vue';
export default {
props: ['mosque_id'],
components: {
timeComponent
},
data(){
return {
month_id: '',
prayertimeshow: '',
months : ['January', 'February', 'March','April','May','June','July','August','September','October','November','December']
}
},
methods:{
monthId(event) {
this.month_id = event;
this.prayertimeshow = true;
}
}
}
</script>
The problem is that when I click on any month for the first time, the month_id value is passed to the child component perfectly.
But it is not working when I click on another month for the second time.
In the child component, I am accessing the prop value like this-
<script>
export default {
props: ['month_id'],
created: function () {
console.log(this.month_id);
}
}
</script>
Is it the correct way to do this?
The issue is that created hook runs only once when the child component has been created.
The better approach to check the updated prop value in the child is to use a watch hook.
You can check the update in month_id like this-
Child Component-
<template>
<div>{{ month_id }}</div>
</template>
<script>
export default {
props: ["month_id"],
watch: {
month_id(newVal, oldVal) {
// newVal = updated month_id
this.getTime(newVal);
},
},
methods: {
getTime(input) {
console.log(input);
},
},
};
</script>
How can I show a custom component- BaseButton.vue in the rows of the table, if table component and button are common components?
I made the component BaseTable.vue. I use this component for all tables.
<template>
<b-table :fields="fields" :items="items"></b-table>
</template>
<script>
export default {
props: {
fields:{
type: Array,
require: true
},
items:{
type: Array,
require: true
},
}
fields - object in a parent component.
items - object from API.
Parent component (base button doesn't appear in table on page):
<template>
<base-table :fields="fields" :items="items">
<template #cell(actions)>
<base-button></base-button>
</template>
<base-table>
</template>
<script>
import BaseButton from '...';
export default {
data() {
return {
fields: [
{ key: 'Caption', label: 'Name' },
{ key: 'PreviousMonthCounter', label: 'Prev. month' },
{ key: 'CurrentMonthCounter', label: 'Curr. month' },
{ key: 'actions', label: 'Action' }
],
items: []
}
},
components: {
BaseButton
}
}
And now I need to render BaseButton.vue in the last column of the table and I can't to make it - table has only text fields from items.
Need to create a child component (BaseTable):
<template>
<b-table :items="items" :fields="fields">
<slot v-for="slot in Object.keys($slots)" :name="slot" :slot="slot" />
<template v-for="slot in Object.keys($scopedSlots)" :slot="slot" slot-scope="scope">
<slot :name="slot" v-bind="scope"></slot>
</template>
</b-table>
</template>
And use it in the parent component like , but must be replaced by your component name.
For example:
<base-table :fields="fields" :items="items">
<template #cell(nameOfTheField)="{item}">
{{ item.key }}
</template>
<template #cell(nameOfTheField)>
<base-button></base-button>
</template>
What I want to achieve is something like:
<li v-for="(item, index) in items" :key="index>
<div v-if="item.Component">
<item.Component :value="item.value" />
</div>
<div v-else>{{ item.value }}</div>
</li>
But anyway I don't like at all this solution. The idea of defining Component key for an item in items list is hard to maintain since at least it is hard to write it in template-style way (usually we are talking about too long HTML inside). Also I don't like to wrap item.Component inside div.
data() {
return {
list: [{
value: 'abc',
Component: {
props: ['value'],
template: `123 {{ value }} 312`
}
}]
};
}
Does anyone know the best-practice solution for this and where Vue describes such case in their docs?
You can use Vue's <component/> tag to dynamically set your component in your list.
<li v-for="(item, index) in items" :key="index>
<component v-if="item.Component" :is="item.Component" :value="item.value"></component>
<div v-else>{{ item.value }}</div>
</li>
<script>
...,
data: () => ({
list: [{
value: 'abc',
Component: {
props: ['value'],
template: `<div>123 {{ value }} 312</div>` // must be enclosed in a element.
}
}]
})
</script>
You can also import a component too so you can create a new file and put your templates and scripts there.
Parent.vue
<script>
import SomeComponent from "#/components/SomeComponent.vue"; //import your component here.
export default {
data() {
return {
list: [
{
value: "abc",
Component: SomeComponent // define your imported component here.
},
]
};
}
};
</script>
SomeComponent.vue
<template>
<div>123 {{ value }} 312</div>
</template>
<script>
export default {
name: "SomeComponent",
props: ["value"]
};
</script>
Here's a demo.
In my project, I use vue.js.
I want to display content of list with nested loop。 In parent page, i have defined:
<template>
<div>
<detail-header></detail-header>
......
<detail-list></detail-list>
</div>
</template>
The component of detail-list is :
<template>
<div>
<div v-for="(item, index) of list" :key="index">
<div class="item-title border-bottom">
<span class="item-title-icon"></span>
{{item.title}}
</div>
<div v-if="item.children" class="item-children">
<detail-list :list="item.children"></detail-list>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'DetailList',
props: {
list: Array
},
data () {
return {
list: [{
title: 'adult',
children: [{title: 'threePeople',children: [{ title: 'threePeople-w'}]}, {title: 'fivePeople'}]
}, {
title: 'student'
}, {
title: 'child'
}, {
title: 'offer'
}]
}
}
}
</script>
unlucky, I got a error message:
Duplicated key 'list' of list: [{ in detail-list
who can help me ?
If you want this to work, keep the list in props (and remove it from DetailList's data) and define in your parent page's data.
So the first DetailList and its children will have the list as a prop.
So you'll have in the parent page :
<template>
<div>
<detail-header></detail-header>
......
<detail-list :list="list"></detail-list>
</div>
</template>
<script>
export default {
name: 'Parent',
data () {
return {
list: [{ ... the list ... }]
}
}
I am trying to build a table component.
I want to define and pass the column metadata for the grid as an array prop and also pass the actual data as another prop to the grid.
I was able to achieve that without many issues.
But, now I would like to pass a dynamic component as a part of each column definition so that the user can define/control the way the cell gets rendered (content with edit delete buttons in same cell etc.)
Is there a way to pass a dynamic component as a prop and then have this component rendered?
<parent-comp>
<tr class="" v-for="result in dataSource">
<template v-for="column in columns">
<td>
<template v-if="column.customComponent">
######## How do I render this customComponent ########
</template>
</td>
</template>
</tr>
</parent-comp>
where the dataSource data can be something like
[
columns: [{
name: "something",
customComponent: SomeCustomComponent
}, {
name: "another thing",
customComponent: AnotherOtherCustomComponent
}]
]
Will be happy to elaborate/clarify on this if the ask above is not clear.
As suggested in the comments above, you can use a dynamic component in your template and pass the definition of the component in your property.
console.clear()
const ColumnOne = {
template: `<h1>I am ColumnOne</h1>`
}
const ColumnTwo = {
template: `<h1>I am ColumnTwo</h1>`
}
Vue.component("parent-comp",{
props:["columns"],
template:`
<div>
<component v-for="column in columns"
:is="column.customComponent"
:key="column">
</component>
</div>
`
})
new Vue({
el:"#app",
data:{
columns:[{
name: "something",
customComponent: ColumnOne
}, {
name: "another thing",
customComponent: ColumnTwo
}]
}
})
<script src="https://unpkg.com/vue#2.2.6/dist/vue.js"></script>
<div id="app">
<parent-comp :columns="columns"></parent-comp>
</div>