VueJS Simple Alert Appear - vue.js

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.

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
}
}
});

How to dynamically change components in VueJS

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>

VueJs Nested props coming through undefined

I am trying to access an array which is part of a prop (event) passed into a component, but when in created() or mounted() the array part of the event prop (the rest is fine) comes through as undefined.
As can be seen below, when I inspect the props in the vue chrome plugin, the registration_fields are there.
I can add a watcher to the event prop and can access the registration_fields that way, but this seems very awkward to have to do this to access already passed in data.
This is from the Chrome vue inspector:
event:Object
address1_field:"Some Address 1"
address2_field:"Some Address 2"
approved:true
registration_fields:Array[1]
This is what part of my vue file looks like:
export default {
props: ['event'],
data() {
return {
regFields: []
}
},
created() {
this.regFields = this.event.registration_fields // Undefined here!
},
watch: {
event() {
this.regFields = this.event.registration_fields //Can access it here
});
}
}
}
I am using Vue 2.4.4
This is how the component is called:
<template>
<tickets v-if="event" :event="event"></tickets>
</template>
<script>
import tickets from './main_booking/tickets.vue'
export default {
created() {
var self = this;
this.$http.get('events/123').then(response => {
self.event = response.data
}).catch(e => {
alert('Error here!');
})
},
data: function () {
return {event: {}}
},
components: {
tickets: tickets
}
}
</script>
Thank you
It actually works fine without the watcher.
new Vue({
el: '#app',
data: {
event: undefined
},
components: {
subC: {
props: ['event'],
data() {
return {
regFields: []
}
},
created() {
this.regFields = this.event.registration_fields // Undefined here!
}
}
},
mounted() {
setTimeout(() => {
this.event = {
registration_fields: [1, 3]
};
}, 800);
}
});
<script src="//cdnjs.cloudflare.com/ajax/libs/vue/2.4.2/vue.min.js"></script>
<div id="app">
<sub-c v-if="event" :event="event" inline-template>
<div>
{{regFields}}
</div>
</sub-c>
</div>
If, as Belmin Bedak suggests in the comment below, event is populated asynchronously, it comes in as undefined because it's undefined. In that case, you need a watcher, or, somewhat more elegantly, use a computed:
new Vue({
el: '#app',
data: {
event: {}
},
components: {
subC: {
props: ['event'],
computed: {
regFields() {
return this.event.registration_fields;
}
}
}
},
// delay proper population
mounted() {
setTimeout(() => { this.event = {registration_fields: [1,2,3]}; }, 800);
}
});
<script src="//cdnjs.cloudflare.com/ajax/libs/vue/2.4.2/vue.min.js"></script>
<div id="app">
<sub-c :event="event" inline-template>
<div>
{{regFields}}
</div>
</sub-c>
</div>

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>