cannot update the property of vue object inside axios success callback - javascript

I have axios callback and I want to update the property of vue object and I have 2 way binding between the input tag and the edited property so that text box is hidden or displayed depending the edited property. when I update edited property to false inside callback, the textbox won't be hidden. but textbox is hidden or displayed when I updated outside axios callback.
editBtnClicked: function (index) {
var promise = null;
axios.put('/rest/project', this.projects[this.currentIndex]).then(response => {
// textbox won't be hidden or displayed even if this statement is executed.
this.projects[this.currentIndex].edited = !(this.projects[this.currentIndex].edited);
});
// textbox is hidden or displayed when this statement is excuted.
// this.projects[this.currentIndex].edited = !(this.projects[this.currentIndex].edited);
}
Could anyone tell me why? and you can check the full code: https://gist.github.com/inherithandle/e61a5ab2809581a5d36de08b4e4349f1

My opinion is that is caused by the property edited of project item in projects array is added to the project dynamically.
Adding property to Ojbect
When adding the property to Object, you have to use $set.
Due to the limitations of modern JavaScript (and the abandonment of Object.observe), Vue cannot detect property addition or deletion.
However, it’s possible to add reactive properties to a nested object using the Vue.set(object, key, value) method:
Please try the below code in line 181 and other lines that changes edited of project item in your source code of github.
this.$set(this.projects[this.currentIndex], 'edited', false);

Your currentIndex always be 0, and your two if statements will always return false.

Related

Error writing to input e.target.value in array

There is an array of inputs in two forms: 1. Empty when created. 2. With the created values from the server after creation and saving. My code for adding a value for each of the inputs in the array doesn't work in the second case and I don't understand what could be wrong. In the first case, the values are written normally, and in the second, nothing happens.
<input
defaultValue={sectionValue[i].text}
value={sectionValue[i].text}
onChange={(e: React.ChangeEvent<HTMLInputElement>): void => {
sectionValue[i].text = e.target.value;
setSectionValue([...sectionValue]);
}}
/>
There are two issues:
Any time you're changing state based on existing state, you're best off using the callback form of your state setter, because it's really easy for the handler to close over a stale copy of the state (sectionValue in your case).
You're breaking one of the rules of state: don't directly modify state. You're directly modifying the sectionValue[i] object. So it's the same object, but with a different text property. Later you're copying the array, but you need to copy the object as well. Sometimes it'll happen to render correctly when you do this, but other times — often — it won't.
To fix both, change:
sectionValue[i].text = e.target.value;
setSectionValue([...sectionValue]);
to
setSectionValue(oldSectionValue => {
const newSectionValue = [...oldSectionValue];
newSectionValue[i] = {
...oldSectionValue[i],
text: e.target.value
};
return newSectionValue;
});
There's more than one way to do that, but the basic things are: 1. Use the callback, and 2. Copy both the object and the array.
Side note: Since sectionValue is an array, I'd suggest using the plural for it (sectionValues).

How to check if any value was changed in an editable Object without using FormGroup?

I am having nested objects with multiple properties. I have created an input field on the UI for every property and value is changed by using [(ngModel)]. I want to implement a functionality where if any value in the object is changed I should be able to detect it and enable the reset option to show initial values again.
I have tried exploring this and the majority of answers were related to form controls only.
if you bind with two-way binding to data you will not be able to hold old record. You can write a fn to compare to objects if you bind with [ngModel].Here is pseudo.
in .ts
value = {}
onValueChange(data:any){
const changedData = getChangedData(this.value, data);
}
in .html
<input [ngModel]="value" (ngModelChange)="onValueChange($event)">
}

How to reasign object with `this` in Angular component?

I have 2 variables inside an Angular component/class FilterComponent that point to an object. The first one (localStorage) stores the results of the current filtering choices that the user makes and is connected with toogle buttons with [(ngValue)]. The second one (globalStorage) gets the results of filtering choices that the user made earlier within other components (if you for example click on fruit on home page,globalStorage.food gets updated, but not the localStorage within FilterComponent). I have a method that should assign gloablStorage to localStorage when the user open the filter that looks like this:
openFilter() {
this.localStorage = this.globalStorage; //this line is responsible for strange behaviour
...
}
This works exactly like it should, but only after the apply button on filter (within FilterComponent) is clicked, which is responsible for sending the contents of localStorage to globalStorage (via next/behaviourSubject).
For some mysterious reason, before that happens, the values of toggleButtons get assign to globalStorage as well as to localStorage (although they are connected only to localStorage). Why is this happening and how can I fix it?
when you do this.localStorage = this.globalStorage; and these 2 variables are objects, it means this.localStorage now point/refer to this.globalStorage, so from now on this 2 variable will refer to 1 place.
If you just want to copy data inside it you should do a copy instead of simple assign by using either Object.assign(shallow copy) or this.local = {...this.global} for loop deep copy
Thank you all for your help. The solution (that's good enough for now) was to reassign each property:
this.localStorage.property1 = this.localStorage.property1;
this.localStorage.property2 = this.localStorage.property2;

How to let a user modify a computed property in Vue?

I'm collecting some data that I want to allow the user to download, and I'm trying to figure out the best approach for dealing with the filename. The default filename should be dynamic and based on the current date, so I figured that I need to create a computed property for it. I also want the user to have the option to change the filename. However, when I set that as the v-model on an input form, none of the changes inside the form register on the property. I can't figure out how I can capture that new value so I can later generate the file with it. I've tried various combinations of v-modeland using multiple computed properties, yet none of them give me the intended result.
Here is a jsfiddle with the minimum code needed to see my issue.
Usually you would want v-model to reference a data property rather than a computed property. That approach would look something like:
<input v-model="filename">
You could then initialize the data property with a default value
data: {
filename: this.defaultFilename()
}
And define defaultFilename() as a method
methods: {
defaultFilename() {
return "whatever";
}
}

ES6: how to update a property inside a Class, using getters and methods

I want a method to update the value of a property, defined through a getter.
The property gets the value of an HTML data-attribute, and the method mut just increment it by 1 and update it in the HTML.
The method is called when a button is clicked. Than the next time the button gets clicked again, I suppose the getter to redefine the value of the property, getting the new value of the data-attribute. But it doesn't occur.
The environment allows me to use jQuery, because the 'element' which is the main argument of the constructor, is a jQuery object representing the HTML target element of the component.
This is the code:
import {Components} from 'MyCustomLibrary';
export default class Calendar extends Components{
get CurrentMonth(){
return this.element.data('calendar-currentmonth');
}
onClickHandler(){
this.getNewCalendar(this.CurrentMonth);
}
getNewCalendar(month){
/****
/* jQuery substituted with Vanilla for suggestion in the comments
$('[data-calendar-currentmonth]').data('calendar-currentmonth',month+1);
****/
let dataAttributes = document.querySelectorAll('[data-calendar-currentmonth]');//.data('calendar-currentmonth',month+1);
dataAttributes.forEach(function(e){
e.setAttribute('data-calendar-currentmonth',parseInt(month)+1);
});
}
constructor(element){
super(element) //element is the html target of component, and it's a jQuery object
this.element.on('click', this.onClickHandler.bind(this));
}
}
In the html I have my button which is an anchor tag with 'data-calendar-currentmonth=2' attribute.
Initally it is set to 2, than the first time I click, the function updates the attribute, I can see "3" in my html DOM through console.
But than when I click again, the value of CurrentMonth is again "2", and more, the html doesn't update anymore (maybe just because the property value doesn't update and than 2+1 is always 3).
Isn't getter supposed to be executed every time the property it defines is called? So why does it doesn't get the new value inside the HTML When it is called the second time?
Actually I found it out that it was jQuery that made the mess. I simply redefined all the variables in Vanilla and it worked perfectly.
I have eliminated the get CurrentMonth statement and I have simply passed it directly in the invocation of getNewCalendar.
Than my code became like this:
onClickHandler() {
this.getNewCalendar(this.element[0].getAttribute('data-calendar-currentmonth'));
/*** 'this.element' is actually a jQuery object. Calling element[0] it comes back to plain js ***/
}
getNewCalendar(month) {
/****
/* jQuery substituted with Vanilla for suggestion in the comments
$('[data-calendar-currentmonth]').data('calendar-currentmonth',month+1);
****/
let dataAttributes = document.querySelectorAll('[data-calendar-currentmonth]');//.data('calendar-currentmonth',month+1);
dataAttributes.forEach(function(e) {
e.setAttribute('data-calendar-currentmonth', parseInt(month) + 1);
});
}
constructor(element) {
super(element) // element is the html target of component, and it's a jQuery object
this.element.on('click', this.onClickHandler.bind(this));
}

Categories