Store binded object before change - javascript

I have an object which contains information on a group:
selectedGroup: {
name: Test Group,
id: 10,
description: a group,
owner: 88,
ownerIsUser: False
}
I have textbox which binds to the selectedGroups.name property. I need to store that name in it's own variable before a change occurs.
I have an AJAX library that allows me to update the groups info like name and description but it needs the old name to identify which group to update. I tried:
var oldName = selectedGroup.name
But this doesn't work since as soon as I start typing something oldName gets updated.

You can create a watch:
$scope.$watch('selectedGroup.name', function(newVal, oldVal) {
// save/copy oldVal somewhere
});
PD: To deep copy the object you can use angular.copy()

Related

AngularJS Material - ng-checked on md-radio-button with ng-value as an object

I've looked around to see if anyone has answered this and it appears they have not. I want to use an object rather than a string or integer as the value of the radio button. Is this possible? Doesn't seem so because I'm having trouble with the tags md-radio-button recognizing an object rather than string or integer value as having been selected. I can see that it works after the page loads and I select something but I don't know how to check the radio button if the value already exists. You can see a very simple demonstration here: https://codepen.io/anon/pen/ZmjrLN
I've tried class="md-checked" to see if that works, it does not. I've tried ng-checked="selectedStatus.Name == status.Name", it doesn't work either. In fact ng-checked="true" also does not work.
I would think md-radio-button could work with an object!
--- EDIT FOR CLARIFICATION ---
From an answer below, referencing the code from codepen, if I use the same object in $scope.statuses to populate $scope.selectedStatus, it indeed selects the correct radio button on load. HOWEVER, in the real world $scope.selectedStatus is populated with the actual status from the server and $scope.statuses is also populated from that same call. They are the same but 2 different objects.
In a nutshell, I still want to check off the correct one, even though the objects aren't exactly the same, they should be treated like they are, because they are the same.
When comparing objects, ng-checked looks to see if checked is the same object or a reference and does not check the contend of the object. So to set the selected / checked radio button, make the value refer to the object. Here is an example of the Javascript updates for your codepen:
angular
.module('BlankApp')
.controller('AppController', function($scope) {
$scope.statuses = [
{id: 1, Name: 'Active'},
{id: 2, Name: 'Dormant'}
];
$scope.selectedStatus = $scope.statuses[0]; // reference the object directly
});
Or if the value is set outside of the scope and we have to compare, then we can compare manually and then set the selectedStatus by finding the correct object and assigning it.
angular
.module('BlankApp')
.controller('AppController', function($scope) {
$scope.statuses = [
{id: 1, Name: 'Active'},
{id: 2, Name: 'Dormant'}
];
$scope.selectedStatus = {id: 1, Name: 'Active'};
// Find the index of the selected that matches on id
let i = $scope.statuses.findIndex((val) => {
return val.id = $scope.selectedStatus.id;
});
$scope.selectedStatus = $scope.statuses[i]
});

Knockout select bindings overwriting my predefined observable object

Hi i have a application which data is passed from one page to another with predefined data objects/arrays assigned to it, my issue is i can see the observableArray having a value and then it turns the SelectedPeople observable to undefined.
I have eliminated down to the data bind markup as when i remove that my observable array does not set anything to undefined.
Here is how i am binding my observables/observableArray to the elements.
<select data-bind="options: ObservableArray.People, value: ObservableArray.SelectedPeople, optionsText: 'Name'"></select>
ObservableArray.People = Observable Array of objects - works fine and renders all the dropdown options
ObservableArray.SelectedPeople = Observable
Both have the 'Name' object defined to match the optionsText. It works perfectly when selecting data from scratch but when i try have predefined data in it the Observable.SelectedPeople object keeps getting sent as undefined when it tries to load.
Basically my Observable.SelectedPeople has a object on that which should predefined the value of that select and the object 100% matches one of the dropdown ObservableArray.People options. I need it not to set Observable.SelectedPeople to undefined and populate the select box.
Can anyone see why this is happening.
Thanks
...and the object 100% matches one of the dropdown ObservableArray.People options.
This line makes me suspicious of whether you're using an actual reference to the object, or just an object that is similar.
For example, this will not work:
var options = [{ id: 1 }, { id: 2 }, { id: 3}];
var selectedOption = ko.observable({ id: 1 });
Knockout does not perform some sort of deepEquals comparison; if it sees a non-primitive, it does a reference check. options[0] !== { id: 1 }, so this initial selection is not valid.
The code below will work, because you're using an actual object from the array you use in your select element:
var options = [{ id: 1 }, { id: 2 }, { id: 3}];
var selectedOption = ko.observable(options[0]);

Ember dynamically update template (array item)

I am using Ember version 2 and have my component template as below;
{{#each info as |item|}}
<span class="value">{{item.value}}</span>
<span class="label">{{t item.label}}</span>
{{/each}}
Now I want to write values to it dynamically (i.e. assume it is a dynamic total which initially is 0 & updates automatically based on user selection of some rows)
My question is while the below code works;
var info = self.get('gridParas.info');
info.popObject();
info.pushObject({
label: 'MyTotalField',
value: total
});
self.set('gridParas.info', info);
This does not work;
var info = self.get('gridParas.info');
info.label = "MyTotalField";
info.value = total;
self.set('gridParas.info', info);
Is the popObject(), pushObject() the correct way ? What is wrong with the 2nd approach ?
I wanted to avoid pop, push each time.
the set() methods notifies the model that a property has changed, triggering the template to update. Instead of using it, you can call notifyPropertyChange('info') or notifyPropertyChange('info.label') after you update the label and value, which should update the template. More info can be found in the docs here:
http://emberjs.com/api/classes/Ember.Observable.html#method_notifyPropertyChange
You can set the properties directly on the last element, without popping and pushing:
var info = self.get('gridParas.info');
info.getObjectAt(info.get('length') - 1).setProperties({
label: 'MyTotalField',
value: total
});
Or, if you don't want to reuse the current last item, use replace:
info.replace(info.get('length') - 1, 1, [{
label: 'MyTotalField',
value: total
}]);
As you can see, you need to pass replace the index, the count of items to be replaced, and an array of new items to stick in there.
In any case, you do not need this:
self.set('gridParas.info', info);
Because that's already gridParas.info's value.

AngularJS - binding array value to input ngModel

I have $scope.myArray, and it's binding with an input field by ngModel and the expression {{myArray}}
My issue is when I modified myArray by call changeMyArray(), the input's value did not change. But the expression {{myArray}} is display new value.
So, Why the expression work but input field does not?
I have a way to do, but I want to find a better approach
var newArr = $scope.myArray;
newArr.push("b");
$scope.myArray = angular.copy(newArr);;
Example fiddle
Basically, I think what you want to do is bind the input to a "new entry" scope variable, and then push the value of that variable to your array when the user clicks "Push To". Here's what I mean:
In controller:
$scope.changeMyArray = function() {
$scope.myArray.push($scope.newEntry);
$scope.newEntry = "";
}
In HTML:
<input ng-model="newEntry">
But actually:
Really what you want is a way to edit the contents of an array via text, and have updates to that array from elsewhere also update the text. This is actually pretty simple since browsers come with a JSON library.
I implemented it by starting with a known pair of objects:
$scope.myArray = [];
$scope.myArrayString = "[]";
That way you can update the string via ngModel:
<input ng-model="myArrayString">
Watch for changes on this model to update the actual array:
$scope.$watch("myArrayString", function() {
$scope.myArray = JSON.parse($scope.myArrayString);
});
Then update the string in the changeMyArray function:
$scope.changeMyArray = function() {
$scope.myArray.push("b"); // Or whatever you would like to add here
$scope.myArrayString = JSON.stringify($scope.myArray);
}
Experiment in my fork of the Fiddle.
What's going on?
The variable $scope.myArray is an object, and any object in Javascript can be converted to a string (most complex objects end up as the unhelpful "[object Object]"). Arrays will actually display their contents when converted to a string, so binding an array to HTML via {{myArray}} is pretty straightforward.
However, the reverse conversion is not as simple. In general, a text input can't be bound to an array in a two-way fashion as we'd like. The solution, then, is to use an intermediary variable to hold the string value, and use $scope.$watch to keep the two values in sync.
So you seem to be wondering why when pushing to the array, your $watch function doesn't do the increment. That's because the #watch function only checks object reference equality.
When pushing to the array, the reference stays the same. When you copy the array and set it again in the same variable, the reference changes.
That's why #watchCollection works as expected and increments when each item is pushed.
I have an explanation for my question. Please correct me if I wrong, very thank.
My Question:
Why "myArray" input field does not update when $scope.myArray is changed (Model doesn't update View)?
<input ng-model="myArray" id="myArray">
The answer is AngularJs ng-model doesn't know $scope.myArray is changed. Because ng-model does not perform a deep watch of object (rather than a string or number), it only looks for a change of identity or compares the reference of the objects.
In my case, $scope.myArray is collection. So, although $scope.myArray has changed by push new item (structure is changed), it's reference does not change.
As the result, $setViewValue() and $render() never invoked to update the view.
$render
$setViewValue
Solution:
Sol1: Add new item to $scope.myArray, make a copy of myArray object and then asign a copy to $scope.myArray again. By this way, the object reference is changed. AngularJs see that change and update the view.
var newArr = $scope.myArray;
newArr.push("b");
$scope.myArray = angular.copy(newArr);
Sol2: Create custome $watch('email', function(){...}, true). The last parameter is TRUE to let Angular perform a deep watch. Then, in watch's listener function, I manually set $viewValue = newValue and invoke $render() of ngModelController to update the view. In case we have Formatters, we should invokes them in this step.
$scope.$watch('myArray', function(newValue, oldValue) {
if (newValue !== oldValue) {
var ctrl = angular.element(document.querySelector('#myArray')).controller('ngModel');
// Invoke formatter
var formatters = ctrl.$formatters,
idx = formatters.length;
while(idx--) {
newValue = formatters[idx](newValue);
}
ctrl.$render();
}
}, true);
Please see my script

Data Binding to a specific item of an array in Angular

Given a data structure that contains an array of JavaScript objects, how can I bind a certain entry from that array to an input field using Angular?
The data structure looks like this:
$scope.data = {
name: 'Foo Bar',
fields: [
{field: "F1", value: "1F"},
{field: "F2", value: "2F"},
{field: "F3", value: "3F"}
]
};
The fields array contains several instances of the given structure, with each entry having both a field attribute and a value attribute.
How can I bind an input control to the value field attribute of the array entry with the field F1?
<input ng-model="???"/>
I know that I could bind all fields using an ng-repeat, but that's not what I want. The above data is just an example from a much larger list of fields, where I only want to bind a pre-defined subset of fields to controls on the screen. The subset is not based on the attributes in the array entries, but is known at design time of the page.
So for the above example, I would try to bind F1 to one input on the page, and F2 to another one. F3 would not be bound to a control.
I've seen examples where a function was used in the ng-model, but it doesn't seem to work with Angular 1.1.0.
Is there another clever way to bind the input field to a specific array entry?
Here's a fiddle that has an example, but does not work since it's trying to use function in the ng-model attribute: http://jsfiddle.net/nwinkler/cbnAU/4/
Update
Based on the recommendation below, this is what it should look like: http://jsfiddle.net/nwinkler/cbnAU/7/
I personally would reorganize the array in a way that field property of an entry of the array become the identifier of the object. Mhhh that sentence may sound strange. What I mean is the following:
$scope.data = {
name: 'F1',
fields: {
F1: {
value: "1F"
},
F2: {
value: "2F"
}
}
};
If you want to bind a the value dynamically and it's an easy and quick way to achieve it.
Here is your fiddle modified so that it words. http://jsfiddle.net/RZFm6/
I hope that helps
You can use an array of objects, just not an array of strings.
HTML:
<div ng-repeat="field in data.fields">
<input ng-model="field.val"/>
</div>
JS:
$scope.data = {
name: 'F1',
fields: [
{ val: "v1" },
{ val: "v2" }
]
};
I've updated #Flek's fiddle here: http://jsfiddle.net/RZFm6/6/
Edit: Sorry just read your question properly, you can still use an array with:
<label>Bound to F1:</label>
<input ng-model="data.fields[0].value"/>
though maybe stop and think. Is there going to be variable number of fields ? or are you making a predetermined number of fields ? Use an array in the former and an object for the latter.
One way to do it is to simply add the necessary references to the scope, like this:
$scope.fieldF1 = fieldValue('F1');
$scope.fieldF2 = fieldValue('F2');
And then use those references:
<input ng-model="fieldF1.value"/>
<input ng-model="fieldF2.value"/>
Fiddle: http://jsfiddle.net/cbnAU/5/
Note: I'm assuming that $scope.data is static, but if it happens to be dynamic you can always watch for changes on it and recalculate the references...

Categories

Resources