Hello I have a questions on ng-repeat on Angularand function for change value.
I have this ng-repeat that cycling a ObjectArray and have a button for reset value.
<div ng-repeat="element in data.elements">
<button ng-click="reset(element)" >reset</button>
</div>
Where data.elements is array of objects example:
[{id:1, name:"element1"},{id:2, name : "element2"}];
In my Controller I set function Reset in $scope that should do a copy of object passed to an default object:
$scope.reset = function(el){
$scope.defaultObject = {id:500, name:"default"};
el = angular.copy($scope.defaultObject);
}
But doesn't work, but if I do:
$scope.reset = function(el){
$scope.defaultObject = {id:500, name:"default"};
el.name = $scope.defaultObject.name;
}
It work.
So I would like that when I do (in this example):
el = angular.copy($scope.defaultObject);
have the object el equals to object $scope.defaultObject my question is, Can i copy entire object without cycling all properties?
You're passing an object to the reset function, then you're overwriting this object. That's it, nothing happens because it won't affect the original object, which is in the data.elements array.
You need to use a different approach. Track the element by its index :
<div ng-repeat="element in data.elements track by index">
<button ng-click="reset(index)" >reset</button>
</div>
...then amend data.elements[index]:
$scope.reset = function(index){
$scope.data.elements[index] = {id:500, name:"default"};
}
are you saying, your updated object does not reflect on the UI? you can try forcing the scope update by running $scope.$apply() after you have assigned the object.
Related
I have a object like this
var myObject = {'123':'something','345':'something else'}
I need to render it as:
<option value={'123':'something'}>something</option>
Here is what i have tried:
ng-options="someObj as label for (id,label) in myObject
Doesn't work!
The model gets the 'label' and not the whole object. Is it even possible to assign a object to value attribute of a SELECT element?
EDIT
Filter for object of the form: {'123':'something','345':'something else'}
.filter('putKeysandValues',function(){
return function(options){
var arr = [];
angular.forEach(options,function(value,key){
arr.push({'key':key,'label':value})
});
return arr;
}
});
You will need to provide an object with key & value attributes to ngOptions. For this you will need to modify the structure of your object to actually have this attributes.
One way is using a filter that returns a valid array for ngOptions:
ng-options="option.label for option in data.availableOptions | putKeyAndValue"
You can check out this plunker with a working filter.
If input is:
[{'123':'something'}, {'345':'something else'}]
Then output is:
[{"123":"something","id":123,"label":"something","$$hashKey":"object:3"},{"345":"something else","id":345,"label":"something else","$$hashKey":"object:4"}]
If you want to remove the obsolete id attribute, then use this filter.
With input the same input, it will return this:
[{"id":123,"label":"something","$$hashKey":"object:3"},{"id":345,"label":"something else","$$hashKey":"object:4"}]
If you still need to return a new array, then do not do it on the filter. In that case you can process the data before passing it to the view, all in the controller like this.
With input:
{'123':'something', '345':'something else'}
Output this:
[{"id":123,"label":"something"},{"id":345,"label":"something else"}]
Using select as something for a (key,value) in set of Values
ng-options="key as value for (key,value) in myObject"
This will put key into the option value and value into label. With that you need to also use ng-model and ngChange. Since you can't pass objects into values, idea is to do that in your controller.
<select ng-model="val" ng-change="changeVal(val)" ng-options="key as value for (key,value) in ops">
In your controller you already have myObject you are gonna add selected item and changeVal function
<script>
var selectedObject;
$scope.changeVal = function(val){
selectedObject = {};
selectedObject[val] = myObject[val];
}
</script>
It's a workaround without putting object into value attr since it is a string.
In my Angular app, I have a checkbox list which is generated via a nested ng-repeat, like so:
<div ng-repeat="type in boundaryPartners">
<div class="row">
<div class="col-xs-12 highlight top-pad">
<div ng-repeat="partner in type.partners" class="highlight">
<label class="checkbox-inline">
<input type="checkbox" value="partner"
ng-model="ids[$parent.$index][$index]"
ng-true-value="{{partner}}"
ng-false-value="{{undefined}}">
<p><span ></span>{{partner.name}}<p>
</label>
</div>
</div>
</div>
</div>
and in my controller:
$scope.ids = [];
$scope.$watchCollection('ids', function(newVal) {
for (var i = 0, j = newVal.length; i < j; i++) {
// Create new participatingPatners tier if it doesn't exist
if(!$scope.report.participatingPartners[i]) $scope.report.participatingPartners[i] = {};
// Give it an id
$scope.report.participatingPartners[i].id = i + 1;
// Map our values to it
$scope.report.participatingPartners[i].entities = $.map(newVal[i], function(value, index) {
return [value];
});
}
});
The problem is, this $scope.$watchCollection stops watching once I've added one of each top-level ids, so if I add a given number of inputs from the first nested list, then another from the second list, My $scope.report.participatingPartners object never gets updated.
How can I $watch for changes within ids[$parent.$index][$index], making sure updated my object whenever a checkbox gets ticket or unticked?
You are creating an array of arrays:
$scope.ids = [
[],
[],
//...
]
But use $watchCollection to watch for changes in the outer array, i.e. of $scope.ids. This will only identify changes when nested arrays become different objects (or created the first time).
You could use $scope.$watch("ids", function(){}, true) - with true standing for "deep-watch", but that would be very wasteful, since it's an expensive check that would be performed on every digest cycle, whether a checkbox was clicked or not.
Instead, use ng-change to trigger the handler:
<input type="checkbox" value="partner"
ng-model="ids[$parent.$index][$index]"
ng-change="handleCheckboxChanged()">
$scope.handleCheckboxChanged = function(){
// whatever you wanted to do before in the handler of $watchCollection
}
$watchCollection is similar to $watch in that it checks the physical object reference, but goes one step further; it also goes one level deep and does a reference check on those properties.
You'll need to use $watch, but set the objectEquality flag to true. This will tell $watch to perform deep reference checking. Depending on the depth of the item being watched this can hurt performance significantly.
$watch(watchExpression, listener, [objectEquality]);
Can you try to watch for object equality :
$scope.$watchCollection('ids', function(newVal) {
}, true);
So, I have jsfiddle here.
We can add new nodes and delete all the children in parent node. But how can I delete specific child without iterating array? I know that we can use:
Array.prototype.splice()
If we want to remove, for example, this object (screenshot #1), we can get its index and use splice().
But if I want to remove deeply nested object, I don't want iterate array and use splice(), because of perfomance.
In my console I got only:
Object { name: "Node-8-6-2", menu: false, $$hashKey: "object:151" }
And I don't have an access to nodes of parent array. And I need to iterate all array, so that I could remove it.
Anybody knows solution of this issue?
Here is your plunker updated. http://jsfiddle.net/marduke182/uXbn6/2828/
The little changes are:
Adding the parent references to the object using parentNodes .
$scope.add = function(data) {
var post = data.nodes.length + 1;
var newName = data.name + '-' + post;
data.nodes.push({name: newName,nodes: [], parentNodes: data.nodes});
};
Create method delete node and pass the $index, do the splice to the parent given the index attribute:
$scope.delete_node = function(data, index) {
data.parentNodes.splice(index, 1);
};
Add the new method to the template:
<script type="text/ng-template" id="tree_item_renderer.html">
{{data.name}}
<button ng-click="add(data)">Add node</button>
<button ng-click="delete(data)" ng-show="data.nodes.length > 0">Delete nodes</button>
<button ng-click="delete_node(data, $index)" >Delete node {{$index}}</button>
<ul>
<li ng-repeat="data in data.nodes" ng-include="'tree_item_renderer.html'"></li>
</ul>
</script>
When you are building your nested tree, you can add a parent attribute to your arrays:
var parentNode = [];
var node = [];
node.parent = parentNode;
parentNode.push(node);
Now, if you want to remove node, you can say:
var index = node.parent.indexOf(node);
node.parent.splice(index, 1);
I have a ng-repeat loop for a number of values. While looping, I fetch a variable from another array of values and use that:
<div ng-repeat="i in [1,2,3]">
<div ng-init="obj = getObject(i)">
<pre>{{ obj }} </pre>
</div>
</div>
My goal is now to change a property of that variable and do a POST request containing the updated variable. The response for that from the server then contains all values, which I bind to $scope in order to update the view.
<a ng-click="change(obj, 5)">Set property to 5</a>
$scope.change = function(o, value) {
o.prop = value;
// save() sends a POST requests and returns a JSON with all values
$scope.values = save(o);
}
This works, but only the first time I do it. All other changes will be reflected in the $scope.variables, but not in the {{ obj }} variables in the template. $scope.$apply() has no effect, either.
I've created a JS Fiddle to show my problem, which only mocks the HTTP requests. However, I have found that even when I run this code against my REST backend, the first time everything works fine but every time after that reflects no changes at all.
I think the issue is caused because you are using ng-init, which probably sets a non-changing value since you are calling a function. It will work once you change {{ obj }} to {{ getObject(i) }}. The only issue is that your variables are also being referenced and modified in the script allTwo and allThree are being modified since you directly assign them. I fixed that by cloning the objects, but it will probably not be an issue when you are using AJAX.
Here is an updated version of your fiddle: http://jsfiddle.net/0ps2d7Lp/6/
I have made changes to your fiddle.
<div ng-repeat="i in [1,2,3]">
<div>
<pre>{{ getObject(i) }} </pre>
</div>
</div>
Controller changes:
$scope.changeType = function(ids, type) {
angular.forEach($scope.objects, function(o) {
if (ids.indexOf(o.id) > -1) {
o.type = type;
var response = (type === 2) ? allTwo : allThree
$scope.objects = angular.copy(response);
}
});
};
Link to your updated fiddle is here
In your case, getObject() is necessary, but I excluded it in my answer for simplicity sake. I understand that you need to perform a PUT/POST request to update the objects on the server-side, but I don't believe it's necessary at all to re-bind the view to the server's response. Fundamentally, a PUT doesn't require a response other than 200 OK in most cases. The point is you're telling the server to update objects, not create them. Thus, no primary keys change, so, you don't need to rebind the objects. Changes are already resident in memory.
HTML
<div class="example" ng-controller="MyCtrl">
<div ng-repeat="obj in objects">
<div><pre>{{ obj }}</pre></div>
</div>
Change all to Type 2
Change all to Type 3
</div>
JavaScript
var myApp = angular.module('myApp',[]);
function MyCtrl($scope) {
// initial objects
$scope.objects = [
{ id: 1, type: 1 },
{ id: 2, type: 2 },
{ id: 3, type: 3 }
];
$scope.changeType = function(ids, type) {
angular.forEach($scope.objects, function(o) {
if (ids.indexOf(o.id) > -1) {
o.type = type;
// Perform your PUT/POST request here for each
// updated object to update it on the server-side.
// There is no need to bind to a server response.
}
});
};
}
I have a question about Angular watch within an array of objects.
I have an array $scope.chartSeries with objects as following:
[{"location": {values}, "id":"serie-1", "meter":{values}, "name": "seriename", "data":[{1,2,4,5,7,4,6}]}]
This is used to draw a linechart with highcharts.
I want to watch this array, and if a value changes I want to know the index and the value that is being changed.
I found several options for watch but none of them seem to fit my situation. Can you help me out?
If you render and change your array in ng-repeat, you can use ng-change directive and pass in it a $index parameter.
For example:
<div ng-repeat="item in array">
<input type="text" ng-model="item.location" ng-change="changeValue($index)"/>
</div>
Or you can use $watch and work with newValue, oldValue parameters
$scope.$watch('array', function (newValue, oldValue) {
for(var i = 0; i < newValue.length; i++) {
if(newValue[i].location != oldValue[i].location)
var indexOfChangedItem = i;
//bla-bla-bla
}
}, true);
You can use $watchGroup(watchExpressions, listener).
For e.g.:
$scope.chartSeries = [{"location": {values}, "id":"serie-1", "meter":{values}, "name": "seriename", "data":[{1,2,4,5,7,4,6}]}];
$scope.$watchCollection('chartSeries ', randomFunction(var) {
//code
});
Or you can use watch for individual values in array to know which ones got changed.