Creating QPopovers programmatically
-
I have an SPA which creates potentially large numbers of ‘items’ on a page, and each item is a component with a number of sub-components, one of which is a clickable menu button which should then display a popup menu of actions. This was all working fine, with
Item.vue
making use ofQPopover
so that each item had it’s own popover ready to use. However, as the number of items grew, the performance of this approach has rapidly decreased to the point where there is a noticeable lag displaying the page.I posted some early metrics on the Quasar discord channel here, summarised below.
I installed Vue Performance and set up a test page with 20 items. I see this in the page performance report:
Component Count Total QPopover 26 618ms QList 23 1007.4ms
compared to this when the menu button / popover is removed from the
Item.vue
template:Component Count Total QPopover 6 154.2ms QList 3 113.7ms
You can imagine how bad this gets with 200–300 items on a page. So creating the popover ‘on demand’ seems like a good solution, and in the discord chat it was suggested this could be done on mouse hover over an item, before a user gets chance to click the menu button, so there’d be minimal delay between the button and the popover appearing. I used the approach here, but hit a snag I haven’t been able to solve. The code is:
const QPopoverClass = Vue.extend(QPopover) const qPopoverInstance = new QPopoverClass({ propsData: { anchor: 'bottom right', self: 'top right' } }) qPopoverInstance.$mount()
and the error I see in the console is:
[Vue warn]: Error in nextTick: "TypeError: Cannot read property 'removeChild' of null"
Tracing that down, the
mounted()
hook inQPopover
refers tothis.anchorEl
, which is null when the element is created in code as above, and I’m not sure whether it just needs initialising differently, or whether this approach is just never going to work, and there’s a better solution.I did consider putting the popover in the page, but that would mean splitting code out from
Item.vue
and putting it where it doesn’t really belong, and I would also need lots of props and event-handlers to connect the 2 components.What I really want is something like
QActionSheet
but which displays as a popup near the click position, rather than at the bottom of the screen.Any advice / ideas gratefully received!
-
And, it looks like this may radically change in v1.0 from what I can see in the codebase, so perhaps the performance issue will just magically go away :). Roll on Jan!
-
Hey. I did this. https://jsfiddle.net/smolinari/3f41720v/
Can you show where it differs to what you are expecting? I can scroll and create 100s of items and there is no performance issue.
Scott
-
It’s tricky to tell, as I can’t seem to run the Vue Perf plugin on jsfiddle—not sure if it’s possible to get a non-production version of Quasar from a CDN… I’m still seeing the issue in my app though. I will try to put some timing in the code to see if it’s possible to highlight it. However, as per the discussion on Discord, it may (hopefully) be a moot point now, as all this looks set to change in v1.0 anyway.
-
Yes. v1.0 will have some changes. But, it might just be your usage of QPopover. I’d not do the instantiation bit that was suggested in Discord. That shouldn’t be necessary. As you can see in the fiddle, you can “scroll” to create 100’s of list items and the displaying of the popover works without any delay.
Scott
-
I just scrolled in over 500 items with no problems showing the popup menu.
Scott
-
In my app, showing a page with 150 items is pretty quick, but then the browser locks up for several seconds. If I comment out the QPopover, all is well, as is the case with just the menu button connected to showing an action sheet. I’ll stick with Action Sheet for now, and then look again in v1.0.
The item data is coming from firebase, so I’m not sure how much adding an infinite scroll would help me either—I know you were just using that for generating the list, but it did make me think whether that might help streamline things.