2016-04-29 9 views
5

Dzisiaj spotykam się z naprawdę dziwnym zachowaniem w AngularJS przy użyciu funkcji $ watch. I uproszczone mojego kodu na poniższym przykładzie:

https://jsfiddle.net/yLeLuard/

Przykład ten zawiera usługę, która będzie śledzić zmiennej state. Dyrektywy są używane do wiązania zdarzenia kliknięcia z DOM zmieniając zmienną state za pośrednictwem usługi.

Istnieją dwa problemy w tym przykładzie:

  1. Pierwszy przycisk zamykania (właściwość ng kliknij na nim) zmienia tylko stan na drugim kliknięciem
  2. dwa przyciski bez ng- kliknięcie nie zmieniają stanu wcale

main.html

<div ng-controller="main"> 
    State: {{ api.isOpen | json }} 
    <div ng-click="" open> 
    <button>Open - Working fine</button> 
    </div> 
    <div ng-click="" close> 
    <button>Close - Works, but only on second click</button> 
    </div> 
    <div open> 
    <button>Open - Not Working</button> 
    </div> 
    <div close> 
    <button>Close - Not Working</button> 
    </div> 
</div> 

main.js

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

myApp.controller('main', ['$scope', 'state', function($scope, state) { 
    $scope.api = state; 

    $scope.api.isOpen = false; 

    $scope.$watch('api.isOpen', function() { 
    console.log('state has changed'); 
    }); 
}]); 

myApp.directive('open', ['state', function(state) { 
    return { 
    restrict: 'A', 
    scope: {}, 
    replace: true, 
    template: '<button>Open</button>', 
    link: function(scope, element) { 
     element.on('click', function() { 
     state.isOpen = true; 
     }); 
    } 
    }; 
}]); 


myApp.directive('close', ['state', function(state) { 
    return { 
    restrict: 'A', 
    scope: {}, 
    replace: true, 
    template: '<button>Close</button>', 
    link: function(scope, element) { 
     element.on('click', function() { 
     state.isOpen = false; 
     }); 
    } 
    }; 
}]); 

myApp.service('state', function() { 
    return { 
    isOpen: null 
    }; 
}); 
+2

Co powiesz na to, że usuwasz swoje dyrektywy otwierając i zamykając, a zamiast tego użyj 'ng-click =" api.isOpen = true "' i 'ng-click =" api.isOpen = false "'. Lepiej jest używać natywnych dyrektyw niż wiązania "click" event – floribon

+0

@floribon W moich prawdziwych dyrektywach jest o wiele więcej funkcjonalności, dlatego nie mogę używać ng-click. Poza tym, naprawdę chcę wiedzieć, dlaczego to nie działa poprawnie. – Pascal

+0

Rozumiem. Następnie usuń 'ng-click' i zachowaj tylko swoją dyrektywę. Co się dzieje, to, że 'ng-click' uruchamiał skrót zakresu, więc wydawało się, że działa, ale był niewiarygodny. – floribon

Odpowiedz

4

To dlatego, że używasz natywną detektor zdarzeń na click. To wydarzenie jest asynchroniczne i poza cyklem kodowania Angular, więc musisz ręcznie przetrawić zakres.

myApp.directive('open', ['state', function(state) { 
    return { 
    restrict: 'A', 
    scope: {}, 
    link: function(scope, element) { 
     element.on('click', function() { 
     scope.$apply(function() { 
      state.isOpen = true; 
     }); 
     }); 
    } 
    }; 
}]); 

Fixed skrzypce: https://jsfiddle.net/7h95ct1y/

Proponuję zmianę stanu bezpośrednio w ng kliknij: ng-click="api.isOpen = true"

+0

Wygląda na to, że to jest poprawna odpowiedź! Wielkie dzięki. Oto działający przykład mojego problemu z wykorzystaniem twojego rozwiązania: https://jsfiddle.net/yLeLuard/2/ – Pascal

0

Należy umieścić swoją funkcję łącza jako funkcja pre-link, to rozwiązuje problem drugiego kliknięcia.

https://jsfiddle.net/2c9pv4xm/

myApp.directive('close', ['state', function(state) { 
    return { 
    restrict: 'A', 
    scope: {}, 
    link:{ 
    pre: function(scope, element) { 
     element.on('click', function() { 
     state.isOpen = false; 
     console.log('click', state); 
     }); 
    } 
    } 
    }; 
}]); 

Co do części nie działa, jesteś oddanie dyrektywy na div, więc po kliknięciu na div to działa, ale nie na przycisk. Twoja otwarta dyrektywa powinna znajdować się na przycisku.

Edycja: inni komentatorzy sugerowali, aby zmienić stan bezpośrednio w ng-click. Nie polecałbym tego, może to zadziałać w tym przypadku, ale jeśli jesteś zobowiązany do wykonania więcej niż przypisania, nie jest to możliwe.

+0

Podoba mi się to? https://jsfiddle.net/2c9pv4xm/1/ Nie widzę aktualizacji usługi. – Pascal

+0

Mój błąd. Zbyt szybko odczytuję twój kod.Umieszczenie linku jako pre-works, jednak obecność ng-click wydaje się być obowiązkowa z jakiegoś powodu. Edycja: jednak użycie scope. $ Apply nie jest zalecane, wymusza nowy cykl trawienia i jeśli twoja aplikacja zawiera dużo obserwatorów, może spowolnić trawienie całej strony. Jeśli używasz tylko ng-click, umieściłbym go w funkcji. – Seedy