Объекты глубокого слияния с AngularJS
Обычно для объектов с мелкой копией я бы использовал angular.extend()
Вот пример этого:
var object1 = {
"key": "abc123def456",
"message": {
"subject": "Has a Question",
"from": "[email protected]",
"to": "[email protected]"
}
};
var object2 = {
"key": "00700916391"
};
console.log(angular.extend({}, object1, object2));
Дала бы нам:
{
"key": "00700916391",
"message": {
"subject": "Has a Question",
"from": "[email protected]",
"to": "[email protected]"
}
}
Но что, если я хотел объединить объекты, чтобы родительские ключи не были написаны дочерними объектами:
var object1 = {
"key": "abc123def456",
"message": {
"subject": "Has a Question",
"from": "[email protected]",
"to": "[email protected]"
}
};
var object2 = {
"key": "00700916391", //Overwrite me
"message": { //Dont overwrite me!
"subject": "Hey what up?", //Overwrite me
"something": "something new" //Add me
}
};
console.log(merge(object1, object2));
Дала бы нам:
{
"key": "00700916391",
"message": {
"subject": "Hey what up?",
"from": "[email protected]",
"to": "[email protected]",
"something": "something new"
}
}
-
Есть ли функция Angular, которая уже выполняет глубокое слияние, о котором я не знаю?
-
Если нет, есть ли способ для этого в javascript рекурсивно для n уровней в глубину?
Ответы
Ответ 1
Angular 1.4 или новее
Используйте angular.merge
:
В отличие от extend()
, merge()
рекурсивно спускается в свойства объекта исходных объектов, выполняя глубокую копию.
angular.merge(object1, object2); // merge object 2 into object 1
Старые версии Angular:
Нет причин, по которым простой рекурсивный алгоритм не должен работать:)
Предполагая, что они оба являются результатом JSON.stringify или аналогичного:
function merge(obj1,obj2){ // Our merge function
var result = {}; // return result
for(var i in obj1){ // for every property in obj1
if((i in obj2) && (typeof obj1[i] === "object") && (i !== null)){
result[i] = merge(obj1[i],obj2[i]); // if it an object, merge
}else{
result[i] = obj1[i]; // add it to result
}
}
for(i in obj2){ // add the remaining properties from object 2
if(i in result){ //conflict
continue;
}
result[i] = obj2[i];
}
return result;
}
(Обратите внимание, что массивы здесь не обрабатываются)
Ответ 2
В новой версии Angularjs они добавили функцию слияния, которая будет выполнять глубокую копию.
Для более старых версий я создал свою пользовательскую функцию, скопировав код функции слияния из новой версии Angularjs. Ниже приведен код для того же самого,
function merge(dst){
var slice = [].slice;
var isArray = Array.isArray;
function baseExtend(dst, objs, deep) {
for (var i = 0, ii = objs.length; i < ii; ++i) {
var obj = objs[i];
if (!angular.isObject(obj) && !angular.isFunction(obj)) continue;
var keys = Object.keys(obj);
for (var j = 0, jj = keys.length; j < jj; j++) {
var key = keys[j];
var src = obj[key];
if (deep && angular.isObject(src)) {
if (!angular.isObject(dst[key])) dst[key] = isArray(src) ? [] : {};
baseExtend(dst[key], [src], true);
} else {
dst[key] = src;
}
}
}
return dst;
}
return baseExtend(dst, slice.call(arguments, 1), true);
}
Надеюсь, это поможет кому-то, кто задается вопросом, почему angular.merge не работает в более старых версиях.
Ответ 3
здесь - это решение, которое объединяет несколько объектов (более двух объектов):
Вот функция extendDeep, основанная на функции angular.extend. Если вы добавите это значение в область $, вы сможете вызвать
$scope.meta = $scope.extendDeep(ajaxResponse1.myMeta, ajaxResponse2.defaultMeta);
и получите ответ, который вы ищете.
$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;
};
Ответ 4
angular.merge polyfill для angular & lt; 1.4.0
if (!angular.merge) {
angular.merge = (function mergePollyfill() {
function setHashKey(obj, h) {
if (h) {
obj.$$hashKey = h;
} else {
delete obj.$$hashKey;
}
}
function baseExtend(dst, objs, deep) {
var h = dst.$$hashKey;
for (var i = 0, ii = objs.length; i < ii; ++i) {
var obj = objs[i];
if (!angular.isObject(obj) && !angular.isFunction(obj)) continue;
var keys = Object.keys(obj);
for (var j = 0, jj = keys.length; j < jj; j++) {
var key = keys[j];
var src = obj[key];
if (deep && angular.isObject(src)) {
if (angular.isDate(src)) {
dst[key] = new Date(src.valueOf());
} else {
if (!angular.isObject(dst[key])) dst[key] = angular.isArray(src) ? [] : {};
baseExtend(dst[key], [src], true);
}
} else {
dst[key] = src;
}
}
}
setHashKey(dst, h);
return dst;
}
return function merge(dst) {
return baseExtend(dst, [].slice.call(arguments, 1), true);
}
})();
}
Ответ 5
Если вы используете < 1.4
Вы можете использовать lodash
встроенный _. merge(), который делает то же самое, что и angular > 1.4 version
Сохранял меня от написания новых функций, поскольку lodash
довольно популярен среди angular людей уже