how to use Vue watch in objects - javascript

I am pretty new to watch and trying to figure out why my watch isn't triggering when accessing it as an object. I saw this thread, but it isn't clear to me if my problem is the same. Following is my simplified example (full example got more properties and properties with array
<div id="app">
<input type="text" v-model.lazy="userInfo.name"> {{userInfo.name}}
</div>
JS
new Vue({
el: "#app",
data: {
userInfo: {
name: ''
}
},
methods: {
},
watch: {
userInfo : {
name(oldVal, newVal){
console.log(oldVal +" " + newVal)
},
},
deep: true
}
})
Link to the JSFiddle

Change the watcher to something like this:
new Vue({
el: "#app",
data: {
userInfo: {
name: "null"
}
},
methods: {},
watch: {
"userInfo.name": function(oldVal, newVal) {
console.log(oldVal + " " + newVal);
}
}
});
Refer to the documentation for the same here.
Check the last example.

Here is a short example in your case:
new Vue({
el: "#app",
data: {
userInfo: {
name: 'null'
}
},
computed: {
name() {
return this.userInfo.name;
}
},
methods: {
},
watch: {
name(newVal, oldVal) {
alert(newVal);
alert(oldVal);
}
},
})

Related

How to add real time search with VueJS?

I want to add realtime search to find another users that are registered on my twitter clone. So, users are on backend (Firebase)
Users on backend and example of search that i want.
Here is my attempt:
<script>
export default {
data() {
return {
search: "",
users: ['Dummy', 'Users', 'From', 'Backend'],
};
},
methods: {
findUser(){
const result = this.users.find(user => user.includes(this.search))
console.log(result)
},
},
};
</script>
What am I doing wrong?
I think a computed property is better here to have the "real-time" changes, and a .filter to get the filtered list matching search:
new Vue({
el:"#app",
data() {
return {
search: "",
users: ['Dummy', 'Users', 'From', 'Backend'],
};
},
computed: {
filteredUsers(){
return this.users.filter(user =>
user.toLowerCase().includes(this.search.toLowerCase())
);
},
},
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<input v-model="search"/>{{filteredUsers}}
</div>
For real-time search using API, you can use WATCH
Can be applied sorted on FIREBASE
https://firebase.google.com/docs/database/web/lists-of-data
var myUserId = firebase.auth().currentUser.uid;
var topUserPostsRef = firebase.database().ref('user-posts/' + myUserId).orderByChild('starCount')
There may be inconsistencies in the code, but I wanted to convey the essence
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<input v-model="search"/>{{filteredUsers}}
</div>
new Vue({
el:"#app",
data() {
return {
search: null,
users: []
};
},
computed: {
filteredUsers(){
return this.users.filter(user =>
user.toLowerCase().includes(this.search.toLowerCase())
);
},
},
watch: {
watchUser(search) {
if (search) {
this.searchPerson()
}
},
},
methods: {
searchPerson() {
this.loading = true;
this.fetchUsers()
.then(res => {
this.users = normalizeResponse(res)
this.loading = false
})
},
fetchUsers() {
return axios({
method: 'get',
url: '',
params: {
},
headers: {
'Content-type': 'application/json'
}
});
},
}
});

Vue: "_" prefixed property not working in data property

I am trying to set a computed property as prop to a child component in Vue.js.
This is an excerpt of the component code (Here's the full (not) working example):
<div id="app">
<show-me :title="aTitle"></show-me>
</div>
const ShowMe = {
data() {
return {
_title: null
}
},
computed: {
title: {
set: function(val) {
this._title = val;
},
get: function() {
return this._title;
}
}
},
template: `
<div>
should display title: {{title}} <br/>
but instead typeof title: {{typeof title}}
</div>`
};
const vm = new Vue({
el: '#app',
data: {
aTitle: 'A Title'
},
components: {
'show-me': ShowMe
}
});
When running the example the component's title value in the template is undefined. It's pretty simple and straightforward and I don't get it why it's not working.
_ prefixed properties are reserved for Vue's internal properties. They are not available for direct binding (but you can bind to them as
$data._message) - Evan You (Vue creator)
You can't use _ prefixed values cuz it get treated as internal properties in vue system
Reference - https://github.com/vuejs/vue/issues/2098
Most proper explanation available at Docs too -
Properties that start with _ or $ will not be proxied on the Vue
instance because they may conflict with Vue’s internal properties and
API methods. You will have to access them as vm.$data._property
https://v2.vuejs.org/v2/api/#data
So In your case you will do replace this._title with this.$data._title
const ShowMe = {
data() {
return {
_title: null
}
},
computed: {
title: {
set: function(val) {
this.$data._title = val;
},
get: function() {
return this.$data._title;
}
}
},
template: `
<div>
should display title: {{title}} <br/>
but instead typeof title: {{typeof title}}
</div>`
};
const vm = new Vue({
el: '#app',
data: {
aTitle: 'A Title'
},
components: {
'show-me': ShowMe
}
});
const ShowMe = {
props:{
title:{
type:String,
required:true
}
},
template: `
<div>
should display title: {{title}} <br/>
but instead typeof title: {{typeof title}}
</div>`
};
or you can use simply as follows
const ShowMe = {
props:['title'],
template: `
<div>
should display title: {{title}} <br/>
but instead typeof title: {{typeof title}}
</div>`
};
This code works. It will be more consistent practice to avoid using this.$data, though the other answer is correct in that you can certainly use it. It is better to avoid the underscores altogether and find a better naming convention. The naming in this example also isn't best practice.
const ShowMe = {
data() {
return {
cTitle: null
}
},
computed: {
title: {
set: function(val) {
this.cTitle = val;
},
get: function() {
return this.cTitle;
}
}
},
template: `
<div>
should display title: {{title}} <br/>
but instead typeof title: {{typeof title}}
</div>`
};
const vm = new Vue({
el: '#app',
data: {
aTitle: 'A Title'
},
components: {
'show-me': ShowMe
}
});
This code will allow you to initialize the initial value and then reactively change the dataTitle field inside the ShowMe component.
If you want to change a property from an external component, then you should do without the dataTitle field and use only props.
const ShowMe = {
props: {
title: {
type: String,
default: ''
}
},
data () {
return {
dataTitle: this.title
}
},
template: `
<div>
should display title: {{dataTitle}} <br/>
but instead typeof title: {{typeof dataTitle}}
</div>`
};
You may also need the ability to change the property both inside ShowMe and from an external component. Then you can use the following code:
JS
const ShowMe = {
props: {
title: {
type: String,
default: ''
}
},
data () {
return {
dataTitle: this.title
}
},
watch: {
title (newValue, oldValue) {
if (newValue !== oldValue) {
this.dataTitle = newValue;
}
},
dataTitle (newValue, oldValue) {
if (newValue !== oldValue) {
this.$emit('changeTitle', newValue);
}
}
},
template: `
<div>
should display title: {{dataTitle}} <br/>
but instead typeof title: {{typeof dataTitle}}
</div>`
};
const vm = new Vue({
el: '#app',
data: {
aTitle: 'A Title'
},
components: {
'show-me': ShowMe
}
});
HTML
<div id="app">
<show-me :title="aTitle" #changeTitle="aTitle = $event"></show-me>
</div>

get index from watched array

I have a vue application where I watch an array for changes. This is working fine. But I'm not sure how to get the index of the array item which has changed, as the watch callback only passes in the old/new values.
Demo: http://jsfiddle.net/q3zd4fmv/
Simplified Example:
new Vue({
el: '#demo',
data: {
things: [{foo:1}, {foo:2}]
},
watch: {
things: {
handler: function (val, oldVal) {
alert('a thing changed')
},
deep: true
}
},
methods: {
change: function () {
this.things[0].foo = 5
}
}
})
Unfortunately, not out of the box. Using a combination of argument destructuring and a custom watch function, you can achieve something that should do it. For example;
new Vue({
el: '#demo',
data: {
things: [{foo:1}, {foo:2}]
},
methods: {
change: function (...args) {
let [thing, after, before] = args;
console.log(thing);
}
},
mounted: function(){
this.things.forEach(thing => {
this.$watch(() => thing, this.change.bind(null, thing))
});
}
})

Why this computed property is showing changes (Vue)

I am changing properties using some buttons. When I click the buttons the data is updated in the UI, even the computed1, but not the console.log("computed1"). I think it's because I am changing its properties and not the entire object. But if it's not triggered, why the UI is updated ? Could you explain me ? I couldn't find something like this in the documentation.
Code: https://jsfiddle.net/1hr7cy5d/
var example1 = new Vue({
el: '#example',
data: function() {
return {
item: {
aaa: 'aaa',
bbb: 'bbb',
ccc: 'ccc'
}
}
},
computed: {
computed1: function() {
console.log("computed1");
let item = this.item
return item;
},
},
methods: {
method1() {
console.log("method1")
this.item.aaa = 'xxxx';
},
method2() {
console.log("method2")
this.item["bbb"] = 'yyyyy';
},
method3() {
console.log("method3")
this.$set(this.item, "ccc", "zzzzz")
},
method4() {},
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.min.js"></script>
<div id="example">
<button #click="method1()">method1</button>
<button #click="method2()">method2</button>
<button #click="method3()">method3</button>
<button #click="method4()">method4</button>
<pre>{{item}}</pre>
<pre>{{computed1}}</pre>
</div>

Vue.js How to target object with unknown name in an array on v-model input

I am generating an object onclick with an automatically-generated name. The name will be different each time. I then want to change the object's values with an input using v-model. How can I target the object if the name is unknown? Here's what I have so far:
<ul>
<li v-for="type in types" #click="addNew(type)">{{ type }}</li>
</ul>
<form v-if="Object.keys(newFields).length !== 0">
<input type="text" v-model="newFields[0].?????????">
</form>
<script>
new Vue ({
el: '#app',
data: {
types: [
'date',
'number',
'currency',
'text',
],
savedFields: [
],
newFields: [
]
},
methods: {
addNew: function (type) {
const name = `${type}-${Object.keys(this.savedFields).map(key => key === type).length}`;
if (Object.keys(this.newFields).length == 0) {
this.newFields = Object.assign({}, this.newFields, {
[name]: {
'type': type,
'displayLabel': '',
'defaultValue': '',
}
});
}
},
},
});
You can save the name as a reactive data. For e.g., save it in currentName
<script>
new Vue({
el: "#app",
data: {
//...
currentName: null
},
methods: {
addNew: function (type) {
const name = ""; //...
this.currentName = name;
//...
}
}
});
</script>
and for the v-model,
<input type="text" v-model="newFields[0][currentName]">

Categories

Resources