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
?
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);
}
Vous n’avez peut-être besoin que d’un accès au NgModelController:
require: 'ngModel',
link: function(scope, element, attrs, ngModelCtrl) {
console.log(ngModelCtrl);
}
Si vous avez besoin d'accéder aux deux:
require: ['^form','ngModel'],
link: function(scope, element, attrs, ctrls) {
console.log(ctrls);
}
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) {
}]);
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.
Voici un jsFiddle simplifié et fonctionnel .
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);
}
};
});
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.
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:
Points négatifs:
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.
À 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: '^'}
.
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']];
}
}]
}
});