An application for the Fitbit Ionic can quickly become a mess. This micro-framework provides basic support for View and subview patterns in about 100 LOCs, providing the necessary structure to keep the view layer code manageable when your application grows.
Read FitbitOS JavaScript Optimization Guidlines to make sure you understand the limitations of the platform you're working with.
- jQuery-style
$
DOM selectors. View
base class:- views can be dynamically inserted and removed;
onMount()
/onUnmount()
lifecycle hooks;- hierarchical subviews support;
- supressed render when device screen is off to reduce energy consumption.
Application
base class with screen switching support.- Multi-screen application boilerplate.
This repository contains the starting boilerplate for the multi-screen project. You could copy all the files as they are,
or just copy the /app/view.js
file to your project.
jQuery-style $
query to access SVG DOM elements. No wrapping is performed, the raw element or elements array is returned.
If an element
argument is provided, the element's subtree will be searched; otherwise the search will be global.
The query
is the space-separated sequence of the following simple selectors:
#id-of-an-element
- will callelement.getElementById( 'id-of-an-element' )
and return en element..class-name
- will callelement.getElementsByClassName( 'class-name' )
and return elements array.element-type
- will callelement.getElementsByTypeName( 'element-type' )
and return elements array.
If all of the selectors in the query are #id
selectors, the single element will be returned. Otherwise, an array of elements will be returned.
import { $ } from './view'
// Will search for #element globally
$( '#element' ).style.display = 'inline';
// Find the root element of the screen, then show all underlying elements having "hidden" class.
$( '#my-screen .hidden' ).forEach( ({ style }) => style.display = 'inline' );
// The same sequence made in two steps. See `$at()` function.
const screen = $( '#my-screen' );
$( '.hidden', screen ).forEach( ({ style }) => style.display = 'inline' );
Avoid repeated ad-hoc $-queries. Cache found elements when possible. See Elements Group pattern.
Create the $-function to search in the given DOM subtree. Used to enforce DOM elements isolation for different views.
When called without arguments, returns the root element.
import { $at } from './view'
const $ = $at( '#my-screen' );
// Make #myscreen visible
$().style.display = 'inline';
// Will search descendants of #myscreen only
$( '#element' ).style.display = 'inline';
Create the $-function to search in the given DOM subtree wrapping the given element.
const $at = selector => $wrap( $( selector ) );
An obvious and most memory-efficient way to encapsulate UI update logic is to define an update function. The function can be called directly to update encapsulated elements. It should be preferred for small and simple widgets.
function time(){
// Preallocate SVG DOM elements. Ad-hoc DOM lookups should be avoided.
const minutes = $( '#minutes' ),
seconds = $( '#seconds' );
// Return update function.
return seconds => {
minutes.text = Math.floor( seconds / 60 );
seconds.text = ( seconds % 60 ).toFixed( 2 );
}
}
class Timer extends View {
// Create time widget.
time = time();
...
onRender(){
...
this.time( this.seconds );
}
}
SVG DOM element reference takes 32 bytes, and it's highly beneficial if it will be cached. Ad-hoc element lookups should be avoided.
View is the stateful group of elements. The difference from the elements group is that views can me contained in each other and they have onMount
/onUnmount
lifecycle hooks. API:
view.el
- optional root view element. Used to show and hide the view when its mounted and unmounted.view.onMount( options )
- place to insert subviews and register events listeners.options
is the first parameter passed the view's constructor.view.render()
- render the view and all of its subviews if the display is on. No-op otherwise.view.onRender()
- place actual UI update code here.view.onUnmount()
- place to unregister events listeners.view.insert( subview )
- insert and mount the subview.view.remove( subview )
- remove and unmount the subview.
Example:
import { $at } from './view'
const $ = $at( '#timer' );
class Timer extends View {
el = $();
onMount(){
clock.granularity = "seconds";
clock.ontick = () => {
this.ticks++;
this.render();
}
}
ticks = 0;
minutes = $( '#minutes' );
seconds = $( '#seconds' );
onRender(){
const { ticks } = this;
this.minutes.text = Math.floor( ticks / 60 );
this.seconds.text = ( ticks % 60 ).toFixed( 2 );
}
onUnmount(){
clock.granularity = "off";
clock.ontick = null;
}
}
Application is the main view having the single screen
subview.
It's the singleton which is globally accessible through the Application.instance
variable.
MyApp.screens = { View1, View2, ... }
- all screens must be registered here.MyApp.start( 'View1' )
- instantiate and mount the application, display the screen with a goven name.Application.instance
- access an application instance.Application.switchTo( 'screenName' )
- switch to the screen which is the member of an application.app.screen
- property used to retrieve the current screen view.app.render()
- render everything, if display is on. It's called automaticaly when display goes on.
class MyApp extends Application {
screens = { Screen1View, Screen2View, LoadingView }
}
MyApp.start( 'LoadingView' );
...
// To switch the screen, use:
Application.switchTo( 'Screen2View' );
Application may consist of several screens. The following project structure is recommended for this case:
app/
<- standard app folderindex.js
<-Application
subclass class, which will switch the screensview.js
<- copy this file to your projectscreen1.js
<- each screen is defined asView
subclassscreen2/
<- if there are many modules related to the single screen, group them to the folder- ...
resources/
<- standard resources folderindex.gui
<- include screens SVG files with<link rel="import" href="screen/index.gui" />
widgets.gui
<- include screens CSS files with<link rel="stylesheet" href="screen/styles.css" />
screen1.gui
<- put SVG for your screens to different filesscreen2/
<- group SVG, CSS, and images used by a screen to a folder