$watchCollection what has changed? - angularjs

Using $watchCollection to detect the changed key
newValue: Object {contentType: "217", audioType: 1, wordType: 209}
oldValue: Object {contentType: "217", audioType: 1, wordType: 210}
Usually only one key will change at a time. I'd like to detect which one so I can save that change to the cookies rather than having to save all of them even if it didn't change.
Thanks!

You don't need $watchCollection here.
Just use $watch with 3rd parameter as true.

In your case you can create filter that would find difference:
app.filter('diff', function () {
return function (objectA, objectB) {
var propertyChanges = [];
var objectGraphPath = ["this"];
(function(a, b) {
if(a.constructor == Array) {
// BIG assumptions here: That both arrays are same length, that
// the members of those arrays are _essentially_ the same, and
// that those array members are in the same order...
for(var i = 0; i < a.length; i++) {
objectGraphPath.push("[" + i.toString() + "]");
arguments.callee(a[i], b[i]);
objectGraphPath.pop();
}
} else if(a.constructor == Object || (a.constructor != Number &&
a.constructor != String && a.constructor != Date &&
a.constructor != RegExp && a.constructor != Function &&
a.constructor != Boolean)) {
// we can safely assume that the objects have the
// same property lists, else why compare them?
for(var property in a) {
objectGraphPath.push(("." + property));
if(a[property].constructor != Function) {
arguments.callee(a[property], b[property]);
}
objectGraphPath.pop();
}
} else if(a.constructor != Function) { // filter out functions
if(a != b) {
propertyChanges.push({ "Property": objectGraphPath.join(""), "ObjectA": a, "ObjectB": b });
}
}
})(objectA, objectB);
return propertyChanges;
}
});
And then use it in your $watchCollection:
var diff = $filter('diff')(newValue, oldValue);
Credits to How can I get a list of the differences between two JavaScript object graphs?

Related

How to check if Value of an array is true in a cell(Google Script)

I am having issues checking if the string value in my array is true in a cell.
function myFunction() {
var People = ['Amanda', 'John'];
for (var n in People )
{
if( People[0] == true);
Logger.log("BOOKED");
}
else{
Logger.log("FREE");
}
}
Plenty of issues with the code!
Does this do it ?
function myFunction() {
var People = ['Amanda', 'John'];
for (var n=0;n<People.length;n++) {
if( People[n] ) {
Logger.log("BOOKED");
}
else {
Logger.log("FREE");
}
}
}
I'm not sure if you just want to test for members of the array being present, or if you want to test actual names in the array

Angular 2: Delete object in Array

I want to delete an object in an array when that object's ID is equal to the ID of the object getting compared. Currently, it only removes the first object in the array
if(this.selectedProducts.length > 0){
for(let x of this.selectedProducts){
if(prod._id === x._id){
this.selectedProducts.splice(x,1); //this is the part where I 'delete' the object
this.appended = false;
}else{
this.appended = true;
}
}
if (this.appended) {
this.selectedProducts.push(prod);
}
}else{
this.selectedProducts.push(prod);
}
this.selectEvent.emit(this.selectedProducts);
}
this.selectedProducts.splice(x,1);
The first parameter to splice must be the index, not the object.
If you're using for...of, you can't get the index easily. So you should use a regular for loop instead. With some additional simplifications, your code would look like this:
for (let i = this.selectedProducts.length - 1; i >= 0; this.selectedProducts.length; i--) {
if (prod._id === this.selectProducts[i]._id) {
this.selectedProducts.splice(i, 1); //this is the part where I 'delete' the object
}
}
this.selectedProducts.push(prod);
It's highly likely that using filter would be better anyway:
this.selectedProducts = this.selectedProducts.filter(x => prod._id !== x._id).concat(prod);

Angular ng-repeat filtering

I have a deeply nested object. I have some records which contain 2 fields that show keys of object properties. I also have select needed to search records by property of object and input to search by key of object. So if I choose option1 and type in input some text, it will be shown the matches in the first field (not second!). And it's similar for second field.
How I try to realize:
I wrote a filter http://plnkr.co/edit/z9DEmfYz2grW9UonLcFK?p=preview
.filter('appFilter', function() {
return function(value, select, input) {
var result = [];
input = input.toLowerCase();
var reg = new RegExp(input,'g');
if (angular.isArray(value)) {
if (input === '' || $scope.isFiltering) {
return value;
} else if (select.value === 'Sequence') {
for (let i = 0; i < value.length; i++) {
if (value[i].Sequence.toLowerCase().match(reg)) {
result.push(value[i]);
}
}
return result;
} else if (select.value === 'ID') {
for (let i = 0; i < value.length; i++) {
if (angular.isArray(value[i].Document)) {
for (let j = 0; j < value[i].Document.length; j++) {
if (value[i].Document[j].ID.toLowerCase().match(reg)) {
result.push(value[i]);
}
}
}
}
return result;
} else {
console.log('error');
}
}
}
})
In controller I set to select's ng-model first option: $scope.selectParameter = $scope.parameter[0];
In debug I set to input parameter some value (123 for example).
So I searching record by first field that contains 123 value. And result finds and pushes the object. But in browser shows anything.
What's the problem? And I can't avoid the empty option with '?' value in my select :(
UPDATED
Nearly solve my problem: http://plnkr.co/edit/z9DEmfYz2grW9UonLcFK?p=preview
It filters by appropriate field and input value. But I faced with another troubles.
When input is empty it doesn't show any record. And second is when I choose second option (ID) filter duplicates some records.
Also I try to switch off filter without clearing the input text by clicking on checkbox.
It's what I want to do but it doesn't work:
else if (input === '' || $scope.isFiltering) {
return value;
}
$scope.isFiltering is ng-model for checkbox input
I tried using angulars default filter. I'm not sure if this is exactly what you want, but maybe it helps a little.
.filter('appFilter', function($filter) {
return function(value, select, input) {
if( !angular.isDefined(input) || input.length < 1) {
return value;
}
// Angulars "filter" lets you pass in a object-structure to search for nested fields.
var query =
(select.value === 'Sequence') ?
{Sequence:input} : {Document:{ID:input}};
return $filter('filter')(value, query);
}
})
http://plnkr.co/edit/Egkw9bUvTPgooc0u2w7C?p=preview

angularjs - extend recursive

I would like to extend some properties recursive (aka. deep copy).
much like jQuery does. I'm not including jquery only b/c of one thing.
jQuery.extend( true, target, object1 )
is there any elegant way you know of that does it with simple javascript or angularjs?
update
please take a look and try to accomplish the same result
http://plnkr.co/edit/GHabYbyhsqtfBPtplksO?p=preview
i did look into .copy() but the "properties (for objects) are deleted"
Here is an extendDeep function based off of the angular.extend function. If you add this to your $scope, you would then be able to call
$scope.meta = $scope.extendDeep(ajaxResponse1.myMeta, ajaxResponse2.defaultMeta);
and get the answer you are looking for.
$scope.extendDeep = function extendDeep(dst) {
angular.forEach(arguments, function(obj) {
if (obj !== dst) {
angular.forEach(obj, function(value, key) {
if (dst[key] && dst[key].constructor && dst[key].constructor === Object) {
extendDeep(dst[key], value);
} else {
dst[key] = value;
}
});
}
});
return dst;
};
Note: This function has the side-effect of copying values from later arguments into the earlier arguments. For a simple fix to this side effect, you can change dst[key] = value to dst[key] = angular.copy(value).
All the answers here are valid for versions of Angular before 1.4
As of Angular 1.4, you can use angular.merge to do exactly that:
Unlike extend(), merge() recursively descends into object properties of source objects, performing a deep copy.
https://docs.angularjs.org/api/ng/function/angular.merge
function deepExtend(destination, source) {
for (var property in source) {
if (source[property] && source[property].constructor &&
source[property].constructor === Object) {
destination[property] = destination[property] || {};
arguments.callee(destination[property], source[property]);
} else {
destination[property] = source[property];
}
}
return destination;
}
Plunker
Src: https://gist.github.com/gregdangelo/2343158
Building on Ryan's code, you can shorten the object check and you should also NOT extend functions so you don't override object pointers.
var extendDeep = function extendDeep(dst) {
angular.forEach(arguments, function(obj) {
if (obj !== dst) {
angular.forEach(obj, function(value, key) {
if (dst[key] && angular.isObject(dst[key])) {
extendDeep(dst[key], value);
} else if(!angular.isFunction(dst[key])) {
dst[key] = value;
}
});
}
});
return dst;
};
The same solution as Ryan but with support for array merge
function extendDeep(dst) {
angular.forEach(arguments, function (obj) {
if (obj !== dst) {
angular.forEach(obj, function (value, key) {
if (dst[key] && dst[key].constructor && dst[key].constructor === Object) {
extendDeep(dst[key], value);
} else if (dst[key] && dst[key].constructor && dst[key].constructor === Array) {
dst[key].concat(value);
} else if(!angular.isFunction(dst[key])) {
dst[key] = value;
}
}
);
}
}
);
return dst;
}
Angular has a copy method:
angular.copy

Angular JS break ForEach

I have an angular foreach loop and i want to break from loop if i match a value. The following code does not work.
angular.forEach([0,1,2], function(count){
if(count == 1){
break;
}
});
How can i get this?
The angular.forEach loop can't break on a condition match.
My personal advice is to use a NATIVE FOR loop instead of angular.forEach.
The NATIVE FOR loop is around 90% faster then other for loops.
USE FOR loop IN ANGULAR:
var numbers = [0, 1, 2, 3, 4, 5];
for (var i = 0, len = numbers.length; i < len; i++) {
if (numbers[i] === 1) {
console.log('Loop is going to break.');
break;
}
console.log('Loop will continue.');
}
There's no way to do this. See https://github.com/angular/angular.js/issues/263. Depending on what you're doing you can use a boolean to just not going into the body of the loop. Something like:
var keepGoing = true;
angular.forEach([0,1,2], function(count){
if(keepGoing) {
if(count == 1){
keepGoing = false;
}
}
});
please use some or every instances of ForEach,
Array.prototype.some:
some is much the same as forEach but it break when the callback returns true
Array.prototype.every:
every is almost identical to some except it's expecting false to break the loop.
Example for some:
var ary = ["JavaScript", "Java", "CoffeeScript", "TypeScript"];
ary.some(function (value, index, _ary) {
console.log(index + ": " + value);
return value === "JavaScript";
});
Example for every:
var ary = ["JavaScript", "Java", "CoffeeScript", "TypeScript"];
ary.every(function(value, index, _ary) {
console.log(index + ": " + value);
return value.indexOf("Script") > -1;
});
Find more information
http://www.jsnoob.com/2013/11/26/how-to-break-the-foreach/
Use the Array Some Method
var exists = [0,1,2].some(function(count){
return count == 1
});
exists will return true, and you can use this as a variable in your function
if(exists){
console.log('this is true!')
}
Array Some Method - Javascript
As far as I know, Angular doesn't provide such a function. You may want to use underscore's find() function for this (it's basically a forEach which breaks out of the loop once the function returns true).
http://underscorejs.org/#find
If you use jQuery (hence not jqLite) in conjunction with AngularJS you can iterate with $.each - which allows breaking and continuing based on boolean return value expression.
JSFiddle:
http://jsfiddle.net/JEcD2/1/
Javascript:
var array = ['foo', 'bar', 'yay'];
$.each(array, function(index, element){
if (element === 'foo') {
return true; // continue
}
console.log(this);
if (element === 'bar') {
return false; // break
}
});
Note:
Though using jQuery is not bad, both native Array.some or Array.every functions are recommended by MDN as you can read at native forEach documentation:
"There is no way to stop or break a forEach loop. The solution is to use Array.every or Array.some"
Following examples are provided by MDN:
Array.some:
function isBigEnough(element, index, array){
return (element >= 10);
}
var passed = [2, 5, 8, 1, 4].some(isBigEnough);
// passed is false
passed = [12, 5, 8, 1, 4].some(isBigEnough);
// passed is true
Array.every:
function isBigEnough(element, index, array){
return (element >= 10);
}
var passed = [12, 5, 8, 130, 44].every(isBigEnough);
// passed is false
passed = [12, 54, 18, 130, 44].every(isBigEnough);
// passed is true
Concretely, you can exit of a forEach loop, and of any place, throw an exception.
try {
angular.forEach([1,2,3], function(num) {
if (num === 2) throw Error();
});
} catch(e) {
// anything
}
However, it is better if you use other library or implement your own function, a find function in this case, so your code is most high-level.
Try this as break;
angular.forEach([0,1,2], function(count){
if(count == 1){
return true;
}
});
As the other answers state, Angular doesn't provide this functionality. jQuery does however, and if you have loaded jQuery as well as Angular, you can use
jQuery.each ( array, function ( index, value) {
if(condition) return false; // this will cause a break in the iteration
})
See http://api.jquery.com/jquery.each/
Normally there is no way to break an "each" loop in javascript.
What can be done usually is to use "short circuit" method.
array.forEach(function(item) {
// if the condition is not met, move on to the next round of iteration.
if (!condition) return;
// if the condition is met, do your logic here
console.log('do stuff.')
}
break isn't possible to achieve in angular forEach, we need to modify forEach to do that.
$scope.myuser = [{name: "Ravi"}, {name: "Bhushan"}, {name: "Thakur"}];
angular.forEach($scope.myuser, function(name){
if(name == "Bhushan") {
alert(name);
return forEach.break();
//break() is a function that returns an immutable object,e.g. an empty string
}
});
You can use this:
var count = 0;
var arr = [0,1,2];
for(var i in arr){
if(count == 1) break;
//console.log(arr[i]);
}
var ary = ["JavaScript", "Java", "CoffeeScript", "TypeScript"];
var keepGoing = true;
ary.forEach(function(value, index, _ary) {
console.log(index)
keepGoing = true;
ary.forEach(function(value, index, _ary) {
if(keepGoing){
if(index==2){
keepGoing=false;
}
else{
console.log(value)
}
}
});
});
$scope.arr = [0, 1, 2];
$scope.dict = {}
for ( var i=0; i < $scope.arr.length; i++ ) {
if ( $scope.arr[i] == 1 ) {
$scope.exists = 'yes, 1 exists';
break;
}
}
if ( $scope.exists ) {
angular.forEach ( $scope.arr, function ( value, index ) {
$scope.dict[index] = value;
});
}
I would prefer to do this by return. Put the looping part in private function and return when you want to break the loop.
I realise this is old, but an array filter may do what you need:
var arr = [0, 1, 2].filter(function (count) {
return count < 1;
});
You can then run arr.forEach and other array functions.
I realise that if you intend to cut down on loop operations altogether, this will probably not do what you want. For that you best use while.
This example works. Try it.
var array = [0,1,2];
for( var i = 0, ii = array.length; i < ii; i++){
if(i === 1){
break;
}
}
I would use return instead of break.
angular.forEach([0,1,2], function(count){
if(count == 1){
return;
}
});
Works like a charm.
Use Return to break the loop.
angular.forEach([0,1,2], function(count){
if(count == 1) {
return;
}
});
onSelectionChanged(event) {
let selectdata = event['api']['immutableService']['gridOptionsWrapper']['gridOptions']['rowData'];
let selected_flag = 0;
selectdata.forEach(data => {
if (data.selected == true) {
selected_flag = 1;
}
});
if (selected_flag == 1) {
this.showForms = true;
} else {
this.showForms = false;
}
}
Just add $index and do the following:
angular.forEach([0,1,2], function(count, $index) {
if($index !== 1) {
// do stuff
}
}

Resources