Por que e quando usar o angular.copy? (Cópia profunda)

136

Salvei todos os dados recebidos dos serviços diretamente na variável local, controlador ou escopo. O que suponho que seria considerado uma cópia superficial, está correto?

Example:

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

Recentemente me disseram para usar o angular.copy para criar uma cópia profunda.

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

No entanto, as informações detalhadas da cópia parecem estar funcionando da mesma maneira quando usadas pelo meu aplicativo Angular. Há benefícios específicos em usar uma cópia em profundidade (angular.copy) e você pode me explicar?

Superman2971
fonte
2
Você precisa usar angular.copy se precisar de uma cópia do objeto (: D). Se você receber um objeto da chamada ajax ($ http, $ resource, ...), não será necessário copiar. Se, no entanto, você deseja modificar esse objeto à vista, mas manter o objeto original em algum tipo de cache, poderá copiar.
Petr Averyanov

Respostas:

166

Use angular.copy ao atribuir valor de objeto ou matriz a outra variável e esse objectvalor não deve ser alterado.

Sem copiar em profundidade ou usar angular.copy , alterar o valor da propriedade ou adicionar qualquer nova propriedade atualiza todos os objetos que fazem referência a esse mesmo 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>

Sarjan Desai
fonte
1
Muito obrigado pela resposta rápida, adoro a ajuda e acho que entendi. O único tempo real para usar o angular.copy seria uma cópia literal. Significado: só devo usá-lo se precisar de uma duplicata do original para a qual posso alterar as propriedades. Eu poderia salvar as informações em duas variáveis ​​separadas e ajustar suas propriedades separadamente depois, em vez de fazer um angular.copy? Exemplo: $scope.one = response.datae defina $scope.two = response.data. Então faça $scope.two.addProperty = something. Provavelmente eu deveria apenas testar isso :), mas gostaria de ter uma visão da comunidade.
Superman2971
2
Resp: Não. Motivo: Alterar o valor da object propertyatualização do novo valor para todos os objetos com a mesma referência. É por isso que você precisa usar o angular.copy #
Sarjan Desai
44

Nesse caso, você não precisa usar angular.copy()

Explicação :

  • =representa uma referência enquanto angular.copy()cria um novo objeto como uma cópia profunda.

  • Usar =significaria que alterar uma propriedade de response.dataalteraria a propriedade correspondente $scope.exampleou vice-versa.

  • O uso angular.copy()dos dois objetos permaneceria separado e as alterações não refletiriam uma na outra.

Nicolas2bert
fonte
Resposta mais simples.
Astitva Srivastava
Mais fácil de entender. Obrigado
Puneet Verma
7

Eu diria que a angular.copy(source);sua situação é desnecessária se mais tarde você não a usar, sem um destino angular.copy(source, [destination]);.

Se um destino for fornecido, todos os seus elementos (para matrizes) ou propriedades (para objetos) serão excluídos e todos os elementos / propriedades da origem serão copiados para ele.

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

Esko
fonte
Obrigado Esko! Tentando acertar minha cabeça. Isso significa que um benefício para angular.copy seria: se uma variável já tiver dados associados, essa é uma maneira mais limpa de reatribuir os elementos / propriedades?
Superman2971
1
Você usa angular.copy()um objeto para impedir que outro código o modifique. O objeto original pode mudar, mas sua cópia não verá as alterações. Você pode restabelecer a cópia, se necessário.
Esko
1

Ao usar angular.copy, em vez de atualizar a referência, um novo objeto é criado e atribuído ao destino (se um destino for fornecido). Mas tem mais. Há uma coisa legal que acontece depois de uma cópia profunda.

Digamos que você tenha um serviço de fábrica com métodos que atualizam as variáveis ​​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;
}]);

e um controlador que usa esse serviço,

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

Quando o programa acima for executado, a saída será a seguinte,

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

Portanto, o interessante de usar a cópia angular é que, as referências do destino são refletidas com a alteração dos valores, sem a necessidade de reatribuir os valores manualmente, novamente.

Pubudu Dodangoda
fonte
1

Eu sei que já está respondido, ainda estou apenas tentando simplificar. Portanto, angular.copy (data) você pode usar no caso em que deseja modificar / alterar seu objeto recebido, mantendo seus valores originais inalterados / inalterados.

Por exemplo: suponha que eu tenha feito uma chamada de API e recebi meu originalObj, agora eu quero alterar os valores da API originalObj para alguns casos, mas também quero os valores originais, então o que eu posso fazer é: posso fazer uma cópia da minha API originalObj em duplicateObj e modifique duplicateObj dessa maneira, meus valores originais de Ojj não serão alterados. Em palavras simples, a modificação duplicateObj não será refletida no originalObj, diferente da maneira como o js obj se comporta.

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

Resultado é 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"}
Sudarshan Kalebere
fonte
1

Estou apenas compartilhando minha experiência aqui, usei angular.copy () para comparar duas propriedades de objetos. Eu estava trabalhando em várias entradas sem o elemento do formulário, queria saber como comparar as propriedades de dois objetos e, com base no resultado, tenho que ativar e desativar o botão Salvar. Então eu usei como abaixo.

Atribuí um valor de usuário de objeto de servidor original ao meu objeto fictício para dizer userCopy e usei watch para verificar as alterações no objeto de usuário.

Minha API do servidor que obtém dados do 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);

Não tenho certeza, mas comparar dois objetos foi realmente uma dor de cabeça para mim sempre, mas com angular.copy () ocorreu sem problemas.

Sudarshan Kalebere
fonte
-2

Javascript passa variáveis by reference, isso significa que:

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

Agora, por causa da by referenceparte ié [1] e também jé [1], mesmo que apenas tenha isido alterado. Isso ocorre quando dizemos que o j = ijavascript não copia a ivariável e a atribui, jmas faz referência à ivariável j.

A cópia angular permite perder esta referência, o que significa:

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

Agora iaqui é igual a [1], enquanto jainda é igual a [].

Há situações em que esse tipo de copyfuncionalidade é muito útil.

guramidev
fonte
1
JavaScript passa objetos por referência. Não primitivos. Teste seu código.
Oleg
Sim, bem, a idéia é praticamente a mesma, editada embora
guramidev
1
E angular.copyé mais inteligente que a serialização JSON, pois pode lidar com funções.
21415 Oleg #
não sabia disso, eu poderia jurar que lembro de olhar para a fonte angular e ver apenas a serialização JSON, mas verifiquei novamente e você está certo.
precisa saber é o seguinte