Vue Router Push How To Set Optional Query Parameters? - vue.js

I am building a site with a filter and sort menu. When the user clicks on one of the filters, I want that filter option added to the url and then the page to update using this.$router.push. This works fine when I add all of the filters at the same time, but my url shows all of the filters, even if they haven't been selected yet? I changed my code to only show optional queries, but now it doesn't work:
processCheckedTags() {
let queryParams = "";
Object.assign(queryParams,
this.checkedTags !== [] ? {checkedTags: this.checkedTags} : null,
this.checkedSales !== [] ? {checkedSales: this.checkedSales} : null,
this.checkedRatings !== "" ? {checkedRatings: this.checkedRatings} : null,
this.checkedDateAdded !== "" ? {checkedDateAdded: this.checkedDateAdded} : null,
this.sortBy !== "" ? {sortBy: this.sortBy} : null,
this.minPrice !== "" ? {minPrice: this.minPrice} : null,
this.maxPrice !== "" ? {maxPrice: this.maxPrice} : null,
);
this.$router.push(
{
path: this.$route.params.mainNavigation,
query: queryParams
}
);
}
How do I add optional queries to this.$router.push?

With the help of lodash pickBy, you could rewrite what you need in a very succinct way:
processCheckedTags() {
const params = {
checkedTags: this.checkedTags,
checkedSales: this.checkedSales,
checkedRatings: this.checkedRatings,
checkedDateAdded: this.checkedDateAdded,
sortBy: this.sortBy,
minPrice: this.minPrice,
maxPrice: this.maxPrice,
};
this.$router.push(
{
path: this.$route.params.mainNavigation,
query: _.pickBy(params)
}
);
}

Try to define the an array of filters as strings then iterate over them and assign only the property which has a valid value:
let filters=['checkedTags', 'checkedSales', 'checkedRatings', 'checkedDateAdded', 'sortBy', 'minPrice', 'maxPrice']
let queryParams=filters.reduce((acc,curr)=>{
if (this[curr]!==[] || this[curr]!==''){
acc[curr]=this[curr]
}
return acc
},{})
this.$router.push(
{
path: this.$route.params.mainNavigation,
query: queryParams
}
);

Related

Vue tags input custom validation

I'm using vue-tags-input component. In its docs we can find validation. I'm trying to create validation so valid input must have:
min 3 signs
two numbers
comma between numbers
this is what I have:
validation: [{
classes: 'min-length',
rule: tag => tag.text.length < 3,
},{
classes: 'min-length',
rule: ({ text }) => {
const comma = text.indexOf(',') === -1;
if(comma) {
const arr = text.split(',')
if(arr[0] && arr[1]) {
if(arr[0].typeof === 'number' && arr[1].typeof === 'number') {
return true;
}
}
}
return false;
}
}]
So I'm spliting string to array by ,. In result I should have array with two elements. Then I check if both elemenets are numbers. How ever this not work properly because it treat 111 as valid but it shoudn't.
I've created demo on codesanbox.
To check if comma exists you have to check if indexOf comma not equals -1.
const comma = text.indexOf(",") !== -1;
You have to convert the string to number using Number(string).
if (typeof Number(arr[0]) === "number") {..
You have to return false if validation succeeds and true if there is an error,
you are doing the opposite.
The complete code will be:
{
classes: "custom",
rule: ({ text }) => {
const comma = text.indexOf(",") !== -1;
if (comma) {
const arr = text.split(",");
if (arr[0] && arr[1]) {
if (typeof Number(arr[0]) === "number" && typeof Number(arr[1]) === "number") {
return false;
}
}
}
return true;
}
}
A shorter regex rule will be:
{
classes: "custom",
rule: ({ text }) => {
return !text.match(/^\d+,\d+$/);
}
}

React Native: Phone number not updating and continuing to give error message

I have a bug in my application where I am trying to update a phone number and when I click on save, I get the error message and the original phone number stays populated:
Obviously, something has gone wrong with validation. I was hoping it was perhaps the regex although it has a solid one, but I changed it like so:
const regex = {
userName: /^[-.\sa-zA-Z]+$/,
cardName: /^[-\sa-zA-Z]+$/,
password: /^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[##$%^&*()\-+!\\.]?).{8,}$/,
zip: /(^\d{5}$)|(^\d{5}-\d{4}$)/,
memberId: /^\d+$/,
// phoneNumber: /^\(?([0-9]{3})\)?[-. ]?([0-9]{3})[-. ]?([0-9]{4})$/,
phoneNumber: /^[\+]?[(]?[0-9]{3}[)]?[-\s\.]?[0-9]{3}[-\s\.]?[0-9]{4,6}$/im,
email: /^\w+([\.-]?\w+)*#\w+([\.-]?\w+)*(\.\w{2,3})+$/,
};
That did not help.
I am thinking it has to be the validation function, but I am staring at this thing and I can't see anything that sticks out:
_validate = props => {
const validationErrors = {
businessName: props.businessName ? '' : 'Is Required',
businessPhoneNumber:
props.businessPhoneNumber.length === 0 ||
regex.phoneNumber.test(props.businessPhoneNumber)
? ''
: 'Phone number must be valid and contain 10 digits',
};
const isValid = Object.keys(validationErrors).reduce((acc, curr) => {
if (validationErrors[curr] !== '') {
return false;
}
return acc;
}, true);
this.setState({validationErrors, displayErrors: !isValid});
return isValid;
};
UPDATE
I tried the solution in the below answer, but unfortunately that did not work.
Here is whats going on:
When I add the phone number and save it, it is in props here:
_validate = props => { and you can see that here:
{screenProps: undefined, navigation: {…}, businessName: "Ceramic Tile Distributors", businessWebsite: "", businessPhoneNumber: "8667073945", …}
but then it ceases to exist in the validationErrors object here:
const validationErrors = {
businessName: props.businessName ? "" : "Is Required",
businessPhoneNumber:
props.businessPhoneNumber.length === 0 ||
regex.phoneNumber.test(props.businessPhoneNumber)
? ""
: "Phone number must be valid and contain 10 digits"
};
and you can see that here:
{businessName: "", businessPhoneNumber: ""}
Why its re-rendering with the above as empty strings I do not know.
I can tell you that this here:
const isValid = Object.keys(validationErrors).reduce((acc, curr) => {
console.log("On line 84 of BusinessDetails: ", isValid);
if (validationErrors[acc] !== "") {
return false;
}
return acc;
}, true);
returns undefined, but why I do not know.
_validate is being used inside the _saveChanges function like so:
_saveChanges = () => {
const isValid = this._validate(this.props);
if (isValid) {
this.setState({ displaySpinner: true });
this.props
.updateInformation()
.then(() => {
this.setState({ displaySpinner: false }, () => {
this.props.navigation.goBack();
});
})
.catch(() => {
Alert.alert(
"Error",
this.props.businessPhoneNumber.length === 0
? "Please provide a business phone number. If your business phone number no longer exists, please call 1-800-NFIB-NOW to have this information deleted."
: "We couldn't save your changes. Please try again.",
[
{
text: "OK",
onPress: () => this.setState({ displaySpinner: false })
}
],
{ cancelable: false }
);
});
}
};
I can tell you that const isValid = this._validate(this.props); returns false.
When I test your code, it looks like there is no problem with your regex. But the below line is not correct
if (validationErrors[curr] !== '') {
return false;
}
You should use acc to get the values. consider the below code
if (validationErrors[acc] !== '') {
return false;
}
However, I can't run your code in my system. .reduce not working here. As a workaround, you can use below code
_validate = props => {
const validationErrors = {
businessName: props.businessName ? '' : 'Is Required',
businessPhoneNumber:
props.businessPhoneNumber.length === 0 ||
regex.phoneNumber.test(props.businessPhoneNumber)
? ''
: 'Phone number must be valid and contain 10 digits',
};
let isValid = true
Object.keys(validationErrors).map((acc, curr) => {
if (validationErrors[acc] !== '') {
isValid= false
}
});
this.setState({validationErrors, displayErrors: !isValid});
return isValid;
};

How to pass parameter for dynamic route?

I need on click go to the next page which is created with a dynamic route. It gets the id for the route from a vuex store. When I'm clicking it gives this url
http://localhost:8080/worklist/%7Bname:'worklistDynamic',%20params:%7B%20id:sideSwiperItems[indx+1].id%7D%7D
---------------------------------------
here is my code:
//html
.side-button-next(#click='switchPage()')
//function
--------------------------
switchPage() {
this.$router.push(
`{name:'worklistDynamic', params:{ id:this.sideSwiperItems[this.indx+1].id}}`,
)
},
--------------------------
computed: {
sideSwiperItems() {
return this.$store.state.buildingData.sideSwiperItems
},
indx() {
if (this.$store.state.buildingData.index === null) {
return 0
}
return this.$store.state.buildingData.index
},
},
Try to remove the backticks:
switchPage() {
let path = { name: 'worklistDynamic', params: { id: this.sideSwiperItems[this.indx + 1].id } }
// or you can write the full path with backticks and interpolation
let path = `/worklistDynamic/${this.sideSwiperItems[this.indx + 1].id}`
this.$router.push(path)
}

How to pass an array values from one function to another function in vuejs?

I am trying to get the array values from
"validateBeforeSubmit" function to "saveForm" function. But I am
getting values of "undefined" in "arrlength". Please help me to solve.
This my code in vue.js
export default {
name: '',
data() {
return {}
},
ready: function() {
this.validateBeforeSubmit()
this.saveForm();
},
methods: {
validateBeforeSubmit() {
var fieldsVal = new Array();
var firstName = document.getElementById('firstName').value
var lastName = document.getElementById('lastName').value
var designation = document.getElementById('designation').value
if (firstName != "" && lastName != "" && designation != "") {
fieldsVal.push(firstName);
fieldsVal.push(lastName);
fieldsVal.push(designation);
return fieldsVal;
} else {
fieldsVal.length = 0;
return fieldsVal;
}
return fieldsVal;
},
saveForm() {
var fieldsValArray = this.validateBeforeSubmit();
var arrLength = fieldsValArray.length;
}
}
}
I can see multiple issues in your code:
1) Don't apply jQuery-like approach for getting input values. Use v-model instead. This will simplify your code
<template>
<input v-model="form.firstName" type="text"/>
</template>
<script>
export default {
data: {
form: {
firstName: '',
}
},
methods: {
validateBeforeSubmit() {
// take `firstName` directly from `data` not need for `getElementById`
const firstName = this.form.firstName;
}
},
}
</script>
2) Remove validateBeforeSubmit and saveForm from ready. Ready hook is obsolete in vue#2. And also it makes no sense. It's better to call it on form #submit.
3) It's better to create array using [] syntax instead of new Array()
Why never use new Array in Javascript
4) Always provide name for your component for easier debug
export default {
name: 'ValidationForm',
}
5) I don't know where was an issue but it works. Check this link below. I have updated your code. Try to submit form and check the console:
https://codesandbox.io/s/w6jl619qr5?expanddevtools=1&module=%2Fsrc%2Fcomponents%2FForm.vue

Vue Router: how to cast params as integers instead of strings?

When I enter a URL using the browser field, the params are cast as strings, rather than an integer, e.g. /user/1 returns {id: "1"}. However, when when using this.$route.push({}), the params are, correctly, cast as integers {id: 1}.
Is this behavior intended? If not, how do I fix it?
You have to handle casting any params values yourself. In the route object define a props function. Here is an example:
{
path: '/user/:userId',
component: UserProfile,
props: (route) => {
/**
* This would preserve the other route.params object properties overriding only
* `userId` in case it exists with its integer equivalent, or otherwise with
* undefined.
*/
return { ...route.params, ...{ userId: Number.parseInt(route.params.userId, 10) || undefined }
}
}
link to vue router docs this is under Function mode
I'm probably late to the party, but this is my take on this. I wrote a function that returns a function that casts route params values to the props with same name with the given type.
function paramsToPropsCaster(mapping) {
return function(route) {
let nameType = Object.entries(mapping); // [[param1, Number], [param2, String]]
let nameRouteParam = nameType.map(([name, fn]) => [name, fn(route.params[name])]); // [[param1, 1], [param2, "hello"]]
let props = Object.fromEntries(nameRouteParam); // {param1: 1, param2: "hello"}
return props;
}
}
And then, in your route definition:
{
path: '/projects/:param1/editor/:param2',
component: ProjectEditor,
name: 'project-editor',
props: paramsToPropsCaster({'param1': Number, 'param2': String}),
}
This is just a hint on what you can do to solve the problem asked here, don't use this verbatim!
You can use an array in props to support both types
props: {
type:[Number,String],
required:true
}
Seems like Vue Router doesn't provide a shortcut for this, so I've come up with my own. The castParams function below generates a props function that has the specified type casting built in. I've added casting for integers and booleans but you can easily extend this for whatever other types you want to cast to.
// casts should be an object where the keys are params that might appear in the route, and the values specify how to cast the parameters
const castParams = (casts) => {
return (route) => {
const props = {};
for (var key in route.params) {
const rawValue = route.params[key];
const cast = casts[key];
if (rawValue == null) {
// Don't attempt to cast null or undefined values
props[key] = rawValue;
} else if (cast == null) {
// No cast specified for this parameter
props[key] = rawValue;
} else if (cast == 'integer') {
// Try to cast this parameter as an integer
const castValue = Number.parseInt(rawValue, 10);
props[key] = isNaN(castValue) ? rawValue : castValue;
} else if (cast == 'boolean') {
// Try to cast this parameter as a boolean
if (rawValue === 'true' || rawValue === '1') {
props[key] = true;
} else if (rawValue === 'false' || rawValue === '0') {
props[key] = false;
} else {
props[key] = rawValue;
}
} else if (typeof(cast) == 'function') {
// Use the supplied function to cast this param
props[key] = cast(rawValue);
} else {
console.log("Unexpected route param cast", cast);
props[key] = rawValue;
}
}
return props;
};
};
Then you can use it in your route definitions, eg:
{
path: '/contact/:contactId',
component: 'contact-details-page',
props: castParams({contactId: 'integer'}),
},
I do prefer Rodener Dajes answer, and handle type casting and validation within the component instead of in the route definition:
props: {
id: {
type: [Number, String],
default: 0
},
},
The reason is that it will allow me to define the route much simpler and readable:
{
path: '/job/:id',
name: 'Job',
component: InvoiceJobDetail,
props: true
}
Many of these solutions seem unnecessary complex to me.
Here's what I did in my project - note that route params ending in ID or the param id itself, are automatically converted to Number, so in my case I just had to set props: typedProps(), in nearly all of my routes.
/**
* Casts props into proper data types.
* Props ending in 'ID' and the prop 'id' are cast to Number automatically.
* To cast other props or override the defaults, pass a mapping like this:
* #example
* // Truthy values like 'true', 'yes', 'on' and '1' are converted to Boolean(true)
* {
* path: '/:isNice/:age/:hatSize',
* name: 'foo route',
* props: typedProps({ isNice: Boolean, age: Number, hatSize: Number}),
* },
* #param {Object} mapping
* #returns
*/
const typedProps = (mapping) => {
if (!mapping) {
mapping = {}
}
return (route) => {
let props = {}
for (let [prop, value] of Object.entries(route.params)) {
if (prop in mapping) {
if (mapping[prop] === Boolean) {
value = ['true', '1', 'yes', 'on'].includes(value.toLowerCase())
} else {
value = mapping[prop](value)
}
} else if (prop === 'id' || prop.endsWith('ID')) {
value = Number(value)
}
props[prop] = value
}
return props
}
}
This could use some error handling in case a type coercion fails, but I'll leave that as an exercise for the reader :)
Based on the excellent answer from #pongi: https://stackoverflow.com/a/63897213 I came up with a new package: https://www.npmjs.com/package/vue-router-parse-props. It's written in typescript and has types. Please let me know, what you think.
npm i vue-router-parse-props
// src/router/index.ts
import propsParser from 'vue-router-parse-props'
import { parse } from 'date-fns'
const router = new Router({
base: process.env.BASE_URL,
mode: useHistory ? "history" : "hash",
routes: [
{
path: ':day/:userId',
name: 'UserProfile',
component: () => import('#/components/UserProfile.vue'),
props: paramsToPropsCaster({
userId: Number,
day: (val: string): Date => parse(val, 'yyyy-MM-dd', new Date()),
searchId: {
type: id,
routeKey: "query.q"
}
})
}
]
});