Pourquoi et quand utiliser angular.copy? (Copie profonde)

136

J'ai enregistré toutes les données reçues des services directement dans la variable locale, le contrôleur ou l'étendue. Ce que je suppose serait considéré comme une copie superficielle, est-ce exact?

Example:

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

Récemment, on m'a dit d'utiliser angular.copy pour créer une copie profonde.

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

Cependant, les informations de copie profonde semblent fonctionner de la même manière lorsqu'elles sont utilisées par mon application Angular. Y a-t-il des avantages spécifiques à utiliser une copie profonde (angular.copy) et pouvez-vous me les expliquer?

Superman2971
la source
2
Vous devez utiliser angular.copy si vous avez besoin d'une copie de l'objet (: D). Si vous recevez un objet de l'appel ajax ($ http, $ resource, ...), il n'est pas nécessaire de copier. Si toutefois vous souhaitez modifier cet objet dans la vue, mais conserver l'objet d'origine dans une sorte de cache, vous pouvez souhaiter une copie.
Petr Averyanov

Réponses:

166

Utilisez angular.copy lors de l'affectation de la valeur de l'objet ou du tableau à une autre variable et cette objectvaleur ne doit pas être modifiée.

Sans copie complète ou en utilisant angular.copy , la modification de la valeur de la propriété ou l'ajout de toute nouvelle propriété met à jour tous les objets référençant ce même objet.

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>

Sarjan Desai
la source
1
Merci beaucoup pour la réponse rapide, j'adore l'aide et je pense que je comprends. Le seul moment réel pour utiliser angular.copy serait pour une copie littérale. Cela signifie que je ne dois l'utiliser que si j'ai besoin d'un double de l'original auquel je peux modifier les propriétés. Puis-je enregistrer les informations dans deux variables séparées et ajuster leurs propriétés séparément après au lieu de faire une copie angulaire? Exemple: $scope.one = response.dataet définir $scope.two = response.data. Alors fais $scope.two.addProperty = something. Je devrais probablement juste tester ceci :) mais j'aimerais avoir un aperçu de la communauté.
Superman2971
2
Rép: Non. Raison: Modification de la valeur de object propertymise à jour de la nouvelle valeur pour tous les objets ayant la même référence. C'est pourquoi vous devez utiliser angular.copy
Desai
44

Dans ce cas, vous n'avez pas besoin d'utiliser angular.copy()

Explication :

  • =représente une référence tandis que angular.copy()crée un nouvel objet en tant que copie complète.

  • Utiliser =signifierait que changer une propriété de response.datachangerait la propriété correspondante de $scope.exampleou vice versa.

  • L'utilisation angular.copy()des deux objets resterait séparée et les changements ne se refléteraient pas l'un sur l'autre.

Nicolas2bert
la source
Réponse la plus simple.
Astitva Srivastava
Le plus simple à comprendre. Merci
Puneet Verma
7

Je dirais que angular.copy(source);dans votre situation n'est pas nécessaire si plus tard vous ne l'utilisez pas, c'est sans destination angular.copy(source, [destination]);.

Si une destination est fournie, tous ses éléments (pour les tableaux) ou propriétés (pour les objets) sont supprimés, puis tous les éléments / propriétés de la source y sont copiés.

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

Esko
la source
Merci Esko! Essayer de me redresser la tête. Cela signifie-t-il qu'un avantage pour angular.copy serait: si une variable a déjà des données qui lui sont associées, c'est une manière plus propre de réaffecter les éléments / propriétés?
Superman2971
1
Vous utilisez angular.copy()un objet pour empêcher un autre code de le modifier. L'objet d'origine peut changer, mais votre copie ne verra pas les modifications. Vous pouvez rétablir la copie si nécessaire.
Esko
1

Lorsque vous utilisez angular.copy, au lieu de mettre à jour la référence, un nouvel objet est créé et affecté à la destination (si une destination est fournie). Mais il y a plus. Il y a cette chose cool qui se produit après une copie profonde.

Supposons que vous ayez un service d'usine qui a des méthodes qui mettent à jour les variables d'usine.

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

et un responsable du traitement qui utilise ce service,

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

Lorsque le programme ci-dessus est exécuté, la sortie sera la suivante,

****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]

Ainsi, la chose intéressante à propos de l'utilisation de la copie angulaire est que les références de la destination sont reflétées avec le changement de valeurs, sans avoir à réattribuer les valeurs manuellement, à nouveau.

Pubudu Dodangoda
la source
1

Je sais que c'est déjà répondu, mais j'essaie juste de faire simple. Donc angular.copy (data) que vous pouvez utiliser dans le cas où vous souhaitez modifier / changer votre objet reçu en gardant ses valeurs d'origine inchangées / inchangées.

Par exemple: supposons que j'ai fait un appel à l'API et obtenu mon originalObj, maintenant je veux changer les valeurs de api originalObj pour certains cas, mais je veux aussi les valeurs d'origine, donc ce que je peux faire est, je peux faire une copie de mon api originalObj dans duplicateObj et modifiez duplicateObj de cette façon, mes valeurs originalObj ne changeront pas. En termes simples, la modification de duplicateObj ne se reflétera pas dans originalObj contrairement à la façon dont js obj se comporte.

 $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);

Le résultat est comme ...

    ----------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"}
Sudarshan Kalebere
la source
1

Je partage juste mon expérience ici, j'ai utilisé angular.copy () pour comparer les propriétés de deux objets. Je travaillais sur un certain nombre d'entrées sans élément de formulaire, je me demandais comment comparer les propriétés de deux objets et en fonction du résultat, je dois activer et désactiver le bouton Enregistrer. J'ai donc utilisé comme ci-dessous.

J'ai affecté des valeurs utilisateur d'objet serveur d'origine à mon objet factice pour dire userCopy et utilisé watch pour vérifier les modifications apportées à l'objet utilisateur.

Mon API serveur qui me récupère les données du serveur:

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

Je ne suis pas sûr mais comparer deux objets a toujours été un véritable casse-tête pour moi, mais avec angular.copy (), tout s'est bien passé.

Sudarshan Kalebere
la source
-2

Javascript passe des variables by reference, cela signifie que:

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

Maintenant, à cause de la by referencepartie iest [1], et jest [1] aussi bien, même si seule a iété modifiée. En effet, lorsque nous disons que j = ijavascript ne copie pas la ivariable et ne l'affecte pas, jmais référence la ivariable via j.

La copie angulaire nous fait perdre cette référence, ce qui signifie:

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

Maintenant, iici est égal à [1], tandis que jtoujours égal à [].

Il y a des situations où ce type de copyfonctionnalité est très pratique.

guramidev
la source
1
JavaScript transmet les objets par référence. Pas des primitifs. Testez votre code.
Oleg
Ouais bien l'idée est à peu près la même, modifiée cependant
guramidev
1
Et angular.copyest plus intelligente que la sérialisation JSON car elle peut gérer des fonctions.
Oleg
Je ne le savais pas, je pourrais jurer que je me souviens avoir regardé la source angulaire et ne voir que la sérialisation JSON, mais je l'ai vérifié à nouveau et vous avez raison.
guramidev