There is a function that checks the content of the url with window.location.href. If the URL contains a specific string it should return true:
const doesItContain = () => {
const url = window.location.href;
return url.includes('/items');
};
This works fine, if the url contains /items it returns true. The issue comes when it needs to check for multiple strings (multiple pages) and return true if one of them are present. For example:
const doesItContain = () => {
const url = window.location.href;
return url.includes('/items', '/users');
};
It returns true only for the first string in the list. Is there a way to check for both or more than one string?
String.prototype.includes expects one argument. The other arguments are ignored.
You can use some to iterate an array of strings and check if at least one element fullfils the condition:
const doesItContain = () => {
const url = window.location.href;
return ['/items', '/users'].some(el => url.includes(el));
};
You can use a regular expression, separating each of the strings to check with |.
return /\/items|\/users/.test(url);
Related
I have a switch^
let s = "aaa"
switch (true) {
case s.includes('a'):
url = 'https://a.com/wp-admin/post-new.php'
console.log(url)
break
case s.includes('b'):
url = 'https://b.com/wp-admin/post-new.php'
console.log(url)
break
default:
console.log('default')
break
}
I'm trying to change this to a literal object, but I can't figure out how to properly specify the s.includes condition as the object key
const objLit = (s) => {
const cases = {
//s.includes('a'): 'https://a.com/wp-admin/post-new.php',
[`${s.includes('a')}`]: 'https://a.com/wp-admin/post-new.php',
//s.includes('b'): 'https://b.com/wp-admin/post-new.php',
[`${s.includes('b')}`]: 'https://b.com/wp-admin/post-new.php',
}
let url = cases[s]
return url
}
console.log(objLit(s))
Although this does not throw an error, it does not work.
You can't. Property names are strings or Symbols.
When you use [] syntax, the expression is evaluated at object creation time.
You can't assign code that gets evaluated at lookup time. This is what switch and if/else are for.
In your case, since includes() returns a boolean, you are just creating an object with properties named "true" and "false".
You could say let url = cases.true, but it would be a horrible hack.
I've just come across a component I'm struggling to get my head around, but basically the value I care about is allowForClassification, which is a boolean value that is passed down into a child component and determines whether or not we display a button. (Basically meaning that only after a user has typed a letter will the "Find" button appear - currently even a spacebar will trigger the button to appear).
However I'm struggling to understand in this component where exactly that check is being made, I understand that at the bottom allowForClassification is set to true if canClassify & !classifyInProgress are returned but I can't find where they are making the check for the keyboard entry, any advice would be really helpful.
const getCcceValues = (object?: FormObjectModel | null) => {
const ccceInput: $Shape<CcceInput> = {};
if (!object) {
return {};
}
const ccceValues = object.attributeCollection.questions.reduce(
(acc, attribute) => {
const fieldEntry = ccceBeInformedFieldMap.get(attribute.key);
if (fieldEntry) {
acc[fieldEntry] = attribute.value;
}
return acc;
},
ccceInput
);
// ready to perfom classification based on user input
const canClassify = Object.values(ccceValues).every(Boolean);
return { canClassify, ccceValues };
};
export const useCcceEmbed = (
ccceResultAttribute: AttributeType,
onChange: Function
): CcceHook => {
const { object, form } = useFormObjectContext();
const [resultCode, setResultCode] = useState<string | null>(null);
const { canClassify, ccceValues } = getCcceValues(object);
const { handleSubmit } = useFormSubmit();
const [showModal, setShowModal] = useState<boolean>(false);
const handleCloseModal = useCallback(() => setShowModal(false), []);
const handleShowModal = useCallback(() => setShowModal(true), []);
// state value to keep track of a current active classification
const [classifyInProgress, setClassifyInProgress] = useState<boolean>(false);
const handleResult = useCallback(
(result) => {
if (result?.hsCode) {
onChange(ccceResultAttribute, result.hsCode);
setResultCode(result.hsCode);
setClassifyInProgress(false);
handleSubmit(form);
}
},
[ccceResultAttribute, form, handleSubmit, onChange]
);
const handleCancelClassify = useCallback(() => {
setClassifyInProgress(false);
handleCloseModal();
}, [handleCloseModal]);
const handleClassify = useCallback(
(event?: SyntheticEvent<any>) => {
if (event) {
event.preventDefault();
console.log("scenario 1");
}
if (classifyInProgress || !canClassify) {
console.log("scenario 2");
return;
}
const ccce = window.ccce;
if (!ccceValues || !ccce) {
throw new Error("Unable to classify - no values or not initialised");
console.log("scenario 3");
}
setClassifyInProgress(true);
const classificationParameters = {
...ccceValues,
...DEFAULT_EMBED_PROPS,
};
ccce.classify(
classificationParameters,
handleResult,
handleCancelClassify
);
},
[
classifyInProgress,
canClassify,
ccceValues,
handleResult,
handleCancelClassify,
]
);
return {
allowForClassification: canClassify && !classifyInProgress,
classifyInProgress,
dataProfileId,
embedID: EMBED_ID,
handleCancelClassify,
handleClassify,
handleCloseModal,
handleShowModal,
isDebugMode,
resultCode,
shouldShowModal: showModal,
};
};
Solution
There's a lot that's missing in this code and there may be a better way to do this. But here's what I came up with.
Instead of evaluating whether or not the input value is empty, we can evaluate whether or not it contains a character.
Replace this line:
const canClassify = Object.values(ccceValues).every(Boolean);
With these two lines:
const regex = new RegExp('\w', 'g');
const canClassify = Object.values(ccceValues).every(value => regex.test(value));
or this one line:
const canClassify = Object.values(ccceValues).every(value => /\w/g.test(value))
Why That Line?
I arrived at that solution by working backwards. Here's all the steps that I took through the code to arrive at the right place.
allowForClassification: canClassify && !classifyInProgress,
allowForClassification is true if canClassify is true and classifyInProgress is false. We don't want to classify if we are already performing a classification. That makes sense. So it's the canClassify value that we care about.
const { canClassify, ccceValues } = getCcceValues(object);
canClassify comes from the getCcceValues function, and it's value may depend on the value of object.
const { object, form } = useFormObjectContext();
object comes from some external hook which is a dead end for me because I can't see that code. But note that there might be some alternative solution here.
const canClassify = Object.values(ccceValues).every(Boolean);
.every(Boolean) has the same meaning as writing it out longform .every(value => Boolean(value)). canClassify is true if every value of ccceValues is true when cast to a Boolean. Which means that the value is anything other than: false, 0, -0, 0n, "", null, undefined, NaN. (source)
const ccceInput: $Shape<CcceInput> = {};
const ccceValues = object.attributeCollection.questions.reduce(
(acc, attribute) => {
const fieldEntry = ccceBeInformedFieldMap.get(attribute.key);
if (fieldEntry) {
acc[fieldEntry] = attribute.value;
}
return acc;
},
ccceInput
);
ccceValues is bit complex. It created by an array .reduce() operation. It starts with an initial value of ccceInput which is an empty object {}. At each step of the iteration, it maybe sets one property on that object. The final value is the object after a bunch of properties have been set.
The arguments in the callback are acc -- the object that we are building, and attribute -- the current element of the questions array. The key that we are setting is fieldEntry and the value is attribute.value.
Remember that canClassify checks if all values are "truthy". So the key doesn't matter. We only care about the value (attribute.value).
Why That Code?
But working backwards, what we learning is that when canClassify/allowForClassifiation is true in cases where you want it to be false, it's because there are values where Boolean(attribute.value) is true when you want it to be false. Why is that?
I'm going to make a logical assumption that the attribute.value variable contains the string that the user has typed in the box.
Remember what I said earlier about Boolean casting. The empty string '' is false and all other strings are true. So if there is at least one box in object.attributeCollection.questions where the user hasn't entered anything, then allowForClassification will be false because attribute.value will be ''.
As soon as the user hits the spacebar in the last box, the attribute.value becomes ' '. This string has a Boolean value of true, so allowForClassification is now true.
But you want to apply a different ruleset. You want to consider a value as true if and only if it contains at least one letter. We can check that using a simple regular expression. I'm not sure of your exact ruleset (Are numbers ok? What about underscores? What about accented/non-Latin characters?), so you might need to tweak the regex. But this should get you 99% of the way there.
const regex = new RegExp('\w', 'g');
// can also be written as: const regex = /\w/g;
I am using the \w alphanumeric character class and the g global match flag to match any alphanumeric character anywhere in a string.
To check if a string matches this regex, use the .test() method.
const hasCharacter = regex.test(someString);
We want to use this function in the .every() callback of canClassify instead of the Boolean constructor function. So we write:
const canClassify = Object.values(ccceValues).every(value => regex.test(value));
I am trying to get the url query string parameters from current page and I have the following code:
doInit: function (component, event, helper) {
var urlParams = new URLSearchParams(window.location.search);
console.log("params::: ", urlParams);
if (urlParams.has("openSidebar") && urlParams.get("openSidebar") == true) {
console.log("redirection happening....");
component.set("v.showFeed", true);
component.set("v.isSidebarOpen", true);
}
},
for some reason seems that I cannot use this line var urlParams = new URLSearchParams(window.location.search); and I dont know why.
Are there any alternative or salesforce way hwo to get the query string parameters from url?
I am basically getting nothing, the execution seems to stop at the line where I am using URLSearchParams!
Also curious to know why does lightning does not let plain javascript to execute in this case?
Using new URLSearchParams will return an instance of a class, not an object (or map).
You can use this code to convert the key/pair values into an object. Then you can check the value on the object instead:
const searchParams = new URLSearchParams('?openSidebar=true&foo=bar&test=hello%26world')
const params = [...searchParams.entries()].reduce((a, [k, v]) => (a[k] = v, a), {})
console.log(params)
if (params.openSidebar === 'true') {
console.log("redirection happening....");
// do stuff here
}
Note, we use === 'true' since the url parameter will always be a type of string.
Since you said it wasn't working, you could build your own parser:
const qs = '?openSidebar=true&foo=bar&test=hello%26world'
.slice(1) // remove '?'
const d = decodeURIComponent // used to make it shorter, replace d with decodeURIComponent if you want
const params = qs
.split('&') // split string into key/pair
.map(s => s.split('=')) // split key/pair to key and pair
.reduce((a, [k, v]) => ((a[d(k)] = d(v)), a), {}) // set each object prop name to k, and value to v
console.log(params)
Note, we use decodeURIComponent() (or shorthand d()) last, because the parameters may contain ampersands or equals signs. Should we call d() first, we would split on these characters, which we don't want to happen.
URLSearchParams() wasn't working on my browser as well but I came up with a helper function that gets the job done.
function getURLSearchParameters() {
const urlSearchParameters = {};
let searchParameters = decodeURIComponent(window.location.search);
if (searchParameters !== '') {
searchParameters = searchParameters.substring(1).split('&'); // get ride of '?'
for (let i = 0; i < searchParameters.length; i++) {
[key, value] = searchParameters[i].split('=');
urlSearchParameters[key] = value;
}
}
return urlSearchParameters;
}
Unfortunately, I can't answer your other questions because I don't have any experience with salesforce
I'm trying to see if array.reduce would be a better option in this case.
I'd like to return the string result instead of having to set a variable in the forEach loop.
So what I'm doing is seeing if there are any matches inside a string matching my regex. Then getting the text inside that match and replacing it with a variable I pass to it. All works great but I'd like to clean it up if I can. findReplaceTemplateString is a separate function and I'm ok with that. Its the forEach feels like I could use a reducer instead to return the completed string. But I'm new to reducers and not sure if this is a good case for it. Anyone have any thoughts on this.
const reg = /\{.+?\}/g;
const matches = tpl.match(reg);
let str = '';
matches.map(item => item.slice(1, -1)).forEach((placeHolder) => {
str = findReplace(tpl, placeHolder, props[placeHolder] || '');
});
I don't see the point of overcomplicating it. Simply use String.prototype.replace() with function as a second parameter. That will dynamically replace your pattern with valid parameters.
const input = 'Hi there, {name}! What are you doing in {city}?';
const props = {name: 'Alex', city: 'St Petersburg'};
const output = input.replace(/\{.+?\}/g, (p) => {
return props[p.slice(1, -1)] || p /* here you may use '' */;
});
console.log( output );
Currently I am using the RegExp (?:\(\) => (.*)|return (.*);) for a custom nameof function that is called like this: nameof(() => myVariable). Depending on the execution though the lambda is transpiled into something that contains the return myVariable; part therefore I need an alternative branch looking for return.
The transpiled output will be of the form ()=>{cov_26zslv4jy3.f[9]++;cov_26zslv4jy3.s[38]++;return options.type;}.
Examples are the following:
// should return "foo"
() => foo
// should return "foo.bar"
() => foo.bar
// should return "options.type"
()=>{cov_26zslv4jy3.f[9]++;cov_26zslv4jy3.s[38]++;return options.type;}
My current RegExp works however it has two matching groups depending on the type of whether the lambda was transpiled or not. Is it possible to rewrite the expression such I have a single matching group which will contain the name?
For further details, I have attached the full code of my function:
const nameofValidator: RegExp = new RegExp(/(?:\(\) => (.*)|return (.*);)/);
/**
* Used to obtain the simple (unqualified) string name of a variable.
* #param lambda A lambda expression of the form `() => variable` which should be resolved.
*/
export function nameof<TAny>(lambda: () => TAny): string {
const stringifiedLambda: string = String(lambda);
const matches: RegExpExecArray | null = nameofValidator.exec(stringifiedLambda);
if (matches === null) {
throw new ArgumentException("Lambda expression must be of the form `() => variable'.", nameof(() => lambda));
}
if (matches[1] !== undefined) {
return matches[1];
}
if (matches[2] !== undefined) {
return matches[2];
}
throw new ArgumentException("Lambda expression must be of the form `() => variable'.", nameof(() => lambda));
}
You could use:
(?:\(\) =>|.*return) ([^;\r\n]*)
If first side of alternation is not found engine tries the second. If we know one condition should satisfy engine at any time, greedy dot .* will make it to happen earlier. You may need ^ anchor too.
Live demo
There is a second approach too:
\(\) *=>.* ([^;\r\n]+)