I'm trying to create a button that, once is pushed, adds a child component to my component. My code works, except that the HTML is being parsed as a string, here is the parent component:
<template>
<div id="app">
<li v-for="item in items">
{{ item.component }}
</li>
</div>
</template>
<script>
export default {
props:{},
data(){
return {
items: []
}
},
mounted() {},
computed: {},
methods: {
addItem(){
this.items.push({component: '<testcomponent />'})
}
}
}
</script>
And the child component is very basic:
<template>
<div id="app">
<h1>Testing</h1>
</div>
</template>
So the problem with my code is that, every time i push the button, instead of loading every time a new instance of the child component, it will parse the HTML as a string: '<testcomponent />'. How can i fix this? Thanks in advance!
You can push the component name, and use <component :is="item.component"/> in the v-for as follows:
const testcomponent = Vue.component('testcomponent', {
template: '#testcomponent',
});
new Vue({
el:"#app",
components: { testcomponent },
data() { return { items: [] } },
methods: {
addItem() {
this.items.push({ component: 'testcomponent' });
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<template id="testcomponent">
<div><h1>Testing</h1></div>
</template>
<div id="app">
<button #click="addItem">Add</button>
<li v-for="item in items">
<component :is="item.component"/>
</li>
</div>
Related
I have a scenario which i need to build a nested menu and the menu can have an infinite level of nested layers (even thought this will not happen) and I'm wanting to know what is the best way of building the list of child components dynamically?
This is some test code that I put together to try some dynamic code from a function that would essentially give me the list of components in an array etc. What is the best way of problematically building up the child components tree?
<template>
<a-row>
<div v-html="getContent()"></div>
</a-row>
</template>
<script>
export default {
methods: {
getContent() {
return `<div #click="sayHello()">RYAN</div>`
},
sayHello() {
console.log('Hello there');
}
}
}
</script>
You can try with :is and pass component:
new Vue({
el: '#demo',
data() {
return {
component: '',
contents: ['RYAN', 'DJURO', 'SPIRO']
}
},
methods: {
getContent() {
this.component = 'comp'
},
}
})
Vue.component('comp', {
template: `<div #click="sayHello">{{ content }}</div>`,
props: ['content'],
methods: {
sayHello() {
console.log('Hello there ' + this.content);
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="demo">
<button #click="getContent">getContent</button>
<ul v-if="component">
<li v-for="(cont, i) in contents" :key="i">
<component :is="component" :content="cont"> </component>
</li>
</ul>
</div>
I am wanting to put multiple components in a wrapper, so it would look like this,
<div class="wrapper">
<component />
<component />
<component />
</div>
I am trying the following,
<component
v-for="person in persons"
:key="person.key"
:is="chooseType(person)"
:person="person"
:feed="person.self ? handle : aFunction(person)"
:publisher="getPublisher(person)"
/>
My problem is that person is returning as undefined when it runs the chooseType component, why would this be, the persons object is not null, so does have children. How can I loop a dynamic component, as I assume I doing it incorrectly?
v-for is used along with :is in dynamic component. Please find below
Vue.component('dynamic1', {
template: '<div>Dynamic 1</div>'
})
Vue.component('dynamic2', {
template: '<div>Dynamic 2</div>'
})
new Vue({
el: "#app",
data() {
return {
persons: [{
name: 'dynamic1'
}, {
name: 'dynamic2'
}]
}
},
methods: {
getComponent(comp) {
return comp
}
}
})
<script src="https://cdn.jsdelivr.net/npm/vue#2.x/dist/vue.js"></script>
<div id="app">
<component v-for="person in persons" :is="getComponent(person.name)"></component>
</div>
I want to update $parent data from child component. (I know it's bad but very conv for tightly bound component).
So I'm trying like is shown below, but it update field named 'undefined' instead:
Vue.config.productionTip = false;
const child = {
template: `
<div class="hello">
<input type="text" v-model="$parent.obj[this.field]">
<pre>{{ $props }}</pre>
</div>`,
props: ['field'],
}
new Vue({
el: "#app",
components: {
child,
},
data() {
return {
obj: {
f1: 111,
f2: 222
}
}
},
});
<script src="https://cdn.jsdelivr.net/npm/vue#2.5.17/dist/vue.js"></script>
<div id="app">
parent:
<pre>{{ $data }}</pre>
<hr>
child:
<child field="f1" />
</div>
You added this keyword to the property of $parent.obj. You don't need to use this keyword in template.
Here is the working code: codesandbox
I have a component with following template:
<div v-for:"item in store" v-bind:key="item.type">
<a>{{item.type}}</a>
</div>
I have another component called 'StoreComponent'
On click of a element in first component I want to clear the current component and show the StoreComponent and able to pass item.type to StoreComponent.
I don't want to use router-link or router.push as I don't want to create a new url but override the current component with the new one depending on the item.type value.
StoreComponent.vue
export default{
name: 'StoreComponent',
props: ['item'],
data: function () {
return {
datum: this.item
}
},
methods: {
//custom methods
}
}
You could use dynamic components and pass the item-type as a prop.
Vue.component('foo', {
name: 'foo',
template: '#foo'
});
Vue.component('bar', {
name: 'bar',
template: '#bar',
props: ['test']
});
new Vue({
el: "#app",
data: {
theComponent: 'foo', // this is the 'name' of the current component
somethingWeWantToPass: {
test: 123 // the prop we are passing
},
},
methods: {
goFoo: function() {
this.theComponent = 'foo';
},
goBar: function() {
this.theComponent = 'bar';
},
}
})
<script src="https://unpkg.com/vue"></script>
<div id="app">
<button #click="goFoo">Foo</button>
<button #click="goBar">Bar</button>
<component :is="theComponent" v-bind="somethingWeWantToPass"></component>
</div>
<template id="foo">
<div>
Foo
</div>
</template>
<template id="bar">
<div>
Bar
<div>This is a prop: {{ this.test }}</div>
</div>
</template>
I have two Vue components. The parent-component:
Vue.component('parent-component',{
methods: {
test: function(){
alert('Option Selected');
}
},
template: `
<div><slot></slot></div>
`
});
And the animals component:
Vue.component('animals',{
data: function(){
return {
selected: ''
}
},
template: `
<select #change="selectionChanged" v-model="selected">
<slot></slot>
</select>
`,
methods: {
selectionChanged: function(){
this.$emit('optionselected', this.selected);
}
}
});
And here is my HTML:
<div id="app">
<parent-component #optionselected="test()">
<animals>
<option>Aardvark</option>
<option>Bear</option>
<option>Cat</option>
</animals>
</parent-component>
</div>
I am trying to get the selected option from child component (animals) to the parent component (parent-component). I am emitting the optionselected event from the child, but it looks like the parent component is not responding to that event, I mean the method test() is not being called at all. What am I doing wrong here?
Here is the JSFiddle Demo
First, you need to add the listener to the animals component in your template.
<animals #optionselected="test">
Second, it's important to remember that because you are using slots, the elements inside the slots are going to be evaluated in the scope of the component in which they are defined. In this case, that scope is the Vue's scope, not the parent-component scope. In order to allow the elements defined inside a slot to use the containing components data, methods, etc, you need to define a scoped slot. That being the case, your parent component would end up looking like this:
<div><slot :test="test"></slot></div>
And your Vue template becomes
<parent-component>
<template scope="{test}">
<animals #optionselected="test">
<option>Aardvark</option>
<option>Bear</option>
<option>Cat</option>
</animals>
</template>
</parent-component>
Here is the updated code.
console.clear()
Vue.component('parent-component', {
methods: {
test: function(option) {
alert('Option Selected ' + option);
}
},
template: `
<div><slot :test="test"></slot></div>
`
});
Vue.component('animals', {
data: function() {
return {
selected: ''
}
},
template: `
<select #change="selectionChanged" v-model="selected">
<slot></slot>
</select>
`,
methods: {
selectionChanged: function() {
this.$emit('optionselected', this.selected);
}
}
});
new Vue({
el: "#app",
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.4.2/vue.js"></script>
<div id="app">
<parent-component>
<template scope="{test}">
<animals #optionselected="test">
<option>Aardvark</option>
<option>Bear</option>
<option>Cat</option>
</animals>
</template>
</parent-component>
</div>