How to resolve angular ng-options track by deep clone issue? - angularjs

ng-options="branch as branch.name for branch in ordersItemClientInfo.branches track by branch.id"
In my case "branches" is an array of object and each of them have field ref to so deep object (map geoObject). Angular tries to copy thats object and fails!
from angular.js:
getViewValueFromOption: function(option) {
// If the viewValue could be an object that may be mutated by the application,
// we need to make a copy and not return the reference to the value on the option.
return trackBy ? angular.copy(option.viewValue) : option.viewValue;
}
I don't need a copy, I need original object to be selected. How can I make it?

Maybe you can try to do a JSON.stringify (to each item before showing it) and then make the JSON.parse in the template whenever you want to display it?

Related

Changing variable without influencing view

I am displaying a view (ui-router) on the Ionic platform with a message list. New messages should be shown as unread, but then corresponding variables should be changed to read. The problem is that when I set the controller to change the proper variable, the view automatically updates itself immediately with this change and the messages are immediately displayed as read.
How can I deal with this?
In other words: in a variable-dependent view, I want to display the view first and then modify the variable, so that the change of the variable does not affect the view.
The current code (MarkAsRead() is called from within the template):
$scope.messages = $rootScope.Messages.Inbox;
$scope.MarkAsRead = function(ID_NR)
{
angular.forEach($rootScope.Messages.Inbox, function (value, key) {
if ((value.IsRead == 2) && (value.From == ID_NR))
{
value.IsRead = 3;
}
});
}
It's odd you're battling the main thing angular is known for: two-way binding. That said, if you really want to make changes to the variable without updating the view, you can use angular.copy to make a deep copy of the object and manipulate the copy itself. Or display the copy but manipulate the original
Another option, if you're using Angular > 1.3, you can use one-time binding to only update the view first time:
Simply prefix your expression with :: e.g
<p>{{::message.IsRead}}</p>
https://docs.angularjs.org/guide/expression#one-time-binding

Mutating object properties within an array with Polymer

I not sure how to solve this issue. I am sure someone will know this very quickly.
I have an array of objects and modifying a property. I have a firebase listener 'child_changed'. When firebase is updated need to update the array. Here is the code below.
dbRefList.on('child_changed', function(snap) {
var len = this.grocerylist.length;
for(var i=len; i--;) {
if(this.grocerylist[i].key === snap.key) {
this.set(['grocerylist', i, 'selected'], snap.val().selected);
}
}
this.notifyPath('grocerylist', this.grocerylist.slice());
}.bind(this));
When the array is modified I want the template repeat-dom to trigger. I know this.set will not trigger array mutation sub properties but again I am not sure how to solve this. I done research and tried so many solutions.
I can force a render on the template dom-repeat but I would prefer the data binding way.
So this code (just the this.set you have in there now) should cause the value of grocerylist.i.selected to update inside the dom-repeat (assuming it's bound in there so it's actually showing up).
What behavior are you seeing? Are you trying to filter or sort the list based on the selected value? In that case, you might need to add observe="selected" on the dom-repeat.
(Also—have you confirmed that the child-changed callback is being called with the this value you expect—the element—rather than window or something else?)
You should be able to force a refresh by doing this.grocerylist = this.grocerylist.slice() or this.set('grocerylist', this.grocerylist.slice()); ... notifyPath doesn't work here because notifyPath doesn't change the value, it notifies the element about a prior change (the second argument is effectively ignored).

Is there a way to get the name of an ng-model from an element

I want to find the name of the model on the active element. Basically I have document.activeElement how do I find out what it's ng-model is.
<input ng-model="myModel">
So in the above control if that was active how would I be able to find out that we are in the element with the ng-model of myModel. This is technically a workaround for adding an id or a name, but I would rather not do it if I can
As an aside this code is in the controller not a directive.
Here is the Fiddle
You need to get the element object as discribed in the comment in the code and then access it with attr
// get element
var result = document.getElementsByTagName("input")[0];
// get object using jQLite or jQuery
var ele = angular.element(result);
// access 'ng-model' as 'attribute'
alert(ele.attr('ng-model'));
CAUTION: Here it's shown just for example considering just one input element. Use proper class/element/attribute selector to get proper element you want to target.
input/element In Angularjs will bind a ng-model.Without using id or classes to get the element DOM can use $('[ng-model="Model"]') But If the model is under ng-repeat.
if you write
and to get the element use $('[ng-model="name"]'), it will inject that particular element from the DOM.
With document.activeElement, you can access the list of attributes on the returned element like this:
var nameOfModel = document.activeElement.attributes['ng-model'].value;

Binding doesn't update when the original object changes. Is copying the only/best solution?

I'm running into the same problem as stated here.
Binding doesn't update when the original object changes.
I'm looking for more opinions on how to approach this problem.
I would think this would be a very common problem. Pretty much all ajax request in spa's return json which is used to create js objects. New references are created to point to these objects and/or existing references are updated to point to these new objects.
Is copying the object properties from the new object to the existing object the only/best solution? What if the object is a big collection or has a deep graph? It seems like copying wouldn't work well. Is there a way to monitor/watch the reference?
Am I thinking about this wrong?
As request i've setup a jsfiddle to simulate the issue.
https://jsfiddle.net/ho69ofog/
var newObject = {
"subObject": {
"subProperty": Math.random().toString(36).replace(/[^a-z]+/g, '').substr(0, 5)
}
};
myObject = newObject;
Notice that the only scope value that is updating is when the whole factory object attached to the scope. As mentioned above and in the referenced issue Binding doesn't update when the original object changes , copying the object instead of creating a new object(inside the updateTimer function) will cause all scope values to update.
It's not the only solution, but it's one of the simplest.
Here's another. Use a $watch in the controller to detect when the object has changed.
$scope.$watch(function(){
return myFactory.getObject();
},
function(newValue){
$scope.subObject = angular.isDefined(newValue) ? newValue.subObject : undefined;
});

Angular X-Editable - additional parameters to onbeforesave

I am using angular-xeditable to edit a table value inline and want to save that value in the database once the input is blurred. What I want to do is call the save function using the "onbeforesave" attribute.
The problem is the function takes only the changed value as parameter. I also need the id of the tag on which xeditable is applied.
So how can I pass a reference to the DOM element (best option) or just the element id (will suffice in this case).
Many thanks!
on your ng-repeat on front end, on your "onBeforeSave" attribute you can pass the $index so it will be like onBeforesave="myFUnction($index)" and then use the index in targeting the element of array to alter values. so if your front end ng-repeat uses $scope.items, you will use the index to target $scope.items[index] and then you can take all the data on this index and pass data to server to do some database updates.
Keep in mind that you can pass the repeated object, it can help when you'll try to figure out the object from that index,
As explained here :
How to use Angular-Xeditable's onBeforeSave / onAfterSave methods with more than $data as parameter
<div ng-repeat="p in people">
{{p.name}}
</div>
You'd then have the following code in your controller:
$scope.updatePerson = function(person) {
var ret = PersonService.save(person); // or whatever save mechanism you use
// You need to return a error string if save fails, true otherwise
if (ret !== null) { // You have to make sure this logic works for your save service
return ret;
}
return true;
}
I prefer this solution because its avoiding index + filtering problems
as explained here:
http://codeutopia.net/blog/2014/11/10/angularjs-best-practices-avoid-using-ng-repeats-index/

Resources