I am new to angular.js
<input type="email" disabled='disabled' name="email" ng-model="userInfo.emailAddress"
class="input-xlarge settingitem" style="height:30px;">
<span ng-show="myForm.email.$error.email" class="help-inline" style="color:red;font-size:10px;">
Not a Valid Email Address</span>
I have email field, corresponding to which i need to check at server if it already exists in database or not.
Can anyone guide me through the steps how it can be done using angular directives and services for checking at server
I would suggest writing a directive that would plug into NgModelController#$parsers pipeline (check "Custom Validation" from http://docs.angularjs.org/guide/forms).
Here is a sketch of such a directive:
.directive('uniqueEmail', ["Users", function (Users) {
return {
require:'ngModel',
restrict:'A',
link:function (scope, el, attrs, ctrl) {
//TODO: We need to check that the value is different to the original
//using push() here to run it as the last parser, after we are sure that other validators were run
ctrl.$parsers.push(function (viewValue) {
if (viewValue) {
Users.query({email:viewValue}, function (users) {
if (users.length === 0) {
ctrl.$setValidity('uniqueEmail', true);
} else {
ctrl.$setValidity('uniqueEmail', false);
}
});
return viewValue;
}
});
}
};
}])
Where the Users.query is an async call to check if an email is unique or not. Of course you should substitute this with a call to your back-end.
When done, this directive can be used like so:
<input type="email" ng-model="user.email" unique-email>
Example of this directive was taken from the angular-app that some of the AngularJS community members try to put together to illustrate common use-cases. It might be worth checking out to see how all this fits together in the complete app.
Related
I am trying to developed custom validation on input controls. For this I am planning to write attribute directives and link directives to the controls.
<input validpc type="text" id="pc" class="form-control" name="PC" ng-model="answers.pc" required>
<span class="form-error" ng-show="submitted && DTOstep1[que.QuestionData._attributeName].$error.required && DTOstep1[que.QuestionData._attributeName].$invalid">The field is required.</span>
like validpc is a directive.
this will check many many criteria and will return error accordingly. like if entered value is
0, error message can be : 0 is not a valid entry.
1, error message can be : Number should be greater than 50.
if input is 100 then it should return true. Here criteria is wrote only for example purposes.
Here I am stuck how to write directive in this way which will used for vallidation and send error messages. I dont want to write so many ng-messages html tag in index file.
function validpc() {
return {
restrict: 'A',
require: 'text-control-dir',
link: function (scope, element, attrs, ngModel) {
console.log('here in validPc');
}
}
I am using angularjs in my project. In a process/module, the form will not be submitted if a certain data input is already exist in the database. For example, this process: registering /Signing up. If the user inputted a username that's already been used by someone, the form will not be submitted. And it will be checked in a controller where the list of usernames has been loaded prior to user entering the data by comparing (in a for loop). My question is, is this a good way of checking the data or do I have to use $http?
Edit:
<div class="col-xs-6 col-sm-6 col-md-6">
<div class="form-group"><label class="control-label">Username</label>
<input type="text" ng-model="reg.username" usernameAvailable
name="username" class="form-control input-md"
placeholder="Username" required />
<div ng-if="regForm.$pending.usernameExists">checking....</div>
<div ng-if="regForm.$error.usernameExists">username exists
already
</div>
</div>
</div>
mainApp.directive('usernameAvailable', function($timeout, $q, $http) {
return {
restrict: 'AE',
require: 'ngModel',
link: function(scope, elm, attr, model) {
model.$asyncValidators.usernameExists = function() {
//here you should access the backend, to check if username exists
//and return a promise
// var defer = $q.defer();
// $timeout(function(){
// model.$setValidity('usernameExists', true);
// defer.resolve;
// }, 1000);
// return defer.promise;
console.log(model);
return $http.get(BASE_URL+'Register/validate?u='+username).
then(function(res){+$timeout(function(){
model.$setValidity('usernameExists', !!res.data);
}, 1000);
});
};
}
}
});
Php controller:
public function validate(){
$this->load->model('account_model');
$data =$this->account_model->exist($this->input->get('u'));
echo json_encode($data);
}
You should take the data to the server. And there, should fire a query in db to check if this data already exists. If it does then show error message on UI and do not save other wise save it with success message.
Fire query (sample) Something like :
SELECT * FROM users WHERE username='myName'
If records are more than 0, then what you have is a repeated value.
Do not fetch all the records on UI and then loop through them.
Think of :
What if there are 1 Million or more records?
Security ? (You are getting all the user names to client)
And other such things.
Hope it guides you.
I am using validation on AngularJS side as well as serve side (e.g. for duplicate value check) and would like to expose these errors to users. Without ng-fab-form I was able to build custom server error directive and use it like this:
<input type="number" id="level" name="level" ng-model="vm.record.level"
server-error
required>
<div ng-messages="vm.form.role_level.$error">
<p ng-message="server">{{ vm.errors.level }}</p>
</div>
But the goal of the library is to get rid of this kind of duplication. As you can see I am using Controller as syntax and assigning the errors to each field, when saving/updating the model fails:
angular.forEach(result.data.errors, function (errors, field) {
vm.form[field].$setValidity('server', false);
vm.errors[field] = errors.join(', ');
});
I customised validation template to show messages for server error, however, I am not able to display dynamic error text. I suppose the problem is related to scope inheritance. Any ideas how could I achieve the desired effect?
I just found a way how to do it:
Add custom attribute, to hold the server error message value and watch the error in it.
<input type="number" id="level" name="level" ng-model="vm.record.level"
validation="{{ vm.errors.level }}
server-error
required>
In validation template display the attribute value.
<li ng-message="server">{{ attrs.validation }}</li>
I would recommend using $asyncValidators instead of $setValidity from angular#1.3.2 on.
app.directive('username', function($q, $timeout) {
return {
require: 'ngModel',
link: function(scope, elm, attrs, ctrl) {
var usernames = ['Jim', 'John', 'Jill', 'Jackie'];
ctrl.$asyncValidators.username = function(modelValue, viewValue) {
if (ctrl.$isEmpty(modelValue)) {
// consider empty model valid
return $q.when();
}
var def = $q.defer();
$timeout(function() {
// Mock a delayed response
if (usernames.indexOf(modelValue) === -1) {
// The username is available
def.resolve();
} else {
def.reject();
}
}, 2000);
return def.promise;
};
}
};
});
(example taken from the docs)
Then you could just add the message to your own validations template:
angular.module('exampleApp', [
'ngFabForm'
])
.config(function (ngFabFormProvider)
{
ngFabFormProvider.extendConfig({
validationsTemplate : 'path/to/your-fabulous-validation-template.html'
});
});
And in the template add:
<li ng-message="username">I'm not valid!!! ;(</li>
There also was an issue about that on the github-page of the module: https://github.com/johannesjo/ng-fab-form/issues/34
For a cleaner solution there would have to be an interface, which doesn't exist yet, but you could always open an issue for that.
I have a directive that receives whether an element should be required or not from a REST api. Right now, I can't seem to get the form to invalidate when an attribute is set to required.
So, in essence I'm able to dynamically add the 'required' attribute from the directive below, but it doesn't invalidate the form. Looking through chrome I see that, even though the required attribute exists, a required entry in the $error array doesn't exist.
app.directive('requireiftrue', function ($compile) {
return {
require: '?ngModel',
link: function (scope, el, attrs, ngModel) {
if (!ngModel) {
return;
}
if(attrs.requireiftrue==="true"){
console.log('should require');
el.attr('required', true);
$compile(el.contents())(scope);
}
else{
console.log('should not require');
}
}
};
});
Here's a jsfiddle to illustrate the problem. And, here's sample JSON returned from my rest API
{
race: false,
martialStatus: true,
}
EDIT: While the accepted answer got me up and running, I still had a good bit of finagling to do.
Namely:
1. Resolving a deferred promise to ensure that my form actually receives the required fields to validate
2. observing my 'requireiftrue' attribute
My solution
module config:
function config($stateProvider) {
$stateProvider
.state('testState', {
url: '/test/form',
controller: 'TestCtrl',
templateUrl: 'test/form/testForm.tpl.html',
resolve: {
formDefaultService: function getFormDefaults($q, dataservice) {
// Set up a promise to return
var deferred = $q.defer();
var myData = dataservice.getFormDefaults();
deferred.resolve(myData);
return deferred.promise;
//return
}
},
data: {
pageTitle: 'Test Info'
}
});
}
And, finally the directive / HTML that receives api data:
Directive:
.directive('requireiftrue', function ($compile) {
return {
require: '?ngModel',
link: function (scope, el, attrs, ngModel) {
if (!ngModel) {
return;
}
attrs.$observe('requireiftrue', function(value){
if(value==="true"){
el.attr('required', true);
el.removeAttr('requireiftrue');
$compile(el[0])(scope);
}
});
}
};
});
HTML:
<input max="40"
requireiftrue={{defaults.validation.name}}
validNumber
id="name"
name="name"
type="text"
ng-model="test.name"
class="form-control">
You had two issues:
The first is el.contents() returned an empty array. so The first thing you should do is change it to el[0]. But had el.contents() worked you would hav had a much more serious problem. You would have been trying to compile a directive that has itself as a directive which would lead to an infinite loop (well until the browser crashed any way).
So here is the revised code:
var app = angular.module('form-example', []);
app.directive('requireiftrue', function ($compile) {
return {
require: '?ngModel',
link: function (scope, el, attrs, ngModel) {
if (!ngModel) {
return;
}
if(attrs.requireiftrue==="true"){
console.log('should require');
el.attr('required', true);
el.removeAttr('requireiftrue');
$compile(el[0])(scope);
}
else{
console.log('should not require');
}
}
};
});
I should note however that now this directive is a one-off. If the model will change, the directive will not be on the element any longer to deal with it.
Instead of using a directive, use ng-init to initialize requireiftrue.
and assign this value to ng-required like ng-required="requireiftrue" as shown below. As you said you are getting the data from rest api, you can initialize requireiftrue with the value you are getting from api, instead of true or false as shown in example below.
Hope this helps you.
Updated fiddle
http://jsfiddle.net/zsrfe513/3/
<form ng-app="form-example" name='fo' class="row form-horizontal" novalidate>
<div class="control-group" ng-form="testReq">
<h3>Form invalid: {{testReq.$invalid}}</h3>
<label class="control-label" for="inputEmail">Email</label>
<div class="controls" ng-init='requireiftrue = true'>
<input id="inputEmail" placeholder="Email" ng-model="email" name='ip' ng-required='requireiftrue'>
<span ng-show="testReq.ip.$dirty && testReq.ip.$error.required">
Required.
</span>
</div>
</div>
</form>
Try:
1. adding the required directive to the input you want to apply validation to
<input id="inputEmail" class="requireiftrue" placeholder="Email"
ng-model="email" requireiftrue="true" required>
2 Defining the directive as type class and adding the directive class to the HTML input field
JS
app.directive('requireiftrue', function ($compile) {
return {
restrict: 'C',
require: '?ngModel',
.....
HTML
<input id="inputEmail" class="requireiftrue" placeholder="Email" ng-model="email" requireiftrue="true" required>
here is a update of your fiddle - http://jsfiddle.net/4fb6wg30/
You just need to add the "required" attribute to the input.
<input max="40"
requireiftrue={{defaults.validation.name}}
validNumber
id="name"
name="name"
type="text"
ng-model="test.name"
class="form-control"
required="required">
I used <input ng-required="true"> worked fine for my angular validation component.
If your using the new angular component make sure to pass in required: "#" instead of required: "="
scope: {
required: '#'
}
I also took this further and required integer and min/max validation the same way.
I have been playing with angularjs for couple of days and like it so far. I am trying to build a chrome extension which attaches a small widget below every gmail message when the user is on gmail.com. So far so good. As part of authentication code, I handle 401 error in this way. Whenever there is a 401 error, I use $location.path( "/login" ) to redirect the user to the login screen/template. This changes browser address bar which seems to be the default behavior. So, if the current address was https://mail.google.com/mail/u/0/, it becomes https://mail.google.com/mail/u/0/#/login. But mine is not standalone app, its more like widget that attaches to a div when on gmail.com site. My app should not mess with the browser address bar. I am now starting to think if I can really use angularjs for my app as I am going against the default behavior. Should I use angularjs at all?
I also posted it here
https://groups.google.com/forum/?fromgroups#!topic/angular/TrT54r_IYmg
You can emit/broadcast events on rootScope and subscribe to them in your login directive.
Here is little clue http://www.espeo.pl/2012/02/26/authentication-in-angularjs-application
it uses interceptors to catch 401
myapp.config(function($httpProvider) {
var interceptor = ['$rootScope','$q', function(scope, $q) {
function success(response) {
return response;
}
function error(response) {
var status = response.status;
if (status == 401) {
var deferred = $q.defer();
var req = {
config: response.config,
deferred: deferred
}
scope.requests401.push(req);
scope.$broadcast('event:loginRequired');
return deferred.promise;
}
// otherwise
return $q.reject(response);
}
return function(promise) {
return promise.then(success, error);
}
}];
$httpProvider.responseInterceptors.push(interceptor);
});
And simplest directive could be this
myapp.directive("loginForm",function($http){
return function(scope,element,attrs){
element.hide();
scope.$root.$on('event:loginRequired', function(event) {
element.show();
});
scope.login=function(){
// You can set controller for this directive, but I skiped that part for sake of simplicity
var payload = $.param({username: scope.username, password: scope.password});
var config = {
headers: {'Content-Type':'application/x-www-form-urlencoded; charset=UTF-8'}
}
$http.post('/some/login/url', payload, config).success(function(data) {
if (data === 'AUTHENTICATION_SUCCESS') {
element.hide();
}else{
alert("username or password was wrong, please try again");
elements.find("form").reset(); // reset form, or you coud reset just password field
}
});
};
};
});
Now, directive in action
<div login-form>
<form ng-submit="login()">
<label for="username">Username</label>
<input type="text" id="username" ng-model="username"/>
<br />
<label for="password">Password</label>
<input type="password" id="password" ng-model="password" />
<hr/>
<input type="submit" value="Login" />
</form>
</div>
Please note code above is not tested, probably there is some misspell or something. But let me know if you have trouble implementing this, I will try to take some time and effort to make it work.