Boostrap Toogle Switch mit Angular Direktive

Was Jquery Mobile mit dem Flipswitch kennt, sucht man bei Bootstrap vergeblich.

image

Da es ziemlich naheliegend ist, fordert die Community auf Github von Bootstrap seit drei Jahren das Feature. Irgendwie vergeblich. Da Open Source, kann man das natürlich selber schreiben und ins Projekt einchecken. Viele tun das scheinbar nicht. Man findet jede Menge Lösungen offsite, die allerdings in Funktion und Qualität nicht ganz den Ansprüchen genügen. Also selber machen.

Das Layout könnte im HTML Code z.B. so gelöst werden

   1:  <div class="btn-group">
   2:       <a class="btn btn-xs btn-primary active">Wert</a>
   3:       <a class="btn btn-xs btn-default">Wert2</a>
   4:  </div>

Ist optisch nicht ganz ein Flipswitch, aber leicht anpassbar.

image

Für den Zustandswechsel wird ein Stück JavaScript benötigt, das die Klassen btn-primary und btn-default austauscht.

Hier wird aber weiter gedacht. Der Wert soll an ein Viewmodel eines Angular.js Controllers gebunden werden.

   1:    .controller('myController', ['$scope', function ($scope) {
   2:                 $scope.wert = true;

 

Der HTML Teil wird mit einer Angular Direktive erstellt. Als Parameter werden die beiden Beschriftungen übergeben.

   1:  <div class="btn-group" 
   2:     my-toggle my-text1="meins" my-text2="public" model="wert">
   3:  </div>

 

In meinem Angular Trainings lernt man die genauen Details zu Direktiven. Auf einige Angular Stolpersteine soll hier aber hingewiesen werden.

Mit dem Template wird HTML Code in der Page erzeugt, der die beiden Buttons enthält. Komplizierter ist die Gültigkeit des Scope, der das Viewmodel aus dem Controller hält und letztendlich den checked Wert enthält. Wie kann man aus einer Direktive auf den Scope des Controllers zugreifen und diesen auch verändern? Da es mehrere geschachtelte Scopes gibt, somit auf den Parent Scope.

Mit Chrome und der Batarang Erweiterung kann die Internas der Scope Hierarchie auch darstellen.

image

image

Im HTML Code wurde vorher das Attribut model eingeführt, das auf die Property wert (also $scope.wert) bindet. Leider verhält sich die Bindung auf den my-text obwohl optisch ident völlig anders.

In der Direktive wird gesteuert, welche Attribute (@) und Bindungen (=) in den privaten Scope übernommen werden. Nun kann in der Click Funktion des DIV! der Parent Scope ($scope.wert) per Child Scope (scope.model) geändert werden, weil das = eine zwei Wege Bindung bewirkt.

   1:  .directive('myToggle', function () {
   2:         return {
   3:                    restrict: 'A',
   4:                      scope: {
   5:                             model: '=',
   6:                             myText1: "@",
   7:                             myText2: "@"
   8:                         },
   9:                         template: '<a class="btn btn-xs active" 
ng-class="model ? \'btn-primary\' : \'btn-default\'">{{myText1}}</a>'
+
  10:                        ' <a class="btn btn-xs " 
ng-class="model ? \'btn-default\' : \'btn-primary\'">{{myText2}}</a>'
,
  11:                         link: function (scope, element, attr) {
  12:                             element.bind('click', function () {
  13:                                 scope.model = !scope.model;
  14:                                 scope.$apply();
  15:                             });
  16:                 }
  17:           }
  18:     })
Kommentare sind geschlossen