I'm learning VueJS with Maximilian Schwarzmüller in his VueJS course on Udemy and whenever I run his example on my local machine, it doesn't increase my counter variable.
<div id="app">
<button :click="increase">Click me</button>
<p>{{ counter }}</p>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.10/vue.min.js"></script>
<script>
new Vue({
el: '#app',
data: {
counter: 0
},
methods: {
increase() {
return this.counter++
}
}
})
</script>
Any idea what's wrong with the code?
Thanks a lot.
Data needs to be a function:
<script>
new Vue({
el: '#app',
data() {
return {
counter: 0
}
},
...
})
</script>
You have to use the # symbol for events, instead of : which is used for binding props.
<button #click="increase">Click me</button>
try this:
data: () => ({
counter: 0,
})
Your script will execute before your document is ready therefore Vue will not find your element to mount to.
Also as others already mentioned - your data property should be a function to make each instance with unique data object.
document.addEventListener('DOMContentLoaded', mountVue);
function mountVue() {
new Vue({
el: '#app',
data() {
return {
counter: 0
};
},
methods: {
increase() {
return this.counter++;
}
}
});
}
The problem was actually this:
<button :click="increase">Click me</button>
I was using the wrong short-hand syntax. I had to use the # sign (for events) instead.
<button #click="increase">Click me</button>
I was looking at this example here on CodePen and I noticed that their data object is not a function. Must the data object be a function? Are their benefits that I don't know about?
I'm very new to Vue right now and I'm learning from Max's course on Udemy. Thanks a lot.
Related
I have question related to data of Vue.
I created data with an empty object.
data(){
return {
myObj: {}
}
}
and function like this:
methods: {
changeMyObj() {
this.myObj.newKey = 'aaaa';
}
}
Then I show it on template by click
<a #click="changeMyObj">Click change</a>
{{myObj.newKey}}
With this click, the nested key is not rendered on template. How can I resolve this issue?
Note: I do not meet this issue with Vuex or state of React.
This happens because of vue.js reactivity. In fact, here you are modifying a value that was not declared when the component mounted and Vue cannot track the changes.
You can update values by using the Vue.set method which allows Vue to track the data.
Vue.set(this.myObj, "myKey", "Hello world")
You can also make a deep copy of the object instead of just adding the key.
This can be done using the spread operator.
For example
this.myObj = {...this.myObj, myKey: "Hello world"}
Here is a small example using the two versions
new Vue({
el: "#app",
data: () => ({
myObj: {}
}),
methods: {
addKey(){
this.myObj = {...this.myObj, myKey: "Hello world foo"}
},
addKey2(){
Vue.set(this.myObj, "myKey", "Hello world bar")
},
},
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
{{ myObj.myKey }}
<button #click="addKey">Add key</button>
<button #click="addKey2">Add key 2</button>
</div>
I found the solution for this.
this.myObj = {...this.myObj, newKey:'aaaa'}
I do not think it is solution while it has no difference with:
this.myObj['newKey'] = 'aaaa';
or
this.myObj.newKey = 'aaaa';
If someone can explain why please let me know. Thanks
Correct way to assign a new property in an existing object is Vue.set(this.myObj, 'newKey', 'aaaa') to make it reactive instead of this.myObj.newKey = 'aaaa'
Demo :
new Vue({
el: '#app',
data: {
myObj: {}
},
methods: {
changeMyObj() {
Vue.set(this.myObj, 'newKey', 'aaaa')
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<button #click="changeMyObj">Click change</button>
{{ myObj.newKey }}
</div>
I'm wanting to change the global variable below throughout the page
Vue.prototype.$color = 'green';
I tried using the code below but it only changes within the component I created
watch: {
cor(newValue, oldVlue) {
this.$color = newValue;
}
}
is it possible for me to create a way to change the prototype variable across all components of the page?
To have $color globally available, you can use a Mixin, more specifically a Global Mixin.
If you would only want it to be read-only, it is simplest solution (less code). See snippet:
Vue.mixin({
created: function () {
this.$color = 'green';
}
})
new Vue({
el: '#app1',
data: {
message: 'Hello Vue.js!'
},
mounted() {
console.log('$color #app1:', this.$color);
}
})
new Vue({
el: '#app2',
data: {
message: 'Hello Vue.js!'
},
mounted() {
console.log('$color #app2:', this.$color);
}
})
<script src="https://unpkg.com/vue#2.5.15/dist/vue.min.js"></script>
<div id="app1">
<p>app1: {{ message }}</p>
</div>
<div id="app2">
<p>app2: {{ message }}</p>
</div>
Making $color reactive
To mave Vue react everywhere to changes to $color, you could use a Vuex store (see other answer).
But if you don't want to use Vuex just for that, another possibility is to create a Vue instance just to hold the "shared" data. After that, create a mixin with a computed property that references the $data of this "shared" Vue instance. See demo below.
// not using a Vuex store, but a separated Vue instance to hold the data
// only use this if you REALLY don't want to use Vuex, because Vuex is preferrable
let globalData = new Vue({
data: { $color: 'green' }
});
Vue.mixin({
computed: {
$color: {
get: function () { return globalData.$data.$color },
set: function (newColor) { globalData.$data.$color = newColor; }
}
}
})
// this.$color will be available in all Vue instances...
new Vue({
el: '#app1'
})
new Vue({
el: '#app2'
})
// ...and components
Vue.component('my-comp', {template: '#t3'});
new Vue({
el: '#app3',
})
<script src="https://unpkg.com/vue#2.5.15/dist/vue.min.js"></script>
<div id="app1">Color: {{ $color }} <button #click="$color = 'red'">change to red</button></div>
<div id="app2">Color: {{ $color }} <button #click="$color = 'yellow'">change to yellow</button></div>
<template id="t3">
<div>Color: {{ $color }} <button #click="$color = 'purple'">change to purple</button></div>
</template>
<div id="app3"><my-comp></my-comp></div>
For completeness, check below to see how using Vuex and Mixin would be (more details on how to use Vuex in the other answer).
// Using a Vuex to hold the "shared" data
// The store is not added to any instance, it is just referenced directly in the mixin
const store = new Vuex.Store({
state: { $color: 'green' },
mutations: { update$color: function(state, newColor) { state.$color = newColor; } }
});
Vue.mixin({
computed: {
$color: {
get: function() { return store.state.$color },
set: function(newColor) { return store.commit('update$color', newColor); }
}
}
})
// this.$color will be available in all Vue instances...
new Vue({
el: '#app1'
})
new Vue({
el: '#app2'
})
// ...and components
Vue.component('my-comp', {template: '#t3'});
new Vue({
el: '#app3',
})
<script src="https://unpkg.com/vue#2.5.15/dist/vue.min.js"></script>
<script src="https://unpkg.com/vuex#3.0.1/dist/vuex.min.js"></script>
<div id="app1">Color: {{ $color }} <button #click="$color = 'red'">change to red</button></div>
<div id="app2">Color: {{ $color }} <button #click="$color = 'yellow'">change to yellow</button></div>
<template id="t3">
<div>Color: {{ $color }} <button #click="$color = 'purple'">change to purple</button></div>
</template>
<div id="app3"><my-comp></my-comp></div>
If you want a reactive global variable, Mixins may not be a good idea. Because even if you are using global Mixins, Vue actually import and inject this Mixin whenever mount new components, which means every time a new variable $color created.
I believe the mutable data types (Object or Array) combined with Vue.prototype can do the trick:
In your main.js file:
Vue.prototype.$color = {value: "black"};
In your *.vue file:
this.$color.value = "red"
In another *.vue file:
console.log(this.$color.value); // "red"
Since you probably want $color to be a property that is not just available, but reactive (and the same) across all components, a possible solution is to use a quick/small Vuex store.
There's a runnable example below. In it you'll see three different Vue instances that will react to the same $color variable (that is at the Vuex store).
All three examples are functionally identical. I wrote them differently just to portrait different ways of using the API. Use what seems more intuitive for you.
const store = new Vuex.Store({
state: {
$color: 'green'
},
mutations: {
update$color: function(state, newColor) { state.$color = newColor; }
}
});
new Vue({
store: store, // add this so the store is available
el: '#app1',
// explicitly via this.$store
computed: {
$color: function() { return this.$store.state.$color }
},
methods: {
update$color: function(newColor) { return this.$store.commit('update$color', newColor); }
}
})
new Vue({
store, // shorthand for store: store
el: '#app2',
// using helpers mapState and mapMutations
computed: {
...Vuex.mapState(['$color'])
},
methods: {
...Vuex.mapMutations(['update$color'])
},
})
new Vue({
store,
el: '#app3',
// using computed properties, only
computed: {
$color: {
get: Vuex.mapState(['$color']).$color,
set: Vuex.mapMutations(['update$color']).update$color
}
},
})
<script src="https://unpkg.com/vue"></script>
<script src="https://unpkg.com/vuex"></script>
<div id="app1">
Color: {{ $color }} <button #click="update$color('blue')">change to blue</button> (explicitly via this.$store)
</div>
<div id="app2">
Color: {{ $color }} <button #click="update$color('red')">change to red</button> (using helpers mapState and mapMutations)
</div>
<div id="app3">
Color: {{ $color }} <button #click="$color = 'orange'">change to orange</button> (using computed properties, only)
</div>
If you want a global reactive variable, you can use this.$root inside child components. There is an example in vuejs docs:
// The root Vue instance
new Vue({
data: {
foo: 1
},
computed: {
bar: function () { /* ... */ }
},
methods: {
baz: function () { /* ... */ }
}
})
// Get root data
this.$root.foo
// Set root data
this.$root.foo = 2
// Access root computed properties
this.$root.bar
// Call root methods
this.$root.baz()
But consider using Vuex in most cases as official docs recommends.
Do this in the component as well
Vue.prototype.$color= 'colorName'
It worked for me.
One of the canonical examples on Vue website is:
<div id="app-5">
<p>{{ message }}</p>
<button v-on:click="reverseMessage">Reverse Message</button>
</div>
var app5 = new Vue({
el: '#app-5',
data: {
message: 'Hello Vue.js!'
},
methods: {
reverseMessage: function () {
this.message = this.message.split('').reverse().join('')
}
}
})
My problem is that if I rename the reverseMessage function, there is no error at compile time to notify me about this. Is there a way to receive an error at compile time when this happens?
Just a side note, although not really relevant: I am using Vue in conjunction with Typescript.
Thank you!
How do can you compile a HTML string to template within a component method?
This was possible in Vue 1 like in this jsfiddle
new Vue({
el: '#app',
data: {
sampleElement: '<button v-on="click: test()">Test</button>'
},
methods:{
addNewElement: function(){
var element = $('#app').append(this.sampleElement);
/* compile the new content so that vue can read it */
this.$compile(element.get(0));
},
test: function(){
alert('Test');
}
}
});
How can you do this in Vue 2?
This is no longer possible, however, Vue 2 is data driven, so you shouldn't be trying to affect the DOM manually at all, instead you should bind elements to the underlying data in your view model. With that in mind your example will need to be re-written. Firstly, start by making your element a component:
Vue.component('my-button', {
template: '<button v-on:click="test()">{{text}}</button>',
props: ['text'],
methods: {
test() {
alert('Test');
}
}
});
Then you can create your main view model and add your button component using a v-for:
Markup:
<button v-on:click="addNewElement()">Add Element</button>
<my-button v-for="button in buttons" :text="button"></my-button>
View model:
new Vue({
el: '#app',
methods: {
addNewElement: function() {
this.buttons.push('Test');
}
},
data: {
buttons: []
}
});
In this example we are pushing buttons to an array that will then be displayed on the page, rather than manually appending them to the template.
Here's the JSFiddle: http://jsfiddle.net/10q9je5a/
If you want something more generic, then you can simply create an array of different components and use :is to let Vue know which component to render:
Markup:
<div id="app">
<button v-on:click="addNewElement()">Add Element</button>
<div :is="element.component" v-for="element in elements" v-bind="element.props"></div>
</div>
View Model:
new Vue({
el: '#app',
methods: {
addNewElement: function() {
this.elements.push({component: 'my-button', props: {text: 'Test'}});
}
},
data: {
elements: []
}
});
Here's the JSFiddle for that: http://jsfiddle.net/cxo5eto0/
Is there a way to bind input to objects instead of single variables.
I know we can do this simple trick
<input v-model="name">
But the following doesn't work:
<input v-model="user.name">
That's what i was used to in Angular, is there a way to achieve this in vue.js?
you can bind directly to data, code as follow:
var demo = new Vue({
el: "#demo",
data: {
user: {
name: "please enter"
}
}
})
<script src="https://unpkg.com/vue/dist/vue.min.js"></script>
<div id="demo">
<input v-model="user.name">
<span>{{user.name}}</span>
</div>
This works in Vue as well, make sure to define complete object in data which will make it reactive, Here is a working fiddle.
Vue Code:
var demo = new Vue({
el: '#demo',
data: function(){
return {
user: {
name: 'This is working fine'
}
};
}
})