I have a case where a Vue 2 component isn't rendering when I update its data:
Vue.component('framer-deal', {
data: function(name,src) {
return {
name : "Image1",
src : "https://via.placeholder.com/250"
}
},
created: function () {
eventHub.$on('clickedImage', this.updateimage)
},
methods:{
updateimage : function(imgsrc) {
this.src = imgsrc;
this.name = imgsrc;
console.log("thumbnail", this.$data.name)
}
},
template: '<div><h3>Hello {{name}} {{src}}</h3><img v-bind:src="this.src" /></div>'
})
JS Bin example: https://jsbin.com/wokiqozipa/edit?js,output
(I'm using an eventhub to trigger a method on the component, but this shouldn't be why the rendering is not triggered.)
Problem is you have 2 Vue instances in your example:
var vm = new Vue({
el: '#app',
data: {
json: mydata
},
methods: {
open: function(e){
imageframe = e.target.src;
eventHub.$emit('clickedImage', imageframe)
}
}
});
new Vue({ el: '#frame' }) // remove this to make it work
Remove new Vue({ el: '#frame' }) to make it work...
Also do not use this in the template.
Change
<div><h3>Hello {{name}} {{src}}</h3><img v-bind:src="this.src" /></div>
to
<div><h3>Hello {{name}} {{src}}</h3><img v-bind:src="src" /></div>
Related
I have a basic ask. I am trying to get the data from the main area of my vue model to inside an object. I tried using app.$data.name i also tried this.name, but I cannot get it to work without error.
new Vue({
el: "#app",
data: {
name:"Bobby",
currentCard: {},
currentCard: {
author: this.name,
},
},
methods: {
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
{{currentCard.author}}
</div>
I think that is not possible, you could use a computed value.
new Vue({
el: "#app",
data() {
return {
name: "Bobby"
};
},
computed: {
currentCard() {
return {
author: this.name
};
}
},
methods: {}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
{{currentCard.author}}
</div>
When a method sets a computed property, v-ifs are not getting invoked. I thought a computed property logically worked just like a 'regular' property.
// theState can't be moved into Vue object, just using for this example
var theState = false;
var app = new Vue({
el: '#demo',
data: {
},
methods: {
show: function() {
this.foo = true;
},
hide: function() {
this.foo = false;
}
},
computed: {
foo: {
get: function() {
return theState;
},
set: function(x) {
theState = x;
}
}
}
})
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<div id="demo">
<input type=button value="Show" #click="show()">
<input type=button value="Hide" #click="hide()">
<div v-if="foo">Hello</div>
</div>
Am I doing something wrong?
Vue doesn't observe changes in variables outside the component; you need to import that value into the component itself in order for the reactivity to work.
var theState = false; // <-- external variable Vue doesn't know about
var app = new Vue({
el: '#demo',
data: {
myState: theState // <-- now Vue knows to watch myState for changes
},
methods: {
show: function() {
this.foo = true;
theState = true; // <-- this won't affect the component, but will keep your external variable in synch
},
hide: function() {
this.foo = false;
theState = false; // <-- this won't affect the component, but will keep your external variable in synch
}
},
computed: {
foo: {
get: function() {
return this.myState;
},
set: function(x) {
this.myState = x;
}
}
}
})
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<div id="demo">
<input type=button value="Show" #click="show()">
<input type=button value="Hide" #click="hide()">
<div v-if="foo">Hello</div>
</div>
(Edited to remove incorrect info; I forgot computed property setters existed for a while there)
You need to move theState into data. Otherwise it wont be reactive, so vue wont know when its changed, so v-if or any other reactivity wont work.
var app = new Vue({
el: '#demo',
data: {
foo2: false,
theState: false
// 1
},
methods: {
show: function() {
this.foo = true;
},
hide: function() {
this.foo = false;
}
},
computed: {
foo: { // 2
get: function() {
return this.theState
},
set: function(x) {
this.theState = x;
}
}
}
})
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<div id="demo">
<input type=button value="Show" #click="show()">
<input type=button value="Hide" #click="hide()">
<div v-if="foo">Hello</div>
</div>
Below here i provide a sample code. So what happens here is, i have some objects that i will load from API. The object later will be extended by the UI, in case that some of property that will be used for binding in the UI missing #app2. Under normal condition, if all the properties are provided like in #app1, the Vue will do the binding recursively to the content of the data object. But currently, in #app2, the property is missing and in the UI logic, i add the missing property.
The problem now is, when i added the property that way, the app2.contentObject.toggleStatus is not vue's object with getter and setter. how can i manually reinitialize the state of getter and setter so that the changes will be reflected in UI?
var app1 = new Vue({
el: "#app1",
data: {
contentObject: {
toggleStatus: false
}
},
computed: {
content: function(){
var contentObject = this.contentObject;
return contentObject;
}
},
methods: {
toggle : function(){
this.contentObject.toggleStatus = !this.contentObject.toggleStatus;
}
}
})
var app2 = new Vue({
el: "#app2",
data: {
contentObject: {
}
},
computed: {
content: function(){
var contentObject = this.contentObject;
contentObject.toggleStatus = false;
return contentObject;
}
},
methods: {
toggle : function(){
this.contentObject.toggleStatus = !this.contentObject.toggleStatus;
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.min.js"></script>
<div id="app1">
current toggle status: {{content.toggleStatus}}<br/>
<button #click="toggle">Toggle (working)</button>
</div>
<div id="app2">
current toggle status: {{content.toggleStatus}}<br/>
<button #click="toggle">Toggle (not working)</button>
</div>
1. In app2 vue instance's case you are trying to add a new property toggleStatus and expecting it to be reactive. Vue cannot detect this changes. So you got to initialize the properties upfront as you did in app1 instance or use this.$set() method. See Reactivity in depth.
2. You are using a computed property. Computed properties should just return a value and should not modify anything. So to add a property toggleStatus to contentObject make use of created lifecycle hook.
So here are the changes:
var app2 = new Vue({
el: "#app2",
data: {
contentObject: {}
},
created() {
this.$set(this.contentObject, "toggleStatus", false);
},
methods: {
toggle: function() {
this.contentObject.toggleStatus = !this.contentObject.toggleStatus;
}
}
});
Here is the working fiddle
It doesn't work in second case first because in your computed property you always assign false to it.
contentObject.toggleStatus = false;
And secondly you are looking for Vue.set/Object.assign
var app1 = new Vue({
el: "#app1",
data: {
contentObject: {
toggleStatus: false
}
},
computed: {
content: function(){
var contentObject = this.contentObject;
return contentObject;
}
},
methods: {
toggle : function(){
this.contentObject.toggleStatus = !this.contentObject.toggleStatus;
}
}
})
var app2 = new Vue({
el: "#app2",
data: {
contentObject: {
}
},
computed: {
content: function(){
var contentObject = this.contentObject;
return contentObject;
}
},
methods: {
toggle : function(){
this.$set(this.contentObject, 'toggleStatus', !(this.contentObject.toggleStatus || false));
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.min.js"></script>
<div id="app1">
current toggle status: {{content.toggleStatus}}<br/>
<button #click="toggle">Toggle (working)</button>
</div>
<div id="app2">
current toggle status: {{content.toggleStatus}}<br/>
<button #click="toggle">Toggle (not working)</button>
</div>
I can't figure out how to get Vue.js to always evaluate a computed regardless of if I'm actually using it in the page. A simplified version of what I'm trying to accomplish is to have a couple input fields which I want to influence the value of another field when either has been updated. I also want this field to be manually editable too. Example jsfiddle.
html:
<div id="app">
<p v-if="updateUsername">Just here to get the darn thing to run</p>
<div>
yourName:<input v-model="yourName">
</div>
<div>
dogsName:<input v-model="dogName">
</div>
<div>
username:<input v-model="userName">
</div>
</div>
js:
var main = new Vue({
el: '#app',
data: {
yourName: 'Adam',
dogName: 'Barkster',
userName: ''
},
methods: {
},
computed: {
updateUsername: function(){
this.userName = this.yourName + this.dogName;
}
}
});
This works exactly as I want it to but requires I BS the use of "updateUsername" in the html. I'm sure there's a better way.
You could add a watch:
watch: { updateUsername() {} }
Fiddle: https://jsfiddle.net/acdcjunior/k6rknwqg/2/
But it seems what you want are two watchers instead:
var main = new Vue({
el: '#app',
data: {
yourName: 'Adam',
dogName: 'Barkster',
userName: ''
},
watch: {
yourName: {
handler() {
this.userName = this.yourName + this.dogName;
},
immediate: true
},
dogName() {
this.userName = this.yourName + this.dogName;
}
}
});
Fiddle: https://jsfiddle.net/acdcjunior/k6rknwqg/6/
Another option (watching two or more properties simultaneously):
var main = new Vue({
el: '#app',
data: {
yourName: 'Adam',
dogName: 'Barkster',
userName: ''
},
mounted() {
this.$watch(vm => [vm.yourName, vm.dogName].join(), val => {
this.userName = this.yourName + this.dogName;
}, {immediate: true})
}
});
Fiddle: https://jsfiddle.net/acdcjunior/k6rknwqg/11/
For Vue2, computed properties are cached based on their dependencies. A computed property will only re-evaluate when some of its dependencies have changed.
In your example, computed property=updateUserName will be re-evaluate when either dogName or yourName is changed.
And I think it is not a good idea to update other data in computed property, you will remeber you update userName in computed property=updateUserName now, but after a long time, you may meet some problems why my username is updated accidentally, then you don't remember you update it in one of the computed properties.
Finally, based on your example, I think watch should be better.
You can define three watcher for userName, dogName, yourName, then execute your own logic at there.
var main = new Vue({
el: '#app',
data: {
yourName: 'Adam',
dogName: 'Barkster',
userName: 'Adam Barkster'
},
methods: {
},
computed: {
updateUsername: function(){
return this.yourName + this.dogName;
}
},
watch: {
dogName: function(newValue){
this.userName = this.yourName + ' ' + newValue
},
yourName: function(newValue){
this.userName = newValue + ' '+this.dogName
},
userName: function(newValue) {
// below is one sample, it will update dogName and yourName
// when end user type in something in the <input>.
let temp = newValue.split(' ')
this.yourName = temp[0]
this.dogName = temp[1]
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.js"></script>
<div id="app">
<div id="app">
<p v-if="updateUsername">Just here to get the darn thing to run</p>
<div>
yourName:<input v-model="yourName">
</div>
<div>
dogsName:<input v-model="dogName">
</div>
<div>
username:<input v-model="userName">
</div>
</div>
</div>
Here is my code:
var guestContent = Vue.extend({
template: `
<p>Guest content</p>
`,
data: function () {
return {
qs: getQuestionsContent(); // here I would like to get in qs data from function
}
}
});
Here is my App.js:
App = new Vue ({ /
el: '#app',
data: {
topMenuView: "guestmenu",
contentView: "guestcontent",
}
});
I need to display qs instead of Guest content and do for example v-for or some kind of other iteration. How can I do it?
Why:
var guestContent = Vue.extend({
template: `
<p>Guest content</p>
<p>{{foo}}</p> // foo not display
`,
data: function () {
foo: "dddddddddd";
}
});
Why is this not working?
Try something like this:
html
<body>
<guest-content></guest-content>
</body>
<template id="guest-content-template">
Guest Content
<ul>
<li v-for="question in questions">
{{question}}
</li>
</ul>
</template>
js
var guestContent = Vue.extend({
template: "#guest-content-template",
data: function (){
return {
questions: []
}
},
ready(){
this.getQuestionsContent();
},
methods:{
getQuestionsContent(){
this.questions = [
"Why?",
"What?",
"When?"
];
}
}
});
Vue.component('guest-content',guestContent);
new Vue({
el:'body'
});
Anything in a {{}} is assumed to be within the current Vue scope, so you just use foo or questions not guestContent.foo.
The data attribute is a function that has to return the data for the app. If you don't return anything, the component wont have any data.