VeeValidate use custom rule globally - vue.js

I have a custom veevalidate rule that I use to see if the value entered is already in an array in that component. I want to use this rule in different components with different arrays. Is there a way to do this? Here is my current rule in just one component
const isUnique = (value) => {
const reg = new RegExp(`^${value}$`, 'i');
const inputValue = this.myArray.filter(str => reg.test(str));
if (inputValue.length > 0) {
return {
valid: false,
data: {
message: `The ${inputValue} already exists.`,
},
};
}
return { valid: true };
};
Validator.extend('unique', {
validate: isUnique,
getMessage: (field, params, data) => data.message,
});

You sure can - either you can use the existing rule called oneOf which is documented here, or you can change your rule to accept parameters. It looks like your rule is case-insensitive, so probably you'd want to stick with that. All you would need to do is accept a 2nd parameter to your isUnique function and then use that instead of this.myArray:
const isUnique = (value, values) => {
const reg = new RegExp(`^${value}$`, 'i');
const inputValue = values.filter(str => reg.test(str));
Then in your template, you would call it like this:
<ValidationProvider :rules="{isUnique:myArray}">

Related

Vue 3: Field is not updating value

I have a field defined as follows:
<Field
class="form-control form-control-solid"
:value="tradeSizeFactor"
name="tradeSizeFactor"
:v-model="tradeSizeFactor"
/>
In my setup I watch if the value of property changed and if so I get the new value for for example the tradeSizeFactor:
setup(props)
{
const copyAccountId = ref(props.copyAccountId);
const copyAccountName = ref(props.copyAccountName);
let tradeSizeFactor = ref(0);
watchEffect(() => {
console.log(`watch Effect id: ${props.copyAccountId}`);
console.log(`watch Effect name: ${props.copyAccountName}`);
copyAccountId.value = props.copyAccountId;
if (props.copyAccountId !== 0) {
getSettings(props.copyAccountId);
}
});
async function getSettings(copyAccountId) {
store
.dispatch(Actions.GET_COPYACCOUNTSETTINGS, copyAccountId)
.then((data) => {
console.log(
`data: ${JSON.stringify(data.data.settings.tradeSizeFactor)}`
);
Object.assign(copyAccountSettings.value, data.data.settings);
tradeSizeFactor = data.data.settings.tradeSizeFactor;
});
}
return {
tradeSizeFactor,
};
}
Whatever I try however, the value of tradeSizeFactor is not updating in the Field. It keeps showing 0...
In the following line, you're incorrectly overwriting the ref with a literal:
tradeSizeFactor = data.data.settings.tradeSizeFactor;
^^^^^^^^^^^^^^^
tradeSizeFactor is a ref, so the value must be changed via its value property:
tradeSizeFactor.value = data.data.settings.tradeSizeFactor;
👆

How to execute strings of expression in an array with Ramda

I've got an array where strings of expression are listed.
const newPIXI = ["container1 = new PIXI.Container();","container2 = new PIXI.Container();","container3 = new PIXI.Container();"]
I managed to run this with (Function(...newPIXI))()
How can I do this with Ramda?
I tried R.forEach , but didn't work.
These are strings, and not functions. To run them you need to evaluate them using eval() (or Function and then run them). Each string is an expression, and not an actual function. Running the expression will create global variables (container1, container2, and container3).
You've probably heard that eval() is evil. Using eval() is a security risk, and hurts performance, and Function is only slightly less so (read more here):
eval() is a dangerous function, which executes the code it's passed
with the privileges of the caller. If you run eval() with a string
that could be affected by a malicious party, you may end up running
malicious code on the user's machine with the permissions of your
webpage / extension. More importantly, a third-party code can see the
scope in which eval() was invoked, which can lead to possible attacks
in ways to which the similar Function is not susceptible.
Function's advantage is that it runs in the global scope, and can't access variables in the scope it was called in. However, Function still allows running arbitrary code.
Eval example:
const PIXI = {
Container: function () {
this.example = 'Container';
}
};
const newPIXI = ["container1 = new PIXI.Container();","container2 = new PIXI.Container();","container3 = new PIXI.Container();"]
newPIXI.forEach(x => eval(x))
console.log({ container1, container2, container3 });
Function example:
const PIXI = {
Container: function () {
this.example = 'Container';
}
};
const newPIXI = ["container1 = new PIXI.Container();","container2 = new PIXI.Container();","container3 = new PIXI.Container();"]
newPIXI.forEach(x => Function(x)())
console.log({ container1, container2, container3 });
It's better to pass data that tells the browser what you want to do (a command), and not how to do it. Then in the code you can decide how to interpret the command. For example:
const PIXI = {
Container: function () {
this.example = 'Container';
}
};
const newPIXI = [{ type: 'PIXIContainer', name: 'container1' }, { type: 'PIXIContainer', name: 'container2' }, { type: 'PIXIContainer', name: 'container3' }]
const result = {};
newPIXI.forEach(({ type, name }) => {
switch(type) {
case 'PIXIContainer':
result[name] = new PIXI.Container();
break;
default:
throw new Error(`Type ${type} not found`);
}
})
console.log(result);

i18n won't translate correctly when inside array or object in React Native

I'm trying to use i18n-js to translate some strings into other languages. If I have my code in normal code, it works. Ex:
//Displays "Something" (no quotes) where I want it
<Text> translate("Something"); </Text>
But if I put it inside an array or object, then call it later, it stops working and shows a missing message instead of the text I want translated. Ex:
const messages = {
something: translate("Something"),
// other translations...
}
// later on
// Displays "[missing "en.Something" translation]" (no quotes) where I want it
<Text> messages.something </Text>
The following is my code for my translate function, as well as the config for i18n. I'm using lodash-memoize, but that is unrelated to the issue. I've already checked that the text being passed to i18n.t() is the same (including type) no matter if it's in normal code or in the array, but it still doesn't return the correct thing. I have some error checking written up to avoid getting the missing message on screen, but that still doesn't fix the issue that it can't find the translation.
export const translationGetters = ({
en: () => require('./translations/en.json'),
es: () => require('./translations/es.json')
});
export const translate = memoize(
(key, config) => {
text = i18n.t(key, config)
return text
},
(key, config) => (config ? key + JSON.stringify(config) : key)
);
export const setI18nConfig = () => {
// fallback if no available language fits
const fallback = { languageTag: "en", isRTL: false };
const { languageTag, isRTL } =
RNLocalize.findBestAvailableLanguage(Object.keys(translationGetters)) ||
fallback;
// clear translation cache
translate.cache.clear();
// update layout direction
I18nManager.forceRTL(isRTL);
// set i18n-js config
i18n.translations = { [languageTag]: translationGetters[languageTag]() };
i18n.locale = languageTag;
};
I have no idea where to go on this. Any advice would be appreciated!
Same problem here, workaround is to return array/object from inside a function:
Don't work
export const translations = [i18.t('path')]
Works
export function getTranslations() {
const translations = [i18.t('path')]
return translations
}

Always first filtered selected on Quasar Select

I am using Quasar Framework and using the Q-Select with filter.
I would like to the first filtered option always be already marked, because then if I hit enter, the first will selected.
After some research I found out how to achieve this in a generic way.
The second parameter on the function update received at filterFn is the instance of QSelect itself.
Hence, we can use
ref.setOptionIndex(-1);
ref.moveOptionSelection(1, true);
To keep the focus on the first filtered element, regardless of multiselect or not.
The final code is something like
filterFn(val, update) {
update(
() => {
const needle = val.toLocaleLowerCase();
this.selectOptions = this.qSelectOptions.filter(v => v.toLocaleLowerCase().indexOf(needle) > -1);
},
ref => {
ref.setOptionIndex(-1);
ref.moveOptionSelection(1, true);
});
}
There is one option to achieve this is set model value in the filter method if filtered options length is >0.
filterFn (val, update, abort) {
update(() => {
const needle = val.toLowerCase()
this.options = stringOptions.filter(v => v.toLowerCase().indexOf(needle) > -1)
if(this.options.length>0 && this.model!=''){
this.model = this.options[0];
}
})
}
Codepen - https://codepen.io/Pratik__007/pen/QWjYoNo

Vuex Getters Method Style How to Return Function

According to the Vuex documentation, you can pass a payload to a getter method as long as the method returns a function.
https://vuex.vuejs.org/en/getters.html
I'm unclear on how I can format a function that returns a function.
In my case, I'd like to pass a product object to a getter method and use the product.price data along with data in the state to return a calculated price.
Here's a stripped down version of the approach I have currently:
const store = new Vuex.Store({
state: {
product: {
price: 12.99,
},
colors_front: 1,
colors_back: 0,
},
getters: {
printPrice: (state) => (product) => {
return (state, product) => {
var colors_front = state.print_product.colors_front,
colors_back = state.print_product.colors_back;
print_price = parseFloat(product.price) + parseFloat(colors_front * 2.25) + parseFloat(colors_back * 2.25);
return parseFloat(print_price).toFixed(2);
}
},
}
}
When I try to access this getter in my component, I'm receiving the code of the function as a text string.
<template>
<div>{{ printPrice(product) }}</div>
</template>
export default {
computed: {
...mapState(['product']),
...mapGetters(['printPrice']),
}
}
Can anyone help me understand getters that return functions better? Is there a more appropriate way to do this?
I figured since I'm not actually mutating the state data, this method belonged better as a getter than a mutator, but I'm open to all suggestions.
Thanks!
Problem is that your getter is returning a function that also returns a function, so, when Vuex runs the function, it returns another one which seems to be cast to string (maybe by the template parser?).
Just make sure to return one single function with the expected output by changing this:
printPrice: (state) => (product) => {
return (state, product) => {
var colors_front = state.print_product.colors_front,
colors_back = state.print_product.colors_back;
print_price = parseFloat(product.price) + parseFloat(colors_front * 2.25) + parseFloat(colors_back * 2.25);
return parseFloat(print_price).toFixed(2);
}
},
to this:
printPrice: (state) => (product) => {
var colors_front = state.print_product.colors_front,
colors_back = state.print_product.colors_back;
print_price = parseFloat(product.price) + parseFloat(colors_front * 2.25) + parseFloat(colors_back * 2.25);
return parseFloat(print_price).toFixed(2);
},
That way we removed the wrapping function in the first level returning function.