¿Por qué y cuándo usar angular.copy? (Copia profunda)


136

He estado guardando todos los datos recibidos de los servicios directamente en la variable local, el controlador o el alcance. Lo que supongo que se consideraría una copia superficial, ¿es correcto?

Example:

DataService.callFunction()
.then(function(response) {
  $scope.example = response.data;
});

Recientemente me dijeron que usara angular.copy para crear una copia profunda.

$scope.example = angular.copy(response.data);

Sin embargo, la información de copia profunda parece estar funcionando de la misma manera cuando es utilizada por mi aplicación Angular. ¿Existen beneficios específicos al usar una copia profunda (angular.copy) y, por favor, puede explicarme?


2
U necesita usar angular.copy si necesita una copia del objeto (: D). Si recibe un objeto de una llamada ajax ($ http, $ resource, ...) no hay necesidad de copiar. Sin embargo, si desea modificar este objeto a la vista, pero mantiene el objeto original en algún tipo de caché, es posible que desee copiar.
Petr Averyanov

Respuestas:


166

Use angular.copy cuando asigne el valor del objeto o matriz a otra variable y ese objectvalor no debe cambiarse.

Sin una copia profunda o utilizando angular.copy , cambiar el valor de la propiedad o agregar cualquier propiedad nueva actualiza todos los objetos que hacen referencia a ese mismo objeto.

var app = angular.module('copyExample', []);
app.controller('ExampleController', ['$scope',
  function($scope) {
    $scope.printToConsole = function() {
      $scope.main = {
        first: 'first',
        second: 'second'
      };

      $scope.child = angular.copy($scope.main);
      console.log('Main object :');
      console.log($scope.main);
      console.log('Child object with angular.copy :');
      console.log($scope.child);

      $scope.child.first = 'last';
      console.log('New Child object :')
      console.log($scope.child);
      console.log('Main object after child change and using angular.copy :');
      console.log($scope.main);
      console.log('Assing main object without copy and updating child');

      $scope.child = $scope.main;
      $scope.child.first = 'last';
      console.log('Main object after update:');
      console.log($scope.main);
      console.log('Child object after update:');
      console.log($scope.child);
    }
  }
]);

// Basic object assigning example

var main = {
  first: 'first',
  second: 'second'
};
var one = main; // same as main
var two = main; // same as main

console.log('main :' + JSON.stringify(main)); // All object are same
console.log('one :' + JSON.stringify(one)); // All object are same
console.log('two :' + JSON.stringify(two)); // All object are same

two = {
  three: 'three'
}; // two changed but one and main remains same
console.log('main :' + JSON.stringify(main)); // one and main are same
console.log('one :' + JSON.stringify(one)); // one and main are same
console.log('two :' + JSON.stringify(two)); // two is changed

two = main; // same as main

two.first = 'last'; // change value of object's property so changed value of all object property 

console.log('main :' + JSON.stringify(main)); // All object are same with new value
console.log('one :' + JSON.stringify(one)); // All object are same with new value
console.log('two :' + JSON.stringify(two)); // All object are same with new value
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>

<div ng-app="copyExample" ng-controller="ExampleController">
  <button ng-click='printToConsole()'>Explain</button>
</div>


1
Muchas gracias por la rápida respuesta, me encanta la ayuda y creo que entiendo. El único momento real para usar angular.copy sería para una copia literal. Lo que significa que solo debería usarlo si necesito un duplicado del original al que pueda cambiar las propiedades. ¿Podría guardar la información en dos variables separadas y ajustar sus propiedades por separado después de hacer una copia angular? Ejemplo: $scope.one = response.datay conjunto $scope.two = response.data. Entonces hazlo $scope.two.addProperty = something. Probablemente debería probar esto :) pero me encantaría obtener información de la comunidad.
Superman2971

2
Respuesta: No. Motivo: Cambiar el valor de object propertyactualizar el nuevo valor a todos los objetos que tengan la misma referencia. Es por eso que tienes que usar angular.copy
Sarjan Desai

44

En ese caso, no necesita usar angular.copy()

Explicacion :

  • =representa una referencia mientras que angular.copy()crea un nuevo objeto como una copia profunda.

  • Usar =significaría que cambiar una propiedad de response.datacambiaría la propiedad correspondiente de $scope.exampleo viceversa.

  • El uso de angular.copy()los dos objetos permanecería separado y los cambios no se reflejarían entre sí.


La respuesta más simple.
Astitva Srivastava

Más fácil de entender. Gracias
Puneet Verma

7

Yo diría que angular.copy(source);su situación es innecesaria si más tarde no la usa sin un destino angular.copy(source, [destination]);.

Si se proporciona un destino, todos sus elementos (para matrices) o propiedades (para objetos) se eliminan y luego todos los elementos / propiedades de la fuente se copian en él.

https://docs.angularjs.org/api/ng/function/angular.copy


Gracias Esko! Tratando de aclarar mi cabeza. ¿Eso significa que un beneficio para angular.copy sería: si una variable ya tiene datos asociados, esta es una forma más limpia de reasignar los elementos / propiedades?
Superman2971

1
Usas angular.copy()un objeto para evitar que otro código lo modifique. El objeto original puede cambiar, pero su copia no verá los cambios. Puede restablecer la copia si es necesario.
Esko

1

Cuando se utiliza angular.copy, en lugar de actualizar la referencia, se crea un nuevo objeto y se asigna al destino (si se proporciona un destino). Pero hay más. Hay algo genial que sucede después de una copia profunda.

Supongamos que tiene un servicio de fábrica que tiene métodos que actualizan las variables de fábrica.

angular.module('test').factory('TestService', [function () {
    var o = {
        shallow: [0,1], // initial value(for demonstration)
        deep: [0,2] // initial value(for demonstration)
    }; 
    o.shallowCopy = function () {
        o.shallow = [1,2,3]
    }
    o.deepCopy = function () {
        angular.copy([4,5,6], o.deep);
    }
    return o;
}]);

y un controlador que usa este servicio,

angular.module('test').controller('Ctrl', ['TestService', function (TestService) {
     var shallow = TestService.shallow;
     var deep = TestService.deep;

     console.log('****Printing initial values');
     console.log(shallow);
     console.log(deep);

     TestService.shallowCopy();
     TestService.deepCopy();

     console.log('****Printing values after service method execution');
     console.log(shallow);
     console.log(deep);

     console.log('****Printing service variables directly');
     console.log(TestService.shallow);
     console.log(TestService.deep);
}]);

Cuando se ejecuta el programa anterior, la salida será la siguiente:

****Printing initial values
[0,1]
[0,2]

****Printing values after service method execution
[0,1]
[4,5,6]

****Printing service variables directly
[1,2,3]
[4,5,6]

Por lo tanto, lo bueno de usar la copia angular es que las referencias del destino se reflejan con el cambio de valores, sin tener que reasignar los valores manualmente, nuevamente.


1

Sé que ya ha respondido, pero solo estoy tratando de hacerlo simple. Entonces angular.copy (datos) puede usar en caso de que desee modificar / cambiar su objeto recibido manteniendo sus valores originales sin modificar / sin modificar.

Por ejemplo: supongamos que hice una llamada a API y obtuve mi OriginalObj, ahora quiero cambiar los valores de api originalObj en algún caso, pero también quiero los valores originales, así que lo que puedo hacer es hacer una copia de mi api originalObj en duplicateObj y modifique duplicateObj de esta manera mis valores originalesObj no cambiarán. En palabras simples, la modificación duplicateObj no se reflejará en originalObj a diferencia de cómo se comporta js obj.

 $scope.originalObj={
            fname:'sudarshan',
            country:'India'
        }
        $scope.duplicateObj=angular.copy($scope.originalObj);
        console.log('----------originalObj--------------');
        console.log($scope.originalObj);
        console.log('-----------duplicateObj---------------');
        console.log($scope.duplicateObj);

        $scope.duplicateObj.fname='SUD';
        $scope.duplicateObj.country='USA';
        console.log('---------After update-------')
        console.log('----------originalObj--------------');
        console.log($scope.originalObj);
        console.log('-----------duplicateObj---------------');
        console.log($scope.duplicateObj);

El resultado es como ...

    ----------originalObj--------------
manageProfileController.js:1183 {fname: "sudarshan", country: "India"}
manageProfileController.js:1184 -----------duplicateObj---------------
manageProfileController.js:1185 {fname: "sudarshan", country: "India"}
manageProfileController.js:1189 ---------After update-------
manageProfileController.js:1190 ----------originalObj--------------
manageProfileController.js:1191 {fname: "sudarshan", country: "India"}
manageProfileController.js:1192 -----------duplicateObj---------------
manageProfileController.js:1193 {fname: "SUD", country: "USA"}

1

Solo estoy compartiendo mi experiencia aquí, utilicé angular.copy () para comparar las propiedades de dos objetos. Estaba trabajando en varias entradas sin elemento de formulario, me preguntaba cómo comparar las propiedades de dos objetos y, en función del resultado, tengo que habilitar y deshabilitar el botón Guardar. Así que usé como a continuación.

Asigné un valor de usuario de objeto de servidor original a mi objeto ficticio para decir userCopy y usé watch para verificar los cambios en el objeto de usuario.

La API de mi servidor que me proporciona datos del servidor:

var req = {
    method: 'GET',
    url: 'user/profile/' + id,
    headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
}
$http(req).success(function(data) {
    $scope.user = data;
    $scope.userCopy = angular.copy($scope.user);
    $scope.btnSts=true;
}).error(function(data) {
    $ionicLoading.hide();
});

//initially my save button is disabled because objects are same, once something 
//changes I am activating save button

$scope.btnSts = true;
$scope.$watch('user', function(newVal, oldVal) {
    console.log($scope.userCopy.name);

    if ($scope.userCopy.name !== $scope.user.name || $scope.userCopy.email !== $scope.user.email) {
        console.log('Changed');
        $scope.btnSts = false;
    } else {
        console.log('Unchanged');
        $scope.btnSts = true;
    }    
}, true);

No estoy seguro, pero comparar dos objetos fue realmente un dolor de cabeza para mí siempre, pero con angular.copy () fue sin problemas.


-2

Javascript pasa variables by reference, esto significa que:

var i = [];
var j = i;
i.push( 1 );

Ahora, debido a que by referenceparte ies [1], y también jes [1], aunque solo ise modificó. Esto se debe a que cuando decimos que j = ijavascript no copia la ivariable y la asigna, jsino que hace referencia a la ivariable j.

La copia angular nos permite perder esta referencia, lo que significa:

var i = [];
var j = angular.copy( i );
i.push( 1 );

Ahora iaquí es igual a [1], mientras que jtodavía es igual a [].

Hay situaciones en las que este tipo de copyfuncionalidad es muy útil.


1
JavaScript pasa objetos por referencia. No primitivos. Prueba tu código.
Oleg

Sí, bueno, la idea es bastante la misma, aunque editada
guramidev

1
Y angular.copyes más inteligente que la serialización JSON porque puede manejar funciones.
Oleg

no sabía eso, podría jurar que recuerdo haber visto la fuente angular y ver solo la serialización JSON, pero lo revisé nuevamente y tienes razón.
guramidev
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.