Dynamic v-model bindings in input fields do not update visually
-
Hi, I am using this code to dynamically create a list of input fields with dynamic v-model bindings like v-model=“newSession[column.name]”.
<q-item v-for="column in columns" :key="column.name" dense > <q-item-section> // display array-valued fields <q-select v-if="Array.isArray(newSession[column.name])" clearable standout="bg-teal text-white" :label="column.label" v-model="newSession[column.name]" use-input use-chips multiple hide-dropdown-icon input-debounce="0" new-value-mode="add" /> // display boolean-valued fields <q-field v-else-if="typeof newSession[column.name] === 'boolean'" clearable standout="bg-teal text-white" v-model="newSession[column.name]" :label="column.label" > <template v-slot:control> <q-checkbox :value="newSession[column.name]" @change="event => { newSession[column.name] = event.target.value; $forceUpdate()}" /> </template> </q-field> // display other fields <q-input v-else clearable standout="bg-teal text-white" v-model="newSession[column.name]" :label="column.label" :hint="typeof newSession[column.name]" /> </q-item-section> </q-item>
I have also initialised the underlying ‘newSession’ object and a ‘columns’ object array (used as another existing Q-Table´s column definition).
Everything is displayed correctly first. But if I change any input field´s value and jump to the next field, the old field´s value will be restored. So it seems that I am not able to change any field´s value at all. But when I look at the underlying model (‘newSession’), I can see that it has been changed after switching to the next field. So there are two questions for me:- Why has any input field´s value not updated visually according to the model change?
- Why is the model value not changing directly e.g. on typing in chars?
I have tried to find some answers on my own, but I could only get rid off the first point, and also I am not happy with this solution. With using the following code, I pushed for a complete re-rendering after model change, so at least the q-input components get visually updated after model changes. Here is the code:
<q-input v-else clearable standout="bg-teal text-white" :value="newSession[column.name]" @change="event => { newSession[column.name] = event.target.value; $forceUpdate()}" :label="column.label" :hint="typeof newSession[column.name]" />
Also this only works with q-input and not with other ‘field-components’ I used above.
It seems to me that my expected behavior according to my two questions mentioned above should work without more fine-tuning, because I think it should be a default scenario. But maybe I missed something essentially (especially concerning @input and @change events).
Can someone please help me out of this?Thanks,
Tony -
Can you share your code within the
<script>
section of your component please?Scott
-
@tonyskulk much better if a reproduction pen is provided, otherwise it’s a hard game of guessing :/.
-
@s-molinari This is the <script> part. Besides this there is a separate column definition in a separated js file, because its very big. It´s a default table column definition. I will put it in the next post below.
<script> import { Loading, extend } from 'quasar' import { columns } from '../constants' import _ from 'lodash' export default { name: 'PageIndex', data () { return { selected: [], newDialog: false, newSession: {}, loading: true, columns, data: [] } }, mounted () { this.onRefresh() }, methods: { openNewSessionDialog () { this.columns.map(column => { let value = '' if (typeof column.field === 'function') { value = column.field(this.selected[0]) } else { value = this.selected[0][column.field] } return ({ name: column.name, value: value }) }).forEach(field => { this.newSession[field.name] = field.value }) console.log(this.newSession) this.newDialog = true } } } </script>
@metalsadman I tried using codepen to get a reproducing example, but I don´t know how to use it for .vue components. Can you recommend a tool to provide that?
-
Column definition from above:
import _ from 'lodash' export const columns = [ { name: 'session_id', label: 'Session ID', align: 'left', reference: '_id.$oid', field: row => _.get(row, '_id.$oid'), // format: val => `${val}`, sortable: true, immutable: true }, { name: 'label', // align: 'left', label: 'Label', field: 'label', sortable: true }, { name: 'data_source_training', label: 'Datasource Training', reference: 'data_sources.data_sources.training.path', field: row => _.get(row, 'data_sources.data_sources.training.path'), sortable: true }, { name: 'data_source_test', label: 'Datasource Test', reference: 'data_sources.data_sources.test.path', field: row => _.get(row, 'data_sources.data_sources.test.path'), sortable: true }, { name: 'total_timesteps', label: 'Total timesteps', field: 'total_timesteps', sortable: true }, { name: 'policy', label: 'Policy', reference: 'training.policy.target', field: row => _.get(row, 'training.policy.target'), format: val => val.substring(val.lastIndexOf('.') + 1), sortable: true }, { name: 'epsilon', label: 'Exploration', reference: 'training.epsilon', field: row => _.get(row, 'training.epsilon'), sortable: true }, { name: 'duration_training', label: 'Training duration', reference: 'training_metrics.duration_training', field: row => _.get(row, 'training_metrics.duration_training', ''), sortable: true, immutable: true } ]
-
I’m not sure it makes a difference, but you start with newSesssion as an object in data, but it should be an array.
Also, try this for getting your code up as a working example: https://codesandbox.quasar.dev.
Scott
-
Actually it´s an object. I am referencing the fields via ‘[]’, only because I want to use dynamic field names. I will try to put my code online with your link, thanks!
-
From the QSelect API in the docs.
Scott
-
@s-molinari Yes, but in the QSelect I am using a model (newSession[column.name]) that is an array (see my first post).
<q-select v-if="Array.isArray(newSession[column.name])" clearable standout="bg-teal text-white" :label="column.label" v-model="newSession[column.name]" use-input use-chips multiple hide-dropdown-icon input-debounce="0" new-value-mode="add" />
-
My problem is that I want to use different input components according to the different types that newSession[column.name] can have.
This can be array, number, boolean or string.
So newSession should still be in object and I want to access its properties dynamically using [] what is actually nothing else than newSession.dynamicpropertyname. -
Well. I can only tell you what Quasar is expecting.
Scott
-
@s-molinari I got my full code up as a working example here: https://codesandbox.io/s/codesandbox-app-5hxoc
When you select a row in the table and then click on the copy-Button, a dialog opens where I want to be able to change any field values. But only when I close the dialog, the changes are applied on the model (see the model output on top of the table). After changing a field and leaving it to switch to the next field, the old value is visible again.
Everything takes place in the file Index.vue and constants.js for simplicity.
The model underlying the dialog is ‘newSession’ and the table data is based on ‘data’. On dialog opening I copy the selected row data into a newSession model (see the function openNewSessionDialog()).
(I mocked the backend request with static as you can see.) -
Ok. I see the use cases and sort of what you want to do.
Questions:
With the select you are trying to build in the dialog entry mask, what data is it supposed to be selecting with/ for? Your code loses me on that.
I think I understand the copying of the selected session. The idea is to then change parts of it to make a new session and add it to the table data, correct?
If I can get the answers to those questions, I might be able to help. Maybe.
Scott
-
To answer your questions:
With the select you are trying to build in the dialog entry mask, what data is it supposed to be selecting with/ for? Your code loses me on that.
I am not sure if I got your question. The dialog entry mask is build based on the selected table entry. So I make a transformation from the ‘complicated’ raw model ‘data’ to a simplified model ‘newSession’. In the dialog I want to make changes based on the underlying ‘newSession’ model. And after clicking the save-button, I am transforming the simplified ‘newSession’ model back to the ‘data’ model. (However the transformations are working fine I think. )
I think I understand the copying of the selected session. The idea is to then change parts of it to make a new session and add it to the table data, correct?
Yes, that´s 100% correct.
I hope I could answer your questions and I am glad for your support in this
Tony
-
For my first question, you have a QSelect being used in your dialog, which is what you are asking about. What part of the data is that supposed to be showing a selectable and addable selection field in your dialog?
Scott
-
Every field in the dialog has an underlying model like ‘newSession[column.name]’, eg. ‘newSession.label’. And depending on what type ‘newSession[column.name]’ has, it should display an editable component like this:
- boolean -> checkbox
- array -> QSelect
- everything else -> plain QInput
-
So, which part of the data is supposed to be remapped to an array? Can you give me an example of a key in the data?
Scott
-
Needless to say, what you’ve attempted isn’t going to work (as we can see). You’re going to need to do more with the data, before it reaches QTable. I’m just trying to understand how the select is going to offer the object data as an array selection from your data.
Scott
-
So, which part of the data is supposed to be remapped to an array? Can you give me an example of a key in the data?
In the column definition in constants.js there is for example the field ‘net_arch_pi’ defined by
{ name: 'net_arch_pi', label: 'Policy Network', reference: 'training.policy_kwargs.net_arch.args.pi', field: row => _.get(row, 'training.policy_kwargs.net_arch.args.pi', [64, 64]), format: val => '[' + val.reduce((a, b) => a + ' - ' + b) + ']', sortable: true },
which is an array of integers like [64,64] or [512,512,512].
So we map this field to a table column basically byfield: row =>.get(row, 'training.policy_kwargs.net_arch.args.pi')
which is just the lodash implementation of
field: row => row.training.policy_kwargs.net_arch.args.pi
So when opening the dialog we actually map
data.training.policy_kwargs.net_arch.args.pi
to
newSession.net_arch_pi
which should be editable on the dialog screen by a QSelect component.
But I wonder if it is only concerned to QSelect components, because I have the same effect on any ‘input field’ on any newSession.XXXX attribute. I also think that the mapping is fine as we can see that in the dialog the data is correctly displayed at first.
-
Tldr; as far as i can see above, theres nothing wrong in the components in question, its how you are structuring your data, imo i think you need some more time restructuring it or see and understand more how the component works, the docs should help. A codesandbox should also help so you can show what is working so far or whatnot, and in the end we can support you further.
Check The quasar codesandbox what @s-molinari linked above, imo would rather see a running code than code you posted in this thread, when at first look is rather complex or need restructuring*.