CONTEXT
I’m working on a note taking app. User can create a note and save it with a name. So note will have 2 properties: name and content.
Therefore, the database has a table having 2 columns:
- name (name of note)
- content (content of note)
WHAT I WANT
I’m wanting to show the list of notes by their name (as buttons). Such that clicking on a note shall display the content of that note adjacently (in a label).
MY CODE (WHAT I TRIED)
def display_notes(self):
notes=anvil.server.call('get_notes')
for row in notes:
button=Button(text='%s'%(row['name']))
def handle_click(**e):
self.note_content.visible=True
self.note_content.text=row['content']
button.set_event_handler("click",handle_click)
self.linear_panel_1.add_component(button)
THE PROBLEM
Clicking on any of the note displays the content of the last note.
This is such a simple thing, I’ve done a similar thing on Android (Java) innumerable times and it worked perfectly each time there.
Why are all event handlers getting overwritten to the last button’s event handler?
Seeking some anvil community expertise.
Welcome to the forum!
This is a Python thing, nested functions like that bind to variable values as late as possible. What you want is to pass the row into the function, e.g.:
for row in notes:
button=Button(text='%s'%(row['name']))
def handle_click(row=row, **e):
self.note_content.visible=True
self.note_content.text=row['content']
button.set_event_handler("click",handle_click)
self.linear_panel_1.add_component(button)
That way the function gets the current value of row, instead of binding to the outer scope later.
That’s untested and might need some work to get the click event to allow the row argument. Might need to work partials in there somewhere.
4 Likes
Solution by @jshaffstall should work. Just to add my solution when I faced similar issue…I used a closure to carry over the correct context to the handler.
for row in notes:
button=Button(text='%s'%(row['name']))
def handle_click():
def handler(**e):
self.note_content.visible=True
self.note_content.text=row['content']
return handler
button.set_event_handler("click",handle_click())
self.linear_panel_1.add_component(button)
1 Like
Is it because you are using set_event_handler
instead of add_event_handler
?
1 Like
Some additional references that might be handy
And my favourite video that speaks to python closures inside loops being as frustrating as you might have found them (note some curse words used):
1 Like
It worked!
Thank you for pointing out the working of variable value binding.
Earlier, I had also tried lambda as well but that didn’t work and actually got really frustrating.
2 Likes
Thanks for pointing out an alternate solution. This also works perfectly.
I’m pretty sure that’s not the reason because the only difference between set_event_handler and add_event_handler is that set_event_handler overwrites the previously existing event handler.
In the context of my problem both would work the same.
But anyways, thanks for trying to help.
Well, it actually gets really weird and frustrating.
Thanks for the reference though.
1 Like