Why no refresh_data_bindings() for write back data binding?

How is what you want different from ordinary “write back”?

@owen.campbell @hugetim Some context: what I’m trying to achieve:
I have created a for with regular components and set up their data binding + write back. Not special so far.
But now I’m catching the Change event for all components and want to send the self.item to the server for validation. Of course not all fields are complete/correct (yet), but it gives me the opportunity to give early feedback to the user that something is amiss or missing.

The problem is that self.item is only populated when the lost_focus event is triggered for the fields. So self.item is empty for a field until a field visited.

Then I thought, no problem, I will just call refresh_data_bindings to fill self.item before sending it to the server. But refresh_data_bindings only works one way (not for write back).

So the only solution is to fiddle with all fields and write their contents to self.item. In effect this emulates the write back process. But it requires a lot of finicky code: iterating over the fields and depending on their type convert their contents and copy it to self.item. Also in code I don’t know how to access the write back bindings. So I don’t know the field name.

refresh_data_bindings for write back would enable a generic solution.

HTH

1 Like

Putting in a server call each time a change event fires is going to result in a lot of server calls.

Have you had a look at the validation library for doing this kind of thing client side?

Another way to do this would be to create a custom component. You can change the data binding write back event to the change event. It’s not perfect since it’s still requires some boiler plate code. But it’s agnositic about the writeback property binding.

Here’s a proof of concept:
https://anvil.works/build#clone:DKFNP5PUGCWK4Q73=ZPRL7RXNKFPG55MSGFYAWIJ5

I’m with @owen.campbell - the change event for a server call feels like it’ll be a lot of server calls.

You could reduce the number of server calls by creating some sort of call to the server if the user stop typing after for some short interval. While they’re typing the change event resets the timer interval. When the timer fires you call the server.

Situations like this are why I have explicit Save and Cancel buttons.

When the cat walks across your keyboard, you don’t want that automatically posted to the database. (At least, I don’t.) This way, no updates hit the servers in England until the end-user says so. I don’t have to micro-manage my keystrokes (“ooh, mustn’t change any of these fields by accident!”) while exploring a data-entry window. And it eliminates confusion for the end-user as to exactly what gets saved, and when.

4 Likes

Another aside on validation: The recommendation here to do it both client- and server-side (by putting the logic in the module that can be used both places) makes sense to me.

1 Like

@stucork @hugetim @p.colbert You all make valid points. However, that is not what I wanted to try.

In almost all projects I find Anvil client side data validation facilities lacking. I’d love to see a decent set of data entry fields (=data entry+label+help+error msg) with sophisticated validation. Currently client side field validation of components is unreliable (depends on browser). The validation library had its problems as well.
I have created a set of custom components that did the above, but I found custom components lacking as well: the representation of the comp in the IDE is static. The setting of properties is primitive. I needed an external pubsub library (currently in Anvil Extras - great initiative) to makke it work. In the end I found my set of components not flexible enough for the complexity of maintaining them.

So it was time for a third experiment: leverage Pydantic on the server. Pydantic is a very nice validation library. The approach was to use the IDE to create the components on the form as usual, then use databinding. Then on form init, have a method “augment” the fields with a label, error message label, event handlers.
The “change” event handler catches all changes and sends the self.item to a fairly generic function on the server. That server function uses Pydantic to validate the data and returns fairly detailed error messages (+their locations). On the client this error info is used to populate the error labels. It tries to only show the error messages only if they are touched by the user.
It works surprisingly well, has the advantage of having the validation only in one place. The validated result is on the server ready for use. It also offers error messages as soon as possible and as localised as possible.
There is fairly little code involved and is generic.
This approach has its limitations:

  • Radiobutton has no write back (I use a DropDown instead)
  • Comp placeholder is (ab)used as a label
  • No control over layout (I put everything in a Grid), but is decent enough.
  • the code not the nicest (a lot of special cases)
  • and obviously more client server traffic

In my experimenting I concluded that a refresh_data_bindings for write backs would simplify my code and make it more generic. The same for being able to set the databinding in code (which I don’t know how to do).
So I think I’ll proceed with this approach especially for apps with a lot of forms.

1 Like

I’ve wanted to add validation based on how pydantic (or attrs) do things to my ORM library for a while. I’ve just not had the time to do much about it.

I’ve created an Issue for it.

2 Likes

I’ve created a feature request: Please support refresh_data_bindings for write back as well

1 Like

I’ve been following this thread, trying to understand what isn’t currently provided that you’re asking for. I think I’ve got it now, but maybe not?

I currently rely on writeback to self.item in my projects, and it works fine. There’s no way to trigger it programmatically, it just happens for any writeback field as soon as that field changes.

If self.item is a data table row, that change propagates to the data table. If self.item is a dictionary, the dictionary changes. I use this version quite a lot.

It sounds like you want to interpose validation code between the data table row and the propagation to the data table. So you want self.item updated (as currently happens) but not the data table row until you’ve had a chance to validate it.

Or am I totally confused?

I’m in the phase of validating the data, so no Data table involved just yet. The ‘weird’ thing I do is doing the validation server side (because I want to use Pydantic). To that end I want to frequently (on every change) post the whole self.item (even if some components have not been visited yet). I do not want to wait for the whole form to be submitted.

A (currently non existing) refresh_data_bindings_for_write_back would enable this: it would in essence take all the fields’ data and write it to the self.item.
You might say (as the docs do) just do it yourself, but that would require me to traverse over all fields, get the data (different for each type). Doable but a refresh_data_bindings_for_write_back would make it trivial.

Hope this clears it up a little bit?

It feels like we’re talking about different things, maybe.

Right now, if I put a dictionary into self.item, and configure fields for write_back, the dictionary gets updated automatically anytime the field changes. No traversing of fields is needed. To me, that sounds like what you’re asking for, but that’s what’s already happening. So I’m obviously misunderstanding what you’re asking for.

Here’s a quick clone link showing the dictionary getting updated: https://anvil.works/build#clone:VWGE5DB4JC5ERVTG=CLFWCYLJDTNLSCU5YQ5WTMED

I’m mostly poking at this because I still have no idea what the feature request is for, so can’t say whether I’d vote for it or not. I’d like to understand, so I know what cool feature I’m just totally not understanding.

My understanding is that the goal is to be able to cleanly implement a write-back-type functionality that triggers on the ‘change’ event of a TextBox (for instance), whereas the built-in write-back is only triggered by ‘pressed_enter’ or ‘lost_focus’ events.

@hugetim That’s correct.

1 Like

@jshaffstall I doubt if it is cool :wink:
ATM fields in self.item are populated one at a time, indeed, any time a field’s lost_focus (etc) event is triggered. There is no way to say populate the whole self.item NOW even if the contents of the fields have not changed.

The existing binding behavior is fine for the vast majority of cases. Rather than saddle everyone with a very significant change in behavior, I suggest that it might be better to argue for a second kind of binding altogether, one that behaves as @mjmare desires. One could then let the developer choose which kind of post-binding behavior is appropriate for their situation.

I don’t see how the addition of a “refresh_data_bindings_for_write_backs” would alter existing behaviour.

What’s the use case where you need to populate everything in self.item at the same time? With writeback self.item is always up-to-date, especially if the fields have not changed.

Sorry, still struggling to understand in what situation this is needed.

If it’s the case that you want self.item updated on events other than lost focus (e.g. field changed), that makes sense, since there is a period while the user is typing when self.item and a single field contents don’t match.

Or maybe it’s that you want writeback to happen even when you programmatically change the contents of a field? That also makes sense, since writeback doesn’t trigger then. I could see a way to programmatically trigger writeback useful in that situation.

Also, if at any point I’m just so far off base that it’s clear I’m never going to get it, feel free to say so. Not trying to generate any frustration here, just trying to wrap my mind around the proposed feature.

1 Like

That is indeed the case: ideally I’d like to have the whole self.item updated on each field change.

For those following this thread - I’ve added a feature to the augment library. The latest version can be found within the anvil-extras package (This feature will get added in the next version release)

Anvilistas - The Greatest Hits

It overloads the trigger method that already existed when using that library. It aims to address the issue highlighted in this thread that it’s not possible to trigger a writeback on TextBox or TextArea change events and creating a custom component to do the same thing is a little cumbersome

It can be used as follows:

from anvil_extras import augment

def textbox_change(self, **event_args):
    event_args['sender'].trigger('writeback')

This will work on any component that has writeback properties set.

3 Likes