Hi I want to create a custom directive to do form validation. I have a form with checkboxes and some text fields. I want to make sure the user doesn't leave any of the fields empty.
When the user leaves a form empty after pressing submit, I want the directive to highlight the border of the field red. My problem is that when I make an isolate scope directive it doesn't work. When it isn't an isolate scope, all the fields turn red when only one is empty. How can I fix this?
directive.js:
directive('createprofileformerrormsg', function() {
return {
scope:{createprofileformerrormsg:'#'},
restrict: 'A',
require: 'ngModel',
link: function(scope, elem, attr, ngModel) {
scope.$watch('formErrors', function() {
if (attr.createprofileformerrormsg == 1) {
elem.css("border", "red solid 1px");
}
});
}
}
});
form.html
<form data-ng-submit="createProfile()">
Ethnicity: <select createprofileformerrormsg="{{formErrors.ethnicity}}" data-ng-
model="ethnicity" >
<option value="Asian">Asian</option>
<option value="Black">Black</option>
<option value="Caucasian">Caucasian</option>
<option value="Hispanic">Hispanic</option>
<option value="Middle Eastern">Middle Eastern</option>
<option value="Native American">Native American</option>
<option value="Other ethnicities">Other ethnicities</option>
</select><br/>
Gender: <select createprofileformerrormsg="formErrors.ethnicity" data-ng-
model="gender">
<option value="Male">Male</option>
<option value="Female">Female</option>
</select>
</form>
Controller.js
$scope.createProfile = function() {
if ($scope.ethnicity == null) {
$scope.formErrors.ethnicity = 1;
error_count++;
}
if ($scope.gender == null) {
$scope.formErrors.ethnicity = 1;
error_count++;
}
}
Try this:
Javascript
app.controller('MainCtrl', function($scope) {
$scope.submit = function() {
$scope.$broadcast('submit');
}
})
.directive('highlightOnError', function() {
return {
restrict: 'A',
require: 'ngModel',
link: function(scope, element, attrs, ngModel) {
scope.$on('submit', function() {
var border = '';
if (ngModel.$invalid)
border = 'red solid 1px';
element.css('border', border);
});
}
};
});
HTML
<body ng-controller="MainCtrl">
<form ng-submit="submit()" novalidate>
<input type="text" ng-model="foo" required highlight-on-error />
<select ng-model="bar" ng-options="option for option in [1, 2, 3]"
required highlight-on-error>
<option value="">choose a number</option>
</select>
<button type="submit">Submit</button>
</form>
</body>
Working Plunker here.
Related
How to add conditionally attribute in angularjs?
For example I only want to set the multiple attribute on a <select> if my component has a binding set to true. This means if the binding is not given the multiple attribute should not exist on my <select>.
The only solution I found was with ng-if.
You can achieve this by implementing a directive (aka ng-multiple) to handle the property multiple of the select element. The following snippet implements this solution. However, you may want to control the state of your model inside this directive, once the multiple prop will produce an array of selected values, the non-multiple will produce a single object, so this may cause an issue when switching between multiple and non-multiple.
angular.module('myApp', [])
.directive('ngMultiple', function () {
return {
require: ['select', '?ngModel'],
restrict: 'A',
scope: {
multiple: '=ngMultiple'
},
link: function (scope, element, attrs, ctrls) {
element.prop('multiple', scope.multiple);
scope.$watch('multiple', function (multiple) {
if(element.prop('multiple') != multiple){
// may be would be convenient change the ngModel
// to [] or {} depending on the scenario
element.prop('multiple', multiple);
}
});
}
};
})
.controller('myController', function ($scope) {
$scope.condition = true;
$scope.myOptions = [];
});
angular.element(document).ready(function () {
angular.bootstrap(document, ['myApp']);
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
<div ng-controller="myController">
<label>
<input type="checkbox" ng-model="condition" /> multiple?
</label>
<br>
<select ng-multiple="condition" ng-model="myOptions">
<option>Option 1</option>
<option>Option 2</option>
<option>Option 3</option>
<option>Option 4</option>
<option>Option 5</option>
</select>
<br>
<tt>myOptions: {{ myOptions }}</tt>
</div>
if boolean condition is true then multiple, else not
<select ng-if="condition" ng-model="some.model" multiple></select>
<select ng-if="!condition" ng-model="some.model"></select>
<select ng-show="condition" ng-model="some.model" multiple></select>
<select ng-hide="condition" ng-model="some.model"></select>
controller,
$scope.condition = true
This question already has answers here:
How do I set default value of select box in angularjs
(7 answers)
Closed 7 years ago.
I have a drop down using angularjs but at initial loading it showing 'nothing selected' ,I want select First value default.
<select id="cbo-db-selection" ng-init="selectedlibrary=selectedlibrary[0].value" ng-model="selectedlibrary" required="required" ng-options="opt.DatabaseName as opt.DatabaseName for opt in DBLibraries">
<option style="display:none" value="">{{selectedlibrary}}</option>
</select>
var app = angular.module('myApp', []);
app.controller('myCtrl', function ($scope) {
$scope.users = [
{id: 1, name: 'Me'},
{id: 2, name: 'You'}
];
});
app.directive('select', function () {
return {
restrict: 'E',
require: '?ngModel',
priority: 10,
link: function postLink(scope, elem, attrs, ctrl) {
if (!ctrl) { return; }
var originalRender = ctrl.$render.bind(ctrl);
ctrl.$render = function () {
originalRender();
if (elem.val() === '?') {
ctrl.$setViewValue(undefined);
}
};
}
};
});
.error {
color: Red;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.16/angular.min.js"></script>
<div ng-app="myApp" ng-controller="myCtrl" ng-form="form1">
<select ng-model="userId">
<option ng-repeat="user in users"
value="{{user.id}}">
{{user.name}}
</option>
</select>
<div class="error" ng-show="form1.select1.$invalid">Required field !</div>
<div class="error" ng-show="form1.$invalid">Invalid form !</div>
<br />
<button ng-click="userId=1;">Set valid value</button>
<button ng-click="userId=3;">Set invalid value</button>
<hr />
<pre>userId: {{userId}}</pre>
</div>
I have a directive that wraps a form element with some inputs. One of the options is passing in a formName. Usually, with a form with the example name of myForm, to show an error you would do something like myForm.firstName.$error.required.
But, how do I get access to the errors when the form name is dynamically being passed in to the directive?
example usage
<my-custom-form formName='myForm' formSubmit='parentCtrl.foo()'></my-custom-form>
directive
angular.module('example')
.directive('myCustomForm', [
function() {
return {
restrict: 'E',
replace: true,
templateUrl: 'myCustomForm.directive.html',
scope: {
fornName: '#',
formSubmit: '&'
},
require: ['myCustomForm', 'form'],
link: function(scope, element, attrs, ctrls) {
var directiveCtrl = ctrls[0];
var formCtrl = ctrls[1];
scope.data = {};
scope.hasError = function(field) {
// how do i show the errors here?
};
scope.onSubmit = function() {
scope.formSubmit();
};
}
};
}]);
template
<form name="{{ formName }}" ng-submit="onSubmit()" novalidate>
<div class="form-group" ng-class="{'is-invalid': hasError('fullName') }">
<input type="text" name="fullName" ng-model="data.full_name" required />
<div ng-show="hasError('fullName')">
<p>How do I show this error?</p>
</div>
</div>
<div class="form-group" ng-class="{'is-invalid': hasError('email') }">
<input type="text" name="email" ng-model="data.email" ng-minlength="4" required />
<div ng-show="hasError('email')">
<p>How do I show this error?</p>
</div>
</div>
<button type="submit">Submit</button>
</form>
I think the only problem with your code is that the directive requires itself, I don't think that will work. Just removing the myCustomForm from the require works fine.
To check if the field has errors, you just need to check if the $error object in the form controller is empty.
require: ['form'],
link: function(scope, element, attrs, ctrls) {
var formCtrl = ctrls[0];
scope.data = {};
scope.hasError = function(field) {
// Field has errors if $error is not an empty object
return !angular.equals({}, formCtrl[field].$error);
};
Plunker
I want to populate the select dropdown from database. Right now the data is coming from scope array.
html:
<div ng-controller="DropDownController">
Country:
<select id="country" class="input-sm" name ="country" ng-model="states" ng-options="country for (country, states) in countries" drop-down required>
<option value=''>Select</option>
</select>
States: <select id="state" class="input-sm" name="state" ng-disabled="!states" ng-model="cities" ng-options="state for (state,city) in states" required>
<option value=''>Select</option></select>
City: <select id="city" class="input-sm" name="city" ng-disabled="!cities || !states" ng-model="city" required>
<option value=''>Select</option>
<option ng-repeat="city in cities" value='{{city}}'>{{city}}</option></select>
</div>
Directive:
app.directive('DropDown', function ($http) {
return {
restrict: 'A',
require: 'ngModel',
link: function (scope, element, attrs, ngModel) {
$http.get('DropDown.do').success(function (data) {
if (data) {
}
});
}
};
});
I am not sure above directive is the right way of approach for my requirement. The servlet or url is not being called when I click on the drop down option. How do I achieve the same? I am still a beginner in angular.
Directives in Angular are used when you want to more easily interact with the DOM, ajax calls have no business there. You should rather use a service or a factory to process the asynchronous request and then simply return the result in the controller for further manipulations. A promise will also be needed since we are dealing with asynchronous jobs.
'use strict';
var app = angular.module('app', []);
app.factory('countryFactory', function($http) {
var getCountries = function() {
return $http.get('ajax.php').success(function(data) {
return data;
})
}
return { getCountries: getCountries }
})
app.controller('DropDownController', function($scope, countryFactory) {
var ajaxPromise = countryFactory.getCountries();
// Promises are executed once $http is done with the asynchronous job
ajaxPromise.then(function(result) {
$scope.countries = result.data;
})
})
The server side (ajax.php) is simply returning an array, here you should replace it with whatever information from the database you are needed
<?php
echo json_encode(array(
array('id' => 1, 'name' => 'USA'),
array('id' => 2, 'name' => 'Australia'),
));
Instead of using a directive, for select and respectively option elements we can use ng-options so the view would look like this:
<div ng-controller="DropDownController">
<select ng-options="country.name for country in countries track by country.id" ng-model="selected">
</div>
check this snippet you had error in directive declaration its camel case , so it should be dropDown not DropDown!
var app = angular.module('app', []);
app.directive('dropDown', function ($http) {
return {
restrict: 'A',
require: 'ngModel',
link: function (scope, element, attrs, ngModel) {
//replace with your call
//$http.get('DropDown.do').success(function (data) {
//// if (data) {
//scope.countries = data;
//}
//});
scope.countries =
{
'USA':
{
'Alabama': ['Montgomery', 'Birmingham'],
'California': ['Sacramento', 'Fremont'],
'Illinois': ['Springfield', 'Chicago']
},
'Australia':
{
'New South Wales': ['Sydney'],
'Victoria': ['Melbourne']
}
};
}
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<body ng-app="app">
Selected country : {{states}}
<select id="country" class="input-sm" name="country" ng-model="states" ng-options="country for (country, states) in countries" drop-down required>
<option value=''>Select Country</option>
</select>
</body>
I would like to set the validity of a form element based on a custom boolean value. Consider the following password fields:
<input type="password" name="password" ng-model="user.password" required>
<input type="password" name="passwordRepeat" ng-model="user.passwordRepeat" required>
I would like to mark the second input field valid if the repeated password matches the original password. Something like:
<input type="password" name="passwordRepeat" ng-model="user.passwordRepeat" my-validation-check="user.passwordRepeat === user.password" required>
I was not able to find any Angular directive for this purpose. Any ideas? Perhaps create my own directive for this? Unfortunately, I'm not an Angular expert... it should be something like this:
angular.module('app').directive('myValidationCheck', function() {
return {
scope: true,
require: 'ngModel',
link: function(scope, elm, attrs, ngModel) {
// eval and watch attrs.myValidationCheck
// and use ngModel.$setValidity accordingly
}
};
});
Thanks!
I have spent quite a bit of time finding the best answer based on your answers below (thanks a lot!). What did the trick for me was simply:
angular.module('myApp').directive('myValidationCheck', function() {
return {
scope: {
myValidationCheck: '='
},
require: 'ngModel',
link: function(scope, elm, attrs, ngModel) {
scope.$watch('myValidationCheck', function(value) {
ngModel.$setValidity('checkTrue', value ? true : false);
});
}
};
});
for
<input type="password" name="passwordRepeat" my-validation-check="user.password === user.passwordRepeat" ng-model="user.passwordRepeat" required>
And this is really flexible. You can use anything you want in my-validation-check, e.g. make sure a checkbox is checked or any more complex expression is true.
Hope this helps not just myself.. :-)
Why do you need special directive for it?
Why not make so:
<div ng-controller="MyCtrl">
<form name="myForm" ng-submit="processForm()">
<input type="password" ng-model="password" placeholder="password" required/>
<input type="password" ng-model="repeatedPassword" placeholder="repeat password" required/>
<input type="Submit" value="Submit" ng-disabled="passwordsMissmatched()"/>
<span ng-show="passwordsMissmatched()">
Password mismatched
</span>
</form>
</div>
And your JS:
function MyCtrl($scope) {
$scope.passwordsMissmatched = function(){
return $scope.password && $scope.repeatedPassword
&& ($scope.password != $scope.repeatedPassword);
}
$scope.processForm = function(){
if($scope.password == $scope.repeatedPassword){
alert("Form processing..");
}
};
}
This approach should work like a charm.
I've created JSFiddle for you.
Please see demo below
var app = angular.module('app', []);
app.directive('mcheck', function() {
return {
require: 'ngModel',
link: function(scope, elm, attrs, ngModel) {
scope.$watch(attrs.ngModel, function(value) {
if (value == attrs.mcheck) {
ngModel.$setValidity('notEquals', true);
} else {
ngModel.$setValidity('notEquals', false);
}
});
}
};
});
app.controller('fCtrl', function($scope) {
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app">
<div ng-controller="fCtrl">
<form novalidate name="login">
<input type="text" name="password" ng-model="user.password" mcheck="{{user.passwordRepeat}}" required>
<input type="text" name="passwordRepeat" ng-model="user.passwordRepeat" mcheck="{{user.password}}" required>
<HR/>
<span ng-show="login.password.$error.notEquals && login.passwordRepeat.$error.notEquals && login.$dirty">Passwords are not equal</span>
<HR/>
</form>
</div>
</div>