I've been playing with angular lately, so far so good, but im struggling with directives.
I'm trying to create a directive that generates the html mark up for a standard bootstrap form group with its corresponding validation messages.
So basically I'm trying to convert this:
<form-group label="Password">
<input type="password" data-ng-model="vm.password" name="password" id="password" class="form-control form-control-validate"
required data-ng-minlength="6"
data-required-error="Password is required" data-minlength-error="Your password must have at least 6 characters" />
</form-group>
into this:
<div class="form-group" data-ng-class="{'has-error': invalid}">
<label for="password" class="col-md-2 control-label">Password</label>
<div class="col-md-10">
<input data-ng-model="vm.password" type="password" id="password" name="password" class="form-control"
required data-ng-minlength="6"/>
<div data-ng-show="changePasswordForm.$dirty && changePasswordForm.oldPassword.$invalid">
<label data-ng-show="changePasswordForm.oldPassword.$error.required" class="label label-danger">
Password is required
<br />
</label>
<label data-ng-show="changePasswordForm.oldPassword.$error.minlength" class="label label-danger">
Your password must have at least 6 characters
</label>
</div>
</div>
</div>
So far this is what I have:
app.directive('formGroup', function () {
return {
templateUrl: 'app/directives/formGroup.html',
restrict: 'E',
replace: true,
transclude: true,
require: "^form",
scope: {
label: "@",
},
link: function (scope, element, attrs, formController) {
var input = element.find(":input");
var id = input.attr("id");
scope.for = id;
var inputName = input.attr("name");
// Build the scope expression that contains the validation status.
// e.g. "form.example.$invalid"
var inputInvalid = [formController.$name, inputName, "$invalid"].join(".");
scope.$parent.$watch(inputInvalid, function (invalid) {
scope.invalid = invalid;
});
}
};
});
formGroup.html:
<div class="form-group" ng-class="{ 'has-error': invalid }">
<label class="col-md-2 control-label" for="{{for}}">{{label}}</label>
<div class="col-md-10">
<div data-ng-transclude=""></div>
</div>
</div>
This sets correctly the bootstrap class "has-error" to the form-group if the input is invalid.
Now I want to add validation messages, and I couldn't find a way that works. This is what I have:
app.directive('formControlValidate', function () {
return {
templateUrl: "app/directives/formControlValidate.html",
restrict: 'C',
require: ["^form", "ngModel"],
scope: { },
transclude: true,
//replace: true,
link: function (scope, element, attrs, controls) {
var form = controls[0];
var inputName = attrs.name;
var inputErrors = [form.$name, inputName, "$error"].join(".");
scope.$parent.$watch(inputErrors, function (newValue) {
if (newValue) {
scope.errors = [];
angular.forEach(newValue, function (value, key) {
var error = attrs[key + 'Error'];
if (value && error) {
scope.errors.push(error);
}
});
}
}, true);
}
};
formControlValidate.html:
<div class="controls" ng-transclude></div>
<div data-ng-repeat="error in errors">
<div class="label label-danger">
{{error}}
</div>
</div>
But this doesn't work. I'm randomly changing parameters in both directives but can't figure out what how to make it work.
Any ideas or improvements would be greatly appreciated.
Thanks!
UPDATE: this is my latest gist (angular 1.3): https://gist.github.com/lpsBetty/3259e966947809465cbe
OLD solution:
I tried something similiar, maybe this link can help you too: http://kazimanzurrashid.com/posts/create-angularjs-directives-to-remove-duplicate-codes-in-form
This was my solution. I don't know why but I had to use form.$dirty, it was not possible to use input.$dirty.. (and I use angular-translate)
HTML:
<form-group input="form.password">
<input type="password" class="form-control" placeholder="{{ 'user.password' | translate }}" required
name="password" ng-model="user.password" />
</form-group>
Directive:
app.directive('formGroup', function ($parse) {
return {
restrict: 'E',
require: '^form',
transclude: true,
replace: true,
scope: {
cssClass: '@class',
input: '='
},
template: '<div class="form-group" ng-class="{\'has-error\':hasError, cssClass:true}">'+
'<div ng-transclude></div>' +
'<div ng-show="hasError">' +
'<span ng-repeat="(key,error) in input.$error" class="help-block"' +
'ng-show="input.$error[key]">{{\'form.invalid.\'+key | translate}}</span>' +
'</div>' +
'</div>',
link: function (scope, element, attrs, ctrl) {
var form = ctrl;
var input = attrs.input;
scope.$parent.$watch(input+'.$invalid', function (hasError) {
scope.hasError = hasError && form.$dirty;
});
}
};
});
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With