Hi there, and welcome to the forum!
This is an interesting design question, and something we thought a lot about when we created data bindings. I’ll start with the concrete advice, and then talk a bit about why we did it this way.
First things first - if you want to cause a refresh after updating self.item
, the standard way is something like this:
def update_value():
self.item['value'] = 'Something'
self.refresh_data_bindings()
Likewise, if you want to update the rest of your form every time a text box changes, you should bind to its change
or pressed_enter
or lost_focus
events and either do an update or call refresh_data_bindings()
then. (If you have multiple TextBoxes, you can make one ‘refresh’ event handler, and set all their lost_focus
events to call the same method.)
It would be nice to update everything automatically whenever you change some variable. So why didn’t we do it? Well, we decided it would be impractical, for a number of reasons:
-
There is no guarantee of what sort of object self.item
really is. (It could be a dict
; it could be a table row; it could be something else entirely! There’s a convention that it’s a dict-like object you can access with square brackets, but you can use it however you like. Eg if you set a RepeatingPanel’s items
property to a list of strings, the item
of each template instance will be a string.)
-
It’s totally normal to bind to nested expressions (eg if self.item
is a table row, and that table has an author
column that links to the Users table, I could bind a Label’s text to self.item['author']['email']
to display the author’s email address). If you wanted to do automatic updates, you’d somehow need to track every change to the nested object too!
-
Also, data bindings can use any Python expression - there’s no requirement to bind to self.item['something']
. Eg if you want to calculate a value in a method, you can bind to self.my_method()
.
(Just be aware that data bindings run when you call init_components()
. So if my_method
uses some internal attributes, make sure you initialise them before calling init_components()
.)
(Bonus advanced hint: If you want to bind to a complicated piece of code, but you also want to write back, make a settable @property
and then bind to that.)
Fundamentally, Python does not have a way of automatically finding out when an arbitrary object is modified. (You can approximate it with, eg, the Traits module, but that can be can be a bit mind-bending, even for experienced Python users. And we care about Anvil being comprehensible! Trying to be too clever about automatic updates is one of the things that makes, eg, Angular, hard to use.)
So we decided to cut with the grain of the language, and make updates (ie refresh_data_bindings()
) an explicit action, rather than trying to do it automatically.
I hope that gave you a bit of insight into why we built Anvil that way 