Delegate event in vue - vue.js

Im trying to delegate an avent from one instance to another.
I have a toolbar on the top of the page with a button like this
<div id="toolbar">
<button v-on:click="add" class="btn btn-default" type="submit"><span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Ny</button>
</div>
This add event working in this vue instance
var toolbarApp = new Vue({
el: '#toolbar',
data: {
},
methods: {
add: function (event) {
alert('lol');
}
}
});
But now i want to attach this add event to another instance like this
var contactApp = new Vue({
mixins: [toolbarApp],
el: '#contact',
data: {
showGrid: true,
showForm: false,
editMode: false
},
created: function () {
this.getContacts();
},
methods: {
getContacts: function () {
$.getJSON(this.apiGrid, function (data) {
this.contacts = data;
}.bind(this));
},
add: function (event) {
alert('hej');
}
}
});
But i cant attach this because of diffrent instance. Is it any way to do this?
Have also tried with mixedin with no luck.
Thanks in advice

what you are trying to do is not unique, there's even a title for it "Event bus"
EventBus = new Vue();
var toolbarApp = new Vue({
el: '#toolbar',
data: {
},
methods: {
add: function (event) {
EventBus.$emit('add-something', this.somevar);
}
}
});
then in your other instance:
var contactApp = new Vue({
mixins: [toolbarApp],
el: '#contact',
data: {
showGrid: true,
showForm: false,
editMode: false
},
created: function () {
this.getContacts();
EventBus.$on('add-something', function(somevar) {
// do cool stuff, like this.getContacts...
});
},
methods: {
getContacts: function () {
$.getJSON(this.apiGrid, function (data) {
this.contacts = data;
}.bind(this));
},
add: function (event) {
alert('hej');
}
}
});
Definition:
Sometimes you need a quick and easy solution to pass data between Vue.js components.
For an application with simple architecture it’s enough to communicate between components using events. For this we can create a quick solution and implement EventBus. EventBus allows us to emit an event in one component and listen for that event in another.
https://medium.com/#andrejsabrickis/https-medium-com-andrejsabrickis-create-simple-eventbus-to-communicate-between-vue-js-components-cdc11cd59860

Related

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.

Where I should place handler of emit?

I have child component and want to pass some data to it's parent.
My child component looks like:
// <button #click="sendClick($event)">Send</button>
// ...
data: function (){
return {
mycode: ""
}
},
methods: {
sendClick(e)
{
bus.$emit('change', this.mycode);
}
}
My parent component looks:
var app = new Vue({
el: '#app',
data: {
currentView: 'past-form',
mycode: ''
},
methods:
{
changeView()
{
this.currentView = 'past-form'
console.log(this.mycode);
},
},
created()
{
bus.$on('change', function(mycode){
this.mycode = mycode;
});
}
})
I haven't found a better place for placing bus.$on (bus is declared globally) than in created(), but the docs state that created() is for stuff that should be initialized after the page is loaded. The created() block works; I checked it by placing in it console.log(this.mycode), but should I move emit handler somewhere else?
It's look like my code does not execute mycode: '', because console.log(this.mycode); does not print anything.
As I mentioned in the comment, if your component is a direct child of your Vue, then there is no need for a bus.
That said, the created handler is fine for adding your bus event handler.
I expect the issue you have is a this issue. Try changing your handler to
bus.$on('change', mycode => this.mycode = mycode)
See How to access the correct this inside a callback?
Here is an example.
console.clear()
const bus = new Vue()
Vue.component("child", {
template: `<button #click="sendClick($event)">Send</button>`,
data: function() {
return {
mycode: "something"
}
},
methods: {
sendClick(e) {
bus.$emit('change', this.mycode);
}
}
})
var app = new Vue({
el: '#app',
data: {
currentView: 'past-form',
mycode: ''
},
methods: {
changeView() {
this.currentView = 'past-form'
console.log(this.mycode);
},
},
created() {
bus.$on('change', mycode => {
this.mycode = mycode
this.changeView()
})
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.3.4/vue.js"></script>
<div id="app">
<child></child>
Parent mycode: {{mycode}}
</div>

Reference component in another one

In a VueJS file i have two component :
var firstComponent = Vue.extend({
template: '#component1',
[...]
methods:
comp1function: function() {
[...]
comp2function()
}
[...]
}),
var secondComponent = Vue.extend({
template: '#component2',
[...]
methods:
comp2function: function(){
do things here
}
[...]
})
so what i want to do is to say : do comp2function when you finished to do comp1function. So is there any way i can reference a function from my template component2 in my template component1 ?
You should use Vue Events. I assume you are using VueJS 1.x?
So let's imagine you have those two components as you stated in your question:
var firstComponent = Vue.extend({
template: '#component1',
methods: {
doSomething () {
// Do things here
console.log('I do things')
// Fire an event when you want:
this.$dispatch('some-event')
}
}
})
var secondComponent = Vue.extend({
template: '#component2',
methods: {
doSomethingElse () {
console.log('I do something else')
}
},
events: {
trigger () {
this.doSomethingElse()
}
}
})
You need to trigger an event from your first component using the $dispatch method. Then, in your Vue Instance, you need to get that event, and use $broadcast to broadcast a new event to the other Vue children:
new Vue({
el: '#app',
components: {
firstComponent,
secondComponent
},
events: {
'some-event' () {
this.$broadcast('trigger')
}
}
})
You will then be able to get the event in your secondComponent and trigger whatever method you want
You can learn more about custom events on this page: http://v1.vuejs.org/guide/components.html#Parent-Child-Communication