Trying to understand QDialog and $refs
-
I’ve run into some weird behavior with QDialog and with $refs. I don’t know if they’re the same problem or two separate issues. Here it goes.
I have a component called UserRegistration, that is essentially a dialog that collects the user data for registration. So it looks something like:
<template> <q-dialog v-model="isOpen" persistent> <q-card> <q-bar dark class="bg-primary text-white"> <div>User registration</div> <q-btn dense flat rounded icon="close" @click="handleCancel"> <q-tooltip>Close</q-tooltip> </q-btn> </q-bar> <q-card-section> <record-input-form ref="inputForm" :currentRecord="this.record" :schema="this.schema" :defaultRecord="this.defaultRecord" /> </q-card-section> <q-card-actions align="right"> <q-btn flat color="primary" label="OK" @click="handleOK" /> <q-btn flat color="secondary" label="Cancel" @click="handleCancel" /> </q-card-actions> </q-card> </q-dialog> </template>
The RecordInputForm is a list of QField, one for each field in
this.record
as defined bythis.schema
.The first time I ran the code I encountered problem #1:
this.$refs
was an empty object. I usedconsole.log
to look for its value increated
,mounted
and inhandleOK
. In all three cases it was empty, no fields whatsoever. This does not make sense, since I have aref="inputForm"
set on the<record-input-form>
component.I set a ref on the QDialog and then I started seeing the value of $refs as:
{ dialog: ...., // QDialog component inputForm: undefined }
Again this happened on
created
,mounted
and inhandleOK
. I figured maybe increated
andmounted
the content of the dialog is not actually created, but inhandleOK
it should be, since it is displayed. So I addedconsole.log(this.$refs.inputForm);
to the
handleOK
. And that returned a Vue component that was the RecordInputForm. At the same time though, $refs showed aninputForm
field with anundefined
value.So I am looking for some kind of explanation of what happens here. When are components that are the contents of a QDialog created? And more importantly, when would a component that has a
ref
attribute show in$refs
?Problem #2 showed when I started looking at the Vue dev tools in Chrome. While the QDialog is listed in the component tree, it shows no children, even when the dialog is displayed. Not only that, but using the finder to locate for example the OK button, locates nothing. It’s as if the QDialog is empty, no children whatsoever. Which I know it is not true, since I wrote the code, and I can see the content on the page.
I will mention problem #3, although I think it deserves a separate discussion. Debugging does not work. VS Code refuses to set breakpoints, and that’s the end of that.
debugger
statements are not reliable. Sometimes they work, other times they don’t. And sometimes it stopped in code from one or the other package that I use in the project, without me setting any kind of breakpoint there. Really weird behavior. Quasar 0.x did not have this problem. Something got broken in 1.0 and it needs a fix. Badly. The Quasar docs have a section about debugging in VS Code, but it is empty. -
@tdumitr if you are accessing your ref in js part of your sfc, since its referencing a component that will be rendered at later time, just wrap your
$refs
call inside a$nextTick(()=>{ this.$refs.yourRef.someMethod()})
. https://vuejsdevelopers.com/2019/01/22/vue-what-is-next-tick/ -
Hah, I forgot to mention in my original post that I tried that too. It does not work. The reason is probably what the Vue dev tools in Chrome reveal.
The content of the QDialog does not show up in the Vue dev tools even when the dialog is open. It should have a QCard child with two QCardSection and one QCardActions under it and so on. Please see the attached screen shot.
Your answer seems to suggest that QDialog does not render at the same time as the component containing it. That makes sense, there’s no reason to take the time to render it until the QDialog is displayed. But that does not explain why in handleOK (which is called when the OK button in the QDialog is called)
$refs.inputForm
isundefined
. At this time the QDialog is obviously fully rendered.Here is a console trace where I load the page, open the registration dialog and then press the OK button in the dialog.
in mounted this.$nextTick(() => console.log(this.$refs)); {} __proto__: Object in handleOK this.$refs {inputForm: VueComponent} inputForm: undefined __proto__: Object this.$refs.inputForm VueComponent {_uid: 53, _isVue: true, $options: {…}, _renderProxy: Proxy, _self: VueComponent, …} this.$children [VueComponent] 0: VueComponent {_uid: 39, _isVue: true, $options: {…}, _renderProxy: Proxy, _self: VueComponent, …} length: 1 __proto__: Array(0) this.$children[0].$children []
So in
mounted
,this.$refs
is empty with or without using$nextTick.
In
handleOK
,this.$refs
shows a fieldinputForm
that isundefined
, and that is contradicted by the line immediately below that shoesthis.$refs.inputForm
as being aVueComponent
. Equally interesting isthis.$children
that shows a single child: theQDialog
andthis.$children[0].$children
that is an empty array. In other words, even with the dialog open, theQDialog
has no children, consistent with what the Vue dev tools shows. And yet the page clearly displays content in theQDialog
.To properly display the content of the dialog, I need to interrogate the API that backs the app for the default record and for the record schema and pass it on to the
inputForm
component which uses that to build its content. With Quasar 0.X, I was doing this increated
and everything was OK, because$refs
was already populated. With Quasar 1.0 I clearly cannot do that. But I am unclear when can I pass on the data to the inputForm. Do I do this after theQDialog
is displayed? Will$refs
have theinputForm
field populated at that point? -
Weird, i did encounter something like this before v1 but it was for panels, can try to wrap your dialog inside keep-alive. Can maybe provide a minimal pen of your setup so we can look it up.
edit. nvm, that didn’t work.what you could do is pass your schema as a prop to your Qdialog, then down to your child components that will use it. like so https://0ybb3.sse.codesandbox.io/dialog-sample, source https://codesandbox.io/s/0ybb3. (Vuex will work too)
you are correct with your observations, i think this could be an issue about portal-vue which QDialog component is using (iirc), child components are getting recreated*. https://github.com/LinusBorg/portal-vue/issues/118
-
I ran into a similar issue (not using refs though) … because I expected to see my components on my QDiloag in Chrome Dev Tools and couldn’t understand why they did not show.
By using console.log in mounted of the QDialog, I determined that the components on the QDialog are recreated each time the QDialog is opened … I am opening, closing, and re-opening the QDialog several times hence how I first noticed this.
I ended up making use of Vuex because of this.
-
Yes, I was able to observe the same. The behavior is different from the old QModal, where everything was rendered when the page was loaded, with the dialog kept hidden, but very much in the DOM. There’s an opportunity for improving the docs (they are really great as they are, but there’s always room for improvement) to explain exactly what gets created when, when can one use refs for fields inside the dialog, etc.
I moved the code for processing the schema and populating the dialog on
this.$nextTick
right after the dialog is open. Everything works well now.Thank you @metalsadman and @digiproduct for your help. Really appreciate it.