I have a list and a list_item component that I reuse a lot inside my application. On a simplified form:
contact_list.vue
<template lang="pug">
.table
.table-header.table-row
.table-col Contact
.table-col Info
.table-body
contact-list-item(v-for='contact in contacts',
:contact='contact',
#click='doSomething()')
</template>
contact_list_item.vue
<template lang="pug">
.table-row(#click='emitClickEvent')
.table-col {{ contact.name }}
.table-col {{ contact.info }}
</template>
When I use contact_list inside a specific component, I want to be able to send a slot that will add some new columns to the contact_list_item component. This slot will use data of the specific contact that is being rendered inside that contact_list_item component to generate the new columns.
How could I achieve that? Is using slot the best approach?
Thanks in advance.
Slots are the best approach and you will need to use a scoped slot for the contact-list-item component. I'm not really familiar with pug, so I will use HTML for the example.
In contact-list you would add a slot. Notice in this case that the contact is being passed as a property. This is so we can take advantage of scoped slots.
<div class="table">
<div class="table-header table-row">
<div class="table-col">Contact</div>
<div class="table-col">Info</div>
</div>
<div class="table-body">
<contact-list-item v-for='contact in contacts'
:contact="contact"
#click="doSomething"
:key="contact.id">
<slot :contact="contact"></slot>
</contact-list-item>
</div>
</div>
Then add a slot to the contact-list-item.
<div class="table-row" #click="emitClickEvent">
<div class="table-col">{{contact.name}}</div>
<div class="table-col">{{contact.info}}</div>
<slot></slot>
</div>
Finally, in your Vue template, use the scoped template.
<div id="app">
<contact-list :contacts="contacts">
<template scope="{contact}">
<div class="table-col">{{contact.id}}</div>
</template>
</contact-list>
</div>
Here is a working example. I have no idea what your styles are but notice the id column is now displayed in the contact-list-item.
You can use template for registering slot to the child of child component.
There is also a case when you want to have many named slots.
child.vue
<template>
<div>
<h2>I'm a father now</h2>
<grandchild :babies="babies">
<template v-for="(baby, id) in babies" :slot="baby.name">
<slot :name="baby.name"/>
</template>
</grandchild>
</div>
</template>
grandchild.vue
<template>
<div>
<p v-for="(baby, id) in babies" :key="id">
<span v-if="baby.isCry">Owe...owe...</span>
<slot :name="baby.name">
</p>
</div>
</template>
parent.vue
<template>
<div>
<h2>Come to grandpa</h2>
<child :babies="myGrandChilds">
<button slot="myGrandChilds[2].name">baby cry</button>
</child>
</div>
</template>
Add to #DrSensor's answer.
In Vue3, you should use dynamic slot name.
<template>
<div>
<h2>I'm a father now</h2>
<grandchild :babies="babies">
<template v-for="(baby, id) in babies" #[baby.name]>
<slot :name="baby.name"/>
</template>
</grandchild>
</div>
</template>
To translate several slots one level down conveniently to use the method described at this link, and if you modify it a little, perhaps you will be able to transfer it deeper.
Related
I have a parent comp which content should be displayd conditionally
when the userinput variable is false
The <div class="parent">
should be removed and the PopUp component displayed
I tried to use `v-if` but dont know how to remove the entire div with it elements
I could only change single elements with v-if else
parent comp
<template>
<div>
<h1>header</h1>
<div class="parent">
<h2>infos</h2>
<button>x</button>
</div>
</div>
</template>
<script>
import PopUp from './PopUp.vue';
export default {
data() {
return {
userinput: true,
}
}
}
</script>
child component - PopUp
<template>
<div>
<button>x</button>
<p>errorMessage</p>
</div>
</template>
Add v-if="userinput" doesn't work ?
If I understand what you want, you should have something like this :
// parent comp
<template>
<div>
<h1>header</h1>
<div v-if="userinput" class="parent">
<h2>infos</h2>
<button>x</button>
</div>
<PopUp v-else/>
</div>
</template>
Do not hesitate to read the Vue.js documentation
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app-gridview">
<div>
<button class="button" v-on:click="switchView()"><span>{{ buttonSwitchViewText }}</span></button>
<button class="button" v-on:click="switchData()"><span>{{ buttonSwitchDataText }}</span></button>
</div>
<div v-bind:class="[ isGridView ? 'grid-wrapper' : 'list-wrapper' ]">
<div class="grid-row" v-if="isGridView">
<div class="grid-header" v-for="name in gridData.columns">{{ name }}</div>
</div>
<!-- GridView structure -->
<div v-if="isGridView" class="grid-row" v-for="row in gridData.data">
<div class="list-row-item" v-for="name in gridData.columns">
<div>{{ row[name] }}</div>
</div>
</div>
<!-- ListView structure -->
<div v-if="!isGridView" class="list-row" v-for="row in gridData.data">
<img v-bind:src="row.ImagePath" class="list-image" />
<div class="list-property">
<div class="list-row-item" v-for="name in gridData.columns">
<div class="list-property-name">{{ name }}</div>
<div>{{ row[name] }}</div>
</div>
</div>
</div>
</div>
</div>
Getting error as
error Elements in iteration expect to have 'v-bind:key' directives vue/require-v-for-key
How can I solve this issue, With some reference, I come to know we need to pass v-bind-key for the value, But not sure how to pass it.
Whenever we are using v-for in vuejs, we need to pass a unique value which is key which helps in properly rendering the components whenever the loop runs.
You can pass it like <div class="list-row-item" v-for="(name, index) in gridData.columns" :key="index">
The value of the key needs to be unique.
You should use uniq index inside your loop. Look hear https://v2.vuejs.org/v2/guide/list.html
<div v-if="!isGridView" class="list-row" v-for="(row, index) in gridData.data" :key="index">
Hi with my script i can show only the first reccord because [0]
what should i do to show all reccords ? because sometimes are only one reccord and sometimes many.
<v-tab :title="sitePartGSM[0].serial_no" v-if="sitePartGSM[0]">
<div v-for="siteParts in sitePartGSM">
{{ siteParts.serial_no }}
<div class="description" v-for="item in siteParts.part_attributes">
<small>
<strong>{{item.x_name}}</strong>
{{item.x_value}}
</small>
</div>
</div>
</v-tab>
thanks
hi can use a index inside v-for for example
<div v-for="(siteParts, index) in sitePartGSM">
<v-tab :title="sitePartGSM[index].serial_no" v-if="sitePartGSM[index]">
{{ siteParts.serial_no }}
<div class="description" v-for="item in siteParts.part_attributes">
<small>
<strong>{{item.x_name}}</strong>
{{item.x_value}}
</small>
</div>
</v-tab>
</div>
with this method you can see all records
You can iterate over all your sitePartGSM elements and show them in your v-tab.
When using v-for you should define a :key attribute to improve the rerendering procedure has mentioned in the documentation
<v-tab v-for="siteParts in sitePartGSM" :title="siteParts.serial_no" :key="siteParts.serial_no">
<div>
{{ siteParts.serial_no }}
<div class="description" v-for="item in siteParts.part_attributes" :key="item.x_name">
<small><strong>{{item.x_name}}</strong> {{item.x_value}}</small>
</div>
</div>
</v-tab>
So here’s my case:
I’m creating a modal via:
<Modal id="modal">
<my-component></my-component>
</Modal>
Now I want the content inside the modal to be dynamic, so I can put <input> or <table> or w/e. I’ve tried using slots (it works), but it isn’t really dynamic. I’m wondering if I missed something that would allow the slot to be a bit more dynamic.
Here’s how my modal is set up:
<div
:id="id"
class="main"
ref="main"
#click="close_modal"
>
<div ref="content" class="content" :style="{minHeight: height, minWidth: width}">
<div ref="title" class="title" v-if="title">
{{ title }}
<hr/>
</div>
<div ref="body" class="body">
<slot></slot>
</div>
</div>
</div>
I think using slots is a good choice for this. With the introduction of slot-scope in 2.5, you basically get a reverse property capability where you can set defaults within the child component and access them within the parent. They are of course completely optional to use and you're free to place whatever content you like in the slots.
Here's an example component that would allow you to customize the header, body and footers as you see fit:
// MyModal.vue
<template>
<my-modal>
<slot name="header" :text="headerText"></slot>
<slot name="body" :text="bodyText"></slot>
<slot name="footer" :text="footerText"></slot>
</my-modal>
</template>
<script>
export default {
data() {
return {
headerText: "This is the header",
bodyText: "This is the body.",
footerText: "This is the Footer."
}
}
}
</script>
// SomeComponent.vue
<template>
<div>
<my-modal>
<h1 slot="header" slot-scope="headerSlotScope">
<p>{{ headerSlotScope.text }}</p>
</h1>
<div slot="body" slot-scope="bodySlotScope">
<p>{{ bodySlotScope.text }}</p>
<!-- Add a form -->
<form>
...
</form>
<!-- or a table -->
<table>
...
</table>
<!-- or an image -->
<img src="..." />
</div>
<div slot="footer" slot-scope="footerSlotScope">
<p>{{ footerSlotScope.text }}</p>
<button>Cancel</button>
<button>OK</button>
</div>
</my-modal>
</div>
</template>
<script>
import MyModal from './MyModal.vue';
export default {
components: {
MyModal
}
}
</script>
I have a html template as below
AdvanceTemplatePage.vue
<template>
<div class="content edit-page management">
<md-card class="page-card">
<md-card-header class="page-card-header">
<md-card-header-text class="page-title">
<div class="md-title">{{ 'AdvanceDetail' | translate }}</div>
</md-card-header-text>
</md-card-header>
<md-card-content class="page-content">
<div class="info-container">
<div class="label">{{ 'AdvanceStatus' | translate }}</div>
<div class="value">{{ '#AdvanceStatus' }}</div>
</div>
<div class="info-container">
<div class="label">{{ 'Amount' | translate }}</div>
<div class="value">{{ '#Amount' }} {{ '#Currency' }}</div>
</div>
<div class="info-container">
<div class="label">{{ 'RefundStartingAt' | translate }}</div>
<div class="value">{{ '#RefundStartingAt' }}</div>
</div>
<div class="info-container">
<div class="label">{{ 'RefundInstallmentQuantity' | translate }}</div>
<div class="value">{{ '#RefundInstallmentQuantity' }}</div>
</div>
<div class="info-container">
<div class="label">{{ 'Description' | translate }}</div>
<div class="value">{{ '#Description' }}</div>
</div>
</md-card-content>
</md-card>
</div>
</template>
I need to access this template html from another page.
I am trying to access html like this on another page, but I do not know how to do it.
import AdvanceTemplatePage from 'pages/print/template/AdvanceTemplatePage.vue';
methods: {
onPrint() {
const vm = this;
const template = new AdvanceTemplatePage({ el: '#templateDiv' })
}
}
How can i get html info from another page in vue.js
Any help will be appreciated.Thanks.
The template will be compiled to a render function so your code won't work. And basically you can't get the original html template.
I'm not sure what you are trying to do. If you want to get the source template content, the easiest way to achieve this is to save the template in a variable so that you can ref to it in the future.
Note that .vue doesn't support named exports so you need to put this in another .js file:
export const templateOfAdvanceTemplatePage = `
<div class="content edit-page management">
<md-card class="page-card">
...
</md-card>
</div>
`
and in your AdvanceTemplatePage.vue
import templateOfAdvanceTemplatePage from 'path/to/templateOfAdvanceTemplatePage.js'
export default {
template: templateOfAdvanceTemplatePage,
...
}
Now you can simply import templateOfAdvanceTemplatePage everywhere you want since it's just a variable.
If you want the compiled html instead of the source template, I found out a tricky way to achieve. Simply render the component and use innerHTML to get the html:
in another component, you render but hide it, also give it a ref:
<template>
...
<advance-template-page v-show="false" ref="foo"></advance-template-page>
...
</template>
now you can get the html content in your methods:
onPrint() {
const template = this.$refs.foo.$el.innerHTML
}
You can just add <slot></slot> (documentation) to your component