S
Solara2mo ago
JP

How do i remove items from GridDraggable?

Trying to make adding and removing grid items possible. Already made the layouts reactive. adding and {item:layout} pair does create the new item, but removing the {item:layout} pair doesn't remove the item.
6 Replies
pikaa
pikaa2mo ago
Are you mutating the dictionary correctly? Can you send a code snippet of your add/remove code so we can have a look?
parzival
parzival2mo ago
In Solara, managing dynamic grid layouts requires proper state handling to ensure both adding and removing items work correctly If removing an {item: layout} pair doesn’t update the UI, the issue likely lies in how the reactive state is managed Ensure that when an item is removed, the state is properly updated using solara.use_state, triggering a re-render Additionally, conditional rendering should be implemented to reflect state changes, and unique keys should be assigned to grid items to help Solara track updates accurately Hope that helps
JP
JPOP2mo ago
def append_item(name, index): file_names.value.append(name) layout = { "h": 3, "i": index, "moved": False, "w": 7, "x": index * 3, "y": 0 } grid_layout.value.append(layout) save_grid() def delete_item(fname, index): print(grid_layout.value) if not fname == None: os.remove('assets/vista_files/'+fname) for card in items: print(card.key) if card.key == fname: for layout in range(len(grid_layout.value)): if index == grid_layout.value[layout]['i']: print('removing layout with index: ' +str(grid_layout.value[layout]['i'])) grid_layout.value.pop(layout) print(grid_layout.value) save_grid() both cases i use reactive grid_layout in the the first function, the append works, a new griditem is made But in the delete function, it doesn't work Printing the grid_layout.value shows the layout frame is indeed removed, but still the ui is not changed not untill i refresh the frame I'm gonna try use_state and see if it changes anything No such luck
pikaa
pikaa2mo ago
Because reactives won't notice updates to mutated variables (lists, dicts, etc), you need to reinstantiate them to trigger the rerender. The rerender in the first one is triggered likely because you update file_names, which might be referenced/updated by something else or similar. Do something like:
def append_item(name, index):
...
new = grid_layout.value + [layout]
grid_layout.set(grid_layout.value + [layout])
...

def delete_item(fname, index):
...
for card in items:
if card.key == fname:
for layout in range(len(grid_layout.value)):
if index == grid_layout.value[layout]['i']:
print('removing layout with index: ' +str(grid_layout.value[layout]['i']))
grid_layout.set(grid_layout.value[:q] + grid_layout.value[q + 1:])
save_grid()
def append_item(name, index):
...
new = grid_layout.value + [layout]
grid_layout.set(grid_layout.value + [layout])
...

def delete_item(fname, index):
...
for card in items:
if card.key == fname:
for layout in range(len(grid_layout.value)):
if index == grid_layout.value[layout]['i']:
print('removing layout with index: ' +str(grid_layout.value[layout]['i']))
grid_layout.set(grid_layout.value[:q] + grid_layout.value[q + 1:])
save_grid()
See more by Marteen at: https://github.com/widgetti/solara/pull/595 Additionally, see https://github.com/widgetti/solara/issues/867#issuecomment-2568694422 for notes about shared globals.
Monty Python
Monty Python2mo ago
rileythai
This is something that isn't documented, but is caused by parsing something that is pre-evaluated into a global reactive. This can happen when you do something like: - sl.reactive({}) :: the dict is already made - sl.reactive(pd.read_csv(filename)) :: the dataframe is made and then referenced by each reactive instance To check if this is the case, open two sessions of your app and print the hex(id(state.value)). Both of these will be the same between both sessions. A way to resolve this is by running a startup function to set the initial variable, either by sl.use_effect at your top-level component, or solara.lab.on_kernel_start. For example, your state could be:
state = solara.reactive(cast(UpcomingStateTypeHere, None))
state = solara.reactive(cast(UpcomingStateTypeHere, None))
and it is set to a unique starting value per kernel/user with:
@solara.lab.on_kernel_start
def on_start():
state.set(get_initial_thing())

@solara.component
def Page():
...
# do this OR the kernel_start method, not both
def effect_to_run():
state.set(get_initial_thing())
solara.use_effect(effect_to_run, dependencies=[]) # runs once after first render, never again
...
@solara.lab.on_kernel_start
def on_start():
state.set(get_initial_thing())

@solara.component
def Page():
...
# do this OR the kernel_start method, not both
def effect_to_run():
state.set(get_initial_thing())
solara.use_effect(effect_to_run, dependencies=[]) # runs once after first render, never again
...
You can add cleanup functions as well to each, although it is much simpler on the on_kernel_start decorator.
Comment on widgetti/solara#867
JP
JPOP4w ago
thnx

Did you find this page helpful?