Conditional validation using single piece of code - AngularJS - javascript

The code contains two functions. First one is defined as follows
scope.validateContactName = function() {
scope.address.invalidName = false;
if (!scope.address.name) {
scope.address.invalidName = true;
}
}
which is invoked by the function validateContactName();
Now i have another function
scope.validateContactPhone = function() {
scope.address.invalidPhone = false;
if (!scope.address.phone) {
scope.address.invalidPhone = true;
}
}
which is invoked by the function validateContactPhone();
Instead of two functions, is there a way i can use a single function and do conditional validation?
Something like
validateContactInfo('name');
function validateContactInfo(attr) {
//do validation based on the attribute
// There is only one single piece of code for both conditions
}

Maybe smth like this could work:
scope.validateField = function(field, errorField) {
scope.address[errorField] = false;
if (!scope.address[field]) {
scope.address[errorField] = true;
}
}
Or a shorter version:
scope.validateField = function(field, errorField) {
scope.address[errorField] = !scope.address[field];
}

I would suggest something like this(ES6):
scope.address = [
{
type: "phone",
invalid: false
},
{
type: "name",
invalid: false
}
];
const validate = type => {
let data = scope.address.find(a => a.type === type);
if(!data.type) {
data.invalid = true;
}
};
validate("phone");
validate("name");

Assuming contact information is used in a form to get input from the user. I would recommend to use angular's own form validation
If it is not the case, here is a generic way of checking if values exists in a object. Which you can add in project''s utilities
const contactInfo = {
name: 'My name',
phone: '123123123',
address: ''
}
function validateExistence(obj){
const emptyKeys = [];
for(let key in obj){
if(!obj[key]) emptyKeys.push(key)
}
return emptyKeys
}
console.log(validateExistence(contactInfo));

Related

condense if, else JS with similar condition rules

trying to find a way to condense this. wasnt sure of the best way to do it. basically if criteria is met i display an alert with a parameter that is the message. i was thinking of maybe trying it in function. this is part of a larger function react component. i was also thinking if i could find a way to condense the else if's i could use a ternary. thanks in advance for the assistance.
const handleUpdatePassword = () => {
const allFilled = !reject(passwords).length;
const passwordsMatch = newPassword === conPassword;
const isDifferent = curPassword !== newPassword;
const meetsPasswordRequirements = validatePassword();
const usesName = isUsingName();
const usesUserID = isPartOfUserID();
const isValidPassword = meetsPasswordRequirements && isDifferent;
if (allFilled) {
if (!isDifferent) {
Alert.alert(difPassWord);
} else if (!passwordsMatch) {
Alert.alert(noMatch);
} else if (!meetsPasswordRequirements) {
Alert.alert(pasReqs);
} else if (usesName || usesUserID) {
Alert.alert(pasName);
}
} else {
Alert.alert(fieldNotComplete);
}
if (isValidPassword) {
changePasswordPost(
{
userId,
curPassword,
newPassword
},
partyId
);
}
};
You can create an array of objects for your validation rules, each containing a function which returns a boolean indicating whether that validation passes, and a string with the error message to display.
Then loop over the rules array and alert the message for the first rule that returns false. If they all return true, do the post.
You can split each if statement into a function, then chain them. For example
// here we make a closure to validate, and return a Promise
// condition can be a function
const validate = (condition, error) => ()=> new Promise((res, rej)=>{
if(condition()){
res();
}else{
rej(error);
}
});
const handleUpdatePassword = () => {
const validateFieldsComplete = validate(
()=>!reject(passwords).length,
fieldNotComplete
);
const validateDifPassword = validate(
()=> curPassword !== newPassword,
difPassWord
);
// ...
validateFieldsComplete()
.then(validateDifPassword)
.then(...)
.catch(Alert.alert)
}
It would be much cleaner with pipe. You can take a look at ramda. Or if you are intrested in functional way, you might consider using Monad.
I'd recommend DRYing up the Alert.alert part since all branches have that in common, and just come up with an expression that evaluates to the alert message. Compactness isn't always everything, but if you want it, then nested conditional operators can fit the bill. I'm also rearranging your conditions so that it can be a flat chain of if/elses:
const message
= reject(passwords).length ? fieldNotComplete
: curPassword === newPassword ? difPassWord
: newPassword !== conPassword ? noMatch
: !validatePassword() ? pasReqs
: (isUsingName() || isPartOfUserID()) ? pasName
: null;
const isValid = !message;
if (!isValid) {
Alert.alert(message);
}
(feel free to use any other sort of code formatting pattern; nested conditionals always look awkward no matter which pattern you use, IMO.)
Edit:
Also inlined conditionals which will short-circuit evaluation and make it even more compact.
I'd setup a validations object that has the tests and error messages and then loop over it. If validation fails, it'll throw the last validation error message. Using this method, you only have to maintain your tests in one place and not mess with a block of conditional statements.
const handleUpdatePassword = () => {
const validations = {
allFilled: {
test() {
return newPass && oldPass
},
error: 'Must fill out all fields'
},
correct: {
test() {
return curPass === oldPass
},
error: 'Incorrect password'
},
[...]
}
const invalid = () => {
let flag = false
for (let validation in validations) {
if (!validations[validation].test()) {
flag = validations[validation].error
}
}
return flag
}
if (invalid()) {
Alert.alert(invalid())
} else {
changePasswordPost(
{
userId,
curPass,
newPass
},
partyId
)
}
}
hi everyone this was the method i used for a solution
const messages = [
{
alertMessage: difPassWord,
displayRule: different()
},
{
alertMessage: noMatch,
displayRule: match()
},
{
alertMessage: pasReqs,
displayRule: validatePassword()
},
{
alertMessage: pasName,
displayRule: !isUsingName() || !isPartOfUserID()
}
];
if (allFilled) {
const arrayLength = messages.length;
for (let i = 0; i < arrayLength; i++) {
if (messages[i].displayRule === false) {
Alert.alert(messages[i].alertMessage);
}
}

React object property assignment only works the first time

For the following code, parameters are js objects whose structures are initialized as follows:
statePiece = {
field_name: { disabled: false, exampleValue: "arbitrary" },
field_name2: {
/* ... */
},
field_nameN: {
/* ... */
}
};
userField = "field_name_string";
sesarValues = {
format: "one2one",
selectedField: "latitude",
disabledSelf: true,
addField: 0
};
This function works correctly and returns the modified statePiece as returnTemp the first time a particular statePiece.field_name is modified
export let setUserField = (statePiece, userField, sesarValues) => {
console.log("set user field", userField, "set mappval", sesarValues);
var temp = { ...statePiece }; //(this.state.fields[each].mappedTo != null) ? (this.state.fields[userField].mappedTo) : [];
var XUnit = statePiece[userField];
if (typeof userField != "string") {
console.log("not string");
for (var each of userField) {
if (sesarValues) {
temp[each].mappedTo = sesarValues.selectedField;
temp[each].disabled = true;
} else {
temp[each].disabled = !temp[each].disabled;
delete temp[each].mappedTo;
}
}
} else {
//is string
console.log("is string");
console.log(XUnit);
if (sesarValues) {
if (XUnit.disabled === true) XUnit.disabled = false;
console.log("1");
console.log(XUnit);
XUnit.disabled = true;
console.log(XUnit);
XUnit.mappedTo = sesarValues.selectedField;
} else {
console.log("2");
temp[userField].disabled = !temp[userField].disabled;
delete temp[userField].mappedTo;
}
}
let returnTemp = { ...temp, [userField]: XUnit };
console.log("set UF debug ", returnTemp);
console.log(returnTemp["FACILITY_CODE"]);
return returnTemp;
};
But after that, changing the statePiece.userField.mappedTo value fails to alter the object property. Or at least alter it permanently. When I console log the returnTemp variable, I see the entry has lost its mappedTo entry(as should happen) without it being replaced with the new userField.
However, when I console.log(returnTemp[userField]) it shows the entry values with the expected mappedTo key: value pair.
Not sure what's going on here.
From the usage of userField, I can work out that it could be an Array or a String.
However you have done something curious with it in the following expression:
var XUnit = statePiece[userField];
Given userField is a String, the above expression is fine.
However, where it is an array, XUnit will be undefined.
Also doing the same where userField is an Array in the following line means that you're setting the userField.toString() as a key mapped to undefined.
let returnTemp = { ...temp, [userField]: XUnit };
I'd assign XUnit where the condition checks out that userField is a String and just return temp.
else {
//is string
var XUnit = statePiece[userField];
//...
}
return temp;

How do I convert an array into an object within array - Angular and Javascript

What I am trying to do is take an array within an object within an array within an object (I know my data structure is ridiculous, any help with that would be great also) and convert the last array into an object with a "key":"value". I'm using Angular 1.5.7 if there is anything in Angular that could help do this. I know that probably makes no sense. I couldn't figure out a way to say what I am trying to do clearly so let me show you:
I start with an object like this:
{"instructor":[{"instructor_emails":[ "test#test.com","tester#tester.com"]}]}
And I want it to be:
{"instructor":[{"instructor_emails":{ "email":"test#test.com","email":"tester#tester.com"}}]}
I tried a couple of things and the closest I found was:
instructor.instructor_emails.map(function(e) {
return { email: e };
});
But it doesn't quite do what I'm trying to do... Any thoughts?
This was correct all along (Thanks Alex)
instructor.instructor_emails.map(function(e) {
return { email: e };
});
Returns:
{instructor:[instructor_emails[{"email":"example1#example1.com",{"email":"example1#example1.com"}]]}
The data structure is still ridiculous but it will suffice for what I am trying to do
You should read up on Object-oriented programming for optimal data storage, particularly concerning classes. To transition between traditional OOP languages, like Java, to JavaScript you can use TypeScript.
Below is a snippet i created using TypeScript:
/// <reference path="definitions/jquery.d.ts" />
console.clear();
var Instructor = (function () {
function Instructor(name, emails) {
if (name === void 0) { name = ""; }
if (emails === void 0) { emails = []; }
this.name = name;
this.emails = emails;
}
Instructor.prototype.addEmail = function (email) {
if (email === void 0) { email = ""; }
//Run validation
if (email.length > 3) {
this.emails.push(email);
}
};
Instructor.prototype.getEmails = function (type) {
if (type === void 0) { type = "array"; }
var self = this;
type = type.toLowerCase();
var getEmails = {
string: function () {
return self.emails.join(" ");
},
object: function () {
return self.emails.map(function (e) {
return { email: e };
});
}
};
if (getEmails[type] === void 0) {
return this.emails;
}
else {
return getEmails[type]();
}
};
return Instructor;
}());
var instructors = [
new Instructor("Michael Bennet I", ["test#test.com", "tester#tester.com"]),
new Instructor("Michael Bennet II", ["test#test.com", "tester#tester.com"]),
];
console.log('array', instructors[0].getEmails());
console.log('object', instructors[0].getEmails("object"));
console.log('string', instructors[0].getEmails("String"));
/*
// This is TypeScript
class Instructor {
constructor(public name: string = "", public emails: string[] = []) {
}
public addEmail(email: string = "") {
//Run validation
if (email.length > 3) {
this.emails.push(email);
}
}
public getEmails(type: string = "array") {
var self = this;
type = type.toLowerCase();
var getEmails = {
string: function () {
return self.emails.join(" ");
},
object: function () {
return self.emails.map(function (e) {
return { email: e };
});
}
}
if (getEmails[type] === void 0) {
return this.emails;
} else {
return getEmails[type]();
}
}
}
var instructors: Instructor[] = [
new Instructor("Michael Bennet I", ["test#test.com", "tester#tester.com"]),
new Instructor("Michael Bennet II", ["test#test.com", "tester#tester.com"]),
];
console.log('array',instructors[0].getEmails());
console.log('object',instructors[0].getEmails("object"));
console.log('string',instructors[0].getEmails("String"));
<p>Object can have their own functions to "get" data they contain in unique ways.</p>
<p>This way, you can get the same data in several different ways</p>

code refactoring, how to elegantly overwrite object values

so I have a call to factory and after everything goes fine I want to reset the values of imput fields and as you can see in the code below I do this by hand.
vm.submitRequest = () = > {
requestedBooksFactory.requestBook(vm.title, vm.author, vm.link, vm.destination, vm.currentUser, vm.comments)
.then(function (newbook) {
let booksInTotal = vm.requestedBooks.allBooks.length + 1;
vm.requestedBooks.allBooks.push(newbook);
vm.requestedBooks.header = requestedBooksFactory.requestedText(booksInTotal);
}).then(() = > {
vm.title = '';
vm.author = '';
vm.link = '';
vm.comments = '';
vm.destination = false;
vm.submitted = false;
})
}
I dont like the way this looks, but cant come up with a way to solve it
My own way to refactore this code:
vm.submitRequest = submitRequest;
/**************/
function submitRequest() {
return requestBook()
.then(bookReceived)
.then(clearController);
}
function requestBook() {
return requestedBooksFactory.requestBook(vm.title, vm.author, vm.link, vm.destination, vm.currentUser, vm.comments);
}
function bookReceived(book) {
vm.requestedBooks.allBooks.push(book);
vm.requestedBooks.header = requestedBooksFactory.requestedText(vm.requestedBooks.allBooks.length);
}
function clearController() {
vm.title = '';
vm.author = '';
vm.link = '';
vm.comments = '';
vm.destination = false;
vm.submitted = false;
}
Source should not to be too zipped. It should be readable and clear.
Don't use anonymous functions for long methods.
They was designed for short iterators.
Don't afraid function names, they here for you, not for parser.
For parser you'll use obfuscators.
But I try to post what are you looking for :)
function clearController() {
['title', 'author', 'link', 'comments'].forEach(f => {vm[f] = '';});
['destination', 'submitted'].forEach(f => {vm[f] = false;});
}

how to change attribute text of json in jquery?

I am trying to change the property name /attr name of my json object.I try like that but nothing will change.I need to make json object after seen the input json and convert it like outjson
function changeData(data){
var title;
for(var i = 0; i < data.length; i++){
if(data[i].hasOwnProperty("displayName")){
data[i]["label"] = data[i]["displayName"];
delete data[i]["displayName"];
}
if(data[i].hasOwnProperty("displayDetail")){
data[i]["title"] = data[i]["displayDetail"];
delete data[i]["displayDetail"];
}
if(data[i].hasOwnProperty("inputType")){
if(data[i]["inputType"]=="NUMBER"){
data[i]["type"]="number"
}else if(data[i]["inputType"]=="TEXT"){
data[i]["type"]="text"
}else if(data[i]["inputType"]=="SWTICH"){
data[i]["type"]="select"
}
delete data[i]["inputType"];
}
}
console.log(data);
}
Try this - it's possibe to remove the if selection for inputType by creating a tiny lookup table from original value to new value:
function changeData(data) {
var map = { NUMBER: "number", TEXT: "text", SWITCH: "select" };
// data is an object - use for .. in to enumerate
for (var key in data.input) {
var e = data.input[key]; // alias for efficient structure dereferencing
e.label = e.displayName;
e.title = e.displayDetail;
e.type = map[e.inputType];
delete e.displayName;
delete e.displayDetail;
delete e.inputType;
}
};
There's really no need for the hasOwnProperty test these days - only use it if you think there's any risk that someone unsafely added to Object.prototype. jQuery manages without it quite happily, other modern code should do to.
If the mapping of field names was any longer I'd consider using another mapping table with another loop to remove the hard coded copy/delete pairs.
i have a nice Recursive function for that:
usage:
// replace list
var replacedObj = replaceAttrName(sourceObject, {foo: 'foooo', bar: 'baaar'});
so in your case you can easily do:
var newObj = replaceAttrName(json, {displayDetail: 'title', displayName: 'label', inputType: 'type'});
demo: http://jsfiddle.net/h1u0kq67/15/
the function is that:
function replaceAttrName(sourceObj, replaceList, destObj) {
destObj = destObj || {};
for(var prop in sourceObj) {
if(sourceObj.hasOwnProperty(prop)) {
if(typeof sourceObj[prop] === 'object') {
if(replaceList[prop]) {
var strName = replaceList[prop];
destObj[strName] = {};
replaceAttrName(sourceObj[prop], replaceList, destObj[strName]);
} else if(!replaceList[prop]) {
destObj[prop] = {};
replaceAttrName(sourceObj[prop], replaceList, destObj[prop]);
}
} else if (typeof sourceObj[prop] != 'object') {
if(replaceList[prop]) {
var strName = replaceList[prop];
destObj[strName] = sourceObj[prop];
} else if(!replaceList[prop]) {
destObj[prop] = sourceObj[prop];
}
}
}
}
return destObj;
}
If I am getting you right, you just want substitutions:
displayDetail => title
displayName => label
inputType => type.
I came up with the follwoing:
function changeData(data){
return JSON.parse(JSON.stringify(data).replace(/displayDetail/g, "title").replace(/displayName/g, "label").replace(/inputType/g, "type"));
}
Here is the Fiddle to play with.
Edit: I forgot replacements for "NUMBER", "TEXT" and "SWITCH".
function changeData(data){
return JSON.parse(JSON.stringify(data).replace(/displayDetail/g, "title").replace(/displayName/g, "label").replace(/inputType/g, "type").replace(/TEXT/g, "text").replace(/NUMBER/g, "number").replace(/SWITCH/g, "switch"));
}

Categories

Resources