AngularJS Form Validation and Submit Issue - javascript

I'm currently developing an AngularJS application which allows the user to activate their account, input user data and submit JSON data on completion.
What I'm trying to achieve:
If user data is valid (within the JSON data) redirect to '/form/' on submit else display invalid error message 'Invalid Account'.
Current Problems:
1. If the user enters the correct Membership and Activation number first the user is redirect to '/form/' but if the user enters the incorrect details then enters the correct details the redirect doesn't work.
2. I tried implementing an invalid alert to a if else statement but was alerted within 4/5 alerts at the same time, Unsure why this was happening?
I'm developing this project on Plnkr, please feel free to assist and point me in the right direct. Thank you.
http://plnkr.co/edit/5uPSn2ae0yFjYzujPFWm?p=preview
$scope.findMembership = function() {
angular.forEach($scope.membershipData.membershipNumber, function(value, key) {
if (key === $scope.membershipValue && value[0].activationNumber === $scope.activationValue) {
$location.path("/form/");
}
});
};
Update
formCtrl.controller('activation', function($scope, $location, $rootScope) {
var normalized = Object.keys($scope.membershipData.membershipNumber).map(function(k) {
return { key : k, val : $scope.membershipData.membershipNumber[k][0].activationNumber }
});
normalized = [
{"key":"541","val":"541X"},
{"key":"4692","val":"4692X"},
{"key":"45165","val":"45165X"},
{"key":"5464565","val":"5464565X"},
{"key":"54645651","val":"54645651X"},
{"key":"D4554160N","val":"D4554160NX"}
]
$scope.findMembership = function() {
if (normalized.some(function(o) {
return o.key == $scope.membershipValue && o.val == $scope.activationValue
})) $location.path("/form/")
}
});

Try changing your otherwise block to this:
otherwise({
templateUrl: 'view/activation.html',
controller: 'activation'
});
I think the redirect is causing the app to not be able to find the template it's looking for. Also I'd avoid using the $location.path() method inside the angular.forEach loop. Maybe normalize the data to a true array so you can use Array.some() instead.
Something like this:
var normalized = Object.keys($scope.membershipData.membershipNumber).map(function(k) {
return { key : k, val : $scope.membershipData.membershipNumber[k][0].activationNumber }
});
/*
normalized = [
{"key":"541","val":"541X"},
{"key":"4692","val":"4692X"},
{"key":"45165","val":"45165X"},
{"key":"5464565","val":"5464565X"},
{"key":"54645651","val":"54645651X"},
{"key":"D4554160N","val":"D4554160NX"}
]
*/
$scope.findMembership = function() {
if (normalized.some(function(o) {
return o.key == $scope.membershipValue && o.val == $scope.activationValue
})) $location.path("/form/")
}

Related

Checking for login credentials in HTML5 Storage

i'm building a quizz app , which asks me to : Add user authentication: allow users to log in, and save their login credentials to local storage (HTML5 browser storage). what i want to add is to check if the user name && password (together, because you can have the same username and not the same password and vice versa), so i can prompt a "Welcome back (name of the user)".
i spent 3 days trying to figure out which logic works , tried everything but every time i get a logic problem where things doesn't go the way it should be , here's the code :
var usersinfo = {
users : []
}
localStorage.setItem("users", JSON.stringify(usersinfo.users))
function registerInfo(){
var name = document.forms[0].username.value;
var pw = document.forms[0].pw.value;
if (name === "" || pw === "") {
alert ('please complete all the forms')
} else {
var adding = JSON.parse(localStorage.getItem("users"));
// logic that goes here : i tried everything , looping...etc
}
return false;
}
Note that the function is attached to a button , and everything works fine on the HTML , the problem is in login logic .
Untested, but try something like this:
const users = JSON.parse(localStorage.getItem("users"));
if (users.some((user) => {
return user.name === document.forms[0].username.value;
})) {
alert('Welcome back!');
}
Basically, we use some on the array to loop through and figure out if any of the elements have the same name as the one from your form. If they do, we immediately stop looping and return true. If not, we return false.
See also: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/some
And, do take #AnilRedshift's advice and don't store usernames/passwords in localstorage...

Random User Generator API - get user by ID Angular JS

I am new to Angular, but managed to make an Ajax-call and print out users from Random User Generator API in a list view.
Now I want to make a detailed view while clicked on a user.
In my HTML I make a function call: fetchInfoById(user.id.value)
In my script the function:
$scope.fetchInfoById = function(info_id) {
$http.get("https://randomuser.me/api/?id.value="+info_id)
//also tried: $http.get("https://randomuser.me/api/?id/value="+info_id)
.success(function(data) {
$scope.oneUserResult = data.results;
});
}
It does give me a user to a detail view, but not the chosen one. What am I doing wrong?
Thanks for your good suggestions.
I know it is a random generator, but setting parameters for the request to: "seed=...", the same persons is displayed on each listview request:
$http.get('https://randomuser.me/api/?results=15&seed=abc&inc=gender,name,location,email,dob,phone,cell,id,picture,info,nat&nat=gb')
.success(function(response){
$scope.userResult = response.results;
});
Then I fetched the id for each person and passed in as a parameter to the function call for the request for the detail view.
I tried with console.log() to make sure I passed in the right value for the detail view request and then even hardcoded the
parameter for the request ie:
$scope.getInfoById = function(info_id) {
console.log("from HTML: "+info_id.value ); // = JK 00 46 67
$http.get("https://randomuser.me/api/?id="+'JK 00 46 67 H') ...
The jason data behind the API is formatted like this for the id-property:
{
"results": [
{
"id": {
"name": "BSN",
"value": "04242023"
},...
I still haven't figured out how to get the one user by id. Getting different users all the time, even with hard coded id...
Instead of making the second request my solution was to a pass the "clicked user" as a parameter for the detailed view.
Change your code to this:
$scope.fetchInfoById = function(info_id) {
$http.get("https://randomuser.me/api/?id="+info_id)
//also tried: $http.get("https://randomuser.me/api/?id/value="+info_id)
.success(function(data) {
$scope.oneUserResult = data.results;
});
}
Also, make sure you are passing in the correct value to this function.
Fetch a list of users from API call "https://randomuser.me/api/?results=5".
$scope.getAllUsers= function(resultCount) {
$http.get("https://randomuser.me/api/?results="+resultCount)
.success(function(data) {
$scope.users= data.results;
});
Display them on the screen.
On click of one record fetch details for that particular record from users list fetched earlier.
$scope.getUserById= function(userId) {
return $scope.users.filter(function(user) {
return user.id.value=== userId;
})[0]; // apply necessary null / undefined checks wherever required.
}
another way using ng-model:
$scope.user = {};
$scope.fetchInfoById = function() {
$http.get("https://randomuser.me/api/?id="$scope.user.id)
.success(function(data) {
$scope.oneUserResult = data.results;
});
}

AngularJS - autocomplete with $http requests

I have an application in AngularJS and I want to implement an autocomplete input for a certain list of programs.
My problem is that I have lots of programs in my database and I don't want to load them all when the page loads. Instead I load pages and have a button that loads the next page when clicked.
scope.loadPrograms = function() {
Programs.getPage($scope.page)
.success(function(data) {
$scope.allprograms.push.apply($scope.allprograms, data.campaigns);
$scope.page++;
if(data.pagination.pages < $scope.page) {
$scope.page = -1;
}
})
.error(function(data){
alert('There has been an error. Please try again later!');
});
}
and the button
<md-button ng-click="loadPrograms()" ng-show="page != -1">Load more data</md-button>
So this approach makes me do a request everytime I write/delete a letter in the autocomplete input, given the fact that I don't have all the program loaded on $scope. Is it ok to make so many request? Is there another approach?
Thanks.
EDIT
Ok so now I put a delay on the autocomplete, but the method doesn't work anymore.
// Search for programs
scope.querySearch = function(query) {
if (typeof pauseMonitor !== 'undefined') {
$timeout.cancel(pauseMonitor);
}
pauseMonitor = $timeout(function() {
var results = query ? scope.allprograms.filter(createFilterFor(query)) : [];
return results;
}, 250);
};
// Create filter function for a query string
function createFilterFor(query) {
var lowercaseQuery = angular.lowercase(query);
return function filterFn(programs) {
return (programs.name.toLowerCase().indexOf(lowercaseQuery) != -1);
};
};
It enters in the createFilterFor method, finds a good match but doesn't show it anymore.
If you need to retrieve a set of words for the purpose of auto completion from a large database, one simple trick is to use $timeout with some time threshold which can detect the pauses of the user typing.
The idea is to prevent a request being generated for every key. You look for a pause in the user typing pattern and make your request there for the letters typed. This is a simple implementation of this idea in your key handler.
function processInput(input) {
if (typeof pauseMonitor !== 'undefined') {
$timeout.cancel(pauseMonitor);
pauseMonitor = $timeout(function() {
//make your request here
}, 250);
}
Take a look at ng-model-options
you can set a debounce time and some other interesting things.
ng-model-options="{ debounce: '1000' }"
Above line means the input value will be updated in the model after 1 sec

JQuery/AJAX form validation with nested testing...elegant solution?

I'm performing form validation using JQuery/AJAX. When an entry is missing or malformed, I want to send a message via alert() and return the user to the form with all fields as they were so the user can edit them and try submitting the form again. I also don't want the user to get a series of alert() boxes for each field that is malformed, but rather the first field that is discovered to be malformed should issue an alert() box; upon clicking the alert() box, the user may then return to editing the form. If there are 6 malformed fields, I don't want a series of 6 consecutive alert() boxes, but rather just the first one discovered by the routine to be errant and a return to the form (eventually the user will get them all right).
I have utilized a method that works, although it's not elegant and rather tedious to edit and error-prone...it's like a series of Russian dolls, where the first error prevents the successive routines from being run. When there are 5 fields or fields that require multiple kinds of integrity checking, the number of nested IF-ELSE statements increases exponentially, and for forms where I'm passing data via GET to a PHP file, like this:
$.get('admin_email_length_check.php', { new_email: $('#new_email').val(), max_email_length: max_email_length }, function(data) {
if (data != 0) {
alert(data);
} else {
...it has to be closed out with:
}
});
...not just a simple:
}
But here's a short routine for a 2 field validation. I set allow_submit to FALSE and prevent submission until all integrity checks are passed, at which point allow_submit becomes TRUE and I dynamically re-submit the form; this means that the integrity check (and its e.preventDefault();) will be bypassed entirely and the form will then be processed. Again, it works, but the kind of IF-ELSE structures I need to construct for forms with many fields that require many types of form validation requires extremely LONG structures with carefully edited closing braces (or braces + parentheses + ;, etc.) Is there a shorter or more elegant way to do this?:
var allow_submit = false;
$('#change_password_form').on('submit', function(e) {
if (!allow_submit) {
e.preventDefault();
// First ensure that at least one of the fields has a value:
if (($('#new_password').val() == '') && ($('#retype_password').val() == '')) {
alert("Nothing specified in either of the 'Change Password' fields.\n\nAdd data and re-submit.\n");
} else {
// Ensure both fields are filled:
if (($('#new_password').val() == '') || ($('#retype_password').val() == '')) {
alert("When changing your password, both the 'New Password' and the 'Retype Password' fields must be filled.\n\nPlease supply new, matching passwords in both fields and re-submit.\n");
} else {
// Do the two fields match?
if ($('#new_password').val() != $('#retype_password').val()) {
alert("New Password fields do not match.\n\nPlease edit password fields and re-submit.\n");
} else {
allow_submit = true;
$('#change_password_form').submit();
}
}
}
}
});
I have two suggestions:
Use early return statements to de-nest your code a bit.
Whenever you have too much conditional logic, try to use a data structure instead. See this quote by Linus Torvalds.
Here is my attempt:
$('#change_password_form').on('submit', function(e) {
var data = collect_data();
if (!data_is_valid(data)) {
e.preventDefault();
} else {
// Submit.
}
});
function collect_data() {
return {
passwords {
new_: $('#new_password').val(),
retyped: $('#retype_password').val()
},
...
};
}
function data_is_valid(data) {
if (!password_is_valid(data.passwords)) {
return false;
}
...
return true;
}
function password_is_valid(passwords) {
for (var i = 0; i < password_validators.length; i++) {
var validator = password_validators[i];
if (!validator.fails(passwords)) {
alert(validator.message);
return false;
}
}
return true;
}
var password_validators = [
{
fails: function(passwords) {
return passwords.new_ === '' && passwords.retyped === '';
},
message: 'No password provided'
},
{
fails: function(passwords) {
return passwords.new_ !== .passwordsretyped;
},
message: 'Passwords do not match'
},
...
];

Azure - Server Validation

Okay, so I am attempting to validate on the server side. I am using Windows Azure Mobile Services for my Android application. The validation is done in Javascript / Node.js.
I have been doing my best to find solutions to my issue and stumbled upon [this link] (http://blogs.msdn.com/b/carlosfigueira/archive/2012/09/21/playing-with-the-query-object-in-read-operations-on-azure-mobile-services.aspx)!
I intend to use regexp to validate the object before persisting it to the DB.
I would understand how to do this 'pre-query' but as I need access to use regex, I must perform 'post-query' filtering.
Below is the code in which I have (so far) but I want to know how can I validate many fields and deliver appropriate error messages for each invalid fields. If all are valid, then persist to the DB.
Thanks in advance!
function insert(item, user, request) {
var userTable = tables.getTable('User');
userTable.where({email: item.email}).read({
success: emailExists
});
function emailExists(existingItems)
{
if (existingItems.length > 0)
{
request.respond(statusCodes.CONFLICT,
"An account is already registered with this email!.");
}
else
{
// Insert the item as normal.
request.execute({
success: function (results)
{
var regexEmail = /^(([^<>()[\]\\.,;:\s#\"]+(\.[^<>()[\]\\.,;:\s#\"]+)*)|(\".+\"))#((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
var filtered = results.filter(function(item)
{
return regexEmail.test(item.email);
});
request.respond(statusCodes.OK, filtered);
}
});
}
}
}
If I understand what you want to do correctly, you first need to validate the input's e-mail against the items in the database (to maintain unicity), then validate other fields in the input before inserting that. If that's the case, then after the query validation (to prevent duplicate e-mails) you can validate the item fields individually, as shown in this document. The code below shows an example of such validation.
function insert(item, user, request) {
var userTable = tables.getTable('User');
userTable.where({email: item.email}).read({
success: emailExists
});
function emailExists(existingItems)
{
if (existingItems.length > 0)
{
request.respond(statusCodes.CONFLICT,
"An account is already registered with this email!.");
}
else
{
// Validate fields *before* inserting
var regexEmail = /^(([^<>()[\]\\.,;:\s#\"]+(\.[^<>()[\]\\.,;:\s#\"]+)*)|(\".+\"))#((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
if (!regexEmail.test(item.email)) {
request.respond(statusCodes.BAD_REQUEST, { error: 'E-mail is invalid' });
return;
}
if (!item.name || item.name.length < 10) {
request.respond(statusCodes.BAD_REQUEST, { error: 'Item must have a name of at least 10 characters' });
return;
}
// If all validation succeeded, then insert the item
request.execute();
}
}
}

Categories