Skip to main content
Version: Next

Data Binding

HTTP

Http module is used for sending different types of http requests.

Consider using Fetch or XmlHttpRequest to handle native quirks better. That way, you don't have to deal with different request types.

requestImage function:

Sends a http request to given url. If request ends successfully onLoad callback will be called with received UI.Image object.

requestJSON function:

Sends a http request to given url. If request ends successfully onLoad callback will be called with received JSON object.

Handling Response of a Request

  • Each http method has an onLoad callback and an onError callback. You can handle error and response using these callbacks.

ListView

ListView is a View that displays given items as an one-column vertical list. You can interact with each item in the list.

In this tutorial, we will be creating ListView and ListViewItem from UI Editor.

Using ListView or GridView with UI Editor

Since ListView has complex life-cycle, it is not advised to assign different values inside of callbacks.

This sample assignment below is anti-pattern.

this.data: DataType = []; // Your data from service

yourListView.onRowHeight = (index: number) => {
this.data[index].height = 50;
console.info(this.data[index].isBound); // Might be undefined
return 50;
}

yourListView.onRowBind = (listViewItem, index: number) => {
console.info(this.data[index].height); // Might be undefined
this.data[index].isBound = true;
}

Since execution order of the callback is not guaranteed, this behavior might not work as expected. For this reason, it is strongly advised to calculate & assign necessary data before callbacks.

Assume service file has a async function to return relevant user data. This way, we have complete control over which listViewHeight use what height or anything else. That way, we can also assign UI specific stuff like separator, dynamic border assignment or zebra methodology.

/scripts/pages/page1.ts
import Page1Design from 'generated/pages/page1';
import { processUserData } from 'lib/processor/user'
import { getUserList } from 'service/user'
import { Route, Router } from "@smartface/router";


export default class Page1 extends Page1Design {
data: ReturnType<typeof processUserData> = [];
constructor(private router?: Router, private route?: Route) {
super({});
}
async getUsers() {
try {
const response = await getUserList();
this.data = processUserData(response); // We pass the function to process it.
this.refreshListView();
}
}
refreshListView() {
this.yourListView.itemCount = this.data.length;
this.yourListView.refreshData();
}
initListView() {
// ... your listView code goes here
}

onShow() {
super.onShow();
this.getUsers();
}

onLoad() {
super.onLoad();
this.initListView();
}
}

Creation and RecyclerView

On Smartface, ListView and GridView uses RecyclerView method to gain performance. This document is from Android, but iOS behaves similarly as well.

When using ListView, all data assignments should be done into onRowBind method. onRowCreate method should only contain component creations. When using UI editor with one row type, onRowCreate shouldn't exist.

ListView onRowBind and onRowHeight

Due to ListView using RecyclerView, data should be bound with care. Since the ListViewItem you customized will be used on the ones which aren't rendered yet, you should re-assign all the values.

this.data: DataType = []; // Your data array for listView
// This onRowBind is anti-pattern, will cause recycle issue
y.onRowBind = (listViewItem, index: number) => {
listViewItem.title = this.data[index].title;
if (this.data[index].isProfile) {
listViewItem.profileImage = this.data[index].profileImage;
}
// profileImage variable will stay in the upcoming ListViewItem components.
}

// This onRowBind will behave correctly.
listView.onRowBind = (listViewItem, index: number) => {
listViewItem.title = this.data[index].title;
if (this.data[index].isProfile) {
listViewItem.profileImage = this.data[index].profileImage;
}
else {
listViewItem.profileImage = null; // It is imporant to re-set the value.
}
// profileImage variable will stay in the upcoming ListViewItem components.
}

ListView Complete Example with XmlHttpRequest

In this tutorial, we will be creating ListView component and displaying random users data which is captured from random user API. ListView component was used to display name and picture of user.

/scripts/pages/page1.ts
import Page1Design from "generated/pages/page1";
import { User, getUsers } from "services/user";
import LviUser from "components/LviUser";
import { Route, Router } from "@smartface/router";


export default class Page1 extends Page1Design {
private data: User[] = [];
router: any;
constructor(private router?: Router, private route?: Route) {
super({});
}

initListView() {
// onRowHeight can be used as alternative
this.lvUsers.rowHeight = LviUser.getHeight();
this.lvUsers.onRowBind = (listViewItem: LviUser, index: number) => {
listViewItem.lblName.text = this.data[index].name.first;
listViewItem.imgName.loadFromUrl({
url: this.data[index].picture.thumbnail,
useHTTPCacheControl: true,
});
};

this.lvUsers.onPullRefresh = () => {
this.refreshListView();
this.lvUsers.stopRefresh();
};
}

refreshListView() {
this.lvUsers.itemCount = this.data.length;
this.lvUsers.refreshData();
}

async getUsers() {
try {
const response = await getUsers();
this.data = response.results;
this.refreshListView();
} catch (e) {
alert(JSON.stringify(e, null, "\t"));
}
}

onShow() {
super.onShow();
}

onLoad() {
super.onLoad();
this.initListView();
this.getUsers();
}
}
tip

Make sure to type your service response properly.