2016-04-29 18 views
5

Heute treffe ich ein wirklich seltsames Verhalten in AngularJS mit $ watch. Ich vereinfachte meinen Code auf das folgende Beispiel:

https://jsfiddle.net/yLeLuard/

Dieses Beispiel enthält einen Dienst, die Spur eines state variabel halten. Die Anweisungen werden verwendet, um ein Klickereignis an das DOM zu binden, wobei die Variable state über den Service geändert wird.

Es gibt zwei Probleme in diesem Beispiel:

  1. Die erste Schließen-Schaltfläche (mit ng Sie auf Eigenschaft auf sie) ändert nur den Zustand auf dem zweiten Klick
  2. Die beiden Tasten ohne ng- Klick ändern sich nicht um den Zustand an allen

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

Wie wäre es, wenn Sie Ihre Anweisungen öffnen und schließen und stattdessen 'ng-click =" api.isOpen = true "' und 'ng-click =" api.isOpen = false "' verwenden. Es ist besser, native Direktiven zu verwenden, anstatt ein 'click' Ereignis zu binden. – floribon

+0

@floribon es gibt viel mehr Funktionalität in meinen echten Direktiven, daher kann ich nicht ng-click verwenden. Außerdem möchte ich wirklich wissen, warum das nicht richtig funktioniert. – Pascal

+0

Ich verstehe. Entfernen Sie dann den 'ng-Klick' und behalten Sie nur Ihre Anweisung. Was passierte, war, dass 'ng-click' einen Scope-Digest auslöste, so dass es scheinbar funktionierte, aber unzuverlässig war. – floribon

Antwort

4

Das ist, weil Sie einen native Ereignis-Listener auf click verwenden. Dieses Ereignis ist asynchron und liegt außerhalb des Angular-Digest-Zyklus. Daher müssen Sie den Bereich manuell verdauen.

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

Fest Geige: https://jsfiddle.net/7h95ct1y/

würde ich vorschlagen, den Zustand direkt in die sich wandelnde ng Klick: ng-click="api.isOpen = true"

+0

Es scheint, als ob dies die richtige Antwort ist! Danke vielmals. Hier ist ein funktionierendes Beispiel für mein Problem mit Ihrer Lösung: https://jsfiddle.net/yLeLuard/2/ – Pascal

0

Sie Ihre Link-Funktion als Pre-Link-Funktion setzen sollte, dies löst das Problem des zweiten Klicks.

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

Was die nicht Arbeitsteil, setzen sind Sie Ihre Richtlinie über die div, so dass, wenn Sie auf dem div funktioniert es klicken, aber nicht auf die Schaltfläche. Ihre offene Anweisung sollte auf der Schaltfläche sein.

Edit: andere Kommentatoren vorgeschlagen, dass Sie den Status direkt in der ng-Klick ändern. Ich würde es nicht empfehlen, es könnte in diesem Fall funktionieren, aber wenn Sie verpflichtet sind, mehr als eine Zuweisung zu tun, ist es nicht lebensfähig.

+0

So? https://jsfiddle.net/2c9pv4xm/1/ Ich sehe den Service nicht aktualisieren. – Pascal

+0

Mein Fehler. Ich habe deinen Code zu schnell gelesen.Wenn Sie den Link als vorinstalliert verwenden, scheint das Vorhandensein von ng-click aus irgendeinem Grund verpflichtend zu sein. Edit: jedoch mit Scope. $ Apply wird nicht wirklich empfohlen, es zwingt einen neuen Digest-Zyklus und wenn Ihre App viele Beobachter enthält, könnte es den Digest der ganzen Seite verlangsamen. Wenn Sie nur den ng-Klick benutzen, würde ich es in eine Funktion setzen. – Seedy

Verwandte Themen