helpers/sync_state_to_query_params.js (26 lines of code) (raw):
/**
* An object of the form {field: 'foo', param: 'bar', default: 'yes'}.
* This would mean that the Vue Component gets a computed prop "this.foo" and it is synced as a query parameter 'bar'
* So for example if this.foo = 'yes', then ?bar=yes in the URL.
*
* @typedef {Array} QueryField
* @property {String} field - The property on the Vue component, which can be used in the template and other computed
* @property {String} param - The corresponding query parameter
* @property {String} default - The default value
*/
/**
* Creates internal values for the QueryField
*
* @param fields {Array[QueryField]}
* @returns {Object}
*/
export const mapQueryFieldsToData = (fields) =>
fields.reduce((acc, { field, param }) => {
acc[`$${field}_${param}`] = null;
return acc;
}, {});
/**
* This is where the magic happens. This syncs an internal value with a query parameter.
*
* Imagine the config [{field: 'foo', param: 'bar', default: 'yes'}], this would utilize an internal field:
* `$foo_bar` to store the state. The value can be accessed via `this.foo` and it is synced to the URL under `bar`.
* Initially the state of the internal value is null, so we fall back on the parameter `bar`, if that is also falsey,
* we fall back on the default value.
*
* ## Updating the Query Params
*
* We are not using Vue Routers `.push/.replace`, because if we do the input fields loose focus.
* Therefore the URL is updated with `window.history.pushState`. If the value matches the default
* value, the query paraemeter isn't set
*
* @param fields {Array[QueryField]}
* @returns {Object}
*/
export const mapQueryFieldsToComputed = (fields) =>
// eslint-disable-next-line max-params
fields.reduce((acc, { field, param, default: defaultValue }, idx, array) => {
acc[field] = {
get() {
return this.$data[`$${field}_${param}`] ?? (this.$route?.query?.[param] || defaultValue);
},
set(val) {
this.$data[`$${field}_${param}`] = val;
const query = {};
// eslint-disable-next-line no-restricted-syntax
for (const queryField of array) {
if (this[queryField.field] !== queryField.default) {
query[queryField.param] = this[queryField.field];
}
}
const url = new URL(this.$route.path, window.location);
url.search = new URLSearchParams(query).toString();
window.history.pushState({}, '', url);
},
};
return acc;
}, {});