py-shiny

How to properly remove previously inserted UI in Shiny?


Working on a Shiny app where the user can press a button and receive all tests that evaluate to False. I'm able to successfully insert these test messages into the UI; however, every time I press the button to rerun these tests, the UI adds onto the test results from before. How can I properly remove UI before inserting more UI?

from shiny import App, reactive, ui, run_app


# for reference, I run itertools.filterfalse over this to obtain a list of messages whose conditions evaluate to False
test_results = {'Test message #1':True, 'Test message #2':False, 'Test message #3': False}
test_results_output = ['Test message #2', 'Test message #3']


app_ui = ui.page_fluid(
    ui.input_action_button('button_scan_directory', 'Scan directory'),
    ui.output_ui('output_scan_report')
)

def server(input, output):
    @reactive.Effect
    @reactive.event(input.button_scan_directory, ignore_init=True, ignore_none=True)
    def output_scan_report():
        for test in test_results_output:
            ui.remove_ui(selector="p:has(> #App Error)")
            ui.insert_ui(
                ui.p(f"App Error :: {test}"),
                selector='#output_scan_report',
                where='afterEnd'
            )


app = App(app_ui, server)
run_app(app)


Solution

  • As long as you don't need each new line to have its own UI, a solution is to return the new UI object directly. This is maybe what you where trying to do as you created a ui.output_ui object.

     ### app ui 
     ui.output_ui('output_scan_report')
    
     ### server function
     @output
     @render.ui
     @reactive.event(input.button_scan_directory, ignore_init=True, ignore_none=True)
     def output_scan_report():
         message = '<br>'.join(f"- {test}" for test in test_results_output)
         return ui.HTML(message)