Data Binding

Object to UI and UI to Object is the provided as part of the core framework. It is achieved using storing objects in certain scope of the application and the ui synchronizing it whenever an event happens. This provides easy but powerful way to achieve synchronizing ui to objects and vice versa by configuring few attributes on widgets.

Scope

Scope where an object is stored can be one of the following:

  • view - It is stored on the fragment which is the root for all views. When a fragment is destroyed, the object is also removed along with the fragment.
  • component - It is stored in view scope with key <component id>#<key>.
  • session - It is stored on the application object. As long as the application is alive, the object is available.
  • loopvar - Whenever we loop on object, a loopvar is created and stored on the widget on which we are looping.
  • local - It is stored on the widget on which the modelParam is declared.
  • component - It is stored at the component level. The include tag serves as component in Ashera.
  • strings - Readonly scope to retrieve data from strings.xml.
  • constants - Readonly scope to retrieve data directly from the constant used in the expression.

Data Type

Objects can be stored in scope as:

  • map - Simple map containing key value pair.
  • list - list of objects.
  • plainmap - hierarchical map. This simplifies querying map for inner objects.
  • bool - boolean
  • int - integer
  • number - floating point number
  • string - String

Expression Statement

Expression statement is written to identity, move, store objects from ui to object and vice versa.

var - e.g let x = . from y->view into session as map

The above statement stores a intermediate variable x into session as map

Name Description
modelPojoToUi e.g text=abcd from x->view Set text attribute on widget to abcd attribute from object x stored in scope view.
modelUiToPojo e.g abcd=text into x->view Set abcd attribute on object x stored in scope view to text attribute on widget.
Event e.g y=z from z->view Store y to from object in scope view into event map.
var store e.g y->intent as pathmap. Store the object into a variable y on intent with type pathmap.
var get e.g z from z->view Get z attribute from object z in scope view.
loop var e.g let x in c from y->view into session as pathmap. Loop over c attribute of type list on object y in scope view. Create loop var x of type pathmap.

Custom Methods/Attributes

The View/ViewGroup has been enhanced with custom attributes to handle data binding.

Name Description
modelPojoToUi Used for synchronizing object to ui.
modelPojoToUiParams Method expression params.
modelUiToPojo Used for synchronizing ui to object.
modelUiToPojoEventIds List of widget ids to be refreshed using refreshUiFromModel after an event occurs.
modelSyncEvents Used for synchronizing ui to object.
refreshUiFromModel Used for refreshing the ui from the path configured using modelUiToPojo.
modelParam Store intermediate variables to scope.
updateModelData Used to update object data in certain scope.
modelFor Used to loop on list to create multiple user interface elements.
modelIdPath Identifier property path in the object.
addModel Append object to list stored in scope and update the user interface element.
removeModelAtIndex Remove object to list at index stored in scope and update the user interface element.
removeModelById Remove object to list by id stored in scope and update the user interface element.

Usage

The following is general flow of application and this depicts where the methods and expression has to be used:

Navigate to Page

Javascript data objects are born in the ts fragment files and finally stored in scope in native code. When we redirect to native screen, data is passed into method and this data is stored in certain scope as defined by the statement expression.

await this.navController.navigate(recyclerview_1000_items, 
	  "testObj->view as pathmap", 
	  {looptest: {textlayout: data}}).executeCommand();

The example above uses var store expression to store data specified into key testObj with scope view.

Sync of data displayed

The data stored in scope is synched back and from the ui to object using modelPojoToUi, modelUiToPojo and modelSyncEvents.

<EditText 
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    modelPojoToUi="text = emailIntent from testObj->session"
    modelUiToPojo="emailIntent = text into testObj->session"
    modelSyncEvents="onTextChange"
    onTextChange=""/>

The above example shows that testObj is present in session scope. The property text of the EditText is always kept in sync with the emailIntent of testObj in session scope. The sync from UI to Object only happens when text change event is triggered.

Get Modified data

The object stored in scope are modified in step 2. When event occurs, we can request the modified data such as click of a button.

<Button android:layout_width="wrap_content"
	  android:layout_height="wrap_content" 
      android:text="Get Data"
      android:onClick="getData(model=. from items->view)" />

The above example requests an object stored with key items with scope view on event onClick of button.

Sync dependent widget

<EditText 
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    modelPojoToUi="text = emailIntent from testObj->session"
    modelUiToPojo="emailIntent = text into testObj->session"
    modelSyncEvents="onTextChange"
    modelUiToPojoEventIds="label"	    
    onTextChange=""/>
<TextView 
    android:id="@+id/label"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    modelPojoToUi="text = emailIntent from testObj->session"/>

The above example syncs emailIntent on object testObj when onTextChange event occurs. Also after the event has finished, the TextView with id label is refreshed with the latest value of emailIntent.

Login Example

We have already created login.xml with validation in the previous section. Let us add data binding to it.

Enter email as a@a.com and password as test. You will see this alert:

{"password":"test","email":"a@a.com"}

Sync dependent widget

Here is interactive sample of sync dependent widgets discussed earlier.

List View Example

The following example shows how a Recycler view uses the data binding methods to update its view.

The ts code in the backing fragment:

id = 3;
@Inject({ id : "@+id/listView"})
private items!: RecyclerView;
async addItem(obj:any) {
	this.items.addModel({"id":this.id, "name": "test" + (this.id - 2), "gender":"@+id/radio0"});
	await this.executeCommand(this.items);
	this.id++;
}

async removeCurrentItem(obj:any) {
	this.items.removeModelById(obj.model.id);
	await this.executeCommand(this.items);	
}

async getData(obj:any) {
	alert(JSON.stringify(obj.model));
}

async clearItem(obj:any) {
	this.items.updateModelData("items->view as list", []);
	this.items.notifyDataSetChanged(true);
	await this.executeCommand(this.items);
}