How to inject object value to v-text when key was added during usegn aplication? - vue.js

This is my obj form backend
myObj = {
name:'nr123',
empty:''
}
On click function adds new key: value
function userClick(){
this.myObj.status= "accept";
console.log(this.myObj)
}
clg returns
myObj = {
name:'nr123',
empty:'',
satus:'accept'
}
but when i try to display it
<v-text>
Acceptation status {{ myObj.status }}
</v-text>
it won't work.
And this is iteresting when i use "epty" (alredy declared key) to carry 'accept' every thing works fine
so how to show acceptation status when it was added in a midle of the process?
(myObj is one of dinamicly created objects kept in array. Each of myObj's can assume diferent acceptation status)

You can use computed property.
Demo :
new Vue({
el: '#app',
data() {
return {
myObj: {
name:'nr123',
empty:''
}
}
},
computed: {
newObj: function() {
this.myObj.status = 'accept';
return this.myObj;
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<p v-text="newObj.status"></p>
</div>

Related

How to display the value of a variable

Gets data from collection:
const pageTitles = {
homePage: 'Main'
...
}
export default pageTitles
If I make all this way:
<div><span>{{pageTitles.homePage}}</span></div>
everything is ok.
But i need to show the value depending on route. I tried to make this:
pageTitle(){
if (this.$route.path === '/'){
return pageTitles.homePage
}
}
and in div I have {{pageTitle}}, but it doesn't work. Why it doesn't work?
you've omitted the this keyword before pageTitles.homePage in your computed property
pageTitle(){
if (this.$route.path === '/'){
return this.pageTitles.homePage
}
}
It should work, Here you go :
new Vue({
el: '#app',
data: {
pageTitles: {
homePage: 'Main'
}
},
computed: {
pageTitle() {
return this.pageTitles.homePage
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<h2>{{ pageTitle }}</h2>
</div>

How do I trigger a recalculation in a Vue app?

I'm working on a project with Vue and VueX. In my component, I have a calculated method that looks like this:
...mapState([
'watches',
]),
isWatched() {
console.log('check watch');
if (!this.watches) return false;
console.log('iw', this.watches[this.event.id]);
return this.watches[this.event.id] === true;
},
And in my store, I have the following:
addWatch(state, event) {
console.log('add', state.watches);
state.watches = {
...state.watches,
[event]: true,
};
console.log('add2', state.watches);
},
However, this doesn't trigger a recalculation. What's going on?
Try changing return this.watches[this.event.id] === true;
to
return this.$store.commit("addWatch", this.event.id);
The code you have shown is correct, so the problem must be elsewhere.
I assume by 'calculated method' you mean computed property.
Computed properties do not watch their dependencies deeply, but you are updating the store immutably, so that is not the problem.
Here is a bit of sample code to give you the full picture.
Add event numbers until you hit '2', and the isWatched property becomes true.
Vue.use(Vuex);
const mapState = Vuex.mapState;
const store = new Vuex.Store({
state: {
watches: {}
},
mutations: {
addWatch(state, event) {
state.watches = { ...state.watches, [event]: true };
}
}
});
new Vue({
el: "#app",
store,
data: {
numberInput: 0,
event: { id: 2 }
},
methods: {
addNumber(numberInput) {
this.$store.commit("addWatch", Number(numberInput));
}
},
computed: {
...mapState(["watches"]),
isWatched() {
if (!this.watches) return false;
return this.watches[this.event.id] === true;
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vuex/3.1.0/vuex.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div>Watches: {{ watches }}</div>
<div>isWatched: {{ isWatched }}</div>
<br>
<input v-model="numberInput" type="number" />
<button #click="addNumber(numberInput)">
Add new event
</button>
</div>

How to reinitialize vue.js's getter and setter bindings?

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>

Filtering a list of objects in Vue without altering the original data

I am diving into Vue for the first time and trying to make a simple filter component that takes a data object from an API and filters it.
The code below works but i cant find a way to "reset" the filter without doing another API call, making me think im approaching this wrong.
Is a Show/hide in the DOM better than altering the data object?
HTML
<button v-on:click="filterCats('Print')">Print</button>
<div class="list-item" v-for="asset in filteredData">
<a>{{ asset.title.rendered }}</a>
</div>
Javascript
export default {
data() {
return {
assets: {}
}
},
methods: {
filterCats: function (cat) {
var items = this.assets
var result = {}
Object.keys(items).forEach(key => {
const item = items[key]
if (item.cat_names.some(cat_names => cat_names === cat)) {
result[key] = item
}
})
this.assets = result
}
},
computed: {
filteredData: function () {
return this.assets
}
},
}
Is a Show/hide in the DOM better than altering the data object?
Not at all. Altering the data is the "Vue way".
You don't need to modify assets to filter it.
The recommended way of doing that is using a computed property: you would create a filteredData computed property that depends on the cat data property. Whenever you change the value of cat, the filteredData will be recalculated automatically (filtering this.assets using the current content of cat).
Something like below:
new Vue({
el: '#app',
data() {
return {
cat: null,
assets: {
one: {cat_names: ['Print'], title: {rendered: 'one'}},
two: {cat_names: ['Two'], title: {rendered: 'two'}},
three: {cat_names: ['Three'], title: {rendered: 'three'}}
}
}
},
computed: {
filteredData: function () {
if (this.cat == null) { return this.assets; } // no filtering
var items = this.assets;
var result = {}
Object.keys(items).forEach(key => {
const item = items[key]
if (item.cat_names.some(cat_names => cat_names === this.cat)) {
result[key] = item
}
})
return result;
}
},
})
<script src="https://unpkg.com/vue"></script>
<div id="app">
<button v-on:click="cat = 'Print'">Print</button>
<div class="list-item" v-for="asset in filteredData">
<a>{{ asset.title.rendered }}</a>
</div>
</div>

Notify vue that item internal has been changed

Say I have some instance of class, which could be modified internal by method call (some props could be added, some removed, etc).
How could I notify vue about these changes (how to say vue “reload this item to be reactive”)?
Thanks.
PS: there are no access to vue from class (suppose it is external library)
Here is an example https://jsfiddle.net/h34a7s0n/50/
And possible solution:
// Let's say structure of 'item' is changed.
// We have to give some kick to Vue to reinitialize observer
// And yes, we need to know 'item' internals :(
// First remove old observer.
delete this.item._data.__ob__;
// Next define the new one
Vue.util.defineReactive(this.item.__ob__.value, '_data', this.item._data);
// And finally notify about changes, like $set does
this.item.__ob__.dep.notify();
Yes, it is dirty. But it works: https://jsfiddle.net/h34a7s0n/89/
Is there any clean way to solve?
The problem is v2 property of _data. v2, specifically, is not reactive.
Official docs - Change Detection Caveats:
Due to the limitations of modern JavaScript (and the abandonment of Object.observe), Vue cannot detect property addition or deletion.
To work around it, you can either declare it in the constructor (this option works best when you know what properties you'll have):
constructor(v1) {
this._data = {
v1,
v2: null // <========= added this
};
}
class C {
constructor(v1) {
this._data = {
v1,
v2: null // <========= added this
};
}
addV2(v2) {
this._data['v2'] = v2;
alert( 'Item is fully loaded' );
console.log(this._data);
}
get value1() {
return this._data.v1;
}
get value2() {
if ('v2' in this._data) {
return this._data.v2;
}
return;
}
}
new Vue({
el: '#app',
template: `
<div>
<div>
{{item.value1}}, {{item.value2}}
<button #click="fullLoad">+</button>
</div>
</div>`,
data: {
item: new C(0)
},
methods: {
fullLoad() {
this.item.addV2(2 * Math.random());
// How to notify vue about changes here?
}
}
})
<script src="https://unpkg.com/vue"></script>
<div id="app"></div>
Or change it using Vue.set(). (This option works best for the general case.)
addV2(v2) {
Vue.set(this._data, 'v2', v2); // <========= changed here
alert( 'Item is fully loaded' );
console.log(this._data);
}
class C {
constructor(v1) {
this._data = {
v1
};
}
addV2(v2) {
Vue.set(this._data, 'v2', v2); // <========= changed here
alert( 'Item is fully loaded' );
console.log(this._data);
}
get value1() {
return this._data.v1;
}
get value2() {
if ('v2' in this._data) {
return this._data.v2;
}
return;
}
}
new Vue({
el: '#app',
template: `
<div>
<div>
{{item.value1}}, {{item.value2}}
<button #click="fullLoad">+</button>
</div>
</div>`,
data: {
item: new C(0)
},
methods: {
fullLoad() {
this.item.addV2(2 * Math.random());
// How to notify vue about changes here?
}
}
})
<script src="https://unpkg.com/vue"></script>
<div id="app"></div>
No access to the third-party class code
If you have no access to C class internals, you can re-add the item data property, as below:
methods: {
fullLoad() {
let i = this.item; // save for reinsertion
this.item = null; // remove
Vue.set(this, 'item', i); // reinsert as fully reactive
this.item.addV2(2 * Math.random());
}
}
Runnable demo:
class C {
constructor(v1) {
this._data = {
v1
};
}
addV2(v2) {
this._data['v2'] = v2;
alert( 'Item is fully loaded' );
console.log(this._data);
}
get value1() {
return this._data.v1;
}
get value2() {
if ('v2' in this._data) {
return this._data.v2;
}
return;
}
}
new Vue({
el: '#app',
template: `
<div>
<div>
{{item.value1}}, {{item.value2}}
<button #click="fullLoad">+</button>
</div>
</div>`,
data: {
item: new C(0)
},
methods: {
fullLoad() {
let i = this.item; // save for reinsertion
this.item = null; // remove
Vue.set(this, 'item', i); // reinsert as fully reactive
this.item.addV2(2 * Math.random());
}
}
})
<script src="https://unpkg.com/vue"></script>
<div id="app"></div>