I have created a function to test a input field, the function currently accepts 6 inputs, 3 of them are text to be typed and the other 3 are error messages that we get when certain text is typed, this works fine. The function will loop depending on the number of texts entered, so in this case it will enter text and then click submit and then check error message. It will do this 3 times because I am passing in 3 text values. However I want to be able to make it dynamic so that it can take any number values and loop according to however many are passed.
Cypress.Commands.add('checkErrMsg', (fieldText1, fieldText2, fieldText3, errorText1,errorText2, errorText3) => {
var fieldValues = [fieldText1, fieldText2, fieldText3];
var errorValues = [errorText1, errorText2, errorText3];
var sum =0;
fieldValues.forEach(function(entry) {
cy.get('.textBox').clear().type(entry)
cy.get('.addBtn').click()
cy.get('#errMsg').should('be.visible').and('have.contain',(errorValues[sum++])).click();
});
})
I add the custom command to namespace as required by typescript project in cyprss.
declare namespace Cypress {
interface Chainable {
checkErrMsg(fieldText1: string, fieldText2: string, fieldText3:string, errorText1:string
,errorText2: string, errorText3:string): Chainable<string>;
}
}
The best way to make this kind of variadic function is with a rest parameter. The tricky part is that you need two - one for the fields, one for the errors - if you have each of them as a separate argument. One option is a [string, string] tuple type for each pair of field and error. The declaration would look like this:
declare namespace Cypress {
type FieldAndErrorValues = [string, string];
interface Chainable {
checkErrMsg(...fieldAndErrorValues: FieldAndErrorValues[]): Chainable<string>;
}
}
And the implementation:
Cypress.Commands.add('checkErrMsg', (...fieldAndErrorValues: Cypress.FieldAndErrorValues[]) => {
fieldAndErrorValues.forEach(function([fieldValue, errorValue]) {
cy.get('.textBox').clear().type(fieldValue);
cy.get('.addBtn').click();
cy.get('#errMsg').should('be.visible').and('have.contain',(errorValue)).click();
});
});
And a possible invocation:
cy.checkErrMsg(['field1', 'error1'], ['field2', 'error2']);
Related
im having trouble getting data from a form group. I have this formGroup called lineitemForm, and I'm wanting to format said form group in the following:
private formatTransferData() {
const depositDates = this.getDepositDates(this.lineItemsForm);
const mappedValues = this.formatFormValues(depositDates);
return this.filterFormValues(mappedValues);
}
private formatFormValues(depositDates) {
return depositDates.map((depositDate) => {
return {
effectiveDates: depositDate.controls.effectiveDates.value,
depositDate: depositDate.controls.depositDate.value,
};
});
}
I have been noticing that the function formatTransferData() breaks down when I pass depositDates into this.FormatValues(), there are two numeric variables inside of depositDates, I can console.log inside the mapping depositDate.controls.effectiveDates and see two numeric values, but as soon as I add on the .value to the end the numeric values either resolve themselves to null or a simple "", is there some sort of conflict that I am unaware of when using .value on a formGroup that contains numeric value/s?
On later reflection, the best way to go about getting values from a formGroup is by doing the following:
this.formGroup.controls['someFormGroupValue'].value
or
this.someForm.get('someFormItem').value;
I'm a backend dev moved recently onto js side. I was going through a tutorial and came across the below piece of code.
clickCreate: function(component, event, helper) {
var validExpense = component.find('expenseform').reduce(function (validSoFar, inputCmp) {
// Displays error messages for invalid fields
inputCmp.showHelpMessageIfInvalid();
return validSoFar && inputCmp.get('v.validity').valid;
}, true);
// If we pass error checking, do some real work
if(validExpense){
// Create the new expense
var newExpense = component.get("v.newExpense");
console.log("Create expense: " + JSON.stringify(newExpense));
helper.createExpense(component, newExpense);
}
}
Here I tried to understand a lot on what's happening, there is something called reduce and another thing named validSoFar. I'm unable to understand what's happening under the hood. :-(
I do get the regular loops stuff as done in Java.
Can someone please shower some light on what's happening here. I should be using this a lot in my regular work.
Thanks
The reduce function here is iterating through each input component of the expense form and incrementally mapping to a boolean. If you have say three inputs each with a true validity, the reduce function would return:
true && true where the first true is the initial value passed into reduce.
true && true and where the first true here is the result of the previous result.
true && true
At the end of the reduction, you're left with a single boolean representing the validity of the entire, where by that if just a single input component's validity is false, the entire reduction will amount to false. This is because validSoFar keeps track of the overall validity and is mutated by returning the compound of the whether the form is valid so far and the validity of the current input in iteration.
This is a reasonable equivalent:
var validExpense = true;
var inputCmps = component.find('expenseform')
for (var i = 0; i < inputCmps.length; i++) {
// Displays error messages for invalid fields
inputCmp.showHelpMessageIfInvalid();
if (!inputCmp.get('v.validity').valid) {
validExpense = false;
}
}
// Now we can use validExpense
This is a somewhat strange use of reduce, to be honest, because it does more than simply reducing a list to a single value. It also produces side effects (presumably) in the call to showHelpMessageIfInvalid().
The idea of reduce is simple. Given a list of values that you want to fold down one at a time into a single value (of the same or any other type), you supply a function that takes the current folded value and the next list value and returns a new folded value, and you supply an initial folded value, and reduce combines them by calling the function with each successive list value and the current folded value.
So, for instance,
var items = [
{name: 'foo', price: 7, quantity: 3},
{name: 'bar', price: 5, quantity: 5},
{name: 'baz', price: 19, quantity: 1}
]
const totalPrice = items.reduce(
(total, item) => total + item.price * item.quantity, // folding function
0 // initial value
); //=> 65
It does not make sense to use reduce there and have side effects in the reduce. Better use Array.prototype.filter to get all invalid expense items.
Then use Array.prototype.forEach to produce side effect(s) for each invalid item. You can then check the length of invalid expense items array to see it your input was valid:
function(component, event, helper) {
var invalidExpenses = component.find('expenseform').filter(
function(ex){
//return not valid (!valid)
return !ex.get('v.validity').valid
}
);
invalidExpenses.forEach(
//use forEach if you need a side effect for each thing
function(ex){
ex.showHelpMessageIfInvalid();
}
);
// If we pass error checking, do some real work
if(invalidExpenses.length===0){//no invalid expense items
// Create the new expense
var newExpense = component.get("v.newExpense");
console.log("Create expense: " + JSON.stringify(newExpense));
helper.createExpense(component, newExpense);
}
}
The mdn documentation for Array.prototype.reduce has a good description and examples on how to use it.
It should take an array of things and return one other thing (can be different type of thing). But you won't find any examples there where side effects are initiated in the reducer function.
I'm currently trying to get the values a user would insert into an input form. In vanilla javascript, I can just target the element by id or class, etc, and then I can just use the .value method to actually get in use that method. For some reason, typescript cannot do that, which I do not understand because typescript is a superset of javascript. Is there a specific way to get a value from an input element in pure typescript or do I have to use angular or something?
Typescript code:
interface IUserFoodOptions {
food: string;
calories: number;
foodDate: any;
}
class Food implements IUserFoodOptions {
food: string;
calories: number;
foodDate: any;
// store all the calories in an array and calculate the total amount of calories
caloriesArray: number[] = [];
// constructor
constructor(food: string, calories: number, foodDate: any) {
this.food = food;
this.calories = calories;
this.foodDate = foodDate;
}
}
// event listener for when the add food button is clicked
let addFood = document.getElementById("add-food-button").addEventListener("click", () => {
// get the values from inputs and store them in an array
let foodName = document.getElementById("food-name-val");
let foodCalories = document.getElementById("calories-val");
let dateVal = document.getElementById("date-val");
// store these values in a list and display them below
// user will have the ability to edit and delete them
// am I create event listeners within event listeners
});
If you are using an editor like VSCode to write Typescript, I've found the ability to inspect code very valuable in learning more about what's occurring in the typing system. In VSCode you can right click on the method(or type) and choose Go to definition.
Inspecting the method in your question, getElementById, you can see it returns an HTMLElement. This type doesn't have a value property on it. This makes sense as getElementById can return any HTMLElement on the page as long as it has an id attribute. Not every HTMLElement though has a value property(for instance a div/span/p, etc).
Since you know what type you are expecting, but the type system can't, to get this to work, you have to tell Typescript what type of element you expect to be selecting. You would do that through casting the type of the selected element as follows:
const inputElement = <HTMLInputElement> document.getElementById("food-name-val");
or
const inputElement = document.getElementById("food-name-val") as HTMLInputElement;
Now, since Typescript recognizes the selected element as an HTMLInputElement, it won't error when you access the value property on it.
In your case that would look like:
let foodName = (document.getElementById("food-name-val") as HTMLInputElement).value;
Yeah, TypeScript has this "little issue", but it is for safety.
You can get the input value doing something like this:
var inputValue = (<HTMLInputElement>document.getElementById(elementId)).value;
You can see more about this casting <> thing here: TypeScript: casting HTMLElement
Hope it works!
You can get the value of an input in TypeScript.
For a number,
var num = parseFloat((<HTMLInputElement>document.getElementById("myValue")).value);
or
let num : number = parseFloat((<HTMLInputElement>document.getElementById("myValue")).value);
Strings;
var str = (<HTMLInputElement>document.getElementById("myUnit")).value;
or
let str : string = (<HTMLInputElement>document.getElementById("myUnit")).value;
It's important to cast HTMLElement to HTMLInputElement, otherwise 'value' property doesn't exist for HTMLElement in TypeScript and TypeScript compiler will show an error.
// event listener for when the add food button is clicked
let addFood = document.getElementById("add-food-button").addEventListener("click", () => {
// get the values from inputs and store them in an array
let foodName = (<HTMLInputElement>document.getElementById("food-name-val")).value;
let foodCalories = parseFloat((<HTMLInputElement>document.getElementById("calories-val")).value);
let dateVal = (<HTMLInputElement>document.getElementById("date-val")).value;
// And so on ...
});
I find the following code more readable:
const user: string = document.querySelector<HTMLInputElement>('input[name="user"]').value;
I would like the autocomplete to show the entire list when the input box gets focused (no input is given). Would also like the auto complete to match substrings without having to fiddle with private variables.
At the moment the code is:
autocomplete = goog.ui.ac.createSimpleAutoComplete(
gsa.Game.gameData.teams, team2, false);
matcher=autocomplete.getMatcher();
matcher.useSimilar_=true
autocomplete.setMatcher(matcher);
Similar matches work but have to set a private variable for that (no getter or setter available).
The other one I have not been able to find out; how to show all data when no input is given (like a smart select input). So when the textbox receives focus it'll show all data since there is no filter text given. These are basic things that one would like to configure but can't find it in the API documentation.
You need to create descendants of goog.ui.ac.AutoComplete, goog.ui.ac.ArrayMatcher and goog.ui.ac.InputHandler. Also you will directly create the instance of auto complete object instead of calling goog.ui.ac.createSimpleAutoComplete.
In goog.ui.ac.AutoComplete descendant you assign custom input handler and matcher.
goog.provide('my.ui.ac.AutoComplete');
goog.require('goog.ui.ac.Renderer');
goog.require('my.ui.ac.ArrayMatcher');
goog.require('my.ui.ac.InputHandler');
my.ui.ac.AutoComplete = function(data, input, opt_multi, opt_useSimilar) {
var renderer = new goog.ui.ac.Renderer();
var matcher = new my.ui.ac.ArrayMatcher(data, !opt_useSimilar);
var inputhandler = new my.ui.ac.InputHandler(null, null, !!opt_multi, 300);
goog.ui.ac.AutoComplete.call(this, matcher, renderer, inputhandler);
inputhandler.attachAutoComplete(this);
inputhandler.attachInputs(input);
};
goog.inherits(my.ui.ac.AutoComplete, goog.ui.ac.AutoComplete);
In goog.ui.ac.ArrayMatcher descendant you need to override getPrefixMatches() method, since the default behaviour discards empty strings. So if there is an empty string, we just return the first x rows from the data.
goog.provide('my.ui.ac.ArrayMatcher');
goog.require('goog.ui.ac.ArrayMatcher');
my.ui.ac.ArrayMatcher = function(rows, opt_noSimilar) {
goog.ui.ac.ArrayMatcher.call(this, rows, opt_noSimilar);
};
goog.inherits(my.ui.ac.ArrayMatcher, goog.ui.ac.ArrayMatcher);
my.ui.ac.ArrayMatcher.prototype.getPrefixMatches = function(token, maxMatches) {
if (token == '')
{
// for empty search string, return first maxMatches rows
return this.rows_.slice(0, maxMatches);
}
else
{
return goog.base(this, 'getPrefixMatches', token, maxMatches);
}
};
In goog.ui.ac.InputHandler descendant you need to override processFocus() method, and force to show the autocomplete popup. This can be done by calling update() method with first parameter set to true.
goog.provide('my.ui.ac.InputHandler');
goog.require('goog.ui.ac.InputHandler');
my.ui.ac.InputHandler = function(opt_separators, opt_literals, opt_multi, opt_throttleTime) {
goog.ui.ac.InputHandler.call(this, opt_separators, opt_literals, opt_multi, opt_throttleTime);
};
goog.inherits(my.ui.ac.InputHandler, goog.ui.ac.InputHandler);
my.ui.ac.InputHandler.prototype.processFocus = function(target) {
goog.base(this, 'processFocus', target);
// force the autocomplete popup to show
this.update(true);
};
I am doing a form for my javascript class, and I am getting stuck on a certain portion of it. I have a separate validator javascript file and call the function on the html file. All the validation works if the form areas are not filled in. What I want to do is if the fields are left blank they will fail the validation and will insert a value into that field. Below are an example of the form field, javascript function in the html page, and the external validator js file.
call function in html head:
function formvalidation(thisform) {
with (thisform) {
if (textbox_validation(first_name,"Please enter your first name.")==false)
{first_name.blur(); return false;};
if (textbox_validation(business_name,"Please enter your business. Please enter N/A if
you do not have one.")==false) { business_name.focus(); return false;
business_name.value=="N/A";};
The external js validator:
function textbox_validation(entered, alertbox) {
with (entered) {
if (value==null || value=="") {
alert(alertbox);
return false;
}
else {
return true;
}
}
}
So the validator works and focuses on the empty fields, but for some of my fields I want them to fill themselves with a certain value if validation fails or if it isnt filled int. The business_name line of code is when I tried to make it work. Any help is much appreciated!
Ordinarilly, you wouldn't use alert, but would instead put error messages in a span or div either near the input or at the top (or bottom) of the form. Additionally (as mentioned by #Frits van Campen) it is generally bad practice to use with
Try something like this instead:
function textbox_validation(entered, errormsg) {
var errbox = document.getElementById(entered.id + '-errors'); // just to prevent writing it twice
// Note this requires the input to have an id, and the errer box's id to be the same with an '-errors' suffix.
// Instead of using with, just acces properties normally
if (!entered.value) { // The `!` "neggation" operater makes "falsy" values `true`
// "falsy" values include `false`, the empty string, `0`, `null`, `undefined`, `NaN` and a few others
// Put the error message in the DOM instead of alerting it
errbox.innerHTML = errormsg;
return false;
}
else {
// Wipe any previous error messages
errbox.innerHTML = '';
return true;
}
}
And for the form validator, again; let's not use with. But also, when attempting to assing "N/A" to the value, you've used the comparison operator instead of the assignment operator, and you've done it after returning:
function formvalidation(thisform) {
// just use the `!` "negation" operator
if (!textbox_validation(thisform.first_name,
"Please enter your first name."))
{
thisform.first_name.blur();
return false;
}
if (!textbox_validation(business_name,
"Please enter your business. Please enter N/A if you do not have one."))
{
thisform.business_name.focus();
thisform.business_name.value = "N/A"; // for assignment, use `=`. `==` and `===` are used for comparison
return false; // a return statement ends the function, make sure it's after anything you want to execute!
}
}
Use the DOM to set the placeholder for the fields. Like this.
var myInput = document.getElementById('input1');
myInput.placeholder = 'This validation has failed.';