I am passing an array of object as props from parent to the child component. This works fine.
When I do {{ questionList }} its show me my whole data, so the props is being received.
When I try to output the data in the child component as {{ questionList[0].question }}, this also works fine. But it only shows me the first question.
But when I try to output my data as
{{ questionList.question }}, nothing is being shown.
So how can I ouput all my questions?
Please check out my code below.
Parent Component - questionForm
<template>
<div class="form" >
<questions v-for="question in questionList" vbind:key="question.question" v-bind:questionList="questionList" />
</div>
</template>
<script>
import questionList from '../questions/questionList.js'
import questions from '../components/questions'
export default {
name: 'questionForm',
components: {
questions
},
data () {
console.log(questionList)
return {
questionList
}
}
}
Child component - questions
<template>
<div class="questions">
<p> {{ questionList.question }} </p>
</div>
</template>
<script>
export default {
name: 'questions',
props: ['questionList'],
data () {
return {
}
}
}
Imported data - questionList.js (the data is much bigger but I reduce it for readability purposes)
export default [
{
question: 'Sex',
id: 0,
options: [
{label: 'Woman', value: 2},
{label: 'Male', value: 1}
]
},
{
question: 'Age',
id: 1,
options: [
{label: '8-12', value: 1},
{label: '12-14', value: 2},
{label: '14-16', value: 2}
]
}]
You are passing the whole questionList while you should pass the single question object in the child component like this
<template>
<div class="form" >
<questions v-for="question in questionList" v-bind:key="question.question" v- bind:object="question" />
</div>
</template>
<script>
import questionList from '../questions/questionList.js'
import questions from '../components/questions'
export default {
name: 'questionForm',
components: {
questions
},
data () {
console.log(questionList)
return {
questionList
}
}
}
where question here v-bind:object="question" is the object from the loop
and then you can use it in the child component in the props
<template>
<div class="questions">
<p> {{ object.question }} </p>
</div>
</template>
<script>
export default {
name: 'questions',
props: ['object'],
data () {
return {}
}
}
You are binding the whole list to each question child with:
v-bind:questionList="questionList"
It seems like you want each child to have a single question since you are using v-for in the parent. If that’s the case, just bind a single question to each child:
<questions v-for="question in questionList" vbind:key="question.id" v-bind:question="question" />
Then in the child you should be able to access the properties of that question (make sure to change the prop name in the child component to question):
{{question.question}}
I changed the code a little bit,
but what you are trying to do is basically looping through the question list then passing each question as a prop in the child question component.
so try this :D
Inside The Parent Component
<template>
<div class="form" >
<questions v-for="{question, index} in questionList" :key="index" :question="question" />
</div>
</template>
<script>
import questionList from '../questions/questionList.js'
import questions from '../components/questions'
export default {
name: 'questionForm',
components: {
questions
},
data () {
console.log(questionList)
return {
questionList
}
}
}
Next inside your child component
<template>
<div class="questions">
<p> {{ question.question }} </p>
</div>
</template>
<script>
export default {
name: 'questions',
props: ['question'],
}
}
Related
I am developing a Vue app with pimcore and twig in the backend. I have to create a component that receives the slot (another component), and render it inside, but with dynamic props.
Here is root in viani.twig.html:
<div>
<viani-accordion>
<viani-filter-checkbox v-slot="{filterRows}"></viani-filter-checkbox>
</viani-accordion>
</div>
There is nothing special. viani-accordion is a parent component and the viani-filter-checkbox is a slot, which I have to render with appropriate props.
Here you can see the VianiAccordion.vue:
<template>
<div class="wrapper">
<AccordionTitle v-for="(item, index) in dataToRender" :item="item" :key="index">
/*I think this is incorrect, but I'm trying to prop data that I need in viani-filter-checkbox*/
<slot :filter-rows="item.items"></slot>
</AccordionTitle>
</div>
</template>
<script>
import AccordionTitle from './Accordion-Title';
export default {
name: "Viani-Accordion",
components: {AccordionTitle},
data() {
return {
dataToRender: [
{
name: 'first item',
items: [
{
name: 'oil olive',
quantity: 10,
},
{
name: 'subitem 2',
quantity: 11,
},
]
},
{
name: 'second item',
items: [
{
name: 'subitem 1',
quantity: 10,
},
{
name: 'subitem 2',
quantity: 11,
},
]
}
]
}
},
}
</script>
Then I have another deeper child component Accordion-Title.vue that is responsible for rendering the slot (so I have to pass the slot through the multiple components):
<template>
<div v-if="isOpen" class="child-wrapper">
/*I think this is incorrect, but I'm trying to prop data that I need in viani-filter-checkbox*/
<slot :filterRows="item.items"></slot>
</div>
</template>
<script>
export default {
name: "Accordion-Title",
props: {
item: {
type: Object,
default: null
}
}
}
</script>
and finally Viani-FiltersCheckbox.vue:
<template>
<div>
//child component which we don't need in this case
<FilterCheckboxRow v-for="(item, index) in filterRows" :item="item" :key="index"/>
</div>
</template>
<script>
import FilterCheckboxRow from './FilterCheckboxRow'
export default {
name: "VianiFilterCheckbox",
components: {
FilterCheckboxRow
},
props: {
//here I expect to get array to render, but got default value (empty array)
filterRows: {
type: Array,
default: function () {
return []
}
},
},
}
</script>
So I need to pass the props (filterRows) to the component (Viani-FiltersCheckbox.vue), which is rendered as a slot. I have read this and this, but still don't get where the mistake and how to get the props I need.
It looks like you're trying to access your props through props.XXX. That's typically only done in templates for functional components. Otherwise, the props would be accessed without the props. prefix (i.e., props.item.items should be item.items).
And to pass filterRows from the scope data to the child component, declare a <template>, and then move your child into that, binding filterRows there:
<viani-accordion>
<!-- BEFORE: -->
<!-- <viani-filter-checkbox v-slot="{filterRows}"></viani-filter-checkbox> -->
<template v-slot="{filterRows}">
<viani-filter-checkbox :filterRows="filterRows"></viani-filter-checkbox>
</template>
</viani-accordion>
My parent app has the following data structure
export default {
name: "app",
components: {
Template1
},
data() {
appState: {
questionCount: 0,
isIntro: true, //when true, perform single initial animation
},
questions: [
{
id: 0,
question: "What do you think is most helpful in increasing workforce diversity?",
}
...
]
}
}
I want to render Template1 with just the question object from the data equal to the current question count, so I tried this:
<template>
<div id="app">
<Template1
questionItem="questions[appState.questionCount]"
/>
</div>
</template>
But I don't think that is the proper way to reference an array index. What is the proper way to do this?
You should bind it using v-bind: or just : as follows :
<template>
<div id="app">
<Template1
:questionItem="questions[appState.questionCount]"
/>
</div>
</template
i'm trying to do mini shop app in VueJS without backend, only adding items to shopping cart.
I have component One(Nmdgrey.vue), where i have e.g. boots and "Add to cart" button.
<button #click="addToCart">Add to cart</button>
And function in first component:
methods: {
addToCart() {
const boots = { name: 'adidas nmd' };
this.cart.push(boots);
}
}
And i have component Two, where i have shopping cart
<div v-for="item in cart">
{{item.name}}
</div>
And JS
import Nmdgrey from '#/components/Nmdgrey.vue';
export default {
name: 'Shoppingcart',
components:
Nmdgrey,
data() {
return {
cart: [
{ name: 'adidas adizero' },
]
}
},
};
How i can add boots from component One to list in component Two?
I have this.cart.push(boots); in component One but it didn't work
This is what i want but button didn't work: codesandbox
Use $emit, when child components need to communicate with parent.
Refer official doc:Listening-to-Child-Components-Events
Nmdgrey.vue
<template>
<div>
<!-- component 1 -->
<button #click="add">Add to cart</button>
</div>
</template>
<script>
export default {
name: "Numdgrey",
methods: {
add() {
const boots = { name: "adidas nmd" };
this.$emit("add", boots);
}
}
};
</script>
Shoppingcart.vue
<template>
<div>
<!-- component 2 -->
<nmdgrey #add="addCart"></nmdgrey>
<br>
<div v-for="(item, index) in cart" :key="index">{{item.name}}</div>
</div>
</template>
<script>
import Nmdgrey from "./Nmdgrey.vue";
export default {
name: "ShoppingCart",
components: {
Nmdgrey
},
data() {
return {
cart: [{ name: "adidas adizero" }]
};
},
methods: {
addCart(good) {
this.cart.push(good);
}
}
};
</script>
Codesandbox demo : https://codesandbox.io/s/z2qz6oy8yp
From your post I've deduced that what you want to do is to share data from two or more Vue components. For this purpose, you could use Vuex, which provides centralized state management.
This way you could use a vuex mutation to add items to the cart, which could be used from any component. You would also be able to retrieve the cart data from any of them by using vuex getters.
you need to create a post method where you take over everything you need from one page to another page. after that everything will get into the cart and you will be able to create your page.
Codesandbox demo : https://codesandbox.io/s/94l44j8j14
Use props to pass the values to cart component.
Nmdgrey.vue
<template>
<div>
<b>Component 1 : NmdGrey</b><br><br>
<button v-for="(product, index) in boots" :key="index"
#click="addToCart(product.name)" >Add {{product.name}} to Cart</button>
<br><br><hr><br>
<shoppingcart :cart="cart" />
</div>
</template>
<script>
import shoppingcart from './shoppingcart.vue';
export default {
name: 'Nmdgrey',
components:{shoppingcart},
data() {
return {
boots:[{name: 'adidas adizero'}, {name: 'puma walker'}, {name: 'nike shoe'}, {name: 'adidas plain'}],
cart:[],
}
},
methods: {
addToCart(boots) {
this.cart.push({ name: boots });
}
}
}
</script>
Shoppingcart.vue
<template>
<div>
<b>Component 2 : Shopping cart</b>
<br>
<br>
<div v-for="(product, index) in cart" :key="index">{{product.name}}</div>
</div>
</template>
<script>
export default {
name: "Shoppingcart",
props: ["cart"],
data() {
return {};
}
};
</script>
Well, If your application is small, then you can create Vue mixins for addToCart and call it whenever you'll require in your component.
Similar to the methods, you can share data across the components with the use of mixins.
Here is the mixins official docs
Here is the working JsFiddle
Hope this helps!
I'm trying to make some reusable template of slot element of VueJS, but it doesn't work well.
The problem I'm facing looks like this. I made 3 Vue files to construct some template.
Guide.vue which will be unit of for loop.
ListView.vue importing ListTemplate.vue and Guide.vue
ListTemplate having a v-for loop for rows props from ListView.vue and
a slot for Guide.vue.
I want to pass row props yielding from v-for loop to Guide.vue through the slot but it seems to be undefined. So i tried to declare slot-scope on template wrapping component element on ListView.vue, but it doesn't work.
// Guide.vue
<template>
<tr onclick="location.href='info_view.html'">
<td>
<p class="alarm_h1">{{ slotProps.row.title }}</p>
<p class="alarm_h2">{{ slotProps.row.title }}</p>
<p class="alarm_h2">{{ slotProps.row.title }}</p>
</td>
</tr>
</template>
<script>
export default {
name: "guide",
props: ['slotProps', 'row']
}
</script>
// List.vue
<template>
<div>
<Guide-head/>
<List-template :rows="guides">
<template>
<component :is="tr_component"></component>
</template>
</List-template>
</div>
</template>
<script>
import Guide from '../../../components/template/list/tr/Guide'
import GuideHead from '../../../components/template/head/GuideHead'
import ListTemplate from '../../../components/template/list/ListTemplate'
export default {
name: "list",
components: {
Guide,
GuideHead,
ListTemplate
},
data() {
return {
guides: [
{id: 1, date: '2018.08.20 15:00', title: 'title 1', emergency: false},
{id: 2, date: '2018.08.20 15:00', title: 'title 2', emergency: false},
{id: 3, date: '2018.08.20 15:00', title: 'title 3', emergency: false}
],
tr_component: 'guide'
}
}
}
</script>
// ListTemplate.vue
<template>
<div>
<table class="reser_check_table" cellpadding="0" cellspacing="0">
<div v-for="(row, idx) in rows" #click.native="clickNotice(idx)" :key="row.id">
<slot v-bind:row="row">{{row.title}}</slot>
</div>
</table>
</div>
</template>
<script>
export default {
name: "list",
props: ['rows'],
/*components: {
rowTemplate: Row
},*/
methods: {
clickNotice(idx) {
console.log('clicked');
//this.$emit('viewNotice', idx); #viewNotice="view"
},
view() {
}
}
}
</script>
I have two components: Parent and Child. The Parent has an array of cars the Child is supposed push objects the cars array. My problem is that my Child component turns cars into an object, instead of pushing an object into the cars array. My Parent component:
<template>
<child v-model="cars"></child>
<ul>
<li v-for="car in cars">
{{ car.model }}
</li>
</ul>
</template>
export default {
data: function() {
return {
cars: []
}
}
}
My Child component:
<template>
<div>
<button type="button" #click="addCar()">Add Car</button>
</div>
</template>
export default {
methods: {
addCar() {
this.$emit("input", { model: "some car model" })
}
}
}
Expected results:
cars gets updated and becomes [{ model: "some car model"}, { model: "some car model"}, etc...]
Actual results:
cars becomes an object {model: "some car model"}
Here is my fiddle:
https://jsfiddle.net/t121ufk5/529/
I assume something is wrong with the way i am using v-model on my child component and/or the way I am emitting is incorrect. Can someone help? Thanks in advance!
Lets discuss, why you not get proper result.Then we discuss other approach to solve this problem.
Firstly we need to understand how v-model works on custom components by default.
When using a text input (including types such as email, number, etc.) or textarea, v-model="varName" is equivalent to :value="varName" #input="e => varName = e.target.value". This means that the value of the input is set to varName after each update to the input varName is updated to the value of the input. A normal select element will act like this too, though a multiple select will be different.
Now we need to understand,
How Does v-model Work On Components?
Since Vue doesn’t know how your component is supposed to work, or if it’s trying to act as a replacement for a certain type of input, it treats all components the same with regards to v-model. It actually works the exact same way as it does for text inputs, except that in the event handler, it doesn’t expect an event object to be passed to it, rather it expects the value to be passed straight to it. So…
<my-custom-component v-model="myProperty" />
…is the same thing as…
<my-custom-component :value="myProperty" #input="val => myProperty = val" />
So when you apply this approach. You have to receive value as a props. and make sure you $emit name is input.
Now you can ask me at this stage,what you do wrong?
Ok, look at like code #input="val => myProperty = val"
when you $emit whit a new value. this newValue will updated our parent value which you wanna update.
Here is your code this.$emit("input", { model: "some car model" }).
You update your parent value with a object. So your Array updated with a Object.
Lets solve the full problem.
Parent Component:
`
<template>
<child v-model="cars"></child>
<ul>
<li v-for="car in cars">
{{ car.model }}
</li>
</ul>
</template>
export default {
data: function() {
return {
cars: []
}
}
}
`
Child Component:
<template>
<div>
<button type="button" #click="addCar()">Add Car</button>
</div>
</template>
export default {
props: ['value']
methods: {
addCar() {
this.$emit("input", this.value.concat({model: "some car model"}))
}
}
}
You can actually solved it several way.
Second Approach,
Parent:
<template>
<child :cars="cars"></child>
<ul>
<li v-for="car in cars">
{{ car.model }}
</li>
</ul>
</template>
export default {
data: function() {
return {
cars: []
}
}
}
Child:
<template>
<div>
<button type="button" #click="addCar">Add Car</button>
</div>
</template>
export default {
props: {
cars: {
type: Array,
default:[]
}
},
methods: {
addCar() {
this.cars.push({ model: "some car model" })
}
}
}
Last Approach:
Parent:
<template>
<child #update="addCar"></child>
<ul>
<li v-for="car in cars">
{{ car.model }}
</li>
</ul>
</template>
export default {
data() {
return {
cars: []
}
}
},
methods: {
addCar() {
this.cars.push({ model: "some car model" })
}
}
}
Child:
<template>
<div>
<button type="button" #click="update">Add Car</button>
</div>
</template>
export default {
methods: {
update() {
this.$emit('update')
}
}
}
It is possible to trigger the event of updating the transmitted value in props
In the parent
<my-component :is-open.sync="isOpen" />
In my-component
this.$emit('update:isOpen', true)