Given an Array sourceArray
I would like to create a targetArray
that depends on the entries of the first one. This means that the created Array should contain an entry for each of the source-entries and is updated whenever sourceArray
changes. However modifying the targetArray
should never update the source.
This Plunker kind of works as long as sourceArray
is static. As soon as you start to modify the source-entries, it obviously won't update the target properly because i am lacking a proper databinding mechanism.
Do i need to update targetArray
manually by observing sourceArray
or is there any kind of 1-way-databinding mechanism implemented by Angular which i can use to keep two arrays synchronized?
As Pritam said. You should Use $watch. But must bind the Collection to it, in order to make it work. And inside the watch merge the arrays.
Find this working sample:
$scope.$watchCollection(angular.bind(this, function () {
return this.sourceArray;}), function (newVal, oldVal) {
var arr = [];
for(var i in vm.sourceArray){
var shared = false;
for (var j in vm.targetArray)
if (vm.targetArray[j].id == vm.sourceArray[i].id) {
shared = true;
break;
}
if(!shared) arr.push(vm.sourceArray[i])
}
console.log(arr);
vm.targetArray = vm.targetArray.concat(arr);
},true);
http://plnkr.co/edit/E2inRLtwfWnb1VBymNNl?p=preview
You should use $watch
. And then add the necessary function.
You can look here and the official documentation.
$watch(watchExpression, listener, [objectEquality]); Registers a listener callback to be executed whenever the watchExpression changes.
The watchExpression is called on every call to $digest() and should return the value that will be watched. (watchExpression should not change its value when executed multiple times with the same input because it may be executed multiple times by $digest(). That is, watchExpression should be idempotent.) The listener is called only when the value from the current watchExpression and the previous call to watchExpression are not equal (with the exception of the initial run, see below). Inequality is determined according to reference inequality, strict comparison via the !== Javascript operator, unless objectEquality == true (see next point) When objectEquality == true, inequality of the watchExpression is determined according to the angular.equals function. To save the value of the object for later comparison, the angular.copy function is used. This therefore means that watching complex objects will have adverse memory and performance implications. This should not be used to watch for changes in objects that are or contain File objects due to limitations with angular.copy. The watch listener may change the model, which may trigger other listeners to fire. This is achieved by rerunning the watchers until no changes are detected. The rerun iteration limit is 10 to prevent an infinite loop deadlock. If you want to be notified whenever $digest is called, you can register a watchExpression function with no listener. (Be prepared for multiple calls to your watchExpression because it will execute multiple times in a single $digest cycle if a change is detected.)
After a watcher is registered with the scope, the listener fn is called asynchronously (via $evalAsync) to initialize the watcher. In rare cases, this is undesirable because the listener is called when the result of watchExpression didn't change. To detect this scenario within the listener fn, you can compare the newVal and oldVal. If these two values are identical (===) then the listener was called due to initialization.
Here's a snippet I made. Note when changing source array both arrays are affected, however when you change only the target the source stays intact.
angular.module('app', [])
.controller('mainCtrl', function($scope) {
var vm = this;
vm.sourceArray = [];
vm.source = '["change me!",{"a":3},[100]]';
$scope.$watch('vm.source', function(newVal) {
try {
vm.sourceArray = JSON.parse(newVal);
vm.target = newVal;
vm.serr = null;
} catch (e) {
vm.serr = 'Invalid JSON';
}
});
$scope.$watch('vm.target', function(newVal) {
try {
vm.targetArray = JSON.parse(newVal);
vm.terr = null;
} catch (e) {
vm.terr = 'Invalid JSON';
}
});
//Copy whole array on change
$scope.$watch('vm.sourceArray', function(newVal) {
vm.targetArray = angular.copy(newVal);
}, true);
return this;
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app" ng-controller="mainCtrl as vm">
<span>Change the inputs one at a time to see the changes take effect</span>
<h5>Source:</h5>
<input type="text" ng-model="vm.source" ng-model-options="{debounce: 300}" placeholder="Enter json formatted string for source array"><span>{{vm.serr}}</span>
<div>Model: {{vm.sourceArray|json:null:2}}</div>
<br>
<h5>Target:</h5>
<input type="text" ng-model="vm.target" ng-model-options="{debounce: 300}" placeholder="Enter json formatted string for source array"><span>{{vm.terr}}</span>
<div>Model: {{vm.targetArray|json:null:2}}</div>
</div>
You can use $watch expression.
Here is another way:-(download underscore.js or CDN)
http://plnkr.co/edit/hrOrEdaQ0M7wEgWlRHlO?p=preview
underscore.js extend method.
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
var vm = this;
vm.sourceArray = [{id: '0', name: 'someObject'}, {id: '1', name: 'anotherObject'}];
vm.targetArray = angular.copy(vm.sourceArray);
// angular.copy(vm.sourceArray, vm.targetArray);
vm.push = function(){
let found = false;
angular.forEach(vm.sourceArray, function(el){
if (el.id === vm.id){
el.name = vm.name;
found = true;
}
});
if (!found){
vm.sourceArray.push({id: vm.id, name: vm.name});
_.extend(vm.targetArray, vm.sourceArray);
}
};
vm.pushTarget = function(){
let found = false;
angular.forEach(vm.targetArray, function(el){
if (el.id === vm.id1){
el.name = vm.name1;
found = true;
}
});
if (!found){
console.log({id: vm.id, name: vm.name})
vm.targetArray.push({id: vm.id1, name: vm.name1});
}
};
});
you can get underscore.js code:-
_.extend = createAssigner(_.allKeys);
// An internal function for creating assigner functions.
var createAssigner = function(keysFunc, undefinedOnly) {
return function(obj) {
var length = arguments.length;
if (length < 2 || obj == null) return obj;
for (var index = 1; index < length; index++) {
var source = arguments[index],
keys = keysFunc(source),
l = keys.length;
for (var i = 0; i < l; i++) {
var key = keys[i];
if (!undefinedOnly || obj[key] === void 0) obj[key] = source[key];
}
}
return obj;
};
};
// Retrieve all the property names of an object.
_.allKeys = function(obj) {
if (!_.isObject(obj)) return [];
var keys = [];
for (var key in obj) keys.push(key);
// Ahem, IE < 9.
if (hasEnumBug) collectNonEnumProps(obj, keys);
return keys;
};
// Extend a given object with all the properties in passed-in object(s).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With