Server errors for AngularJS ng-fab-form - javascript

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.

Related

validating data in database in angularjs

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.

AngularJS dynamic required attribute in directive and form validation

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.

AngularJS : Compile directives inside HTML returned by an API

So I have access to a REST API that I am hitting, that returns the following pre-generated HTML:
<p class="p">
<sup id="John.3.16" class="v">16</sup>
<span class="wj">“For </span>
<span class="wj">God so loved </span>
<span class="wj">the world,</span>
<span class="wj">that he gave his only Son, that whoever believes in him should not </span>
<span class="wj">perish but have eternal life.</span>
</p>
This has presented an interesting new challenge for me in my learning of AngularJS. I have no control over the HTML that is returned from the API, since it's not an API that I built.
What I'm trying to do (and this could be the completely wrong approach) is to build a class directive on the "v" class, so that I can add an ng-click attribute to the verse number and pass the verse information on to another part of my application.
Below is the code I currently have, which doesn't seem to do anything, though I thought it would.
var app = angular.module('ProjectTimothy');
app.filter("sanitize", ['$sce', function($sce) {
return function(htmlCode){
return $sce.trustAsHtml(htmlCode);
}
}]);
app.controller("timothy.ctrl.search", ['$scope', '$http', function($scope, $http){
$scope.url = "http://apiendpoint.com/";
$scope.copyright = "";
$scope.search = function() {
// Make the request to the API for the verse that was entered
// Had to modify some defaults in $http to get post to work with JSON data
// but this part is working well now
$http.post($scope.url, { "query" : $scope.searchwords, "version": "eng-ESV"})
.success(function(data, status) {
// For now I'm just grabbing parts of the object that I know exists
$scope.copyright = data.response.search.result.passages[0].copyright;
$scope.result = data.response.search.result.passages[0].text;
})
.error(function(data, status) {
$scope.data = data || "Request failed";
$scope.status = status;
});
};
}]);
app.directive("v", ['$compile', function($compile) {
return {
restrict: 'C',
transclude: true,
link: function(scope, element, attrs) {
element.html("<ng-transclude></ng-transclude>").show();
$compile(element.contents())(scope);
},
scope: { id:'#' },
/*template: "<ng-transclude></ng-transclude>",*/
replace: false
};
}]);
HTML Template that is being populated with the HTML returned by API:
<div class="bible_verse_search_container" ng-controller="timothy.ctrl.search">
<div class="input-group">
<input type="text" class="form-control" placeholder="Bible Verse To Read (i.e. John 11:35)" ng-model="searchwords">
<span class="input-group-btn">
<button class="btn btn-default" type="button" ng-click="search()">Search</button>
</span>
</div>
<div class="well" ng-show="copyright" ng-bind-html="copyright | sanitize"></div>
<div ng-bind-html="result | sanitize"></div>
</div>
So What I was hoping would happen would be that the HTML is populated into the bottom div that binds the html, and then somehow $compile would be called to convert the "v" class sup's into directives that I can modify. Again, I'm pretty new to Angular, so there may be a super easy way to do this like most other things in Anguler that I just haven't found yet.
Really, the end goal is that each verse number is converted into a directive of its own to be able to make it clickable and access the id attribute that it has so that I can send that information with some user content back to my own API.
This feels like a lot of information, so let me know if anything is unclear. I'll be working on it, so if I figure it out first, I'll be sure to update with an answer.
IN PROGRESS
Checked out this question: https://stackoverflow.com/a/21067137/1507210
Now I'm wondering if it would make more sense to try and convert the section where the verse is displayed into a directive, and then making the search controller populate a scope variable with the HTML from the server, and then use that as the template for the directive... think think think
I think your second approach--convert the section where the verse is displayed into a directive--would be a nice way of doing it.
You could replace this:
<div ng-bind-html="result | sanitize"></div>
with a directive like this:
<verse-display verse-html="{{result}}"></verse-display>
The directive definition would look like this:
app.directive('verseDisplay', ['$compile', function($compile) {
function handleClickOnVerse(e) {
var verseNumber = e.target.id;
// do what you want with the verse number here
}
return {
restrict: 'E',
scope: {
verseHtml: '#'
},
replace: true,
transclude: false,
template: '<div ng-bind-html="verseHtml | sanitize"></div>',
link: function(scope, element, attrs) {
$compile(element.contents())(scope);
element.on('click', '.v', handleClickOnVerse);
}
};
}]);
So you could apply your own click handler to the element.
Here's a fiddle. (Open the console to see the verse number getting logged out.)
Probably the most unwisest of things I have posted here, but it's pretty cool code. I do not know if I recommend actually running this, but here's a jsfiddle
So one of the reasons I call this unwise is because the injected code will run any directives you have not just the one you wanted. There may be many other security risks beyond that as well. But it works fantastically. If you trust the HTML you are retrieving then go for it.
Check out the fiddle for the rest of the code:
function unwiseCompile($compile) {
return function (scope, element, attrs) {
var compileWatch = scope.$watch(
function (scope) { return scope.$eval(attrs.unwiseCompile); },
function (unwise) {
element.html(unwise);
$compile(element.contents())(scope);
// for better performance, compile once then un-watch
if (scope.onlyOnce) {
// un-watch
compileWatch();
}
});
};
}

Client side duplicate data validation in angularJS

I'm trying to figure out if it is possible to validate data client side to ensure that no duplicates are sent to the database. I have an angular app which gets data from an api call. This is my current controller for adding a new subject (functioning perfectly, but without data validation):
angular.module('myApp.controllers')
.controller('SubjectNewCtrl', ['$scope', 'SubjectsFactory', '$location', '$route',
function ($scope, SubjectsFactory, $location, $route) {
// callback for ng-click 'createNewSubject':
$scope.createNewSubject = function () {
SubjectsFactory.create($scope.subjects);
$location.path('/subjects');
}
}]);
And here is what I have been attempting for data validation:
angular.module('myApp.controllers')
.controller('SubjectNewCtrl', ['$scope', 'SubjectsFactory', '$location', '$route',
function ($scope, SubjectsFactory, $location, $route) {
// callback for ng-click 'createNewUser':
$scope.createNewSubject = function () {
var newSubject = $scope.subject.name;
var someSubject = $scope.subjects;
var oldSubject;
if(newSubject){
angular.forEach($scope.subjects, function(allSubjects){
if(newSubject.toLowerCase() == allSubjects.name.toLowerCase()){
oldSubject = true;
}
});
if (!oldSubject){
SubjectsFactory.create($scope.subjects);
}
}
}
}]);
This gives me a console error- TypeError: Cannot read property 'name' of undefined. How do I access the 'name' property of my new subject from the html? Can anyone tell me if what I am trying to do is possible/ makes sense?
If I understand your question correctly, you should use a directive for the specific field you are trying to validate. A unique email directive would be a common example. Here is one I have used in the past. Nothing fancy.
MyApp.directive('uniqueEmail', ['$http', function($http) {
return {
restrict: 'A',
require: 'ngModel',
link: function(scope, element, attrs, ctrl) {
//set the initial value as soon as the input comes into focus
element.on('focus', function() {
if (!scope.initialValue) {
scope.initialValue = ctrl.$viewValue;
}
});
element.on('blur', function() {
if (ctrl.$viewValue != scope.initialValue) {
var dataUrl = attrs.url + "?email=" + ctrl.$viewValue;
//you could also inject and use your 'Factory' to make call
$http.get(dataUrl).success(function(data) {
ctrl.$setValidity('isunique', data.result);
}).error(function(data, status) {
//handle server error
});
}
});
}
};
}]);
Then in your markup you could use it like so.
<input type="text" name="email" ng-model="item.email" data-unique-email="" data-url="/api/check-unique-email" />
<span class="validation-error" ng-show="form.email.$error.isunique && form.email.$dirty">Duplicate Email</span>
Hope this is what you were looking for.
I have implemented object creation in Angular js many times.
My createNew button method typically just created a new javascript Object() and set the scope.currentObject to the new Object();
In your case it appears that $scope.subject is not initialized to anything, hence the error.
I guess that there must be a html input on your form that is bound the subject.name field but without a subject Object to hold the name it is effectively unbound.
If I wanted users to enter a name then click create button to validate that the name is not used. I would bind the new Name input to a different $scope variable (perhaps $scope.newName)
Then in the createNewSubject method you can actually create a new subject like this:
$scope.subject = new Object();
$scope.subject.name = $scope.newName;
Then you can run your validation code.

check if data already exists on server in angular.js

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.

Categories

Resources