Dlaczego i kiedy używać angular.copy? (Głęboka kopia)

136

Zapisałem wszystkie dane otrzymane z usług bezpośrednio do zmiennej lokalnej, kontrolera lub zakresu. To, co, jak przypuszczam, można by uznać za płytką kopię, czy to prawda?

Example:

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

Niedawno powiedziano mi, żebym użył angular.copy w celu stworzenia głębokiej kopii.

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

Jednak informacje głębokiej kopii wydają się działać w ten sam sposób, gdy są używane przez moją aplikację Angular. Czy korzystanie z głębokiej kopii (angular.copy) ma szczególne zalety i czy możesz mi je wyjaśnić?

Superman2971
źródło
2
Jeśli potrzebujesz kopii obiektu (: D), musisz użyć angular.copy. Jeśli otrzymasz obiekt z połączenia AJAX ($ http, $ zasób, ...) nie ma potrzeby kopiowania. Jeśli jednak chcesz zmodyfikować ten obiekt na widoku, ale zachować oryginalny obiekt w jakiejś pamięci podręcznej, możesz chcieć skopiować.
Petr Averyanov

Odpowiedzi:

166

Użyj angular.copy podczas przypisywania wartości obiektu lub tablicy do innej zmiennej i ta objectwartość nie powinna być zmieniana.

Bez głębokiego kopiowania lub używania angular.copy , zmiana wartości właściwości lub dodanie dowolnej nowej właściwości aktualizuje wszystkie obiekty odwołujące się do tego samego obiektu.

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
źródło
1
Dziękuję bardzo za szybką odpowiedź, kocham pomoc i myślę, że rozumiem. Jedyny rzeczywisty czas użycia angular.copy to dosłowna kopia. Oznacza to, że powinienem go używać tylko wtedy, gdy potrzebuję duplikatu oryginału, na który mogę zmienić właściwości. Czy mogę zapisać informacje w dwóch osobnych zmiennych i osobno dostosować ich właściwości po zamiast tworzyć angular.copy? Przykład: $scope.one = response.datai ustaw $scope.two = response.data. Więc zrób $scope.two.addProperty = something. Powinienem prawdopodobnie to przetestować :), ale chciałbym uzyskać wgląd w społeczność.
Superman2971
2
Odp: Nie. Powód: zmiana wartości object propertyaktualizacji nowej wartości na wszystkie obiekty mające to samo odniesienie. Dlatego musisz użyć angular.copy
Sarjan Desai
44

W takim przypadku nie musisz używać angular.copy()

Wyjaśnienie :

  • =reprezentuje odniesienie, podczas gdy angular.copy()tworzy nowy obiekt jako głęboką kopię.

  • Użycie =oznaczałoby, że zmiana właściwości of response.datazmieniłaby odpowiednią właściwość $scope.examplelub odwrotnie.

  • Korzystanie z angular.copy()tych dwóch obiektów pozostawałoby odrębne, a zmiany nie odbijałyby się od siebie.

Nicolas2bert
źródło
Najprostsza odpowiedź.
Astitva Srivastava
Najłatwiejsze do zrozumienia. Dzięki
Puneet Verma
7

Powiedziałbym, że angular.copy(source);w twojej sytuacji jest to niepotrzebne, jeśli później nie korzystasz z niego bez celu angular.copy(source, [destination]);.

Jeśli podano miejsce docelowe, wszystkie jego elementy (dla tablic) lub właściwości (dla obiektów) są usuwane, a następnie wszystkie elementy / właściwości ze źródła są do niego kopiowane.

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

Esko
źródło
Dzięki Esko! Próbuję wyprostować głowę. Czy to oznacza, że ​​korzyść dla angular.copy byłaby następująca: jeśli zmienna ma już powiązane dane, jest to czystszy sposób ponownego przypisania elementów / właściwości?
Superman2971
1
Używasz angular.copy()do obiektu, aby uniemożliwić modyfikowanie go przez inny kod. Oryginalny obiekt może się zmienić, ale Twoja kopia nie zobaczy zmian. W razie potrzeby możesz przywrócić kopię.
Esko
1

Podczas korzystania z angular.copy zamiast aktualizowania odniesienia tworzony jest nowy obiekt i przypisywany do miejsca docelowego (jeśli podano miejsce docelowe). Ale to nie wszystko. Jest taka fajna rzecz, która dzieje się po głębokiej kopii.

Załóżmy, że masz usługę fabryczną, która ma metody aktualizujące zmienne fabryczne.

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

i administratora korzystającego z tej usługi,

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

Po uruchomieniu powyższego programu dane wyjściowe będą wyglądać następująco:

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

Dlatego fajną rzeczą w używaniu kopii kątowej jest to, że odniesienia do miejsca docelowego są odzwierciedlane wraz ze zmianą wartości, bez konieczności ponownego ręcznego przypisywania wartości.

Pubudu Dodangoda
źródło
1

Wiem, że już odpowiedział, ale staram się tylko to uprościć. Więc angular.copy (data) możesz użyć w przypadku, gdy chcesz zmodyfikować / zmienić otrzymany obiekt, zachowując jego oryginalne wartości niezmienione / niezmienione.

Na przykład: załóżmy, że wykonałem wywołanie api i otrzymałem mój originalObj, teraz chcę zmienić wartości interfejsu API originalObj w niektórych przypadkach, ale chcę też oryginalnych wartości, więc mogę zrobić kopię mojego oryginalnego interfejsu API w duplicateObj i zmodyfikuj duplicateObj w ten sposób moje wartości originalObj nie zmienią się. W prostych słowach, modyfikacja duplicateObj nie będzie odzwierciedlać w originalObj, w przeciwieństwie do zachowania 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);

Wynik jest taki ...

    ----------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
źródło
1

Dzielę się tutaj swoim doświadczeniem, użyłem angular.copy () do porównania właściwości dwóch obiektów. Pracowałem na wielu wejściach bez elementu formularza, zastanawiałem się, jak porównać właściwości dwóch obiektów i na podstawie wyniku muszę włączyć i wyłączyć przycisk Zapisz. Więc użyłem jak poniżej.

Przypisałem oryginalne wartości użytkownika obiektu serwera do mojego obiektu fałszywego, aby powiedzieć userCopy, i użyłem watch, aby sprawdzić zmiany w obiekcie użytkownika.

Moje API serwera, które pobiera mi dane z serwera:

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

Nie jestem pewien, ale porównanie dwóch obiektów było dla mnie naprawdę bólem głowy, ale z angular.copy () poszło gładko.

Sudarshan Kalebere
źródło
-2

Javascript przekazuje zmienne by reference, co oznacza, że:

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

Teraz, ponieważ by referenceczęść ito [1], i jjest również [1], mimo że tylko izostała zmieniona. Dzieje się tak, ponieważ gdy mówimy, że j = ijavascript nie kopiuje izmiennej i nie przypisuje jej, jale odwołuje się do izmiennej za pośrednictwem j.

Kopia kątowa pozwala stracić to odniesienie, co oznacza:

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

Teraz irówna się [1], podczas gdy jnadal równa się [].

Są sytuacje, w których taka copyfunkcjonalność jest bardzo przydatna.

guramidev
źródło
1
JavaScript przekazuje obiekty przez odniesienie. Nie prymitywne. Przetestuj swój kod.
Oleg
No
1
I angular.copyjest bardziej inteligentny niż serializacja JSON, ponieważ może obsługiwać funkcje.
Oleg
nie wiedziałem tego, mógłbym przysiąc, że pamiętam, jak patrzyłem na źródło kątowe i widziałem tylko serializację JSON, ale sprawdziłem to ponownie i masz rację.
guramidev