2013-10-15 4 views
5

Ich benutze v1.2.0 rc2 von AngularJS und möchte wissen, was die beste Methode ist, um mehrere Controller gemeinsame Funktionalität bereitzustellen.

Ich habe folgende Validierungsfunktionen, die ich in allen Controllern verwenden möchten, die ein Modell bearbeiten:

$scope.canSave = function (formController) { 
    return formController.$dirty && formController.$valid; 
}; 

$scope.validationClasses = function (modelController) { 
    return { 
     'has-error': modelController.$invalid && modelController.$dirty, 
     'has-success': modelController.$valid && modelController.$dirty 
    }; 
}; 

Ich möchte meine Controller halten trockne ich so eine Fabrik wie folgt definiert:

angular.module('myModule', []) 
    .factory('validationFactory', [function() { 
     return { 
      validationClasses: function (modelController) { 
       return { 
        'has-error': modelController.$invalid && modelController.$dirty, 
        'has-success': modelController.$valid && modelController.$dirty 
       }; 
      }, 
      isFormValid: function (formController) { 
       return formController.$dirty && formController.$valid; 
      } 
     }; 
    }]); 
Zunächst

, mischte ich nur die Fabrik in die Steuerungen, die es benötigt, wie folgt:

$scope.canSave = validationFactory.isFormValid; 

$scope.validationClasses = validationFactory.validationClasses; 

Aber ich erkannte ich sie an die $ rootScope in der Modul run-Methode hinzufügen könnte wie folgt:

angular.module('myModule', []) 
    .run([ 
     '$rootScope', 
     'validationFactory', 
     function ($rootScope, validationFactory) { 
      $rootScope.canSave = $rootScope.canUpdate = validationFactory.isFormValid; 
      $rootScope.validationClasses = validationFactory.validationClasses; 
     }]); 

Jetzt sind sie in jedem Controller verfügbar sind wahllos und es gibt weniger Verkabelung zu tun auf.

Die Funktionen werden in den Ansichtsvorlagen wie folgt verwendet:

<form name="questionForm" novalidate> 
    <div class="form-group" ng-class="validationClasses(questionForm.question)"> 
     <label for="questionText" class="control-label">Text</label> 
     <input type="text" ng-model="question.text" name="question" 
       id="questionText" class="form-control" required/> 
     <span ng-show="questionForm.question.$error.required" 
       class="help-block">Question text is required</span> 
    </div> 
    ... 
    <div class="form-group" ng-switch on="action"> 
     <button type="button" ng-switch-when="New" ng-click="save()" 
       ng-disabled="!canSave(questionForm)" 
       class="btn btn-primary">Save</button> 
     <button type="button" ng-switch-default ng-click="update()" 
       ng-disabled="!canUpdate(questionForm)" 
       class="btn btn-primary">Update</button> 
     <button type="button" ng-click="cancel()" 
       class="btn btn-default">Cancel</button> 
    </div> 
</form> 

Meine Fragen sind:

  1. soll ich das Hinzufügen gemeinsame Funktionen, um den $ rootScope vermeiden? Wenn ja, was sind die Fallstricke?
  2. Ist es besser, die allgemeine Funktionalität nur bei Bedarf zu mischen?
  3. gibt es eine bessere Möglichkeit, das gleiche Ergebnis zu erzielen?

Aktualisiert Lösung

Ich entschied ich benutzerdefinierte Richtlinien statt mit Funktionen, um den $ rootScope, das hatte einen üblen Geruch zu, es zu benutzen.

I erstellt benutzerdefinierte Attribute validation-class-for="<input.name>" und disabled-when-invalid so das Markup sieht wie folgt aus:

<div class="form-group" validation-class-for="question"> 
    <label for="questionText" class="control-label">Text</label> 
    <input type="text" ng-model="question.text" name="question" 
      id="questionText" class="form-control" required/> 
    <span ng-show="questionForm.question.$error.required" 
      class="help-block">Question text is required</span> 
</div> 

<button type="button" ng-click="save()" disabled-when-invalid 
    class="btn btn-primary">Save</button> 

Die Richtlinien einfach ein Formular Vorfahren benötigen und eine Funktion beobachten Zustand zu bestimmen.

angular.module('myModule', []) 
    .directive('validationClassFor', function() { 
     return { 
      require: '^form', 
      link: function (scope, element, attributes, formController) { 
       scope.$watch(function() { 
        var field = formController[attributes.validationClassFor]; 
        if (field.$invalid && field.$dirty) { 
         element.removeClass('has-success').addClass('has-error'); 
        } else if (field.$valid && field.$dirty) { 
         element.removeClass('has-error').addClass('has-success'); 
        } else { 
         element.removeClass('has-error').removeClass('has-success'); 
        } 
       }); 
      } 
     }; 
    }) 
    .directive('disabledWhenInvalid', function() { 
     return { 
      require: '^form', 
      link: function (scope, element, attributes, formController) { 
       scope.$watch(function() { 
        return formController.$dirty && formController.$valid; 
       }, function (value) { 
        element.prop('disabled', !value); 
       }); 
      } 
     }; 
    }); 

Jetzt gibt es auch keine Notwendigkeit für die Validierungsfabrik.

Antwort

1

Wie verwenden Sie diese validationFactory? Soll das Aussehen von Submit-Buttons geändert werden? Wenn ja, würde ich vorschlagen, benutzerdefinierte Komponenten für die Schaltflächen Erstellen von selbst, und haben die Komponente Referenz die validationFactory.

+0

Ich bin mit den Funktionen zu steuern, ob das Speichern/Aktualisieren-Taste deaktiviert ist und die CSS-Klassen, die an die Form Gruppe angewendet werden. – gwhn

2

Ich würde nicht vorschlagen, den Bereich für die gemeinsame Logik zu verwenden. Das reduziert die Wiederverwendbarkeit und hat Einfluss, wenn Sie es testen wollen.

  • würde ich eine der folgenden Ansätze vorschlagen: Sie Klassenerweiterung verwenden können: http://ejohn.org/blog/simple-javascript-inheritance/ gemeinsame Logik zu extrahieren.
  • Sie können die Nachrichtenübertragung von angular verwenden, um Ereignisse zu senden, die den Status wie "gültig" von einem Überprüfungslistener umschalten.
  • Sie können einen Interceptor verwenden, um Back-End-Anforderungen basierend auf bestimmten Bedingungen zu verhindern.
+0

Ich glaube, Sie einen guten Punkt, um über die Auswirkungen auf die Testbarkeit. TBH fragte ich die Frage, weil ich dachte, dass es ein Code Geruch war. – gwhn