Realtime UI DB connection

What I’m trying to do:
Have a TextBox update automatically when a linked table on the server is updated.

What I’ve tried and what’s not working:
Created a TextBox and did the two way data binding show below.
image

Code Sample:

class test_databind(test_databindTemplate):
  def __init__(self, **properties):
    self.init_components(**properties)
    self.item = app_tables.databind_test_table.get(id="2")

When I update data directly in the database, I would love it to refresh the value in the TextBox automatically:
image

Utils from AnvilExtras https://anvil-extras.readthedocs.io/en/latest/guides/modules/utils.html didn’t work either for that.

Timer is a suggestion here:

Chat-GPT is saying to use a Global Variable on server / Live Object on the server, but I wasn’t able to make that work.

MQTT broker solution here:

The discussion continues here, where in 2017 the feature was on the planning table:

Before I try to incorporate Firebase Realtime DB / firestore, is there another Anvil way?
(if there isn’t, I will post my answer in a few weeks when I get a chance to figure it out).

Data binding is not useful for this situation, and not intended for this use case.

Your best bet for a vanilla Anvil solution is a Timer, although what you’re asking to do will involve a fair amount of back and forth to the server due to polling frequently via server calls.

A Firebase solution will minimize that by allowing Firebase to let you know when the value changes, but is more involved to setup. That’s probably your best bet, though, if efficiency is a concern.

3 Likes

If you do go the firebase route, you don’t need to make your own solution as there’s a good solution in the community.

1 Like

Here is my solution to make sure UI is updated if there is a data change in another browser.

Launching the form subscribes to a Firebase realtime database where I store last_updated value for each field identified.
Any time the client writes to Anvil, Firebase date is also updated.
Any time firebase last update date is updated, a redownload of data from anvil is triggered (instead of doing it based on a timer).

Limitations: 100 active fields can be tracked total in the free tier. That’s for all fields and for all users.
That is not much, but you can optimize this by using one tracker for all fields or pay a small amount to get 200k simultaneous connections.

If that is ok for you, here is how without going full firebase add-on route:

  1. Sign up for firebase, create a project, specify it as a web app, create a realtime db.
  2. In Anvil create a module called firebase_ and paste with your config version of the code:
import anvil.js
firebaseConfig = {
  'apiKey': "AIzaSyChQb000000000000DrRN-CS1AgY5mk",
  'authDomain': "testtest.firebaseapp.com",
  'projectId': "testtest",
  'storageBucket': "testtest.appspot.com",
  'messagingSenderId': "7500000000245",
  'appId': "1:75273000005:web:b61ca000001cea0000004ba4"
}
firebasejs = anvil.js.import_from('https://www.gstatic.com/firebasejs/10.7.2/firebase-app.js')
initializeApp = firebasejs.initializeApp
firebasedb = anvil.js.import_from('https://www.gstatic.com/firebasejs/10.7.2/firebase-database.js')
getDatabase = firebasedb.getDatabase
onValue = firebasedb.onValue
update = firebasedb.update
ref = firebasedb.ref
app = initializeApp(firebaseConfig)
db = getDatabase(app)

you don’t have to use data bindings, but here is anexample using them:
3) in a form with a field you want to keep updated:

class test_databind(test_databindTemplate): 
  def __init__(self, **properties):
    self.init_components(**properties)
    self.rowid = "1" #can be set through a property if you make this a reusable component.
    starCountRef = firebase_.ref(firebase_.db, 'tmp/a'+str(self.rowid))
    firebase_.onValue(starCountRef, self.update_value)

  def update_value(self, snapshot, three):
    if str(snapshot.val()) != self.latest_update:
      self.item = app_tables.databind_test_table.search(id=self.rowid_value)[0] #.get didn't work
      self.latest_update = str(snapshot.val())

  def form_refreshing_data_bindings(self, **event_args):
    self.latest_update = str(datetime.now())
    firebase_.update(firebase_.ref(firebase_.db, 'tmp'), {"a"+str(self.rowid): self.latest_update})

This code sets up a writer that triggers to write to Firebase when data bindings are refreshed (in this case a two way binding that updates the DB as well).
It also sets up a listener onValue to call to get data from Anvil.

Security:
The firebase database has to be open and someone talented could overwrite the data or abuse the firebase db. No content data is shared with firebase, just the update date which is less sensitive.
image

Here is a gif of it working when set to trigger on Enter and Lost_focus for the textbox and using data binds that write to Anvil on the same event.
on_lost_focus

Future plans:
You can make it live (with a small delay) if you don’t use data bindings, instead read, write and update directly from code.

Delayed live version (direct read write, and on change that triggers it):
on_change

In my apps where multiple users could edit the same document, I use a timer as mentioned by @jshaffstall.

The document is served with a timestamp.

Every 5 seconds a timer checks if that document has been modified, and if it has been modified it shows an alert showing the name of the user that modified it and asking to reload.

When the user saves the document, the timestamp is checked again and the save is aborted if the document was modified in the last 5 seconds.

I have about 10 users using the app at any given time, and they are happy with this setup.

1 Like