Page Management

By default, Smartface Project Sample grants us two sample pages. For some applications one or two pages are enough. For some, the application should be more than one page.

On Smartface, the backend side of page consists of those files:

  • Page UI representation

    • This will be located under .ui directory

    • The Drag & Drop process you have done will be reflected into this file

  • Transpiled (generated) code

    • This will be located under scripts/generated/pages directory

    • This will created by reading the .pgx file.

    • scripts/generated directory is under .gitignore by default.

  • User Page code

    • This will be located under scripts/pages directory.

    • This will be created automatically after creating a new page

    • This is the file we will be writing most of our code.

Also, the javascript compile result are located in dist directory.

The files on scripts/generated directory will be overwritten by every save action of representative .ui files, therefore it is not recommended to edit the files on this directory manually.

Adding a Page on Smartface Cloud IDE

On Smartface Cloud IDE, Click on the three dot, then click on New Page

Afterwards, you will be prompted as a name for the page. Type something relevant which can describe what the purpose of the page will be.

You might encounter that most sample projects or naming patterns on Smartface will include pg prefix. For example, pgLogin . This naming convention is not necessary to keep your pages organized.

Respective UI Page Representation and a template for your code will be added to the project. If your project is a git repository, you can check the new files via diff or Smartface Git Tool

Your newly added page will look like this on default(IDE Version 7.14.2). This might change over time.

Page Code
Page UI
Page Code
scripts/pages/newPage001.ts
import NewPage001Design from 'generated/pages/newPage001';
export default class NewPage001 extends NewPage001Design {
constructor() {
super();
// Overrides super.onShow method
this.onShow = onShow.bind(this, this.onShow.bind(this));
// Overrides super.onLoad method
this.onLoad = onLoad.bind(this, this.onLoad.bind(this));
}
}
/**
* @event onShow
* This event is called when a page appears on the screen (everytime).
* @param {function} superOnShow super onShow function
* @param {Object} parameters passed from Router.go function
*/
function onShow(superOnShow: () => void) {
superOnShow();
}
/**
* @event onLoad
* This event is called once when page is created.
* @param {function} superOnLoad super onLoad function
*/
function onLoad(superOnLoad: () => void) {
superOnLoad();
}
Page UI
.ui/newPage001.pgx
{
"components": [
{
"type": "Page",
"id": "a866-cc15-af52-0217",
"userProps": {},
"className": ".sf-page",
"props": {
"name": "newPage001",
"parent": null,
"children": [
"f8b6-4ed3-2272-da3b",
"bf0b-54b9-b4a7-4e44"
],
"orientation": "PORTRAIT"
},
"initialized": false
},
{
"type": "StatusBar",
"id": "f8b6-4ed3-2272-da3b",
"userProps": {},
"className": ".sf-statusBar",
"props": {
"name": "statusBar",
"parent": "a866-cc15-af52-0217",
"children": [],
"visible": true,
"isRemovable": false
}
},
{
"type": "HeaderBar",
"id": "bf0b-54b9-b4a7-4e44",
"userProps": {
"title": "newPage001"
},
"className": ".sf-headerBar",
"props": {
"name": "headerBar",
"parent": "a866-cc15-af52-0217",
"children": [],
"title": "newPage001",
"visible": true,
"isRemovable": false
}
}
],
"componentByID": {
"674c-b5af-d358-f512": {
"type": "Page",
"id": "a866-cc15-af52-0217",
"userProps": {},
"className": ".sf-page",
"props": {
"name": "newPage001",
"parent": null,
"children": [
"f8b6-4ed3-2272-da3b",
"bf0b-54b9-b4a7-4e44"
],
"orientation": "PORTRAIT"
},
"initialized": false
},
"c92a-b980-5c93-4532": {
"type": "StatusBar",
"id": "f8b6-4ed3-2272-da3b",
"userProps": {},
"className": ".sf-statusBar",
"props": {
"name": "statusBar",
"parent": "a866-cc15-af52-0217",
"children": [],
"visible": true,
"isRemovable": false
}
},
"c2f2-1f68-7ee8-a34f": {
"type": "HeaderBar",
"id": "bf0b-54b9-b4a7-4e44",
"userProps": {
"title": "newPage001"
},
"className": ".sf-headerBar",
"props": {
"name": "headerBar",
"parent": "a866-cc15-af52-0217",
"children": [],
"title": "newPage001",
"visible": true,
"isRemovable": false
}
}
}
}

To learn more about constructor, onLoad and onShow, head over to Page Life-Cycle documentation.

After the page addition is complete, you need to configure your routes in order to use your newly created page

  • pages/index.ts (Optional)

    • This file is to organize the pages which are added on Smartface Project.

    • You can navigate through pages

  • routes/index.ts

    • This file is to navigate your route and manage your navigation

Here is the default pages/index.ts. This file is used for router and when you need to import all of pages for typing purposes.

scripts/pages/index.ts
export { default as Page1 } from './page1';
export { default as Page2 } from './page2';
export { default as NewPage001 } from './newPage001';

After it is added, you can edit your routes/index.ts file to navigate through that directory

scripts/routes/index.ts
import buildExtender from "@smartface/extension-utils/lib/router/buildExtender";
import {
NativeRouter as Router,
NativeStackRouter as StackRouter,
Route
} from "@smartface/router";
import * as Pages from 'pages';
import "@smartface/extension-utils/lib/router/goBack"; // Implements onBackButtonPressed
const router = Router.of({
path: "/",
isRoot: true,
routes: [
StackRouter.of({
path: "/pages",
routes: [
Route.of({
path: "/pages/page1",
build: buildExtender({
getPageClass: () => Pages.Page1,
headerBarStyle: { visible: true }
})
}),
Route.of({
path: "/pages/page2",
build: buildExtender({
getPageClass: () => Pages.Page2,
headerBarStyle: { visible: true }
})
}),
// Newly added
Route.of({
path: "/pages/newPage001",
build: buildExtender({
getPageClass: () => Pages.NewPage001,
headerBarStyle: { visible: true }
})
}),
// If you don't want to use pages/index.ts, use this old implementation
Route.of({
path: "/pages/oldimplementation",
build: buildExtender({
getPageClass: () => require('pages/newPage001').default,
headerBarStyle: { visible: true }
})
})
]
})
]
});
export default router;

Alternative method to dynamically add pages when they are added

If you have multiple pages, you can do something like this to automatically add the routes inside:

scripts/routes/index.ts
import buildExtender from "@smartface/extension-utils/lib/router/buildExtender";
import {
NativeRouter as Router,
NativeStackRouter as StackRouter,
Route
} from "@smartface/router";
import * as Pages from 'pages';
import "@smartface/extension-utils/lib/router/goBack"; // Implements onBackButtonPressed
function generateRoutes(basePath: string) {
const allPages = Object.keys(Pages);
return allPages.map((pageName, index) => {
return Route.of({
path: `${basePath}/${pageName}`,
build: buildExtender({ getPageClass: () => Pages[pageName], headerBarStyle: { visible: true } })
})
});
}
const router = Router.of({
path: "/",
isRoot: true,
routes: [
StackRouter.of({
path: "/pages",
routes: generateRoutes("/pages")
})
]
});
export default router;

With this method, all of the pages inside of pages/index.ts will be automatically added to your router.

Duplicating a Page

When you wish to use one page as UI template or you want to duplicate it, click on this button:

It will automatically duplicate it and create a new fileset for the new page. This will behave like a new page.

You can also duplicate a Library component or normal component the same way.

Extending a Page

When you want to use the same UI components with a page but you don't want to duplicate the UI representation, you can extend the page like any TypeScript Class.

Here, it uses the Page1 UI Representation and you can reach the page itself by Using Page1:

scripts/pages/page1.ts
import Page1Design from 'generated/pages/page1';
import componentContextPatch from "@smartface/contx/lib/smartface/componentContextPatch";
import PageTitleLayout from "components/PageTitleLayout";
import System from "@smartface/native/device/system";
export default class Page1 extends Page1Design {
router: any;
constructor () {
super();
// Overrides super.onShow method
this.onShow = onShow.bind(this, this.onShow.bind(this));
// Overrides super.onLoad method
this.onLoad = onLoad.bind(this, this.onLoad.bind(this));
this.btnNext.onPress = () => {
this.router.push("/pages/page2", { message: "Hello World!" });
}
}
}
/**
* @event onShow
* This event is called when a page appears on the screen (everytime).
*/
function onShow(superOnShow: () => void) {
superOnShow();
this.headerBar.titleLayout.applyLayout();
}
/**
* @event onLoad
* This event is called once when page is created.
*/
function onLoad(superOnLoad: () => void) {
superOnLoad();
console.info('Onload page1');
this.headerBar.leftItemEnabled = false;
this.headerBar.titleLayout = new PageTitleLayout();
componentContextPatch(this.headerBar.titleLayout, "titleLayout");
if (System.OS === "Android") {
this.headerBar.title = "";
}
}

By manually creating a different file on pages directory, you can specify a different page while using the same layout set with another page.

Lets say the name of our new page is page1New:

scripts/pages/page1New.ts
import Page1Design from 'generated/pages/page1';
import componentContextPatch from "@smartface/contx/lib/smartface/componentContextPatch";
import PageTitleLayout from "components/PageTitleLayout";
import System from "@smartface/native/device/system";
// Notice how we are declaring a different name for class
export default class Page1New extends Page1Design {
router: any;
constructor () {
super();
// Overrides super.onShow method
this.onShow = onShow.bind(this, this.onShow.bind(this));
// Overrides super.onLoad method
this.onLoad = onLoad.bind(this, this.onLoad.bind(this));
this.btnNext.onPress = () => {
this.router.push("/pages/page2", { message: "Hello World!" });
}
}
}
/**
* @event onShow
* This event is called when a page appears on the screen (everytime).
*/
function onShow(superOnShow: () => void) {
superOnShow();
this.headerBar.titleLayout.applyLayout();
}
/**
* @event onLoad
* This event is called once when page is created.
*/
function onLoad(superOnLoad: () => void) {
superOnLoad();
console.info('Onload page1');
this.headerBar.leftItemEnabled = false;
this.headerBar.titleLayout = new PageTitleLayout();
componentContextPatch(this.headerBar.titleLayout, "titleLayout");
if (System.OS === "Android") {
this.headerBar.title = "";
}
}

You can even extend your components the same way ! Smartface Pages and Components are basically a TypeScript Classes.