I have a class of validations that I have created in JS:
let test = new Validator(req.body);
Now I want to test something, maybe that a specific key in this object is 2-5 char length, I would do it like this:
let myBoolean = test.selector("firstName").minLength(2).maxLength(5);
// firstName is like: req.body.firstName
And how this could be done in the class?
EDIT
I made something like this:
audit.isLength({selector: "from", gte: 2, lte: 35})
class Validator {
constructor(obj) {
this.obj = obj;
this.isValid = true;
}
isExists(sel) {
if (typeof this.obj[sel] === "undefined") return false;
return true;
}
isLength(info) {
let sel = this.obj[info.selector];
if (typeof sel === "undefined") return false;
if (info.gte) {
if (sel.length<info.gte) return false;
}
if (info.lte) {
if (sel.length>info.lte) return false;
}
if (info.gt) {
if (sel.length<=info.gt) return false;
}
if (info.lt) {
if (sel.length>=info.lt) return false;
}
return true;
}
}
Try something like this - assign the object to validate to a property on the instantiation, return this from each validating call, and when validating, assign to an isValid property on the object (if it isn't already false). Note that you need to access the isValid property finally in order to retrieve the boolean.
class Validator {
constructor(obj) {
this.obj = obj;
this.isValid = true;
}
selector(sel) {
this.sel = sel;
return this;
}
minLength(min) {
if (this.isValid) this.isValid = this.obj[this.sel].length >= min;
return this;
}
maxLength(max) {
if (this.isValid) this.isValid = this.obj[this.sel].length <= max;
return this;
}
}
const test = new Validator({firstName: 'foobar'}); // 6 chars: invalid
console.log(test.selector("firstName").minLength(2).maxLength(5).isValid);
const test2 = new Validator({firstName: 'fooba'}); // 5 chars: valid
console.log(test2.selector("firstName").minLength(2).maxLength(5).isValid);
const test3 = new Validator({firstName: 'f'}); // 1 char: invalid
console.log(test3.selector("firstName").minLength(2).maxLength(5).isValid);
Create a class with fluent methods/chainable methods, that return this, which is an instance of the class itself and when you finally run validation according to the rules, call .validate(), which will act as a final method to return the result:
class Validator {
constructor (body) {
this._body = body;
}
selector(str) {
this._selector = str;
return this;
}
minLength(num) {
this._minLength = num;
return this;
}
maxLength(num) {
this._maxLength = num;
return this;
}
validate() {
// run your validation logic here and return true or false accordingly
return true
}
}
const req = { body: 'body' };
const test = new Validator(req.body);
const myBoolean = test
.selector('firstName')
.minLength(2)
.maxLength(5)
.validate();
console.log('rules:');
console.log(test);
console.log(`result: ${myBoolean}`);
This is the builder pattern (sort of). You'll probably want to define a separate class that has a minLength and maxLength function. Those functions will set some state on the builder, and return either this (the builder its self), or a new builder that's a copy of this. Then you'd have some finalize function on the builder, which looks at the state, handles all the logic based on the min/max, and returns a boolean.
Related
I have a class like this:
class Outcome {
constructor(name) {
this.name = name;
this.inProgress = "";
this.success = null;
this.messages = [];
}
addMessage(type, text) {
this.messages.push({
type,
text
});
}
getMessagesByType(type) {
return this.messages.filter((message) => message.type === type);
}
}
In my React component I imported it, and I would like to use it like this:
submit() {
let outcome = new Outcome("submit");
outcome.inProgress = true;
this.setState({
outcome // Save in state so I can show a spinner
});
if (!formsDataValid) {
outcome.inProgress = false;
outcome.success = false;
outcome.addMessage("error", "Data are not valid");
this.setState({
outcome
});
return;
}
fetch().then((response) => {
outcome.inProgress = false;
if (response.ok) {
outcome.success = true;
outcome.addMessage("success", "Operation correctly performed");
} else {
outcome.success = false;
outcome.addMessage("error", response.error);
}
this.setState({
outcome
});
});
}
then in render I can check the result in this way:
render() {
{this.state.outcome?.inProgress ?
"Spinner here"
: this.state.outcome?.messages.length > 0 ?
"Here render the messages"
: null}
<button type="submit" disabled={this.state.outcome?.success || false}>Submit button</button>
}
This should works, but the problem is that in handle submit, when I'm doing for example outcome.success = false; it will edit the state directly, because the object is a reference.
Is there a clean way to do that without edit the state directly? I tried
this.setState({
outcome: { ...outcome }
});
but in this way it will remove the methods of the class in the object it clone in to the state.
I know we should use React Hooks, but the components is an old component and we have no time to change that.
One option would be to create a method that can produce a copy of an Outcome. Essentially when you do {...outcome} you're losing the prototype chain from outcome and only copying its members.
class Outcome {
constructor(name) {
this.name = name;
this.inProgress = "";
this.success = null;
this.messages = [];
}
addMessage(type, text) {
this.messages.push({
type,
text
});
}
getMessagesByType(type) {
return this.messages.filter((message) => message.type === type);
}
clone() {
const result = new Outcome(this.name);
result.inProgress = this.inProgress;
result.success = this.success;
result.messages = this.messages;
return result;
}
}
You could then use it like this:
this.setState({
outcome: outcome.clone();
});
There's also a generic way to do this that'll work for (most) classful objects.
function cloneInstance(obj) {
return Object.create(Object.getPrototypeOf(obj), Object.getOwnPropertyDescriptors(obj));
}
If you want to be able to easily mutate the result too for more of a functional style, I'd probably use something like this:
function immutableModify(obj, cb = o => o) {
const result = Object.create(
Object.getPrototypeOf(obj),
Object.getOwnPropertyDescriptors(obj)
);
cb(result);
return result;
}
which can then be used like
this.setState({
outcome: immutableModify(outcome, outcome => outcome.name = "test")
});
The function skeleton is at the top of mixin.js. It takes in a target object (o) and another object with properties to be mixed in (mixin).
You should return a Proxy object from this function. Override the "get" trap so that:
1) If the object already has a property, the object's property is returned.
2) If the object does not have the property, it returns the property from the mixin object.
3) If the property is "__original", it returns the original object "o". (This design provides a way to "unmix" a mixin.)
4) If none of the other cases hold, undefined should be returned as the result.
You should not make any changes to this file outside of the addMixin function definition.
This is what I have so far,
function addMixin(o, mixin) {
let oldValue = {};
return new Proxy(o, {
get: function(target, property) {
if(target.hasOwnProperty(property) === true) {
return target[property];
} else if (target.hasOwnProperty(property) === false) {
oldValue[property] = target[property];
return mixin[property];
} else if (property === '__original') {
return target[property] = oldValue[property];
} else {
return undefined;
}
}
})
// A sample mixin.
let PlayableMixin = {
// Plays a system bell 3 times
play: function() {
console.log("\u0007");
console.log("\u0007");
console.log("\u0007");
},
duration: 100,
};
function Song(name, performer, duration) {
this.name = name;
this.performer = performer;
this.duration = duration;
}
Song.prototype = addMixin(Song.prototype, PlayableMixin);
Song.prototype.display = function() {
console.log(`Now playing "${this.name}", by ${this.performer}. (${this.duration})`);
}
let s = new Song("Gun Street Girl", "Tom Waits", "4:17");
s.display();
s.play();
console.log(s.duration);
s = s.__original;
console.log(s.play);
Expected Output
Now playing "Gun Street Girl", by Tom Waits. (4:17)
4:17
undefined
Gives me an error instead of printing undefined.
You don't really need the if else chain. return already breaks out of the function. __original should be checked first, because it should return the original object regardless of whether it exists as a property or not; it is essentially a reserved symbol name for a computed property.
Your code is also missing a closing bracket.
function addMixin(o, mixin) {
let oldValue = {};
return new Proxy(o, {
get: function(target, property) {
if (property === '__original') {
return o;
}
if (target.hasOwnProperty(property) === true) {
return target[property];
} else if (target.hasOwnProperty(property) === false) {
oldValue[property] = target[property];
return mixin[property];
} else {
return undefined;
}
}
})
}
// A sample mixin.
let PlayableMixin = {
// Plays a system bell 3 times
play: function() {
console.log("\u0007");
console.log("\u0007");
console.log("\u0007");
},
duration: 100,
};
function Song(name, performer, duration) {
this.name = name;
this.performer = performer;
this.duration = duration;
}
Song.prototype = addMixin(Song.prototype, PlayableMixin);
Song.prototype.display = function() {
console.log(`Now playing "${this.name}", by ${this.performer}. (${this.duration})`);
}
let s = new Song("Gun Street Girl", "Tom Waits", "4:17");
s.display();
s.play();
console.log(s.duration);
s = s.__original;
console.log(s, s.play);
Just a side comment: I'd complain about the problem description. The way declared conditions are listed almost seems like it was purposely set up to be confusing. It's like one of those silly riddles where someone makes you repeat what they say and say something stupid about yourself.
I have written below code to check for locator type and based on whether element is visible or not, I am returning element. I am receiving error on call type(locatorType, value,text) method with appropriate values.
this.type = function(locatorType,value,text){
this.getElement(locatorType,value).sendKeys(text)
};
this.getElement = function(locatorType,value){
if(locatorType=='model'){
console.log(locatorType)
console.log(value)
return this.waiterFunc(element(by.model(value)));
}
else if(locatorType=='xPath'){
return this.waiterFunc(element(by.xPath(value)));
}
else if(locatorType=='buttonText'){
return this.waiterFunc(element(by.buttonText(value)));
}
};
this.waiterFunc = function(element){
console.log('In waiterfunc')
//console.log(element.getText())
browser.wait(function() {
return this.isVisible(element).then(function(){
return element;
})
})
};
this.isVisible = function(element){
return EC.visibilityOf(element);
};
Below is the error being received:
WebDriver is not able to find the element and perform the actions on it. Please suggest where I am wrong.
Separate the waiting function with the element return:
this.getElement = function(locatorType, value) {
var elm;
if (locatorType == 'model') {
elm = element(by.model(value));
this.waiterFunc(elm);
}
else if (locatorType == 'xPath') {
elm = element(by.xpath(value)); // also renamed xPath -> xpath
this.waiterFunc(elm);
}
else if (locatorType == 'buttonText') {
elm = element(by.buttonText(value));
this.waiterFunc(elm);
}
return elm;
};
In this case the waiterFunc would become simpler:
this.waiterFunc = function(element){
browser.wait(this.isVisible(element));
};
I have a text input that I want to enable users to call functions from.
Essentially I want to tie strings to functions so that when a user types a certain 'command' prefaced with a backslash the corresponding function is called.
Right now for example's sake you can type /name, followed by a value and it will set name as a property of the user object with the value the user gives.
So how would I do this with 20 or so 'commands'?
http://jsfiddle.net/k7sHT/5/
jQuery:
$('#textCommand').on('keypress', function(e) {
if(e.keyCode==13) {
sendConsole();
}
});
var user = {};
var sendConsole = function() {
value = $('#textCommand').val();
if (value.substring(0,5) === "/name") {
user.name = value.substring(6,20);
alert(user.name);
} else {
$('body').append("<span>unknown command: "+value+"</span><br />")
$('#textCommand').val("");
}
}
HTML:
<input id="textCommand" type="text"><br/>
Store your functions in an object, so you can retrieve and call them by key:
// Store all functions here
var commands = {
name : function() {
console.log("Hello");
}
}
var sendConsole = function() {
value = $('#textCommand').val();
// Strip initial slash
if(value.substring(0,1) === '/') {
value = value.substring(1);
// If the function exists, invoke it
if(value in commands) {
commands[value](value);
}
}
}
http://jsfiddle.net/NJjNB/
Try something like this:
var userFunctions = {
run: function(input)
{
var parts = input.split(/\s+/);
var func = parts[0].substr(1);
var args = parts.slice(1);
this[func].call(this, args);
},
test: function(args)
{
alert(args.join(" "));
}
};
userFunctions.run("/test hello there"); // Alerts "hello there".
You can do:
if(window["functionName"])
{
window["functionName"](params);
}
function Validator(formIsValid) {
if(this.formIsValid) {
alert('Form is valid!');
}
else {
alert('Form is invalid...');
}
}
Validator.prototype = { // Notice the .prototype here, it's important!
formIsValid: true,
enforceTextFieldMinLength: function(field, minLength) {
if (!field.value || field.value.length < minLength) {
this.formIsValid = false;
}
},
enforceLabelHasText: function(label) {
if (!label.text) {
this.formIsValid = false;
}
}
}
//var val = new Validator();
The above is my Val.js. This is how i am using in my otherFile.js
AddPatient.Firstname = FirstNameValue || Validator.enforceLabelHasText(FirstName);
I get an error saying cannot find function enforceLabelHasText in Object function Validator(formIsValid)
You can't put expressions in an object definition. If you want code to be executed after an object instance is created, you should use:
function Validator() {
if(this.formIsValid) {
alert('Form is valid!');
}
else {
alert('Form is invalid...');
}
}
Validator.prototype = { // Notice the .prototype here, it's important!
formIsValid: true,
enforceTextFieldMinLength: function(field, minLength) {
if (!field.value || field.value.length < minLength) {
this.formIsValid = false;
}
},
enforceLabelHasText: function(label) {
if (!label.text) {
this.formIsValid = false;
}
}
}
var a = new Validator();
This is a dummy solution; you will want to add arguments to the Validator() function, to initialize formIsValid and the other values. I suggest you should read the MDC's description on prototypes.
EDIT: If you went with the prototype solution, you need to call val.enforceLabelHasText(FirstName), after making val a global variable (either by omitting the var or by using var window.val = new Validator()).
This is not valid syntax.
You've dumped an if/else condition inside an object definition, like this:
var myObj = { a, b, c, d,
if (true) {
alert('WTF!');
}
};
Procedural code like this must be inside a function.
You can insert logic into an object literal, using an iife. Like this;
const testable = 1
const obj = {
a: 'value1',
b: (() => {
if (testable === 1) {
return 'testable was 1'
} else {
return 'testable was not 1'
}
})()
}
console.log(obj)
Validator is an object literal and you can only assign properties there, not arbitrary code.
You could assign a function which includes your code to a property.
Bind this to a variable in the beginning.
var that = this;
This keeps this changing and point to something else.
And use firebug!