Vue2 Variable unexpected behaviour - vuejs2

I'm having some issues with Vue2 variables.
My aim is to create a backup of a variable pulled down from an Axios call, this will then be used to reset the main variable back to the original values. The main variable is called loaded_data, and the copy is called original_data.
My issue is that when I copy the variable over, it's passing it by reference, not value. So when I update my loaded_data variable, the original_data
How the variables are defined:
data() {
return {
loaded_data: false,
original_data: false,
};
},
How the variables are set:
this.loaded_data = axios_response.response.data;
this.original_data = axios_response.response.data;
Reset data function.
reset_data() {
console.log('ORIGINAL', this.original_data);
console.log('LOADED', this.loaded_data);
this.loaded_data = this.original_data;
},
So when I update my loaded_data variable. eg:
loaded_data.value = 'this is an update to the data';
It's also updating the original_data variable. Console log looks like this if I run the reset_data() function.
ORIGINAL {__ob__: Observer}
value: "this is an update to the data"
LOADED {__ob__: Observer}
value: "this is an update to the data"
What it should look like:
ORIGINAL {__ob__: Observer}
value: ""
LOADED {__ob__: Observer}
value: "this is an update to the data"
It doesn't really make sense because loaded_data and original_data aren't linked beside the reset_data function and setting it with axios_response.
Any help or ideas would be greatly helpful.

JavaScript copies the same object into both variables. You would need a second copy of your object as original_data. One way to get that is to use
this.original_data = JSON.parse(JSON.stringify(axios_response.response.data));
after you got the data.

Related

Vue3 quick access printing Target in a "Proxy" object

Vue3 prints everthing as an a "Proxy" object.
I know the value is inside [[Target]], but I don't want to keep expading Proxy->[[Target]] every single time.
Just wanted to let clear, this does't change the output at all. it is just a 'cleaner' way to see stuff.
An example:
const b = 'value'
console.log(value)
Output -> { Proxy: { [[Handler]]: ..., [[Target]]: 'value', [[IsRevoked]]: false }
okay, this works.
console.log(JSON.parse(JSON.stringify(b))).
for more info.
Accessing a Proxy object in Vue3

Replace Vue data() completely

I am looking for a way to replace the object under data.
data() {
return {
form:{ .... }
}
}
I have learnt that I cannot directly change data itself so I moved all my variables under form. I want to replace all the data inside the form so that my form values are changed.
I have found a way to update single values like this;
this.$set(this.someObject, 'planes', true) where the solution is here but I want to replace all the form object.
update_form(){
let self = this
$.ajax({
url: '/formdata/',
type: 'GET',
success: function(response){
self.$set(self.form, needToUpdateAll)
}
});
},
I am stuck right where it says needToUpdateAll. From the docs, it says target, key, value.
I am looking for a solution because I do not want to assign all values one by one (well the object has nested and nested objects :()
Any walk-around would be appreciated
This is not complicated. You don't need $set. Is update_form() in methods? If so, just do this.form = response. Top level names in your data are directly available in the rest of your Vue object.

Skip watcher in vueJS

I have a form for updating document entity.
The document entity consists of list of employees (which is an array of objects) and each employee has a post which is just a string.
I have a dropdown (kind of wrapper for vue-multiselect) which accepts the array of employees and syncs selected employee to a selectedEmployee variable in data().
And also I have a watcher for selectedEmployee which sets the post input automatically when an employee is selected in the dropdown.
So, when creating a document in the form everything's fine, however, when I update the document, then I fetch existing document from server, set selectedEmployee and set employee's post. But, the document also keeps employee's post, so the first time when I open document's form in order to update it, I don't want to automatically update document's post. I want it to be updated only when user actually selects employee himself.
But the watcher gets called the first time too.
So, imagine we have John Doe and his a manager. When I create the document, I change his post to designer. Then, I open up the document form in order to update it, and I should see that for this specific document John Doe's post is "designer", but the watcher gets called and returns the post to manager.
I tried to make a fake variable in data(), like doneFetching, but it works only if I update this var directly in watcher, which looks quite dangerous, plus, in other entities I have many different kinds of selected employees, so making tons of fake flags is not an option.
Here is real code sample (employee = representative in my case):
selectedApproveRepresentative(representative) {
if (!representative) {
this.memoData.approve_representative_id = null
return
}
this.memoData.approve_representative_id = representative.id
// Here is temporary solution, but I have many watchers for many different kinds of employees. If I move the doneFetching flag after I initialized the form, it'll be set to true, and only after that the watcher will be called
if (this.mode === 'update' && !this.doneFetching) {
this.doneFetching = true
return
}
// In normal case a representative might have or have not post, so depending on this case we set it to be empty or filled. But this should not be called the first time I open the form
this.memoData.approve_representative_post_dative_case =
representative.post_dative_case ?
representative.post_dative_case : ''
},
Here is where I initialize data:
created() {
if (this.memo) {
this.memoData = _.cloneDeep(this.memo)
this.selectedApproveRepresentative = _.cloneDeep(this.memo.approve_representative)
}
},
as I understood, your problem is the watcher executed when you init the component. Have you tried setting the immediate property of the watcher to false?
Not everybody knows that the watchers can be defined in different ways.
The simple one that everybody know
watchers: {
propertyToWatch() { //code... }
}
Passing the name of a function as 'string'
watchers: {
propertyToWatch: 'nameOfAfunctionDefinedInMethodsSection'
}
The object declaration
This one is the most descriptive way of declaring a watcher. You write it as an object with a handler property (it can be the name of a function passed as string as above), and other properties like deep to watch nested properties of an object, or in your case immediate which tells to the watcher if the should run immediately when the component is mounted.
watchers: {
propertyToWatch: {
immediate: false,
handler: function() { //code.. }
}
}

Vuejs - Assigning values in a constant

I am reviewing an app build in Vuejs (I am not a vue developer), so be patient with me.
I found this line of code:
const {property, $rxFirebase: {actions: {properties}}} = this
I guess this works as in other languages. "This" is assigning values to the object in the left.
I am trying to read also {sources: {properties}}, so I have added the code like this:
const {property, $rxFirebase: {actions: {properties}, sources: {properties}}} = this
But when I build it, I get an error:
Module build failed: Duplicate declaration "properties"
Any ideas?
This is not just assignment its destructuring assignment.
This line:
const {property, $rxFirebase: {actions: {properties}}} = this
is equivalent to
const property = this.property, properties = this.$rxFirebase.actions.properties;
So you can not add another properties variable because it is already declared. You should add different name for second properties declaration, like this:
const {property, $rxFirebase: {actions: {properties}, sources: {properties: myProperties }}} = this; // where myProperties some name for variable
console.log(myProperties === this.$rxFirebase.sources.properties); // true

How to 'watch' newly created rows in local storage in VueJs?

I am working to create a grid like component in VueJs. You can see the JSFiddle here: https://jsfiddle.net/masade/nrb9f4j2/
In the above fiddle the <datacell> component is used to allow inline edit for any cell.
I am 'watch'-ing changes to the rows and saving them in local storage
A newly created row is being pushed to parent object by this.$parent.unshift(newrow) in the <addrow> component
The problem is when I add a new row, though it is being added to the rows local storage it is not being watched for changes
Steps to replicate the issue (on JS fiddle):
Click on Add Row and enter a name to add
Once the row is added, click on the second column (year) and update it
Any changes to newly added row is not being updated
However if you refresh the page, and modify the same again, I am able to update it again.
Please help me understand if I am doing something wrong
You stumbled into one of the change detection caveats in Vue. Vue cannot detect when you have added a property to an object using the index of that object.
You define newrow like this:
data: function(){
return {
newrow : {}
}
},
which means that newrow has no properties. After that, you always add properties to newrow using an index, like here:
this.$parent.cols.map(function(col,index){
if(typeof newrow[col.m] == "undefined")
newrow[col.m] = "";
})
This means that Vue doesn't know about any of the properties you just added.
I added a method to your fiddle called makeNewRow which adds the properties you need to newrow correctly using the $set method.
makeNewRow(){
this.newrow = {};
this.$parent.cols.map((col,index) => this.$set(this.newrow, col.m, null))
},
Then I call it in mounted and in your addRow method. This fixes your bug. Here is the updated fiddle.
Note: there was an additional bug in your code that I fixed. If someone opened your app and did not previously have grid-vue-local in their local storage, then the uid for gridStorage would be zero, and the first row they added would have the id 0. To fix this I simply added
save: function (rows) {
gridStorage.uid = rows.length
localStorage.setItem(STORAGE_KEY, JSON.stringify(rows))
}
data(){
return{
items:localStorage.fetch()
}
}
watch:{
items:{
handler:function(items){
localStorage.save(items);
},
deep:true
}
}
you can save in items,and watch items,if the items has changed,it will updata