Ashera Native runs on Apache Cordova, a popular open source library for building cross platform user interfaces with html5 and JavaScript. Ashera Native hides the webview in Apache Cordova and introduces its own native ui based on Android xml. Webview is only used for event handling. Hence it combines the best of native and hybrid development. The main power of Ashera lies in reusing the cordova plugins which have already been developed rather than developing from scratch.
This section can get you started or can serve as a refresher course.
We’re going to cover the core concepts behind Ashera:
The rest of this introduction to Ashera uses cats in its examples: friendly, approachable creatures that need names and a cafe to work in. Here is your very first Cat component:
Here is how you do it: To define your Cat component, first declare file cat_hello.xml with backing CatHello.ts file. Also you need to map your UI to TS component in FragmentMapper.
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
tools:ignore="MissingPrefix">
<data></data>
<TextView
autoInject="true"
fragment="CatHello"
android:id="@+id/cat"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello, I am your cat!"></TextView>
</layout>
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/nav_graph"
app:startDestination="@id/index">
<fragment
android:id="@+id/cat_hello"
android:name="com.ashera.core.GenericFragment"
android:label="Cat Hello"
tools:layout="@layout/cat_hello"></fragment>
</navigation>
//start - import
import { Fragment, Inject } from './app/Fragment';
import { NavController, InjectController } from './navigation/NavController';
import { ScopedObject } from './app/ScopedObject';
import { TextView } from './android/widget/TextViewImpl';
//end - import
//start - className
export default class CatHello extends Fragment
//end - className
{
//start - body
@InjectController({})
navController!: NavController;
@Inject({ id: "@+id/cat" })
private cat!:TextView;
async goToPreviousScreen() {
await this.navController.reset().popBackStack().executeCommand();
}
//end - body
}
import ErrorFragment from './ErrorFragment';
import ErrorDetailFragment from './ErrorDetailFragment';
//start - import
import CatHello from './CatHello';
import Index from './Index';
//end - import
export const fragmentMapper : any = {
'layout/error.xml': ErrorFragment,
'layout/error_detail.xml': ErrorDetailFragment,
'layout/dialog_sample.xml': Index,
'layout/dialog_child.xml': Index,
//start - body
'layout/cat_hello.xml': CatHello,
'layout/index.xml': Index,
//end - body
};
If you are an Android developer, you must be familiar with the above snippet. In android you create a xml which represents your ui and create a fragment which inflates your ui on onCreateView method. For navigation, you give an entry in nav_graph.xml and use appropriate methods on NavController to control navigation.
In Ashera native, you create your ui by creating xml "cat_hello.xml" using Android Studio. Ashera provides a generic fragment where in you associate your ui xml to fragment in nav_graph.xml. Next, you associate your ts component "CatHello.ts" on the webview to the xml file created in FragmentMapper.ts. With this set up, Ashera creates a native ui using cat_hello.xml. The CatHello.ts which is backing Fragment for the Generic Fragment on the webview side starts receiving events like button clicks, life cycle events like onResume, onCreate etc.
You’ve already met Ashera Native’s Core Components. Ashera Native lets you nest these components inside each other to create new components using the include tag. For example, You can render this component multiple times and in multiple places without repeating your code by using <include> tag:
Android view xml are inflated in a fragment's onCreateView. Fragment has click handler and lifecycle methods to handle interaction with the ui. The general life cycle of fragment can be read here : Fragment lifecycle
Ashera Native provides a generic implementation of fragment. It provides a webview fragment which receives lifecycle and ui events. This way the webview fragment provides a cross platform implementation of ui interaction utilizing javascript in webview.
//start - import
import { Fragment, Inject } from './app/Fragment';
import { NavController, InjectController } from './navigation/NavController';
import { ScopedObject } from './app/ScopedObject';
import { TextView } from './android/widget/TextViewImpl';
//end - import
//start - className
export default class CatHello extends Fragment
//end - className
{
//start - body
@InjectController({})
navController!: NavController;
@Inject({ id: "@+id/cat" })
private cat!:TextView;
async goToPreviousScreen() {
await this.navController.reset().popBackStack().executeCommand();
}
//end - body
}
The code above declares CatHello fragment. goToPreviousScreen is a ui handler. This can be called on click of button. CatHello extends Fragment i.e Fragment has lifecycle method which can be overridden in CatHello component.
Ashera native provides on(*) attributes on widgets which needs to be be used in xml to tie click listener to method on Webview fragment.
The fragment backing the xml has a method callInlineFunction which will be called on click of the Button:
<Button
android:id="@+id/cat"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="callInlineFunction"
android:text="Poke Me!"></Button>
async callInlineFunction() {
window.inlineFunction();
}
The webview Fragment has the following methods:
public onCreateView (obj:any) {
}
public onAttach(obj:any) {
}
public onCreate(obj:any) {
}
public onResume(obj:any) {
}
public onError(obj:any) {
}
public onPause(obj:any) {
}
public onDestroy(obj:any) {
}
public onDetach(obj:any) {
}
public onCloseDialog(obj:any) {
}
The native android fragment will send events to these lifecycle methods in the webview fragment.
State is like a component’s personal data storage. State is useful for handling data that changes over time or that comes from user interaction. State gives your components memory!
Given that ashera native has a native side backed by webview component, we have choice to store state in either webview or on the native component. However, it is recommended to store state on the native side and keep the webview stateless and use it as webclient for making http calls.
The following example takes place in a cat cafe where two hungry cats are waiting to be fed. Their hunger, which we expect to change over time (unlike their names), is stored as state. To feed the cats, press their buttons—which will update their state.
First you create a component. Variables used in the component like isHungry and name are scoped to the component.
<LinearLayout
android:id="@+id/parent"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
modelPojoToUi="text = . from name->component"
textFormat="I am %s."
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/status"
modelPojoToUi="text = ternary(.) from isHungry->component"
modelPojoToUiParams="false:hungry;true:Full"
textFormat="I am %s"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<Button
android:id="@+id/feedMeBtn"
android:onClick="callInlineFunction"
modelPojoToUi="text = ternary(.) from isHungry->component"
modelPojoToUiParams="false:Pour me some milk, please!;true:Thank You!!!"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="40dp"/>
</LinearLayout>
Next you reference the component using include tag. Initial parameters values and type of parameter are passed to the component.
<include
name="Munkustrap"
componentId="test"
isHungry="false"
isHungry_type="bool"
layout="@layout/hungry_cat_component" />
On click of button in the component, update the component data using the code below. Component scope data is stored in view scope with key <Component Id>#<variable name>. Component Id is passed as one of the event parameters.
window.inlineFunction = function(fragment, command, obj) {
command.updateModelData(obj.componentId + "#" + "isHungry->component as bool", true).refreshUiFromModel(
obj.componentId + "#status," + obj.componentId + "#feedMeBtn");
fragment.executeCommand(command);
}
Now that you’ve covered Ashera Native’s Core Components, let’s dive deeper on some of these core components by looking at handling <EditText>.