Passing props to VueJS component thru v-repeat turns up empty - javascript

I'm a bit new to VueJS. I have this Blade template, which is passed the variable $plans which contains [{id:1,name:'1st Plan'},{id:2,name:'2nd Plan'}]:
#extends('layouts.backend')
#section('content')
#if(Auth::user()->can('edit-plans'))
<edit-plans
:workouts="{!! json_encode($plans) !!}"
></edit-plans>
#endif
#endsection
Within edit.vue:
<template>
<workout
v-repeat="workouts"
></workout>
</template>
<script>
export default {
props: [
'workouts'
]
}
</script>
Within workouts.vue:
<template>
<div>{{ id }} :: {{ name }}</div>
</template>
<script>
export default {
props: [
]
}
</script>
I have these templates registered globally:
Vue.component('edit-plans', require('./components/plans/edit.vue'));
Vue.component('workout', require('./components/plans/workout.vue'));
When I compile with yarn, I simply get one line with :: in it but noid or name values showing up on two lines as expected.
when I check the Chrome Vue JS developer extension, I see this in the EditPlans component...
...but nothing in the Workouts component.

In edit.vue declare the v-for (instead of v-repeat) and pass the props. In workout.vue, declare the props so they are available in the template.
edit.vue:
<template>
<workout
v-for="workout in workouts" :id="workout.id" :name="workout.name" :key="workout.id"
></workout>
</template>
<script>
export default {
props: ['workouts']
}
</script>
workout.vue:
<template>
<div>{{ id }} :: {{ name }}</div>
</template>
<script>
export default {
props: ['id', 'name']
}
</script>
Demo:
Vue.component('edit-plans', {
template: "#edit-plans-tpl",
props: ['workouts']
});
Vue.component('workout', {
template: "#workout-tpl",
props: ['id', 'name']
});
new Vue({
el: '#app',
data: {
message: [1,2]
}
})
<script src="https://unpkg.com/vue#latest/dist/vue.min.js"></script>
<template id="edit-plans-tpl">
<div>
<workout
v-for="workout in workouts" :id="workout.id" :name="workout.name" :key="workout.id"
></workout>
</div>
</template>
<template id="workout-tpl">
<div>{{ id }} :: {{ name }}</div>
</template>
<div id="app">
<edit-plans :workouts='[{"id":1,"name":"1st Plan"}, {"id":2,"name":"2nd Plan"}]'>
</edit-plans>
</div>
Update, per comment: What if I just want to pass each full workout into the child instead of each part of the object? In other words, I'd be able to do {{ workout.id }} :: {{ workout.name }}
You can use v-bind for that. Just change the edit.vue, keep workout.vue the same as above.
edit.vue:
<template>
<workout
v-for="workout in workouts" v-bind="workout" :key="workout.id"
></workout>
</template>
<script>
export default {
props: ['workouts']
}
</script>
workout.vue: same as above.
Demo for this:
Vue.component('edit-plans', {
template: "#edit-plans-tpl",
props: ['workouts']
});
Vue.component('workout', {
template: "#workout-tpl",
props: ['id', 'name']
});
new Vue({
el: '#app',
data: {
message: [1,2]
}
})
<script src="https://unpkg.com/vue#latest/dist/vue.min.js"></script>
<template id="edit-plans-tpl">
<div>
<workout
v-for="workout in workouts" v-bind="workout" :id="workout.id"
></workout>
</div>
</template>
<template id="workout-tpl">
<div>{{ id }} :: {{ name }}</div>
</template>
<div id="app">
<edit-plans :workouts='[{"id":1,"name":"1st Plan"}, {"id":2,"name":"2nd Plan"}]'>
</edit-plans>
</div>
Update, per comment: So no matter what, I have to declare every property within the workout object? I can't just do props: ['workout'] within the workout.vue so I can do {{ workout.id }} :: {{ workout.name }}?
You can declare the workout prop in the child and pass it in the parent like :workout="workout":
edit.vue:
<template>
<workout
v-for="workout in workouts" :workout="workout" :key="workout.id"
></workout>
</template>
<script>
export default {
props: ['workouts']
}
</script>
workout.vue:
<template>
<div>{{ workout.id }} :: {{ workout.name }}</div>
</template>
<script>
export default {
props: ['workout']
}
</script>
Demo for this:
Vue.component('edit-plans', {
template: "#edit-plans-tpl",
props: ['workouts']
});
Vue.component('workout', {
template: "#workout-tpl",
props: ['workout']
});
new Vue({
el: '#app',
data: {
message: [1,2]
}
})
<script src="https://unpkg.com/vue#latest/dist/vue.min.js"></script>
<template id="edit-plans-tpl">
<div>
<workout
v-for="workout in workouts" :workout="workout" :key="workout.id"
></workout>
</div>
</template>
<template id="workout-tpl">
<div>{{ workout.id }} :: {{ workout.name }}</div>
</template>
<div id="app">
<edit-plans :workouts='[{"id":1,"name":"1st Plan"}, {"id":2,"name":"2nd Plan"}]'>
</edit-plans>
</div>
Note: :key="workout.id" added to the loop: This default mode is efficient, but only suitable when your list render output does not rely on child component state or temporary DOM state. (thanks #channasmcs).

Related

How to show newly data added?

I want to ask why the newly added data does not display in the template, but it does show the newly added data in using console.log (see the ViewPost.vue).
This is the result after I add new post: result.png. Someone knows how to achieve this?
Here is my parent component:
<template>
<section>
//more codes here
<ViewPost v-for="data in postData" :key="data.post_id" :data="data" />
</section>
</template>
<script>
import { mapState } from 'vuex';
export default {
components: {ViewPost},
computed: {
...mapState({
postData: state => state.post.datas,
}),
}
//more codes here
};
</script>
And below is the ViewPost.vue
<template>
<span>
<div class="card mb-10">
<h1>body</h1>
{{ data.post_body }}
</div>
</span>
</template>
<script>
export default {
props: {
data: {},
},
created() {
console.log(this.data);
},
};
index as a key may work fine.
<template>
<section>
//more codes here
<ViewPost v-for="(data,index) in postData" :key="index" :data="data" />
</section>
</template>
<script>
import { mapState } from 'vuex';
export default {
components: {ViewPost},
computed: {
...mapState({
postData: state => state.post.datas,
}),
}
//more codes here
};
</script>
ViewPost.vue
<template>
<span>
<div class="card mb-10">
<h1>body</h1>
{{ data.post_body || data.body }}
</div>
</span>
</template>
<script>
export default {
props: ['data']
created() {
console.log(this.data);
},
};

Vue Scoped Slots two way data binding between component and slot [duplicate]

This question already has an answer here:
update data in slot vuejs
(1 answer)
Closed 2 years ago.
I have a scoped slot. I need the content that am passing to the slot to be able to affect the parent template.
This is what i have so far:
Parent.vue
<template>
<div>
<slot :text="text" :msg="msg"/>
<p>{{text}}</p>
<p>{{msg}}</p>
</div>
</template>
<script>
export default {
name: "Parent",
data() {
return {
text: "",
msg: ""
};
}
};
</script>
App.vue
<template>
<parent>
<template #default="{ text, msg }">
<input type="text" v-model="text"/>
<input type="text" v-model="msg"/>
</template>
</parent>
</template>
<script>
import Parent from "./components/Parent";
export default {
name: "App",
components: {
Toolbar
},
}
This does'nt work. How can i do something of the sort?
This is not possible. You can't change props (in this case slot props) given to your component(App.vue), but you can do something like this, with a "handler" method.
Parent.vue
<template>
<div>
<slot :text="text" :msg="msg" :setValue="setValue" />
<p>{{ text }}</p>
<p>{{ msg }}</p>
</div>
</template>
<script>
export default {
name: 'Parent',
data() {
return {
text: '',
msg: ''
};
},
methods: {
// set the current value with a function
setValue(e) {
this[e.target.name] = e.target.value;
}
}
};
</script>
App.vue
<template>
<parent>
<template #default="{ text, msg, setValue }">
Text: {{ text }}<br />
Msg: {{ msg }}<br /><br />
<!-- I have named the input fields after the variables in your data object and have the "setValue" method triggered by #input. -->
<input type="text" name="text" #input="setValue" />
<input type="text" name="msg" #input="setValue" />
</template>
</parent>
</template>
<script>
import Parent from './components/Parent';
export default {
name: 'App',
components: {
Parent
}
};
</script>

How to run component's method via HTML in Vue2

I have a .vue file, ServiceList, which imports the component Information.vue. I would like to run Information's code in a loop in ServiceList's template like so:
ServiceList.vue
<template>
<div>
<h1>Headline</h1>
<div v-for="service in services">
<h2>{{ service.name }}</h2>
<information v-bind:service="service"/>
</div>
</div>
</template>
<script>
import Information from './Information'
... (more components here)
export default {
components: {
Information,
... (more components here)
},
... (rest of export here)
}
</script>
This is what Information looks like:
Information.vue
<template>
<div>
<p v-bind:id="'info'+service.id"/>
</div>
</template>
<script>
export default {
props:['service'],
data: function () {
return {
services: this.$store.state.Services.data
}
},
methods: {
getInfo: function (service) {
var info = '<b>Servicename:</b> <br>';
info += service.name;
... (method adds more to 'info' here)
document.getElementById('info'+service.id).innerHTML = info;
}
}
}
</script>
I have tried to do stuff like
<template>
<div>
<p v-bind:id="'info'+service.id"/>
{{ getInfo(service) }}
</div>
</template>
but it never seems to work. Weird thing is that when I make it a button,
<template>
<div>
<button v-on:click="getInfo(service)">GET INFO</button>
<p v-bind:id="'info'+service.id"/>
</div>
</template>
it works perfectly! But I don't want a button, I just want it to appear.
You don't need to manipulate DOM in Vue js for such trivial case, just add all you want to template and remove getInfo method, example:
Information.vue
<template>
<div>
<p>
<b>Servicename:</b> <br>
{{ service.name }}<br>
<b>Another Field:</b> <br>
{{ service.anotherField }}<br>
<b>Another Field 2 :</b> <br>
{{ service.anotherField2 }}<br>
</p>
</div>
</template>
OR if you really want to work with html do this:
Information.vue
<template>
<div>
<p v-html="getInfo(service)"/>
</div>
</template>
<script>
export default {
props:['service'],
data: function () {
return {
services: this.$store.state.Services.data
}
},
methods: {
getInfo: function (service) {
if (!service) return '';
var info = '<b>Servicename:</b> <br>';
info += service.name;
... (method adds more to 'info' here)
return info;
}
}
}

Polymer DOM-repeat content depending on received data

I have made a DOM-repeat that recieves multiple types of data.
Now I want the DOM-repeat to only show the items related to that type.
Is it possible to do this and how would I be able to achieve this?
When the item.Type is Zone I want it to display the item.Name and item.ZoneID
when the item.Type is Building I want it to display the item.BuildingID, item.Address1 and item.Address2
This is my code:
<template is="dom-repeat" items="[[data]]">
<paper-card id="departmentspaperContainer2">
<div class="card-content">
<h3>Type: {{item.Type}}</h3>
<hr/>
<h4>Name: {{item.Name}}</h4>
<h4>BuildingID: {{item.BuildingID}}</h4>
<h4>ZoneID: {{item.ZoneID}}</h4>
<h4>Address1: {{item.Address1}}</h4>
<h4>Address2: {{item.Address2}}</h4>
</div>
</paper-card>
</template>
Something similar to what 'HakanC' is proposing, but differently, you can use the dom-if and bind the response of your filter function :
Here is a working jsfiddle (to use in chrome)
<dom-module id="os-test">
<template>
<template is="dom-repeat" items="{{data}}">
<paper-card id="departmentspaperContainer2">
<div class="cardContent" type="{{item.Type}}">
<h3>Type: [[item.Type]]</h3>
<hr/>
<template is='dom-if' if='{{showZone(item.Type)}}'>
<h4>Name: [[item.Name]]</h4>
<h4>ZoneID: [[item.ZoneID]]</h4>
</template>
<template is='dom-if' if='{{showBuilding(item.Type)}}'>
<h4>BuildingID:[[item.BuildingID]]</h4>
<h4>Address1: [[item.Address1]]</h4>
<h4>Address2: [[item.Address2]]</h4>
</template>
</div>
</paper-card>
</template>
</template>
</dom-module>
<script>
class OsTestElement extends Polymer.Element {
static get is() {
return 'os-test';
}
static get properties() {
return {
data: {
type: Array,
value: () => {
return [
{
Type:"Zone",
Name:"valueName1",
BuildingID:"valueBuildingID1",
ZoneID:"valueZoneID1",
Address1:"valueAddress11",
Address2:"valueAddress21",
},
{
Type:"Zone",
Name:"valueName2",
BuildingID:"valueBuildingID2",
ZoneID:"valueZoneID2",
Address1:"valueAddress12",
Address2:"valueAddress22",
},
{
Type:"Building",
Name:"valueName3",
BuildingID:"valueBuildingID3",
ZoneID:"valueZoneID3",
Address1:"valueAddress13",
Address2:"valueAddress23",
},
{
Type:"Building",
Name:"valueName4",
BuildingID:"valueBuildingID4",
ZoneID:"valueZoneID4",
Address1:"valueAddress14",
Address2:"valueAddress24",
}
]
}
}
}
}
showZone(item) {
return (item==="Zone");
}
showBuilding(item) {
return (item==="Building");
}
}
window.customElements.define(OsTestElement.is, OsTestElement);
</script>
You may use dom-if into dom-repeat as fallow.
<template is="dom-repeat" items="[[data]]">
<paper-card id="departmentspaperContainer2">
<div class="card-content">
<h3>Type: {{item.Type}}</h3>
<hr/>
<h4>Name: {{item.Name}}</h4>
<template is='dom-if' if='{{_show(item.Type)}}'>
<h4>ZoneID: {{item.ZoneID}}</h4>
</template>
<template is='dom-if' if='{{!_show(item.Type)}}'>
<h4>BuildingID: {{item.BuildingID}}</h4>
<h4>Address1: {{item.Address1}}</h4>
<h4>Address2: {{item.Address2}}</h4>
</template>
</div>
</paper-card>
</template>
....
_show(it){
return (it === "Zone");
}
With above logic should solve the problem.

Access component data in other component using slots in VueJS

I am trying to build a VueJS component. I have two other components which I want to use in a parent child manner. Now, I want to pass parent's data to child. It is like this:
App.vue
<template>
<div id="app">
<parent>
<child></child>
<child></child>
<child></child>
<child></child>
</parent>
</div>
</template>
<script>
import child from './child.vue'
import parent from './parent.vue'
var vm = new Vue({
el: '#app',
components: {
parent,
child
}
})
</script>
parent.vue
<template>
<div class="container">
This is parent component
<slot>
</slot>
</div>
</template>
<script>
export default {
name: 'parent',
data () {
return {
parentMsg: {
'key1': 'value1',
'key2': 'value2'
}
}
}
}
</script>
child.vue
<template>
<div class="container">
Child component
<!-- I have to place a div here which will consume parentMsg object -->
<!-- Each instance of child component rendered should have access to parentMsg -->
<div>
<!-- Some HTML to consume the parentMsg -->
</div>
</div>
</template>
<script type="text/javascript">
export default {
name: 'child',
data () {
return {
demo: 'Demo message changed'
}
}
}
</script>
I want parentMsg from parent.vue to be accessible in child.vue. I don't want to expose parentMsg on App.vue. How can I do this?
I placed the div consuming parentMsg inside (and after too) unnamed slot in parent.vue template and thought that with each instance of child.vue, a copy of div will be rendered. It does not work.
You can pass data in slot in parent.vue
<template>
<div class="container">
This is parent component
<slot :parentMsg="parentMsg"></slot>
</div>
</template>
In app.vue access parentMsg through scope and pass as props to child:
<template>
<div id="app">
<parent>
<template scope="defaultSlotScope">
<child :msg="defaultSlotScope.parentMsg"></child>
<child :msg="defaultSlotScope.parentMsg"></child>
<child :msg="defaultSlotScope.parentMsg"></child>
<child :msg="defaultSlotScope.parentMsg"></child>
</template>
</parent>
</div>
</template
>
In child.vue use msg as props:
<template>
<div class="container">
<div>
{{msg}}
</div>
</div>
</template>
<script type="text/javascript">
export default {
name: 'child',
props:['msg'],
data () {
return {
demo: 'Demo message changed'
}
}
}
</script>
Why dont you put the child-component in the parent template and pass the msg from parent to them ? Slots are not intended to be used like this.
App.vue
<template>
<div id="app">
<parent>
</parent>
</div>
</template>
<script>
import parent from './parent.vue'
var vm = new Vue({
el: '#app',
components: {
parent,
child
}
})
</script>
Parent.vue
<template>
<div class="container">
This is parent component
<child :msg="key1"></child>
<child :msg="key1"></child>
</div>
</template>
<script>
import child from './child.vue'
export default {
name: 'parent',
data () {
return {
parentMsg: {
'key1': 'value1',
'key2': 'value2'
}
}
}
}
</script>
Child.vue
<template>
<div class="container">
Child component
<div>
{{msg}}
</div>
</div>
</template>
<script type="text/javascript">
export default {
name: 'child',
props: {
msg: String
},
data () {
return {
demo: 'Demo message changed'
}
}
}
</script>

Categories