-
Notifications
You must be signed in to change notification settings - Fork 86
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Make it possible to use dynamic field names in mapFields #24
Comments
Hey @rob-wood-browser, unfortunately, this is not possible because // This works:
const pageNumber = 1;
const questionNumber = 2;
export default {
// ...
computed: {
...mapFields([`pages[${pageNumber}].inputs[${questionNumber}].value`])
}
// ...
}
// This doesn't work:
export default {
// ...
props: ['pageNumber', 'questionNumber'],
computed: {
// `this` in the context of `mapFields()` does not reference the component.
// Therefore `this.pageNumber` and `this.questionNumber` are undefined!
...mapFields([`pages[${this.pageNumber}].inputs[${this.questionNumber}].value`])
}
// ...
} I edited your issue title and I'll handle this as a feature request. Although I currently have no idea how I could implement this. Suggestions and / or a pull request are welcome. |
Hey @maoberlehner, Thanks for clarifying this. I'll absolutely have a think about how this feature could be implemented, and equally if you happen to think of a nice solution to my problem that'd be greatly appreciated too! |
Hi @maoberlehner, Beside that, you're package is very helpful! |
Sometimes we need to inject the index of the fields to map with vuex, so it can be helpfull. For the moment I'm doing manually with something like this:
So if you want to create it dynamically with an index passed as a prop:
|
@wanxe Thanks for your ideas. I tried different approaches and just realized that it is better to store in Vuex store all the information required for calculation of the dynamic properties. In my case there is an object received from backend which has the following structure which is saved to the Vuex store: data: {
series: [
{
name: 'ser1',
y-axis: {
name: 'axis',
enabled: true,
...
}
},
...
]
} In UI there is a master-detail form with the list of chart series when user can select one series and edit its parameters like I tried to access series as multi-row fields first but in this case I was unable to use the library to map So I've switched to storing export default {
namespaced: true,
state: {
widgetEditForm: {
data: {},
selectedSeriesIndex: -1
}
},
getters: {
selectedSeriesIndex(state) {
return state['widgetEditForm'].selectedSeriesIndex
},
selectedSeries(state, getters) {
return (state['widgetEditForm'].data.series && state['widgetEditForm'].data.series[getters.selectedSeriesIndex]) || null
},
getSelectedSeriesField(state, getters) {
if (!getters.selectedSeries) {
return getField(undefined)
}
return getField(getters.selectedSeries)
},
},
mutations: {
updateSelectedSeriesField(state, field) {
let series = state['widgetEditForm'].data.series
let selectedSeriesIndex = state['widgetEditForm'].selectedSeriesIndex
if (!series || selectedSeriesIndex == -1) {
return
}
updateField(series[selectedSeriesIndex], field)
},
} In *.vue I just map |
@maoberlehner |
@Nicholaiii interesting idea! I'd consider accepting a pull request which adds this feature. Thx! |
Run into the same problem where I needed a prop to get the id of the field within a list, the solution I came up with is not very elegant, but it gets the job done : 1 _ Define a global variable let self = '';
export default {
props:['item_ID'],
...
computed:{
mapFields('form', [
`list[${slef.item_ID}].name`,
`list[${slef.item_ID}].type`,
`list[${slef.item_ID}].topic`,
`list[${slef.item_ID}].location`,
])
},
...
beforeCreate(){self = this}
} Hope this helps PS: Thanks for this great package, really helped me out |
@AousAnwar that's actually pretty clever 😅 |
Tested on a multiple instances. It doesn't work :/ |
I had the same problem here when the inputs' names were like I ended up leaving Normally, for my field like <span v-show="sample.touched && errors.first('sample')">
{{ errors.first('sample') }}
</span But it wasn't actually a big deal to fall-back to the old technique that doesn't use isMessageShowing(fieldName) {
if (!this.fields[fieldName]) return false;
return (this.fields[fieldName].touched && this.errors.first(fieldName));
}, and then markup: <span v-show="isMessageShowing('blah[0]')">
{{ errors.first('blah[0]') }}
</span The most annoying thing is you can't just use But as I just showed, it's not too bad to work around it. I found this example code here, maybe it's helpful towards augmenting Something like that might actually work as a non-breaking change, because Sorry I probably can't be of any more help than that; I'd have to spend WAY more time looking at the library code to make a coherent idea! [edit] upon further inspection, I'm not super confident that example code I linked could get us any farther. The more I look at it, the more I understand why maoberlehner said "I currently have no idea how I could implement this". It's looking like a very tight spot. |
Here an updated version of @wanxe solution mapDynamicField we are using in our project import arrayToObject from 'vuex-map-fields/src/lib/array-to-object';
/*
Special vuex-map-field like helper to manage fields inside dynamically defined index or object property
Usage :
computed: {
...mapDynamicFields('my/store/module', ['bigArray[].fieldName'], 'indexArray'),
indexArray() {
return 42;
}
}
So you get a fieldName computed property mapped to store my.store.module.bigArray[42].fieldName property
You can use any computed, props, etc... as index property
Fields can also be passed as object to be renamed { myField: 'bigArray[].fieldName' }
*/
export function mapDynamicFields(namespace, fields, indexField) {
const fieldsObject = Array.isArray(fields) ? arrayToObject(fields) : fields;
return Object.keys(fieldsObject).reduce((prev, key) => {
const field = {
get() {
// 'this' refer to vue component
const path = fieldsObject[key].replace('[]', `[${this[indexField]}]`);
return this.$store.getters[`${namespace}/getField`](path);
},
set(value) {
// 'this' refer to vue component
const path = fieldsObject[key].replace('[]', `[${this[indexField]}]`);
this.$store.commit(`${namespace}/updateField`, { path, value });
},
};
prev[key] = field;
return prev;
}, {});
}; |
There's another example in this SO answer for nested fields instead of namespaced: |
If anyone is interested this is how I do it
|
Building off @wanxe and @fabien-michel's solutions, I've modified
Regardless of this issue, thanks for the great library, for my use case, Vuex would be almost worthless if not for your library (I'm building a relatively complicated form builder that has LOTS of v-models that need to bind to the Vuex store). Thanks! |
@miketimeturner what does |
@wdavies973 Could you please show how this would be implemented in a component? |
That's just a helper function that I created that gets the state "$store.state.suppliers" not really important for this to work. The mapFields function just uses the result to map the properties |
@geoidesic You can check out my project here: https://github.com/RobluScouting/master-desktop/blob/master/src/components/metrics/RCheckbox.vue |
Ok tx. Why do you first import |
The mapFields export is what you're importing. (Not this package) Just put it in its own file and import it where you need it. And use it like this...
|
Hi all, Based on the great solutions provided by @fabien-michel and @wanxe above, hereafter is an extended version that should allow generating getters/setters with nested arrays. // Index 1 => prop2
// Index 0 => prop1
...mapDynamicFields(namespace, ['rootArray[=1].nestedArray[=0].field'], ['prop1', 'prop2']) Then, the computed property import arrayToObject from 'vuex-map-fields/src/lib/array-to-object';
function buildFieldPath(vm, fieldsObject, field, indexFields) {
if (Array.isArray(indexFields)) {
return indexFields.reduce(
(path, indexField, index) => path.replace(new RegExp(`\\[=${index}\\]`), `[${vm[indexField]}]`),
fieldsObject[field]
);
}
return fieldsObject[field].replace('[]', `[${vm[indexFields]}]`);
}
export function mapDynamicFields(namespace, fields, indexFields) {
const fieldsObject = Array.isArray(fields) ? arrayToObject(fields) : fields;
return Object.keys(fieldsObject).reduce((prev, key) => {
prev[key] = {
get() {
// 'this' refer to vue component
const path = buildFieldPath(this, fieldsObject, key, indexFields);
return this.$store.getters[`${namespace}/getField`](path);
},
set(value) {
// 'this' refer to vue component
const path = buildFieldPath(this, fieldsObject, key, indexFields);
this.$store.commit(`${namespace}/updateField`, { path, value });
}
};
return prev;
}, {});
} There may be additional features to implement, essentially to provide shortcuts, and a more elegant code may exist. Feel free to comment/improve. |
#24 (comment) message deleted. please update examples. |
with this method, what if pageNumber is dynamic ? |
Hey, thanks for the great library!
I'm wondering if it's possible to use dynamic field names in the
mapFields
function? For example, I'm keen to use...mapFields([`pages[${this.pageNumber}].inputs[${this.questionNumber}].value`])
where pageNumber and questionNumber are props passed to the component.I'm able to use those props in the template of the component so I know they're being passed down, but when I try to use them in mapFields I get
Cannot read property 'inputs' of undefined
.This could just be me misunderstanding Vue - apologies if so!
Thanks
The text was updated successfully, but these errors were encountered: