Blog & News

AngularJS ControllerAs Schreibweise

Die ControllerAs-Schreibweise gibt es zwar schon länger (seit AngularJS 1.2.0), hat sich aber in den letzten Monaten zu einer Best practice entwickelt. Es gibt verschiedene Gründe dafür:

  • $scope-Eigenschaften werden nicht überschrieben bei verschachtelten Controllern/Scopes
  • Wir können die Controller als Klassen nutzen mit this
  • die Schreibweise ist ähnlicher der AngularJS 2-Schreibweise für Controller als die klassische Controller-Schreibweise mit $scope
  • Mit der controllerAs-Eigenschaft in Direktiven können wir Komponenten (Direktive + View) implementieren die AngularJS 2- Komponenten ähnlich sind

In diesem Artikel werden wir uns mit der ControllerAs-Schreibweise in Kombination mit der ng-controller-Direktive beschäftigen. In weiteren Blogartikeln werden wir uns dann ControllerAs mit ngRoute und Direktiven anschauen.

Klassische Controller-Schreibweise

Ausschnitt aus index.html

<div ng-controller="MainCtrl">
  <span>{{name}}</span>
</div>

app.js

angular.module('testApp', [])
.controller('MainCtrl', function($scope) {
    $scope.name = 'MyName';
  });

In diesem kleinen Beispiel ist es klar, dass {{name}} sich auf $scope.name im MainCtrl bezieht. Was ist aber, wenn wir verschachtelte Scopes/Controller haben? Hier noch ein Beispiel mit verschachtelte Controllern, um die Problematik zu verdeutlichen.

Ausschnitt aus index.html

<div ng-controller="MainCtrl">
  <span>{{name}}</span>
  <div ng-controller="SubCtrl">
    <span>{{name}}</span>
  </div>
</div>

app.js

angular.module('testApp', [])
.controller('MainCtrl', function($scope) {
    $scope.name = 'MainCtrl';
  })
.controller('SubCtrl', function($scope) {
    $scope.name = 'SubCtrl';
  });

Jetzt haben wir zweimal {{name}} im HTML. Welcher Controller definiert den Wert für die zwei Ausdrücke? Diese Frage kann nicht beantwortet werden, ohne im JavaScript-Code nachzuschauen. In unserem Beispiel haben beide Controller die name-Eigenschaft im $scope, und somit definiert MainCtrl den Wert für den ersten Ausdruck und SubCtrl denjenigen für den zweiten Ausdruck. Wenn wir die ControllerAs-Schreibweise nutzen, können wir schon im HTML sehen, welcher Controller den Wert für den jeweiligen Ausdruck definiert, ohne im Code nachschauen zu müssen.

ControllerAs-Schreibweise

Ausschnitt aus index.html

<div ng-controller="MainCtrl as mainCtrl">
  <span>{{mainCtrl.name}}</span>
</div>

app.js

angular.module('testApp', [])
.controller('MainCtrl', function() {
  this.name = 'MyName';
});

Wenn wir die ControllerAs-Schreibweise nutzen, ändern sich 3 Dinge gegenüber der klassische Schreibweise. Im JavaScript-Code nutzen wir jetzt kein $scope mehr. Stattdesen nutzen wir this und definieren dort alle Eigenschaften, die wir sonst im $scope definieren würden. Im HTML haben wir zwei Unterschiede. Als erstes nutzen wir as nach dem Namen des Controllers in der ng-controller-Direktive. Mit Hilfe von as definieren wir einen Namespace. In unserem Beispiel ist unser Namespace mainCtrl. Als zweites nutzen wir unseren Namespace als Präfix für alle this-Eigenschaften, also statt {{name}} schreiben wir {{mainCtrl.name}}.

Jetzt schauen wir uns nochmal das Beispiel mit den verschachtelten Controllern an, aber diesmal nutzen wir die ControllerAs-Schreibweise statt der klassischen Controller-Schreibweise.

<div ng-controller="MainCtrl as mainCtrl">
  <span>{{mainCtrl.name}}</span>
  <div ng-controller="SubCtrl as subCtrl">
    <span>{{mainCtrl.name}}</span>
  </div>
</div>

app.js

angular.module('testApp', [])
.controller('MainCtrl', function() {
    this.name = 'MainCtrl';
  })
.controller('SubCtrl', function() {
    this.name = 'SubCtrl';
  });

Jetzt haben wir für jeden unserer Controller einen eigenen Namespace, mainCtrl für MainCtrl und subCtrl für SubCtrl, definiert. Statt {{name}} nutzen wir jetzt {{namespace.name}}, und es ist schon im HTML klar, dass MainCtlr den Wert für die Ausdrücke definiert, da wir in beiden Fällen das Präfix mainCtrl für den Ausdruck {{name}} benutzt haben. Obwohl der SubCtrl auch eine name-Eigenschaft definiert, können wir im HTML genau sagen, ob wir die name-Eigenschaft des MainCtrl oder des SubCtrl nutzen möchten. Bei der klassischen Schreibweise ist das nicht so einfach möglich.

Allerdings bringt das ControllerAs-Konstrukt nicht nur Vorteile mit sich, sondern auch Nachteile. Erstens müssen wir mehr Code im HTML schreiben, und zweitens müssen wir auf die this-Bindung achten, wenn wir mit Callbacks arbeiten. Aus diesem Grund nutzen viele Entwickler this nicht direkt. Oft sieht man am Anfang des Controllers die Anweisung var vm = this;. Dann wird vm benutzt statt this, um Eigenschaften und Funktionen zu definieren. Mehr Informationen über die Bindung des this-Wertes finden Sie in unserem Blogartikel hier.

ControllerAs mit $scope

Auch wenn wir die ControllerAs-Schreibweise nutzen, gibt es Fälle, in denen wir die $scope-Variable brauchen, z. B. wenn wir mit Angular-Events arbeiten. Genauso wie das $scope-Objekt wird das this-Objekt im Controller benutzt, um die View mit dem Modell zu verbinden. Aber das this-Objekt hat keine vordefinierte Methoden wie das $scope-Objekt. Wenn wir Zugriff auf $on, $broadcast und weitere $scope-Methoden brauchen, müssen wir den Scope per Dependency injection injizieren. Hier ein Beispiel, wie das funktioniert.

Ausschnitt aus index.html

<div ng-controller="MainCtrl as mainCtrl">
  <span>{{mainCtrl.name}}</span>
</div>

app.js

angular.module('testApp', [])
.controller('MainCtrl', function($scope) {
  var vm = this;
  vm.name = 'MyName';
  $scope.$on('EventName', function() {
      vm.data = [];
    });
});

Hier injizieren wir das $scope-Objekt genauso, wie wir es bei der klassischen Schreibweise machen, und weisen den this-Wert der Variable vm zu, um später Probleme bei der this-Bindung zu vermeiden.

Zum Schluss möchte ich noch sagen, dass es sich kaum lohnt, alte Controller umzuschreiben, es ist aber durchaus eine gute Idee, die ControllerAs-Schreibweise für neue Controller zu nutzen. Dadurch können wir Fehler vermeiden, und es wird wahrscheinlich einfacher, die ControllerAs-Schreibweise nach AngularJS 2 zu portieren.

Nikolas Poniros
AUTOR

Nikolas Poniros

Bietet mehrjährige Projekterfahrung in der Webentwicklung mit JavaScript