How to dynamically change components in VueJS - vue.js

I want to build an app in vue.js where you can dynamically switch between app views without page reloading.
What is the best way to do it?
And why this code doesn't work? Thank you!
var View01 = {
template: `<button #click="swapComponent('view-02')" type="submit">NEXT VIEW</button>`
}
var View02 = {
template: `<button #click="swapComponent('view-01')" type="submit">BACK TO PREVIOUS VIEW</button>`
}
var vm = new Vue({
el: '#app',
data: {
currentComponent: 'view-01'
},
components: {
'view-01': View01,
'view-02': View02,
},
methods: {
swapComponent: function(component) {
this.currentComponent = component;
}
}
})

You're trying to call a parent method from the child components. You need to pass the method as a prop so it is in the child's scope.
var View01 = {
template: `<button #click="swapComponent('view-02')" type="submit">NEXT VIEW</button>`,
props: ['swapComponent']
}
var View02 = {
template: `<button #click="swapComponent('view-01')" type="submit">BACK TO PREVIOUS VIEW</button>`,
props: ['swapComponent']
}
var vm = new Vue({
el: '#app',
data: {
currentComponent: 'view-01'
},
components: {
'view-01': View01,
'view-02': View02,
},
methods: {
swapComponent: function(component) {
this.currentComponent = component;
}
}
})
<script src="//cdnjs.cloudflare.com/ajax/libs/vue/2.4.2/vue.min.js"></script>
<div id="app">
<div :is="currentComponent" :swap-component="swapComponent"></div>
</div>

Related

How to make a component show different value according to the vue instance it is in?

I have nested the component in both app and app2 divs. I was expecting this component will show value based on the vue instance. Please show the right way to do this. Thanks a lot.
var aboutPageComponent = {
template: "<div><h1>{{ whatPage }}</h1><p>{{ itsMe }}</p></div>",
data: function(){
return{
whatPage: "This is About Page"
}
},
methods: {
}
}
var vm = new Vue({
el: "#app",
data: {
itsMe: "This is vm talking"
},
methods: {
},
components: {
"app_about": aboutPageComponent
}
});
var vm2 = new Vue({
el: "#app2",
data: {
itsMe: "This is vm2 talking"
},
methods: {
},
components: {
"app_about": aboutPageComponent
}
});
The app_about does not know what itsMe is.
But you can pass a itsMe as a prop via the props option.
https://v2.vuejs.org/v2/guide/components-props.html
Vue.config.devtools = false;
Vue.config.productionTip = false;
var aboutPageComponent = {
template: "<div><h1>{{ whatPage }}</h1><p>{{ itsMe }}</p></div>",
props: ["itsMe"],
data: function() {
return {
whatPage: "This is About Page"
}
}
}
var vm = new Vue({
el: "#app",
data: {
itsMe: "This is vm talking"
},
components: {
"app_about": aboutPageComponent
}
});
var vm2 = new Vue({
el: "#app2",
data: {
itsMe: "This is vm2 talking"
},
components: {
"app_about": aboutPageComponent
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<app_about :its-me="itsMe" />
</div>
<div id="app2">
<app_about :its-me="itsMe" />
</div>

listen to events from dynamic vue components

How would you listen to an event emitted by a dynamically created component instance?
In the example, the top component is added in the DOM, while the second is dynamically created in javascript.
Vue.component("button-counter", {
data: function() {
return {
count: this.initial_count
}
},
props: ['initial_count'],
methods: {
add: function() {
this.count++
this.$emit('myevent', this.count)
}
},
template: '<button v-on:click="add">You clicked me {{ count }} times.</button>'
})
let app = new Vue({
el: "#app",
data() {
return {
initial_count: 10,
}
},
mounted: function() {
let initial_count = this.initial_count
let ButtonCounterComponentClass = Vue.extend({
data: function() {
return {}
},
render(h) {
return h("button-counter", {
props: {
initial_count: initial_count
}
})
}
})
let button_counter_instance = new ButtonCounterComponentClass()
button_counter_instance.$mount()
button_counter_instance.$on('myevent', function(count) {
console.log('listened!')
this.say(count)
})
this.$refs.container.appendChild(button_counter_instance.$el)
},
methods: {
say: function(message) {
alert(message)
}
}
})
<script src="https://cdn.jsdelivr.net/npm/vue#2.6.10/dist/vue.js"></script>
<div id="app">
<button-counter initial_count=20 v-on:myevent="say"></button-counter>
<div ref='container'></div>
</div>
If I've understood what you want then you just need to listen for the event on the inner component and pass it on.
I've used arrow functions in a couple of places to avoid problems with this bindings. Otherwise I've tried to leave your code unchanged as much as possible. Changes marked with ****.
Vue.component("button-counter", {
data: function() {
return {
count: this.initial_count
}
},
props: ['initial_count'],
methods: {
add: function() {
this.count++
this.$emit('myevent', this.count)
}
},
template: '<button v-on:click="add">You clicked me {{ count }} times.</button>'
})
let app = new Vue({
el: "#app",
data() {
return {
initial_count: 10,
}
},
mounted: function() {
let initial_count = this.initial_count
let ButtonCounterComponentClass = Vue.extend({
data: function() {
return {}
},
render(h) {
return h("button-counter", {
props: {
initial_count: initial_count
},
// **** Added this ****
on: {
myevent: count => {
this.$emit('myevent', count);
}
}
// ****
})
}
})
let button_counter_instance = new ButtonCounterComponentClass()
button_counter_instance.$mount()
// **** Changed the next line ****
button_counter_instance.$on('myevent', count => {
console.log('listened!')
this.say(count)
})
this.$refs.container.appendChild(button_counter_instance.$el)
},
methods: {
say: function(message) {
alert(message)
}
}
})
<script src="https://cdn.jsdelivr.net/npm/vue#2.6.10/dist/vue.js"></script>
<div id="app">
<button-counter initial_count=20 v-on:myevent="say"></button-counter>
<div ref='container'></div>
</div>
It's important to understand that button_counter_instance is not an instance of your button-counter component. You've wrapped it in another component, albeit a component that doesn't add any extra DOM nodes. So listening on the wrapper component is not the same as listening on button-counter.
Docs for what you can pass to h: https://v2.vuejs.org/v2/guide/render-function.html#The-Data-Object-In-Depth

Vue js class binding in template not working

Having some trouble with this example:
https://v2.vuejs.org/v2/guide/class-and-style.html
I simplified my code for this post:
var classdata = {
"isActive": false
}
Vue.component('app', {
props: ['intel'],
template: '<li v-bind:class="{active: this.isActive}" #click="display(intel)"><p>test</p></li>',
methods: {
display: function (name) {
this.isActive = true;
}
}
})
var app = new Vue({
el: '#app',
data: function() {
return classdata;
},
});
When I console log in methods, (this.isActive) it reflects the updated state, but it doesnt add the class. I also noticed when I do this
<li v-bind:class="{active: true}" it works.
Looking for some clarity!
Try referencing this instead of app:
display: function (name) {
this.isActive = true;
}
Also, if you're managing isActive at the component level, define it in the data instead with props.
Edit:
To manipulate data in the child, pass a method through to its props:
Vue.component('app', {
props: ['intel', 'isActive', 'setActive'],
template: '<li v-bind:class="{active: isActive}" #click="display(intel)"><p>test</p></li>',
methods: {
display: function (name) {
this.setActive(true);
}
}
})
var app = new Vue({
el: '#app',
data: function() {
return classdata;
},
methods: {
setActive: function (val) {
this.isActive = val
}
}
});

VueJS Simple Alert Appear

Can someone help me make this simple alert work? What I wanted was if I click the trigger button, the text would appear but then the component should do its part.
I'm new and learning VueJS and now is in the components part but I haven't fully grasped it yet.
Here's the link: JSBIN
Snippet of the JS Script
Vue.component('alert', {
template: '#alert',
props: {
errors:false
},
data: function() {
return {
message:""
}
},
methods: {
appear: function (status) {
if(status=="yes") {
errors = true;
message = "Appeared";
}
}
}
});
var myapp = new Vue({
components: 'alert',
el: '#app',
data: {
},
methods: {
trigger: function() {
this.$alert.appear("yes");
}
}
});
To make your code work the way I think you are trying to get it to work I made a few changes.
Template
<div id=app>
<button #click="trigger">Trigger</button>
<alert ref="alert"></alert>
</div>
Code
Vue.component('alert', {
template: '#alert',
data: function() {
return {
message:"",
errors: false
}
},
methods: {
appear: function (status) {
if(status=="yes") {
this.errors = true;
this.message = "Appeared";
}
}
}
});
var myapp = new Vue({
components: 'alert',
el: '#app',
data: {
},
methods: {
trigger: function() {
this.$refs.alert.appear("yes");
}
}
})
Here is the updated bin.
This is a pretty atypical way to do this kind of thing though. Here is an example of a more idiomatic alert.
Vue.component('alert', {
props:["message","show"],
template: '#alert',
});
var myapp = new Vue({
el: '#app',
data: {
errors: false,
errorMessage: null
},
methods: {
trigger: function() {
this.errors = true
this.errorMessage = "Whoops!"
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.0.3/vue.js"></script>
<div id=app>
<button #click="trigger">Trigger</button>
<alert :message="errorMessage" :show="errors"></alert>
</div>
<template id="alert">
<div v-show="show">
<div>{{ message }}</div>
</div>
</template>
In this second example, the information to show in the alert is passed down to the alert component via properties and the alert is also triggered via a property. This is how you would typically do it in Vue.

vuejs : passing props down to a component in javascript?

How can i pass down props to a javascript Vue component.
this is how i normally do it.
<child prop="value"></value>
but i want to do it like this
var Child = Vue.extend({
...
});
Chid.passProps( {foo: 'bar'} ) // some thing like this?
is this possible in vue.js?
this is the full code:
var Child = Vue.extend({
props: ['foo'],
methods: {
printIt: function() {
console.log(this.foo)
}
},
template: '#example'
});
var vm = new Vue({
el: '#root',
data: {
foo: 'bar'
},
render: function(createElement) {
return createElement(Child); // pass down foo
}
});
link to jsbin
Please read the section on render functions https://v2.vuejs.org/v2/guide/render-function.html#createElement-Arguments
Essentially you need to pass the props with the call to the render function as part of the data element
var vm = new Vue({
el: '#root',
data: {
foo: 'bar'
},
render: function(createElement) {
return createElement(Child, {
props: {
foo: this.foo
}
})
}
});