2015-04-23 11 views
11

Podejrzewam, że tytuł jest całkiem jasny, o co proszę. Stworzyłem to skrzypce: http://jsfiddle.net/Sourabh_/HB7LU/13142/

W skrzypcach próbowałem replikować scenariusz async. To tylko przykład, ale w wywołaniu AJAX, jeśli nie używam $scope.$apply(), lista nie jest aktualizowana. Chcę wiedzieć, czy można bezpiecznie używać $scope.$apply() za każdym razem, gdy wykonuję wywołanie AJAX, aby zaktualizować listę, czy jest jakiś inny mechanizm, z którego mogę skorzystać?

kod Pisałem do powtórzenia scenariusza (tak samo jak w skrzypcach):

HTML

<div ng-controller="MyCtrl"> 
    <li ng-repeat="item in items"> 
    {{item.name}} 
    </li> 
    <button ng-click="change()">Change</button> 
</div> 

JS

var myApp = angular.module('myApp',[]); 

function MyCtrl($scope) { 
    $scope.items = [{name : "abc"},{name : "xyz"},{name : "cde"}]; 

    $scope.change = function(){ 
    test(function(testItem){ 
     $scope.items = testItem; 
     //$scope.$apply(); 
    }) 
    } 
    function test(callback){ 
    var testItem = [ 
        {name : "mno"}, 
        {name : "pqr"}, 
        {name : "ste"} 
        ]; 
    setTimeout(function(){callback(testItem)},2000); 
    } 
} 
+1

Czy próbujesz naśladować wywołanie REST? Jeśli tak, to: żądanie HTTP http zwraca obietnicę, której można użyć w kontrolerze za pomocą .then() w celu zmiany zakresu. Nie ustawiaj nowego zakresu i $ apply() wewnątrz wywołania REST. Ponieważ to, co teraz robisz, jest bezcelowe. –

+0

Wiem, że to już nie pomaga, ale Angular 2.0 ma naprawić tę okropność. – CodingIntrigue

+0

Każda zmiana zakresu przebiega przez podsumowanie. Limit czasu w wywołaniu funkcji API pachnie dla mnie. Co jeśli serwer potrzebuje więcej czasu? Co jeśli zostanie on wywołany 2 lub 3 razy? –

Odpowiedz

4

Jeśli chcesz immidate o API-Rest-Call, użyj wracającą promise w swoim Controller zamiast ustawiania zakresu wewnątrz Rest-Call.

$http.get('uri') 
    .success(function(data) { 
    $scope.items = data 
}); 

Należy unikać używania $apply(). From the Angular GitHub Repo:

$scope.$apply() powinno nastąpić tak blisko przypadku asynchronicznego obowiązującego możliwe.

NIE losowo posypuj go po całym kodzie. Jeśli robisz, jeśli (!$scope.$$phase) $scope.$apply() dzieje się tak, ponieważ nie jesteś wystarczająco wysoki w stosie wywołań.

na Twoje pytanie:

  • Jeśli znajdziesz się w sytuacji, gdzie trzeba zastosować $(), przemyśleć swoją strukturę.
  • Tylko dla względów bezpieczeństwa: Nie należy używać $apply()
+0

Nie jest prawdą, że nie jest bezpieczniej używać $ apply(). Nie możemy $ apply(). To się wydarzy w cyklu życia aplikacji, bez względu na wszystko. Jest bardzo ważne, aby używać i rozumieć, gdy jest to właściwe. Prawdą jest, że lepiej nie wywoływać $ apply(), gdy tylko jest to możliwe, tj. $ Timeout zamiast setTimeout, ... –

+0

Jedynym powodem, dla którego nie mogę użyć $ apply(), jest to, że chcesz używać $ digest(). ponieważ digest po prostu ocenia zmienne bieżącego zakresu, ale $ apply() kończy wywoływanie $ rootScope. $ digest() przelicza wszystkie wiązania, co jest bardzo kosztowne. Jednakże, jeśli przypadek użycia jest taki, to nie sądzę, że jest coś złego w korzystaniu z ti –

5

Edit to w ponieważ nie było jasne, że OP próbował wykpić telefoniczne wezwanie. Mimo to korzystanie z usługi $timeout jest świetnym sposobem na uniknięcie konieczności ręcznego wywoływania $scope.$apply i jest rozwiązaniem bardziej powszechnym niż korzystanie z obietnicy (w przypadkach, w których nie dzwonisz pod numer $http, nie zawsze ma sens twoje zmiany w następnym cyklu, zawijając je obietnicą).


Zaktualizuj swój kod, aby korzystać z $timeout service i powinien działać bez konieczności dzwonienia pod numer $apply.

$timeout jest owinięcie wokół rodzimej setTimeout z ważną różnicą: $timeout opóźni wykonanie co najmniej do następnych tras rowerowych $digest.

Przekazanie bez opóźnienia spowoduje opóźnienie wykonania do następnego cyklu. Przekazanie w 2000 roku opóźni wykonanie do następnego cyklu po 2000ms.

Stąd jest to łatwy trik upewnić się, że zmiany są odbierane przez kątowe bez konieczności zadzwonić $apply ręcznie (co jest uważane za niebezpieczne w każdym przypadku)

function MyCtrl($scope, $timeout) { 
    $scope.items = [{name : "abc"},{name : "xyz"},{name : "cde"}]; 

    $scope.change = function(){ 
    test(function(testItem){ 
     $scope.items = testItem; 
     //$scope.$apply(); 
    }) 
    } 
    function test(callback){ 
    var testItem = [ 
        {name : "mno"}, 
        {name : "pqr"}, 
        {name : "ste"} 
        ]; 
    $timeout(function(){callback(testItem)},2000); 
    } 
} 
+0

Myślę, że osoba chce unieważnić wywołanie REST, gdzie sugestia polega na użyciu obietnicy. success(), .then() lub .error() zamiast timeout. –

+0

To nie wynika z pytania. OP pyta, kiedy można bezpiecznie użyć aplikacji $ apply, której nigdy nie jest, więc zapewniam alternatywę. Używanie $ q oczywiście rozwiązałoby ten problem, ale użycie '$ timeout' jest znacznie prostszym sposobem na wymuszenie zmian w następnym cyklu. – thomaux

+0

@Azezeo. Przepraszam, jeśli byłem niejasny, jednak funkcja "testowa" polegała tylko na naśladowaniu serwera, na którym wywołanie zwrotne jest odpowiedzią serwera. +1 dla alternatywy. – Zee

1

Trzeba użyć $ zastosowania za każdym razem, gdy używasz czegoś, co nie jest "kanciastą drogą", jak Anzeo powiedział o $ timeout.
Na przykład, jeśli używasz http jQuery zamiast kąta $ http, musisz dodać $ scope. $ Apply.

+0

Dzięki temu bardzo pomogło .. – Zee

+0

@ Sourabh - Powinieneś zaakceptować coś jako odpowiedź. –

3

Należy użyć $apply, gdy kod nie jest wykonywany w pętli skoku kątowego. W normalnych warunkach nie będziemy musieli z niego korzystać, ale możemy go użyć, jeśli mamy kod wywoływany z programu obsługi zdarzeń jQuery lub z metod takich jak setTimeout(). Nawet jeśli masz funkcję, która jest wywoływana z innej funkcji kątowej, takiej jak watch lub sekwencyjne programy obsługi zdarzeń, nie musisz używać funkcji $ apply(), ponieważ te skrypty są wykonywane w cyklu podsumowania.

Jeden bezpiecznym sposobem jest sprawdzenie $scope.$$phase param przed wywołaniem $scope.$apply() jak

if($scope.$$phase){ 
    $scope.$apply(); 
} 

W twoim przypadku, ale można użyć $ limit czasu, jak sugerowano w innej odpowiedzi

0

Jak zaznaczył @gruberb w komentarzach, jeśli próbujesz wyśmiać wywołanie REST, lepiej użyć obietnicy niż $apply.

W tym celu należy użyć $q service, aby utworzyć i zwrócić obietnicę. Następnie po prostu zadzwoń do niego i pracuj z tobą, wywołując metodę then() na zwróconej obietnicy.

function MyCtrl($scope, $q) { 
    $scope.items = [{name : "abc"},{name : "xyz"},{name : "cde"}]; 

    $scope.change = function(){ 

     test().then(function (items) { 
      $scope.items = items; 
      $scope.$apply(); 
     }); 
    }; 

    function test() { 
     var defered = $q.defer(); 

     var testItem = [ 
      {name : "mno"}, 
      {name : "pqr"}, 
      {name : "ste"} 
     ]; 

     setTimeout(function() { 
      defered.resolve(testItem); 
     },2000); 

     return defered.promise; 
    } 
} 
0

Wszystkie powyższe odpowiedzi podać pewne informacje, ale nie odpowiedział kilka wątpliwości miałem albo przynajmniej ja nie rozumiem. Więc daję własną.

Jest bardzo jasno określone w kanciastych docs when to use $apply

wywołanie plecy $ http lub $ limitu czasu lub ng-click, ng -..... zostały $ apply() owinięty w nich. Dlatego, kiedy ludzie mówią, że nie musisz używać $ apply(), gdy robisz kątowy sposób robienia rzeczy, to jest to. Jednak jedna z odpowiedzi wspomina, że ​​funkcje obsługi zdarzeń kątowych są również zawijane za pomocą $ apply(). Nie jest to prawdą lub przez to użytkownik oznacza tylko rodzaj zdarzeń typu ng-click (ponownie ng -....). Jeśli zdarzenie jest transmitowane w rootScope (lub w jakimkolwiek zakresie) poza $ http lub $ timeout lub ng-click, na przykład: z niestandardowej usługi, musisz użyć $ apply() na twoim zasięgu, chociaż $ rootScope . $ broadcast to także kanciasty sposób robienia rzeczy. w większości scenariuszy nie będziemy tego potrzebować, ponieważ stan aplikacji zmienia się, gdy coś się dzieje. tj. kliknięcia, zmiana wyboru itp. ... a te są w kanciastych kategoriach już używają $ apply(), gdy używamy odpowiednio ng-click.Obsługa zdarzeń po stronie serwera za pomocą signalr lub socket.io i podczas pisania dyrektyw niestandardowych, gdy konieczność zmiany tylko zakresu dyrektywy to tylko kilka przykładów, w których bardzo ważne jest użycie $ apply()

Powiązane problemy