22

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"

1
  • 1
    But you could simply copy $.extend from jQuery's source? It's not that hard to find, and pretty stand-alone. Commented Dec 17, 2014 at 20:54

6 Answers 6

28

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).

Sign up to request clarification or add additional context in comments.

7 Comments

That's also a good solution, wich angular team had done something like a first param boolen (like jquery)
Agreed. I would too, just wanted to demonstrate an easy way to do it using angular's methods.
I think the if (dst[key]) should actually be if (dst[key] && dst[key].constructor && dst[key].constructor === Object) - it deosn't seem to work correctly otherwise
This is the better answer since it supports multiple arguments, but would you please incorporate the fix in @BennettMcElwee's comment?
What is the purpose of the check for dst[key].constructor === Object? Would a simple if (dst[key] && angular.isObject(dst[key])) work?
|
17

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

1 Comment

7
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

2 Comments

This answer only supports 2 arguments. The one by Ryan supports multiple arguments like jQuery.extend does, but be sure to use Bennett MeElwee's suggestion.
arguments.callee usage is discouraged now -developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
1

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;
};

Comments

0

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;
    }

Comments

-1

Angular has a copy method:

angular.copy

3 Comments

This solution deletes the contents of the destination. angular.copy is used for a different reason than angular.extend
Definitely not the correct answer for this question, but... this answer helped point me in the right direction for my particular case. :)
I'm thinking the OP meant angular.extend(myDeepCopy, angular.copy(originalObject))

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.