Searching through JSON Object with ng-disabled (AngularJS) - javascript

One of the features of my web application is the possibility to add new users (username + password) through a form. Thereby, I have one JSON object (l_usernames) defined in a controller (UsersController) with all the usernames already chosen by users to avoid the repetition of usernames (it's a unique key).
Sample of my data (fetched-data.json) - format of object "usernames" (l_usernames):
[{"0":"default","USERNAME":"default"},{"0":"user1","USERNAME":"user1"},{"0":"user2","USERNAME":"user2"},{"0":"user3","USERNAME":"user3"}]
There is a sample of the form to add new users (add-user.html):
<div class="row" ng-controller="UsersController">
<div class="col-md-12">
<div class="panel panel-primary">
<div class="panel-heading">
<h3 class="panel-title">Add New User</h3>
</div>
<div class="panel-body">
<form class="form-horizontal" role="form">
<div class="form-group">
<label for="inputUserUsername" class="col-sm-2 control-label"><i class="icon fa fa-user"></i> USERNAME</label>
<div class="col-sm-10">
<input type="text" class="form-control" id="inputUserUsername" placeholder="Username" ng-model="user_username">
</div>
</div>
<div class="form-group">
<label for="inputUserPassword" class="col-sm-2 control-label"><i class="icon fa fa-key"></i> PASSWORD</label>
<div class="col-sm-10">
<input type="password" class="form-control" id="inputUserPassword" placeholder="Password" ng-model="user_password">
</div>
</div>
</form>
</div>
</div>
<form class="form-inline">
<div class="span7 text-center">
<button type="submit" class="btn btn-success" ng-click="addUser()" ng-disabled="(!user_username || !user_password)">Save</button>
</div>
</form>
</div>
</div>
Sample of my controller (userscontroller.js):
var app = angular.module('myApp');
app.controller('UsersController', ['$scope', 'services', function($scope, services) {
services.getData().then(function(data){
$scope.l_usernames = data.data;
});
}])
.factory('services', ['$http', function($http){
var serviceBase = 'services/'
var object = {};
object.getData = function(){
return $http.get('fetched-data.json');
};
return object;
}]);
I would like to know how it is possible to not allow the insert of new users if the username is already chosen - searching through the JSON object l_usernames - with ng-disabled (by disabling the "Save" button). I also want to print a simple message - "Username already chosen" - if such situation occurs. Thank you.

Add a watch on the user_username scope variable. Whenever it changes search through the JSON object, you can use lodash or underscorejs to search through l_usernames to see if the username already exists. If it exists then set a variable in the scope to false. Bind the ng-disabled of the save button to this variable. Use debounce on the user_username for better performance.
Take a look at this fiddle here
Controller
function UsersController($scope) {
$scope.name = 'Superhero';
$scope.l_username = [{"0":"default","USERNAME":"default"},{"0":"user1","USERNAME":"user1"},{"0":"user2","USERNAME":"user2"},{"0":"user3","USERNAME":"user3"}];
$scope.allowSave = true;
$scope.$watch('user_username', function(value) {
if (_.findWhere($scope.l_username, {"USERNAME": value}) !== undefined)
$scope.allowSave = false;
else
$scope.allowSave = true;
})
}
HTML
<button type="submit" class="btn btn-success" ng-click="addUser()" ng-disabled="!allowSave">Save</button>
Whenever the entered username is found in the array, the allowSave variable is changed which disables the 'save' button.
Note: I have used underscore.js to search through the list. You can use you custom method as well.
I have added the warning message and debounced the model for better performance.

I would make a validation directive.
HTML:
<input username-exists type="text" ng-model="userName" ng-model-options="{updateOn: 'default blur', debounce: { 'default': 700, 'blur': 0 }}" />
<div ng-if="myFormName.$error.usernameExists">Username exists!</div>
<button type="button" ng-disabled="myFormName.$invalid">
The ng-model-options is so that your model doesn't go crazy and update always (it delays the validation).
Javascript:
app.directive('usernameExists', function() {
return {
restrict: 'A', //match attributes only
require: 'ngModel',
scope: {
l_usernames: '='
},
link: function(scope, elem, attrs, ctrl) {
ctrl.$parsers.unshift(function(viewValue) {
//first, assume the model is valid until proven otherwise
ctrl.$setValidity('usernameExists', true);
if(viewValue.length > 0 && !ctrl.$error.usernameExists) {
for(var i = 0; i < scope.l_usernames.length; ++i) {
if(scope.l_usernames[i].USERNAME === viewValue) {
//username exists, so set valididty to false
//the form is not valid
ctrl.$setValidity('usernameExists', false);
break; //found match
}
}
}
return viewValue;
});
}
};
})

Related

angularjs $setValidity from a directive not updating

I am trying to build a custom directive (based on an example I saw) to ensure that the confirm password is correct.
I am running into some trouble with the $setValidity tag, although all my console.log() execute and print out, it does not seem that the validity is being updated. Pass stays true even when I read in my console "set to false".
I made sure the name and the ng-model are the same and I also tried using scope.$apply, but that would comeout with an error saying i was trying to apply when something else is being applied.
Here is my code:
HTML
<form ng-submit="signup()" name="profileForm">
<div class="form-group">
<label class="form-label" for="input-example-2">Password</label>
<input class="form-input" ng-model="pnew" type="password" name="pnew" placeholder="Password" required>
</div>
<div class="form-group">
<label class="form-label" for="input-example-2">Confirm Password</label>
<input class="form-input" name="confirm" ng-model="confirm" type="password" placeholder="Password" required pwcheck>
</div>
{{profileForm.confrim.$error.pass}}
<hr>
{{profileForm.confirm.$error}}
<hr>
{{profileForm.confirm.$valid}}
<span ng-show="profileForm.confirm.$error.pwCheck"> the passwords dont match</span>
<div class="form-group">
<button class="btn btn-primary">Sign up</button>
</div>
</form>
JS code for the pwcheck directive
.directive('pwcheck', function() {
return {
require: 'ngModel',
link: function(scope, elm, attrs, ctrl) {
function valid(value){
scope.$watch("confirm", function(newval, oldval){
console.log(value);
console.log(scope.pnew);
if(value==scope.pnew){
console.log("success!");
ctrl.$setValidity('pass', true);
return value;
}
else {
console.log("set to false");
ctrl.$setValidity('pass', false);
return undefined;
}
});
}
ctrl.$parsers.push(valid);
} } });
No need to use $watch on confirm if you're using your directive on same element & having ng-model in require. So your code can be
ctrl.$parsers.unshift(valid);
ctrl.$formatters.unshift(valid);
function valid(viewValue){
console.log(viewValue);
console.log(scope.pnew);
if(viewValue==scope.pnew){
console.log("success!");
ctrl.$setValidity('pass', true);
return viewValue;
}
else {
console.log("set to false");
ctrl.$setValidity('pass', false);
return undefined;
}
}
Most trivial example plunk
https://plnkr.co/edit/vPbICfSCDnwHKh07DAXJ?p=preview

Angular - Form Validation - Cannot read property 'name' of undefined

so I have a form with an input and some other stuff and I'm trying to do some angular validation to make sure that the entered information is actually there (not blank). To do so, I'm using an if statement.
The error message I get is:
Cannot read property 'name' of undefined
It seems as if it can't read an <input> tag name if it's left blank. The function works when I fill in the , but not the others (which are a and . I'm just trying to use an if statement to see if they've been filled out. Here's the html and angular code below:
reviewModal.view.html (shortened form version)
<div class="modal-content">
<div role="alert" ng-show="vm.formError" class="alert alert-danger">{{ vm.formError }}</div>
<form id="addReview" name="addReview" role="form" ng-submit="vm.onSubmit()" class="form-horizontal">
<label for"name" class="col-xs-2 col-sm-2 control-label">Name</label>
<div class="col-xs-10 col-sm-10">
<input id="name" name="name" ng-model="vm.formData.name" class="form-control">
</div>
<button type="submit" class="btn btn-primary">Submit review</button>
</form>
</div>
reviewModal.controller.js
(function() {
angular
.module('loc8rApp')
.controller('reviewModalCtrl', reviewModalCtrl);
reviewModalCtrl.$inject = ['$uibModalInstance', 'locationData'];
function reviewModalCtrl($uibModalInstance, locationData) {
var vm = this;
vm.locationData = locationData;
vm.onSubmit = function() {
vm.formError = "";
if(!vm.formData.name || !vm.formData.rating || !vm.formData.reviewText) {
vm.formError = "All fields required, please try again";
return false;
} else {
console.log(vm.formData);
return false;
}
};
vm.modal = {
cancel : function() {
$uibModalInstance.dismiss('cancel');
}
};
}
})();
locationDetail.controller.js
(function() {
angular
.module('loc8rApp')
.controller('locationDetailCtrl', locationDetailCtrl);
locationDetailCtrl.$inject = ['$routeParams', '$uibModal', 'loc8rData'];
function locationDetailCtrl($routeParams, $uibModal, loc8rData) {
var vm = this;
vm.locationid = $routeParams.locationid;
loc8rData.locationById(vm.locationid)
.success(function(data) {
vm.data = { location: data };
vm.pageHeader = {
title: vm.data.location.name
};
})
.error(function(e) {
console.log(e);
});
vm.popupReviewForm = function() {
var modalInstance = $uibModal.open({
templateUrl: '/reviewModal/reviewModal.view.html',
controller: 'reviewModalCtrl as vm',
resolve : {
locationData : function() {
return {
locationid : vm.locationid,
locationName : vm.data.location.name
};
}
}
});
};
}
})();
vm.formData must be defined before you can assigned/read name property in the html. Update code in reviewModalCtrl to init vm.formData:
vm.formData = {};
Use angular validation instated validating things in controller as follows.
<div class="modal-content">
<div role="alert" ng-show="addReview.$submitted && addReview.$invalid" class="alert alert-danger">All fields required, please try again</div>
<form id="addReview" name="addReview" role="form" ng-submit="vm.onSubmit()" class="form-horizontal">
<label for"name" class="col-xs-2 col-sm-2 control-label">Name</label>
<div class="col-xs-10 col-sm-10">
<input id="name" name="name" ng-model="vm.formData.name" class="form-control" required>
</div>
<button type="submit" class="btn btn-primary">Submit review</button>
</form>
</div>
I have used required in text box and show the error message on top with
ng-show="addReview.$invalid"
You do not need any code on controller.

How to call custom directive on form submit

I have a custom directive which works when I once change input value but when I submit form without changing input field value directive does not display error message.
html code:
<form name="myform" role="form" class="form-horizontal" method="post" novalidate>
<div class="form-group">
<label class="control-label col-sm-3" for="firstName">First name :</label>
<div class="col-sm-9">
<input name='firstName' class="form-control" type='text' required ng-model='name' string>
</div>
</div>
<div class="form-group">
<input type="submit" value="Submit" class="btn btn-success" />
</div>
</form>
directive :
validationModule.directive('string', function () {
return {
restrict: 'A',
require: 'ngModel',
link: function (scope, element, attr, ctrl) {
function validationError(value) {
if (value.length < 1) {
ctrl.$setValidity('required', false);
//showPopOver(element, "Required field");
errorValidation(element, "Required field");
// $(element).popover('show');
}
if (/[a-zA-Z]/.test(value)) {
ctrl.$setValidity('invalid', true);
// $(element).popover('destroy');
successValidation(element);
}
if (/[0-9]/.test(value)) {
ctrl.$setValidity('invalid', false)
errorValidation(element, "number not allowed");
// showPopOver(element, "number not allowed");
// $(element).popover('show');
}
return value;
}
ctrl.$parsers.push(validationError);
}
};
});
How can I call directive to perform validation on submit event ?
I don't know which version of angular you are using, but since 1.3 validators are not hacked into parsers. See $validators in https://docs.angularjs.org/api/ng/type/ngModel.NgModelController
Also using such generic names for directives is discouraged due to possible name collisions. Use some prefix eg myString

$scope doesn't picks up string from form input - AngularJS, JS

I am trying to implement Change Password feature in MVC (Rest server) application from the User Control Panel but because of some strange reason I can't scope values from form input.
My html form:
<accordion-group heading="Change password" is-open="changePasswordStatus.open" style="cursor: pointer">
<div>
<div>
<form class="form-horizontal" role="form">
<form-row model="newPassword" name="New: " size="col-sm-8"></form-row>
<form-row model="repeatedNewPassword" name="Repeat: " size="col-sm-8"></form-row>
<form-row model="currentPassword" name="Current: " size="col-sm-8"></form-row>
<br>
</form>
</div>
<div>
<button class="btn btn-default btn-sm" ng-click="changePassword()">Save</button>
<button class="btn btn-warning btn-sm" ng-click="changePasswordStatus.open = !changePasswordStatus.open">Cancel</button>
</div>
</div>
</accordion-group>
My formRow.html:
<div class="form-group">
<label for="inputText3" class="col-sm-2 control-label">{{name}}</label>
<div class="{{size}}">
<input type="{{type}}" class="form-control" data-ng-model="model">
</div>
</div>
My formRow.js:
collectionsApp.directive('formRow', function(){
return {
restrict: 'E',
replace: true,
scope: {
model: '=',
name: '#',
size: '#',
type: '#'
},
templateUrl: '/directives/formRow.html',
link: function(scope, attrs, element) {
}
}
});
My userController:
$scope.changePassword = function() {
if ($scope.newPassword === $scope.repeatedNewPassword) {
userService.changePassword($scope.newPassword, $scope.currentPassword);
} else {
$scope.alerts.push({
msg : 'Passwords do not match!'
})
}
}
And when I enter values in inputs and place breakpoints and trigger changePassword() in debug i get:
If condition has passed with value of true because they are both undefined.
I believe this may be the case of prototypical inheritance and scope, requiring an object being passed into your scoped parameters. Mind trying to change your parent scope to use an object and bind to the properties and not the primitive values:
$scope.security = {newPassword : '', currentPassword = ''};
then you would use something like this in your attributes:
model="security.newPassword"
Or better yet, not make it confusing with model:
myapp-model="security.newPassword"
or pass in the whole object
myapp-security="security"
Working in this plunker template ?
<form ...>
<form-row model="newPassword" name="New: " size="col-sm-8" required ></form-row>
<form-row model="repeatedNewPassword" name="Repeat: " size="col-sm-8" required ></form-row>
<form-row model="currentPassword" name="Current: " size="col-sm-8" required ></form-row>
</form>

AngularJS watch not being triggered

I'm having a problem with AngularJs. I have created a directive that $watch the model and it takes some action based on the model's current status. However, although during debugging I can see that the $watch is set, it is only being triggered after the model get valid at least once and I don't know why that is happening. Debugging it doesn't even gets into the $watch function when something is typed.
The code is below:
Directive:
(function() {
'use strict';
var app = angular.module('app');
app.directive('tooltipValidation', function () {
return {
restrict: 'A',
require: 'ngModel',
link: function (scope, element, attrs, ngModel) {
var tooltip = $(element).qtip({
content: {
text: element.next('div')
},
show: false,
hide: true
}).qtip('api');
scope.$watch(attrs.ngModel, function() {
if (ngModel.$invalid && ngModel.$dirty) {
tooltip.show();
} else {
tooltip.hide();
}
});
}
};
});
})()
HTML:
<div class="form-address clearfix" ng-show="isNewShippingAddress" ng-form="newShippingAddressForm">
<h3>Include new shipping address:</h3>
<div class="novo-endereco clearfix" id="newAddress">
<div class="required address apelido">
<label for="newShippingAddressAlias">Alias</label>
<input id="newShippingAddressAlias" name="newShippingAddressAlias" type="text" tooltip-validation ng-model="newShippingAddress.Alias" required ng-maxlength="32" />
<div data-ng-show="newShippingAddressForm.newShippingAddressAlias.$dirty && newShippingAddressForm.newShippingAddressAlias.$invalid">
<p data-ng-show="newShippingAddressForm.newShippingAddressAlias.$error.required">obligatory</p>
<p data-ng-show="newShippingAddressForm.newShippingAddressAlias.$error.maxlength">max 32 char</p>
</div>
</div>
<div class="required endereco">
<label for="newShippingAddressStreet">Street</label>
<input id="newShippingAddressStreet" name="newShippingAddressStreet" type="text" tooltip-validation ng-model="newShippingAddress.Street" required ng-maxlength="256" />
<div data-ng-show="newShippingAddressForm.newShippingAddressStreet.$dirty && newShippingAddressForm.newShippingAddressStreet.$invalid">
<p data-ng-show="newShippingAddressForm.newShippingAddressStreet.$error.required">obligatory</p>
<p data-ng-show="newShippingAddressForm.newShippingAddressStreet.$error.maxlength">max 256 char</p>
</div>
</div>
<div class="required cep">
<label for="newShippingAddressZipCode">ZipCode</label>
<input id="newShippingAddressZipCode" name="newShippingAddressZipCode" type="text" tooltip-validation ng-model="newShippingAddress.ZipCode" required ng-pattern="/^[0-9]{8}$/" />
<div data-ng-show="newShippingAddressForm.newShippingAddressZipCode.$dirty && newShippingAddressForm.newShippingAddressZipCode.$invalid">
<p data-ng-show="newShippingAddressForm.newShippingAddressZipCode.$error.required">obligatory</p>
<p data-ng-show="newShippingAddressForm.newShippingAddressZipCode.$error.pattern">8 digits</p>
</div>
</div>
<input type="submit" class="button grey" value="Save new address" data-ng-click="saveShippingAddress()" ng-disabled="newShippingAddressForm.$invalid" />
</div>
</div>
Regards,
dimello
Try:
scope.$watch(function(){
return ngModel.$viewValue; //Watch for view value (the value in your input)
}, function() {
if (ngModel.$invalid && ngModel.$dirty) {
tooltip.show();
} else {
tooltip.hide();
}
});
DEMO
Explanation:
When you type an invalid value into the input with ng-model, the underlying model is not updated, causing your scope.$watch(attrs.ngModel not being fired because you're watching for changes in the model. If you need to fire the function every time the input changes no matter it's valid or not, try the above solution.

Categories

Resources