it-swarm.com.de

AngularJS 1.5+-Komponenten unterstützen keine Beobachter. Worum geht es?

Ich habe meine benutzerdefinierten Anweisungen auf die neue Komponentenarchitektur aktualisiert. Ich habe gelesen, dass Komponenten keine Beobachter unterstützen. Ist das richtig? Wenn ja, wie erkennen Sie Änderungen an einem Objekt? Für ein einfaches Beispiel habe ich eine benutzerdefinierte Komponente myBox, die ein untergeordnetes Komponentenspiel mit einer Bindung für das Spiel hat. Wenn ein Spiel in der Spielkomponente geändert wird, wie kann ich eine Warnmeldung in der myBox anzeigen? Ich verstehe, dass es eine rxJS-Methode gibt. Ist es möglich, dies rein in eckig zu tun? Mein JSFiddle

JavaScript

var app = angular.module('myApp', []);
app.controller('mainCtrl', function($scope) {

   $scope.name = "Tony Danza";

});

app.component("myBox",  {
      bindings: {},
      controller: function($element) {
        var myBox = this;
        myBox.game = 'World Of warcraft';
        //IF myBox.game changes, show alert message 'NAME CHANGE'
      },
      controllerAs: 'myBox',
      templateUrl: "/template",
      transclude: true
})
app.component("game",  {
      bindings: {game:'='},
      controller: function($element) {
        var game = this;


      },
      controllerAs: 'game',
      templateUrl: "/template2"
})

HTML

<div ng-app="myApp" ng-controller="mainCtrl">
  <script type="text/ng-template" id="/template">
    <div style='width:40%;border:2px solid black;background-color:yellow'>
      Your Favourite game is: {{myBox.game}}
      <game game='myBox.game'></game>
    </div>
  </script>

 <script type="text/ng-template" id="/template2">
    <div>
    </br>
        Change Game
      <textarea ng-model='game.game'></textarea>
    </div>
  </script>

  Hi {{name}}
  <my-box>

  </my-box>

</div><!--end app-->
75
Ka Tech

Schreibkomponenten ohne Beobachter

In dieser Antwort werden fünf Techniken beschrieben, die zum Schreiben von AngularJS 1.5-Komponenten ohne die Verwendung von watchers verwendet werden.


Verwenden Sie die ng-change-Direktive

welche alternativen Methoden stehen zur Verfügung, um obj-Zustandsänderungen ohne Vorbereitung von AngularJs2 zu beobachten?

Sie können die ng-change-Direktive verwenden, um auf Eingabeänderungen zu reagieren.

<textarea ng-model='game.game' 
          ng-change="game.textChange(game.game)">
</textarea>

Um das Ereignis an eine übergeordnete Komponente weiterzuleiten, muss der Ereignishandler als Attribut der untergeordneten Komponente hinzugefügt werden.

<game game='myBox.game' game-change='myBox.gameChange($value)'></game>

JS

app.component("game",  {
      bindings: {game:'=',
                 gameChange: '&'},
      controller: function() {
        var game = this;
        game.textChange = function (value) {
            game.gameChange({$value: value});
        });

      },
      controllerAs: 'game',
      templateUrl: "/template2"
});

Und in der übergeordneten Komponente:

myBox.gameChange = function(newValue) {
    console.log(newValue);
});

Dies ist die bevorzugte Methode für die Zukunft. Die AngularJS-Strategie zur Verwendung von $watch ist nicht skalierbar, da es sich um eine Abrufstrategie handelt. Wenn die Anzahl der $watch-Hörer etwa 2000 erreicht, wird die Benutzeroberfläche schleppend. Die Strategie in Angular 2 besteht darin, das Framework reaktiver zu gestalten und zu vermeiden, $watch auf $scope zu platzieren.


Verwenden Sie den $onChanges Lebenszyklus-Hook

Mit version 1.5.3 fügte AngularJS den $onChanges Lebenszyklus-Hook zum $compile-Dienst hinzu.

Aus den Dokumenten:

Der Controller kann die folgenden Methoden bereitstellen, die als Lebenszyklus-Hooks dienen:

  • $ onChanges (changesObj) - Wird aufgerufen, wenn Einweg- (<) oder Interpolationsbindungen (@) aktualisiert werden. Die Variable changesObj ist ein Hash, dessen Schlüssel die Namen der gebundenen Eigenschaften sind, die sich geändert haben, und die Werte sind ein Objekt der Form { currentValue: ..., previousValue: ... }. Verwenden Sie diesen Hook, um Aktualisierungen innerhalb einer Komponente auszulösen, z. B. das Klonen des gebundenen Werts, um eine versehentliche Veränderung des äußeren Werts zu verhindern.

- AngularJS Comprehensive Directive API-Referenz - Lebenszyklushaken

Der $onChanges-Hook wird verwendet, um auf externe Änderungen in der Komponente mit <-Einwegbindungen zu reagieren. Die ng-change-Direktive wird verwendet, um Änderungen vom ng-model-Controller außerhalb der Komponente mit &-Bindungen zu propagieren. 


Verwenden Sie den $doCheck Lebenszyklus-Hook

Mit Version 1.5.8 fügte AngularJS den $doCheck Lebenszyklushaken zum $compile-Dienst hinzu.

Aus den Dokumenten:

Der Controller kann die folgenden Methoden bereitstellen, die als Lebenszyklus-Hooks dienen:

  • $doCheck() - Wird in jeder Runde des Digest-Zyklus aufgerufen. Bietet die Möglichkeit, Änderungen zu erkennen und darauf zu reagieren. Alle Aktionen, die Sie als Reaktion auf die erkannten Änderungen durchführen möchten, müssen von diesem Hook aus aufgerufen werden. Die Implementierung dieses Befehls hat keine Auswirkungen auf den Aufruf von $onChanges. Dieser Hook könnte beispielsweise nützlich sein, wenn Sie eine tiefe Gleichheitsprüfung durchführen oder ein Date-Objekt überprüfen möchten, dessen Änderungen vom Angular-Änderungsdetektor nicht erkannt werden und somit $onChanges nicht auslösen. Dieser Hook wird ohne Argumente aufgerufen. Wenn Änderungen erkannt werden, müssen Sie die vorherigen Werte zum Vergleich mit den aktuellen Werten speichern.

- AngularJS Comprehensive Directive API-Referenz - Lebenszyklushaken


Intercomponent-Kommunikation mit require

Richtlinien können erfordern die Controller anderer Richtlinien, um die Kommunikation untereinander zu ermöglichen. Dies kann in einer Komponente erreicht werden, indem eine Objektzuordnung für die Eigenschaft requir bereitgestellt wird. Die Objektschlüssel geben die Eigenschaftsnamen an, unter denen die erforderlichen Controller (Objektwerte) an den Controller der erforderlichen Komponente gebunden werden.

app.component('myPane', {
  transclude: true,
  require: {
    tabsCtrl: '^myTabs'
  },
  bindings: {
    title: '@'
  },
  controller: function() {
    this.$onInit = function() {
      this.tabsCtrl.addPane(this);
      console.log(this);
    };
  },
  templateUrl: 'my-pane.html'
});

Weitere Informationen finden Sie unter AngularJS-Entwicklerhandbuch - Intercomponent Communication


Push-Werte aus einem Service mit RxJS

Wie sieht es aus in einer Situation, in der Sie beispielsweise einen Dienst haben, der den Status "Halten" hat. Wie kann ich Push-Änderungen an diesen Dienst und andere zufällige Komponenten auf der Seite machen, wenn eine solche Änderung bekannt ist? Ich habe in letzter Zeit Schwierigkeiten gehabt, dieses Problem anzugehen

Erstellen Sie einen Dienst mit RxJS Extensions for Angular .

<script src="//unpkg.com/angular/angular.js"></script>
<script src="//unpkg.com/rx/dist/rx.all.js"></script>
<script src="//unpkg.com/rx-angular/dist/rx.angular.js"></script>
var app = angular.module('myApp', ['rx']);

app.factory("DataService", function(rx) {
  var subject = new rx.Subject(); 
  var data = "Initial";

  return {
      set: function set(d){
        data = d;
        subject.onNext(d);
      },
      get: function get() {
        return data;
      },
      subscribe: function (o) {
         return subject.subscribe(o);
      }
  };
});

Dann abonnieren Sie einfach die Änderungen.

app.controller('displayCtrl', function(DataService) {
  var $ctrl = this;

  $ctrl.data = DataService.get();
  var subscription = DataService.subscribe(function onNext(d) {
      $ctrl.data = d;
  });

  this.$onDestroy = function() {
      subscription.dispose();
  };
});

Kunden können Änderungen mit DataService.subscribe abonnieren, und Produzenten können Änderungen mit DataService.set senden. 

Die DEMO auf PLNKR .

142
georgeawg

Das $watch-Objekt ist innerhalb des $scope-Objekts verfügbar. Sie müssen also $scope in der Controller-Werksfunktion hinzufügen und dann den Beobachter über die Variable stellen.

$scope.$watch(function(){
    return myBox.game;
}, function(newVal){
   alert('Value changed to '+ newVal)
});

Demo hier

Hinweis: Ich weiß, dass Sie directive in component konvertiert haben, um die Abhängigkeit von $scope zu entfernen, sodass Sie .__ einen Schritt näher kommen. Angular2. Aber es scheint, als ob es für diesen Fall nicht entfernt wurde . </ strike>

Update

Grundsätzlich wird bei Winkel 1.5 die .component-Methode hinzugefügt. Sie unterscheidet zwei verschiedene Funktionen. Wie component. Führt ein bestimmtes Verhalten das Hinzufügen von selector aus, wobei directive ein spezifisches Verhalten zu DOM hinzufügt. Direktive ist nur eine Wrapper-Methode für .directive DDO (Direktive Definitionsobjekt). Sie können lediglich die link/compile-Funktion entfernen, während Sie die .component-Methode verwenden, bei der Sie die Möglichkeit hatten, ein Winkel-kompiliertes DOM zu erhalten.

Verwenden Sie den $onChanges$doCheck Lebenszyklus-Hook des Angular-Komponenten-Lebenszyklus-Hooks, der nach der Version Angular 1.5.3+ verfügbar ist.

_/$ onChanges (changesObj) - Wird aufgerufen, wenn Bindungen aktualisiert werden. Die changesObj ist ein Hash, dessen Schlüssel die Namen der gebundenen Eigenschaften sind.

$ doCheck () - Wird bei jeder Änderung des Digest-Zyklus aufgerufen, wenn sich die Bindung ändert. Bietet die Möglichkeit, Änderungen zu erkennen und darauf zu reagieren.

Durch Verwendung derselben Funktion innerhalb der Komponente wird sichergestellt, dass Ihr Code kompatibel ist, um zu Angular 2 zu wechseln.

8
Pankaj Parkar

Für jeden, der an meiner Lösung interessiert ist, greife ich auf RXJS Observables zurück, die Sie verwenden müssen, wenn Sie zu Angular 2 gelangen. Hier ist eine funktionierende Geige für die Kommunikation zwischen Komponenten.

JS FIDDLE RXJS Observables

class BoxCtrl {
    constructor(msgService) {
    this.msgService = msgService
    this.msg = ''

    this.subscription = msgService.subscribe((obj) => {
      console.log('Subscribed')
      this.msg = obj
    })
    }

  unsubscribe() {
    console.log('Unsubscribed')
    msgService.usubscribe(this.subscription)
  }
}

var app = angular
  .module('app', ['ngMaterial'])
  .controller('MainCtrl', ($scope, msgService) => {
    $scope.name = "Observer App Example";
    $scope.msg = 'Message';
    $scope.broadcast = function() {
      msgService.broadcast($scope.msg);
    }
  })
  .component("box", {
    bindings: {},
    controller: 'BoxCtrl',
    template: `Listener: </br>
    <strong>{{$ctrl.msg}}</strong></br>
    <md-button ng-click='$ctrl.unsubscribe()' class='md-warn'>Unsubscribe A</md-button>`
  })
  .factory('msgService', ['$http', function($http) {
    var subject$ = new Rx.ReplaySubject();
    return {
      subscribe: function(subscription) {
        return subject$.subscribe(subscription);
      },
      usubscribe: function(subscription) {
        subscription.dispose();
      },
      broadcast: function(msg) {
        console.log('success');
        subject$.onNext(msg);
      }
    }
  }])
4
Ka Tech

Ein kleines Heads-up bezüglich der Verwendung von ng-change, wie von der akzeptierten Antwort empfohlen, zusammen mit einer Winkelkomponente von 1,5.

Falls Sie eine Komponente überwachen müssen, die ng-model und ng-change nicht funktioniert, können Sie die Parameter wie folgt übergeben:

Markup in welcher Komponente verwendet wird:

<my-component on-change="$ctrl.doSth()"
              field-value="$ctrl.valueToWatch">
</my-component>

Komponente js:

angular
  .module('myComponent')
  .component('myComponent', {
    bindings: {
      onChange: '&',
      fieldValue: '='
    }
  });

Komponentenauszeichnung:

<select ng-model="$ctrl.fieldValue"
        ng-change="$ctrl.onChange()">
</select>
2
Wtower

Ich bin spät dran. Aber es kann einem anderen Volk helfen.

app.component("headerComponent", {
    templateUrl: "templates/header/view.html",
    controller: ["$rootScope", function ($rootScope) {
        let $ctrl = this;
        $rootScope.$watch(() => {
            return $ctrl.val;
        }, function (newVal, oldVal) {
            // do something
        });
    }]
});
0
tetra master

Verfügbar in IE11, MutationObserver https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver . Sie müssen $ element-Service in den Controller einspeisen, der die Trennung von DOM/Controller halbiert, aber ich glaube, dass dies eine grundlegende Ausnahme (dh ein Fehler) in anglejs ist. Da hide/show async ist, benötigen wir einen On-Show-Callback, der von anglejs und angle-bootstrap-tab nicht bereitgestellt wird. Außerdem müssen Sie wissen, welches spezifische DOM-Element Sie beobachten möchten. Ich habe folgenden Code für den anglejs-Controller verwendet, um die Anzeige des Highcharts-Diagramm-Reflow auszulösen.

const myObserver = new MutationObserver(function (mutations) {
    const isVisible = $element.is(':visible') // Requires jquery
    if (!_.isEqual(isVisible, $element._prevIsVisible)) { // Lodash
        if (isVisible) {
            $scope.$broadcast('onReflowChart')
        }
        $element._prevIsVisible = isVisible
    }
})
myObserver.observe($element[0], {
    attributes: true,
    attributeFilter: ['class']
})
0
user982671

Wirklich nett akzeptierte Antwort, aber ich kann hinzufügen, dass Sie auch die Kraft von Ereignissen verwenden können (ein bisschen wie in Qt-Signal/Slots, wenn Sie so wollen).

Ein Ereignis wird gesendet: $rootScope.$broadcast("clickRow", rowId)von einem übergeordneten Controller (oder sogar einem untergeordneten Controller) .__ Dann können Sie das Event in Ihrem Controller wie folgt behandeln: 

$scope.$on("clickRow", function(event, data){
    // do a refresh of the view with data == rowId
});

Sie können auch einige Protokolle dazu hinzufügen (hier entnommen: https://stackoverflow.com/a/34903433/3147071 )

var withLogEvent = true; // set to false to avoid events logs
app.config(function($provide) {
    if (withLogEvent)
    {
      $provide.decorator("$rootScope", function($delegate) {
        var Scope = $delegate.constructor;
        var origBroadcast = Scope.prototype.$broadcast;
        var origEmit = Scope.prototype.$emit;

        Scope.prototype.$broadcast = function() {
          console.log("$broadcast was called on $scope " + this.$id + " with arguments:",
                     arguments);
          return origBroadcast.apply(this, arguments);
        };
        Scope.prototype.$emit = function() {
          console.log("$emit was called on $scope " + this.$id + " with arguments:",
                     arguments);
          return origEmit.apply(this, arguments);
        };
        return $delegate;
      });
    }
});
0
sebius