web-dev-qa-db-fra.com

Passer le formulaire à la directive

Je veux encapsuler mes champs de formulaire dans une directive pour pouvoir le faire simplement:

<div ng-form='myForm'>
  <my-input name='Email' type='email' label='Email Address' placeholder="Enter email" ng-model='model.email' required='false'></my-input>

</div>

Comment puis-je accéder à myForm dans ma directive pour pouvoir effectuer des contrôles de validation, par exemple myForm.Email.$valid?

72
Emad

Pour accéder au FormController dans une directive:

require: '^form',

Ensuite, il sera disponible comme 4ème argument de votre fonction de lien:

link: function(scope, element, attrs, formCtrl) {
    console.log(formCtrl);
}

fiddle

Vous n’avez peut-être besoin que d’un accès au NgModelController:

require: 'ngModel',
link: function(scope, element, attrs, ngModelCtrl) {
     console.log(ngModelCtrl);
}

fiddle

Si vous avez besoin d'accéder aux deux:

require: ['^form','ngModel'],
link: function(scope, element, attrs, ctrls) {
    console.log(ctrls);
}

fiddle

149
Mark Rajcok

Voici un exemple (stylé avec Bootstrap 3.1))

Il contient un formulaire avec plusieurs entrées (nom, email, âge et pays). Nom, email et âge sont des directives. Le pays est une entrée "régulière".

Un message d’aide est affiché pour chaque entrée lorsque l’utilisateur n’entre pas une valeur correcte.

Le formulaire contient un bouton de sauvegarde qui est désactivé si le formulaire contient au moins une erreur.

<!-- index.html -->
<body ng-controller="AppCtrl">
  <script>
    var app = angular.module('app', []);

    app.controller('AppCtrl', function($scope) {
      $scope.person = {};
    });
  </script>
  <script src="inputName.js"></script>
  <script src="InputNameCtrl.js"></script>
  <!-- ... -->

  <form name="myForm" class="form-horizontal" novalidate>
    <div class="form-group">
      <input-name ng-model='person.name' required></input-name>
    </div>

    <!-- ... -->

    <div class="form-group">
      <div class="col-sm-offset-2 col-sm-4">
        <button class="btn btn-primary" ng-disabled="myForm.$invalid">
          <span class="glyphicon glyphicon-cloud-upload"></span> Save
        </button>
      </div>
    </div>
  </form>

  Person: <pre>{{person | json}}</pre>
  Form $error: <pre>{{myForm.$error | json}}</pre>
  <p>Is the form valid?: {{myForm.$valid}}</p>
  <p>Is name valid?: {{myForm.name.$valid}}</p>
</body>

// inputName.js
app.directive('inputName', function() {
  return {
    restrict: 'E',
    templateUrl: 'input-name.html',
    replace: false,
    controller: 'InputNameCtrl',
    require: ['^form', 'ngModel'],

    // See Isolating the Scope of a Directive http://docs.angularjs.org/guide/directive#isolating-the-scope-of-a-directive
    scope: {},

    link: function(scope, element, attrs, ctrls) {
      scope.form = ctrls[0];
      var ngModel = ctrls[1];

      if (attrs.required !== undefined) {
        // If attribute required exists
        // ng-required takes a boolean
        scope.required = true;
      }

      scope.$watch('name', function() {
        ngModel.$setViewValue(scope.name);
      });
    }
  };
});

// inputNameCtrl
app.controller('InputNameCtrl', ['$scope', function($scope) {
}]);

AngularJS form with directives

32
tanguy_k

Edit 2: Je laisserai ma réponse, car cela pourrait être utile pour d'autres raisons, mais l'autre réponse de Mark Rajcok est ce que je voulais à l'origine faire, mais n'a pas réussi à se rendre au travail. Apparemment, le contrôleur parent ici serait form, pas ngForm.


Vous pouvez le transmettre en utilisant un attribut dans votre directive, bien que cela devienne plutôt verbeux.

Exemple

Voici un jsFiddle simplifié et fonctionnel .

Code

HTML:

<div ng-form="myForm">
    <my-input form="myForm"></my-input>
</div>

Parties essentielles de la directive:

app.directive('myInput', function() {
    return {
        scope: {
            form: '='
        },
        link: function(scope, element, attrs) {
            console.log(scope.form);
        }
    };
});

Que ce passe-t-il

Nous avons demandé à Angular) de lier la valeur de la portée nommée dans l'attribut form à notre portée isolée, en utilisant un '='.

En procédant ainsi, vous dissociez le formulaire réel de la directive d'entrée.

Remarque: J'ai essayé d'utiliser require: "^ngForm", Mais la directive ngForm ne définit pas de contrôleur, et ne peut pas être utilisée de cette manière (qui est dommage).


Cela étant dit, je pense que c'est une façon très prolixe et compliquée de gérer cela. Il vaudrait peut-être mieux ajouter une nouvelle directive à l’élément de formulaire et utiliser require pour accéder à cet élément. Je verrai si je peux mettre quelque chose ensemble.

Edit: Utiliser une directive parent

OK, voici le mieux que je puisse comprendre en utilisant une directive parent, je vais en expliquer plus dans une seconde:

Travailler jsFiddle en utilisant la directive parent

HTML:

<div ng-app="myApp">
    <div ng-form="theForm">
        <my-form form="theForm">
            <my-input></my-input>
        </my-form>
    </div>
</div>

JS (partiel):

app.directive('myForm', function() {
    return {
        restrict: 'E',
        scope: {
            form: '='
        },
        controller: ['$scope', function($scope) {
            this.getForm = function() {
                return $scope.form;
            }
        }]
    }
});

app.directive('myInput', function() {
    return {
        require: '^myForm',
        link: function(scope, element, attrs, myForm) {
            console.log(myForm.getForm());
        }
    };
});

Ceci stocke le formulaire dans la portée de la directive parent (myForm) et permet aux directives enfants d'y accéder en demandant le formulaire parent (require: '^myForm') Et en accédant au contrôleur de la directive dans la fonction de liaison (myForm.getForm()).

Avantages:

  • Vous devez seulement identifier le formulaire en un seul endroit
  • Vous pouvez utiliser votre contrôleur parent pour héberger du code commun

Points négatifs:

  • Vous avez besoin d'un noeud supplémentaire
  • Vous devez mettre le nom du formulaire deux fois

Ce que je préférerais

J'essayais de le faire fonctionner en utilisant un attribut sur l'élément de formulaire . Si cela fonctionnait, vous n'auriez qu'à ajouter la directive au même élément que ngForm.

Cependant, je commençais à avoir un comportement étrange avec la portée, où la variable myFormName serait visible dans $scope, Mais serait undefined lorsque j'aurais essayé d'y accéder. Celui-là m'a confondu.

17
OverZealous

À partir de AngularJS 1.5.0, il existe une solution beaucoup plus propre pour cela (par opposition à l’utilisation directe de la fonction link). Si vous souhaitez accéder au FormController d'un formulaire dans le contrôleur de directive de votre sous-composant, vous pouvez simplement insérer l'attribut require sur la directive, comme suit:

return {
  restrict : 'EA',
  require : {
    form : '^'
  },
  controller : MyDirectiveController,
  controllerAs : 'vm',
  bindToController : true,
  ...
};

Ensuite, vous pourrez y accéder dans votre contrôleur de modèle ou de directive comme vous le feriez pour toute autre variable de portée, par exemple:

function MyDirectiveController() {
  var vm = this;
  console.log('Is the form valid? - %s', vm.form.$valid);
}

Notez que pour que cela fonctionne, vous devez également disposer du bindToController: true attribut défini sur votre directive. Voir le documentation pour $compile and this question pour plus d'informations.

Parties pertinentes de la documentation:

require

Requiert une autre directive et injecte son contrôleur en tant que quatrième argument de la fonction de liaison. La propriété require peut être une chaîne, un tableau ou un objet:

Si la propriété require est un objet et que bindToController est véridique, les contrôleurs requis sont liés au contrôleur à l'aide des clés de la propriété require. Si le nom du contrôleur requis est identique au nom local (la clé), le nom peut être omis. Par exemple, {parentDir: '^parentDir'} est équivalent à {parentDir: '^'}.

7
Priidu Neemre

Faites de votre violon ce que je préférerais! Pour une raison quelconque, vous pouviez voir la chaîne "$ scope.ngForm" dans un fichier console.log, mais la consignation directe ne fonctionnait pas, ce qui entraînait l'indétermination. Cependant, vous pouvez l'obtenir si vous transmettez des attributs à la fonction contrôleur.

app.directive('myForm', function() {
return {
    restrict: 'A',
    controller: ['$scope','$element','$attrs', function($scope,$element,$attrs) {
        this.getForm = function() {
            return $scope[$attrs['ngForm']];
        }
    }]
}
});

http://jsfiddle.net/vZ6MD/20/

2
LobotomyKid