I created a directive to compare password and confirm password fields and show error message if it won't match.
(function () {
'use strict';
var compareTo = function () {
return {
require: "ngModel",
scope: {
otherModelValue: "=compareTo"
},
link: function (scope, element, attributes, ngModel) {
ngModel.$validators.compareTo = function (modelValue) {
return modelValue == scope.otherModelValue;
};
scope.$watch("otherModelValue", function () {
ngModel.$validate();
});
}
};
};
angular.module('StarterApp').directive("compareTo", compareTo);
})();
My HTML:
<form name="updatePwdForm" novalidate>
<md-input-container class="md-block">
<label for="password">Password:</label>
<input type="password" name="password" ng-model="ctrl.updatepassword.password" />
</md-input-container>
<md-input-container class="md-block">
<label for="confirmPassword">Confirm Password:</label>
<input type="password" name="confirmPassword" label="confirmPassword" ng-model="ctrl.updatepassword.confirmpassword" required
compare-to="ctrl.updatepassword.password" />
<div ng-messages="updatePwdForm.confirmPassword.$error" style="color:maroon" role="alert">
<div ng-message="required">Password and Confirm Password are not same!</div>
</div>
</md-input-container>
<md-button class="md-raised md-primary" ng-disabled="updatePwdForm.$invalid" ng-click="ctrl.updatePassword()">Update</md-button>
</form>
</md-content>
I am sure that my directive is comparing values and returning false if it won't match because my button is in disable mode still i type correct confirmpassword, but it's not showing my ngMessage.
am i missing something?
Thanks in advance.
Instead of
<div ng-message="required">
you should have
<div ng-message="compareTo">
Since the name of your validator is compareTo.
Related
I have used a directive to format number on textbox.
'use strict';
app.directive('numberOnly', function ($filter) {
return {
require: '?ngModel',
link: function (scope, elem, attrs, ctrl) {
if (!ctrl) {
return;
}
ctrl.$formatters.push(function () {
return $filter('number')(ctrl.$modelValue);
});
elem.bind('blur', function (event) {
var plainNumber = elem.val().replace(/[^\d|\-+|\.+]/g, '');
elem.val($filter('number')(plainNumber));
});
}
};
});
And in the html I was use:
<div class="row" ng-show="step === 1">
<div class="col-lg-6">
<label for="">Number1</label>
<div class="form-group">
<input type="text" class="form-control" number-only ng-model="mynumber1">
</div>
</div>
</div>
<div class="row" ng-show="step === 2">
<div class="col-lg-6">
<label for="">Number2</label>
<div class="form-group">
<input type="text" class="form-control" number-only ng-model="mynumber2">
</div>
</div>
</div>
In controller I set step = 1 as default. So when page loaded I see in textbox Number1 the value display as expected: 23,456
But when I change step = 2 to show the Number2 then the value in this textbox is 23456 (it not formatted).
Please let me know why it is and how to resolved it?
I have $scope.loaded=false; in controller. Seems like directive doesn't pick up the scope, because ng-show="loaded" still shows DIV. But when I click button and controller changes $scope.sent to true, directive gets scope(it changes class as seen in directive).
Why is directive not picking $scope.loaded?
Message directive is loaded in other view:
<form class="form-horizontal" ng-submit="submit()">
<fieldset>
<!-- Form Name -->
<legend>Contact us</legend>
<!-- Text input-->
<div class="form-group">
<label class="col-md-4 control-label" for="textinput">Your name</label>
<div class="col-md-4">
<input id="name" name="textinput" type="text" placeholder="name" class="form-control input-md" ng-model="model.name" required>
</div>
</div>
<!-- Text input-->
<div class="form-group">
<label class="col-md-4 control-label" for="textinput">Your email</label>
<div class="col-md-4">
<input id="from" name="textinput" type="email" placeholder="email" class="form-control input-md" ng-model="model.from" required>
</div>
</div>
<!-- Textarea -->
<div class="form-group">
<label class="col-md-4 control-label" for="textarea">Content</label>
<div class="col-md-4">
<textarea class="form-control" id="content" name="textarea" ng-model="model.content" required>your message to us</textarea>
</div>
</div>
<!-- Button -->
<div class="form-group">
<label class="col-md-4 control-label" for="singlebutton"></label>
<div class="col-md-4">
<button id="singlebutton" name="singlebutton" class="btn btn-primary" ng-click>Submit</button>
</div>
</div>
<message-directive></message-directive>
</fieldset>
</form>
I have a custom directive:
(function (angular) {
angular.module('app')
.directive('messageDirective', [function () {
return {
restrict: 'E',
templateUrl: 'partials/message-directive',
scope: true,
link: function (scope) {
scope.loaded = true;
}
};
}]);
})(angular);
TemplateUrl:
<div class="form-group">
<label class="col-md-4 control-label" for="message"></label>
<div class="col-md-4">
<div ng-show="loaded" ng-class="sent ? 'alert alert-success' : 'alert alert-danger'">
{{message}}
</div>
</div>
</div>
Controller:
(function (angular) {
angular.module('app')
.controller('contactController', [
'$scope', 'contactService', function ($scope, contactService) {
$scope.model = {};
$scope.loaded = false;
var successCallback = function () {
$scope.message = "Sent!";
$scope.sent = true;
}
var errorCallback = function () {
$scope.message = "Error!";
$scope.sent = false;
}
$scope.submit = function() {
contactService.createContact($scope.model).then(successCallback, errorCallback);
}
}]);
})(angular);
the directive load after the controller .so your code
link: function (scope) {
scope.loaded = true;
}
rewrite the $scope.loaded
return {
restrict: 'E',
templateUrl: 'partials/message-directive',
scope: false,
link: function (scope) {
scope.loaded = true;
}
};
I'm trying to understand what exactly is happening.
I had a form with Angular Material, something like:
<div layout-gt-sm="row">
<md-input-container class="md-block" flex-gt-sm>
<label>First name</label>
<input ng-model="user.firstName">
</md-input-container>
<md-input-container class="md-block" flex-gt-sm>
<label>Last Name</label>
<input ng-model="theMax">
</md-input-container>
</div>
... with the onfocus and onblur effecs + inputs layout full.
After that working, I change the form to work with directives and the form start to be like:
<div layout-gt-sm="row" class="core no-padding-bottom">
<!-- effect working -->
<md-input-container class="md-block" flex-gt-sm>
<md-icon><i class="material-icons">email</i></md-icon>
<input type="text" name="asd" placeholder="asd">
</md-input-container>
<!-- effect not working -->
<input-test name="name"></input-test>
</div>
... directive template:
<md-input-container class="md-icon-float md-block" flex-gt-sm md-no-float>
<md-icon class="material-icons">
<i class="material-icons">{{ attrs.icon }}</i>
</md-icon>
<input
type="text"
name="{{ attrs.name }}">
<div ng-messages="form[attrs.name].$error" role="alert">
<div ng-message="server">{{ form[attrs.name].$error.serverMessage }}</div>
</div>
</md-input-container>
... the directive:
app.directive('inputTest', function($injector, $q, $location) {
return {
replace: true,
restrict: 'AE',
scope: {
model: '=',
form: '='
},
templateUrl: baseViewPath('directives/form/text.html'),
link: function($scope, $element, $attrs) {
$scope.attrs = $attrs;
$scope.mask = function() {
if(typeof $attrs.mask !== 'undefined') {
return $attrs.mask;
}
};
}
};
});
Now, the form is working 50%, where is been send and validate as well but there's no Material effects and no full fields on screen.
What could that be?
UPDATE
I think that I "fixed" changing the placeholder to label
<label>{{ attrs.placeholder }}</label>
I have this html form:
<div class="modal-body">
<form name="addAdminForm">
<div class="form-group addPopupLabel">
<div class="container-fluid-full" id="email3">
<input placeholder="Email" type="text" ng-model="model.email" required />
</div>
<div class="container-fluid-full">
<input placeholder="Password (at last 6 characters)" type="password" ng-model="model.password1" id="pw1" name="pw1" required />
</div>
<div class="container-fluid-full">
<input placeholder="Confirm password" type="password" ng-model="model.password2" id="pw2" name="pw2" required password-compare="pw1" />
</div>
<div class="container-fluid-full">
<label>Admin</label>
<input type="radio" class="form-control" name="role" ng-model="model.role" ng-value="userRoles.admin">
</div>
<div class="container-fluid-full">
<label>CS</label>
<input type="radio" class="form-control" name="role" ng-model="model.role" ng-value="userRoles.cs">
</div>
<div>
<span class="errormessage" style="color:red">{{errormessage}}</span>
</div>
</div>
</form>
</div>
<div class="modal-footer">
<button class="btn btn-primary" ng-disabled="addAdminForm.$invalid" ng-click="createForm.$invalid || ok()">Submit</button>
<button class="btn btn-warning" ng-click="cancel()">Cancel</button>
</div>
The problem: button stay on disable mode while one of the fields is on focus.
How can i solve it by FormController?
The addAdminForm is scoped within the form. So one option is to move the buttons within the form, or move the form element so that it wraps the buttons. I would prefer this since it is dead simple and most times doable.
If it is not possible for some reason, you can make a directive to export the $invalid flag of a form to a variable of the outer scope. A simple implementation would be:
app.directive('bindValidity', ['$parse', function($parse) {
return {
restrict: 'A',
scope: false,
controller: ['$scope', '$attrs', function($scope, $attrs) {
var assign = $parse($attrs.bindValidity).assign;
if (!angular.isFunction(assign)) {
throw new Error('the expression of bindValidity is not settable: ' + $attrs.bindValidity);
}
this.setFormController = function(formCtrl) {
if (!formCtrl) {
throw new Error('bindValidity requires one of <form> or ng-form');
}
$scope.$watch(
function () {
return formCtrl.$invalid;
},
function (newval) {
assign($scope, newval);
}
);
};
}],
require: ['?form', '?ngForm', 'bindValidity'],
link: function (scope, elem, attrs, ctrls) {
var formCtrl, bindValidity;
formCtrl = ctrls[0] || ctrls[1];
bindValidity = ctrls[2];
bindValidity.setFormController(formCtrl);
}
};
}]);
Use it as:
<div class="modal-body">
<form name="addAdminForm" bind-validity="some.variable">
...
</form>
<div class="modal-footer">
<button ... ng-disabled="some.variable" ng-click="some.variable || ok()">Submit</button>
I am using a directive that validates number input boxes. I would like to change it to fit my needs. right now it allows me to enter numbers or text and throws the error message on button click. It also triggers the function. I would like it to instantly throw the error message when text is entered and I also would like to have the button disabled until both inputs are valid.
Sum it up, I need the error message triggered immediately on any text entered. and the button needs to be disabled until both fields are valid
<form class="form-horizontal">
<div class="form-group">
<div class="col-md-12">
<label for="inputEmail3" class="control-label">Price</label>
<input class="form-control input-sm" to-number id="Price" type="number" ng-model="customSubjectProperty.price" ng-pattern=" /^\d+$/" placeholder="Price">
</div>
</div>
<div class="form-group">
<div class="col-md-12">
<label class="control-label">Sqft</label>
<input class="form-control input-sm" to-number id="Sqft" type="number" ng-model="customSubjectProperty.sqft" ng-keypress="chartController.newInputKeyPress()" ng-pattern="/^\d+$/" placeholder="Sqft">
</div>
</div>
<div class="form-group">
<div class="col-md-12" style="margin-top:12px">
<button class="btn btn-primary btn-block btn-sm" ng-click="chartController.addSubjectPoint(customSubjectProperty)" ng-enter="chartController.addSubjectPoint(customSubjectProperty)">Add Point</button>
</div>
</div>
</form>
app.directive('toNumber', function () {
return {
require: 'ngModel',
link: function (scope, elem, attrs, ctrl) {
ctrl.$parsers.push(function (value) {
return parseFloat(value || '');
});
}
};
});
app.directive('toNumber', function () {
return {
require: 'ngModel',
link: function (scope, elem, attrs, ctrl) {
ctrl.$parsers.push(function (value) {
$(elem).on('keyup',callback),
return parseFloat(value || '');
$scope.apply()
});
}
};