I'm trying to re-use code as much as I can so I'm wrapping ng-messages in a directive. However, the messages are not shown..
<form name="elementForm" novalidate>
<md-input-container>
<label>Category</label>
<input ng-required="true" ng-model="item.category" name="category" type="text">
<administration-input-validation control="category" formname="elementForm"></administration-input-validation>
</md-input-container>
</form>
Directive:
angular.module('app.analytics')
.directive('administrationInputValidation', function () {
return {
template:
'<div ng-messages>' +
'<div ng-message="required">This is required</div>' +
'</div>',
restrict: 'E',
replace: true,
require: '^form',
scope: {
control: '=',
formname: '='
},
link: function(scope, element, attrs) {
var formStr = attrs.formname + "." + attrs.control;
element.attr("ng-messages", formStr + "." + "$error");
}
};
});
I also tried hardcoding but it's still not showing
<div ng-messages="elementForm.category.$error">
UPDATE
Binding works now, but it's not showing the messages
Related
I have this input with lots of directives on it:
<input class="form-control"
mobile-number
limit-characters
number-only
dir="ltr"
ng-class="{'error-form-control': vm.form.step2.phoneNumber.$invalid && (vm.form.step2.phoneNumber.$dirty || vm.form.step2.phoneNumber.$touched)}"
required
ng-model="vm.formData.phoneNumber"
name="phoneNumber"
type="text">
How can I make it into a reusable directive like so:
<phone-number ng-model="vm.formData.phoneNumber"></phone-number>
and how can I use angular form validation with this directive?
I guees it will look like this
app.directive('phoneNumber', function(){
return {
scope: {
ngModel: '='
classValidation: '=',
inputName: '#'
},
template: ' <input class="form-control" mobile-number limit-characters number-only dir="ltr" ng-class="classValidation" required ng-model="ngModel" name="{{inputName}}" type="text">',
link: function(scope, elem, attr, ctrl) {
}
}
})
HTML
<phone-number ng-model="vm.formData.phoneNumber" input-name="phoneNumber" class-validation="{'error-form-control': vm.form.step2.phoneNumber.$invalid && (vm.form.step2.phoneNumber.$dirty || vm.form.step2.phoneNumber.$touched)}"></phone-number>
I simply want to add the class disable when form.$valid is false. The first submit button (not directive) obiouvsly works, but I don't know how to make visible the form state inside the directive. I cannot hardcode the form name in directive, it should be reusable in several forms..
<form id="tA" name="form" ng-controller="ctrl" ng-submit="form.$valid && save()" novalidate>
<input type="text" class="form-control" placeholder="title" name="name" ng-model="model.name" required ng-required="true">
<button class="btn btn-default" ng-class="{'disabled' : !form.$valid}" class="btn btn-default">Submit</button>
<button-raised disabled="!form.$valid">directive Submit</button-raised>
app.directive('buttonRaised', function() {
return {
restrict: 'E',
template: '<button class="mdl-button" ng-class="ngClass" ng-transclude></button>',
scope: {
ngModel: '='
},
transclude: true,
link: function($scope, el, $attrs,formCtrl) {
console.log(formCtrl)
$scope.ngClass = {
// 'disabled': $scope.$parent.formCtrl.$valid,
};
}
};
});
one way to do this is to pass your boolean as a parameter of your directive :
app.directive('buttonRaised', function() {
return {
restrict: 'E',
template: '<button class="mdl-button" ng-disabled="disabled" ng-class="ngClass" ng-transclude></button>',
scope: {
ngModel: '=',
disabled: '='
},
transclude: true,
link: function($scope, el, $attrs,formCtrl) {
console.log(formCtrl)
$scope.ngClass = {
// 'disabled': $scope.$parent.formCtrl.$valid,
};
}
};
});
You can change the directive and html as per this working this
The directive
app.directive('buttonRaised', function() {
return {
restrict: 'E',
template: '<button class="mdl-button" ng-class="{\'disabled\': disableButton}" ng-transclude></button>',
scope: {
disableButton: '='
},
transclude: true,
link: function($scope, el, $attrs,formCtrl) {
console.log(formCtrl)
}
};
});
The html
<form id="tA" name="form" ng-controller="ctrl" ng-submit="form.$valid && save()" novalidate>
<input type="text" class="form-control" placeholder="title" name="name" ng-model="model.name" required ng-required="true">
<button class="btn btn-default" ng-class="{'disabled' : !form.$valid}" class="btn btn-default">Submit</button>
// directive taking parameter disable-button
<button-raised disable-button="!form.$valid">directive Submit</button-raised>
<form-input label="Product Name" required name="name" model="ProdEdit.product.title"></form-input>
App.directive('formInput', function($timeout) {
var template = '<div class="form-group">'+
'<label for="{{id}}">{{label}}</label>'+
'<input type="text" required name="{{name}}" class="form-control" ng-model="model" id="{{id}}" placeholder="Enter {{label}}">'+
'<div ng-messages="myForm.name.$error" class="text-danger"></div>'+
'<span ng-message="required" >This is mandatory</span>'+
'</div>';
return {
restrict: 'E',
require: "^ngMessages",
template: template,
scope: {
label: "#",
model: "=",
name: "#",
required : "&"
},
replace: true
};
})
I created a form input directive like above. Now I want to show error message in span. Kindly help me out.
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;
});
}
};
});
I have a controller AppCtrl as
scope.transaction = {}
The index looks like
<div class="control-group">
<label class="control-label">Date</label>
<div class="controls">
<div class="control-group input-append date form_datetime">
<date-time-picker data-ng-model="transaction.date"></date-time-picker>
</div>
</div>
</div>
<div class="control-group">
<label class="control-label">Amount</label>
<div class="controls">
<div class="input-append">
<input type="text" name="transactionAmount" ng-model="transaction.amount" required>
</div>
and my custom directive looks like
angular.module('customDirectives', []).directive('dateTimePicker', function() {
return {
restrict: 'E',
replace: true,
scope: {
transaction['date']: '=' # COMPILATION ERROR HERE
},
template: '<div class="control-group input-append date form_datetime">'+
'<input type="text" readonly data-date-format="yyyy-mm-dd hh:ii" name="transactionDate" ng-model="transaction.date" data-date-time required>'+
'<span class="add-on"><em class="icon-remove"></em></span>'+
'<span class="add-on"><em class="icon-th"></em></span>'+
'</div>',
link: function(scope, element, attrs, ngModel) {
var input = element.find('input');
element.datetimepicker({
format: "yyyy-mm-ddThh:ii:ssZ",
showMeridian: true,
autoclose: true,
todayBtn: true,
pickerPosition: 'bottom-left'
});
element.bind('blur keyup change', function(){
console.log('binding element');
scope.$apply(date);
});
function date() {
console.log('setting date',input.val());
scope.ngModel = input.val();
}
date(); // initialize
}
}
});
I want to assign the date value from my directive to $scope.transaction.date but it is failing as compilation error, how can I achieve this?
scope: {
transaction['date']: '=' # COMPILATION ERROR HERE
},
Should be
scope: {
transactionDate: '='
},
And
<date-time-picker data-ng-model="transaction.date"></date-time-picker>
Should be
<date-time-picker transaction-date="transaction.date"></date-time-picker>
Then within your directive you can call scope.transactionDate = myValue;
within scope.$apply();
EDIT: If you want to use ng-model within your directive then you can use
....
restrict: 'E',
require: '?ngModel',
....
And
controller.$setViewValue(value); //this will in directive code where you want set the value of the ng-model bound variable.
In Html
<date-time-picker data-ng-model="transaction.date"></date-time-picker>