Skip to content
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

AG Grids inside hidden tabs don't get mounted #3033

Open
falkoschindler opened this issue May 7, 2024 · 4 comments
Open

AG Grids inside hidden tabs don't get mounted #3033

falkoschindler opened this issue May 7, 2024 · 4 comments
Labels
bug Something isn't working help wanted Extra attention is needed

Comments

@falkoschindler
Copy link
Contributor

falkoschindler commented May 7, 2024

Description

As noticed in #1869 (comment), when creating AG Grids on currently hidden tabs, they don't get mounted and interacting with them can cause problems:

with ui.tabs().classes('w-full') as tabs:
    one = ui.tab('One')
    two = ui.tab('Two')
with ui.tab_panels(tabs, value=one).classes('w-full'):
    with ui.tab_panel(one):
        async def get_data():
            ui.notify(await grid.get_client_data())
        ui.button('Get data', on_click=get_data)
    with ui.tab_panel(two):
        grid = ui.aggrid({
            'columnDefs': [{'headerName': 'Name', 'field': 'name'}],
            'rowData': [{'name': 'Alice'}, {'name': 'Bob'}],
        })

When clicking "Get data" before visiting tab "Two" for the first time, a JavaScript error occurs and we don't see a notification.

How can we make sure every NiceGUI element is created, even if it is hidden on another tab?


Related discussion: #2157

@falkoschindler falkoschindler added bug Something isn't working help wanted Extra attention is needed labels May 7, 2024
@kyloe
Copy link

kyloe commented May 12, 2024

I've done some searching in Quasar and Vue disciussions and have found these topics, quasarframework/quasar#16329 and quasarframework/quasar#15912.

Reading them suggests that rather than being a bug in NiceGUI it's actually a problem with Quasar. Ive looked at the solutions but they involve adding js in the Quasar environemnt - and I'm a bit new to NiceGUI, so struggling to see how NiceGUI can push these code fragments into the Quasar rendering of the nicegui app.

If anyone has a pointer - I will happily pursue the matter :)

@falkoschindler
Copy link
Contributor Author

Interesting news, @kyloe!

So as far as I understand, they suggest to traverse the tabs once they are mounted. We could wrap the Quasar tab components in order to add such code to the mounted hook. But I managed to achieve something similar by adding code to TabPanel.__init__:

class TabPanel(DisableableElement):

    def __init__(self, name: Union[Tab, str]) -> None:
        # ...

        assert isinstance(context.slot.parent, TabPanels)
        panels: TabPanels = context.slot.parent
        current_tab = panels._props['model-value']
        panels.run_method('goTo', self._props['name'])
        panels.run_method('goTo', current_tab)

But this causes a visible animation when loading the page. The same happens on the linked codepen https://codepen.io/metalsadman/pen/OJyoYPB.

Is there a better way to do it?
And what's the matter with the mentioned @before-transition event?

@kyloe
Copy link

kyloe commented May 12, 2024

I tried to capture the before-transition event - but it didn't appear to be firing (this is most likely my lack of familiarity with the various layers (ng/quasar/vue) and how they interact, I was probably doing something incorrectly).

My test code to see if the event was firing ...

from nicegui import Client, app, ui, events, Tailwind
import time 

columns = [
    {'headerName':"Item", 'field': 'item', 'editable': False, 'sortable': False},
    {'headerName':"Value",'field': 'value', 'editable': True,'cellDataType':'text'},
]

rows_A = [{"item":"Item A","value":"FOO"},{"item":"Item B","value":"AAA"}]
rows_B = [{"item":"Item A","value":"FOO"},{"item":"Item B","value":"BBB"}]

@ui.page('/')
def main_page() -> None:
    grid_A = None
    grid_B = None

    async def get_data():
        msg_A = await grid_A.get_client_data()
        print(msg_A)
        msg_B = await grid_B.get_client_data()
        print(msg_B)

    with ui.tabs() as tabs:
        tab_A = ui.tab("A")
        tab_B = ui.tab("B")

    def mounted():
        print("Mounted")

    with ui.tab_panels(tabs,value=tab_A).on('before-transition',mounted) as panels:
        with ui.tab_panel(tab_A) as panel_A:
            with ui.element('div').props('ng-if'):
                grid_A = ui.aggrid({
                                    'columnDefs': columns, 
                                    'rowData': rows_A,
                                    'domLayout': 'autoHeight',
                                    'stopEditingWhenCellsLoseFocus': True
                                }).style('width: 300px')  
        with ui.tab_panel(tab_B) as panel_B:
            with ui.element('div').props('ng-if'):
                grid_B = ui.aggrid({
                                    'columnDefs': columns, 
                                    'rowData': rows_B,
                                    'domLayout': 'autoHeight',  
                                    'stopEditingWhenCellsLoseFocus': True
                                }).style('width: 300px')  

    ui.button("Get data").on('click', lambda: (get_data()))

ui.run(storage_secret='THISISABIGSECRET') 

@kyloe
Copy link

kyloe commented May 14, 2024

Interesting news, @kyloe!

So as far as I understand, they suggest to traverse the tabs once they are mounted. We could wrap the Quasar tab components in order to add such code to the mounted hook. But I managed to achieve something similar by adding code to TabPanel.__init__:

class TabPanel(DisableableElement):

    def __init__(self, name: Union[Tab, str]) -> None:
        # ...

        assert isinstance(context.slot.parent, TabPanels)
        panels: TabPanels = context.slot.parent
        current_tab = panels._props['model-value']
        panels.run_method('goTo', self._props['name'])
        panels.run_method('goTo', current_tab)

But this causes a visible animation when loading the page. The same happens on the linked codepen https://codepen.io/metalsadman/pen/OJyoYPB.

Is there a better way to do it? And what's the matter with the mentioned @before-transition event?

@falkoschindler

If the property 'animated' is removed when the tabs are instantiated - then the creation process is a lot less unpleasant

with ui.tab_panels(ui_tabs, value=tabs['names'][0]).props(remove='animated') as ui_panels:

I'm still looking for a more elegant solution ...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working help wanted Extra attention is needed
Projects
None yet
Development

No branches or pull requests

2 participants