Developing maintainable Cordova applications

97
DISIM | University of L’Aquila Ivano Malavolta Developing maintainable Cordova applications

description

Slides of a talk of a seminars series I gave at WebRatio in January 2014. I implemented many best practices and advices in this presentation in a generic app template available here: https://github.com/iivanoo/cordovaboilerplate

Transcript of Developing maintainable Cordova applications

Page 1: Developing maintainable Cordova applications

DISIM | University of L’Aquila

Ivano Malavolta

Developing maintainable Cordova applications

Page 2: Developing maintainable Cordova applications

Roadmap

• Introduction

• Backbone

• Require JS

• Handlebars

• Conclusions

I implemented all best practices and advices in this presentation in a generic app template available here:

https://github.com/iivanoo/cordovaboilerplate

Page 3: Developing maintainable Cordova applications

Introduction

We are building apps, not web sites

If your code is not structured:

• it is extremely easy that your web app becomes a

big mess of HTML + CSS + JavaScript

• maintaining each part of your app asks for a

deep analysis of ALL its aspects (logic, presentation, etc.)

• you may waste a whole day due to a missing <

Page 4: Developing maintainable Cordova applications

What we want to avoid

Imagine yourself trying to change

• how a movie should be rendered in your app

• the REST API providing info about movies

Page 5: Developing maintainable Cordova applications

Some technical advices from a real project...

Page 6: Developing maintainable Cordova applications

Some technical advices from a real project...

Page 7: Developing maintainable Cordova applications

Some technical advices from a real project...

Page 8: Developing maintainable Cordova applications

Some technical advices from a real project...

Page 9: Developing maintainable Cordova applications

Some technical advices from a real project...

Page 10: Developing maintainable Cordova applications

How I structure my applications

MVC framework for giving structure

File and module loader for code modularization

Templating engine for separation of concerns

Disclaimer this is MY way to structure apps, you can follow yours

Page 11: Developing maintainable Cordova applications

How I structure my applications

Page 12: Developing maintainable Cordova applications

Backbone

• Why Backbone

• Events

• Models

• Collections

• Views

• Routers

• Summary

Page 13: Developing maintainable Cordova applications

Why Backbone

Backbone gives you STRUCTURE

Page 14: Developing maintainable Cordova applications

Why Backbone

From the Backbone website...

manipulate the DOM

lists of models

represent data

Page 15: Developing maintainable Cordova applications

Why Backbone

Additionally, Backbone provides also features for:

sync

for managing how to persist models (default is via REST)

events

for managing how data and control are exchanged within your app

router

for managing the interaction flow among views

Page 16: Developing maintainable Cordova applications

Who is using Backbone?

Page 17: Developing maintainable Cordova applications

Backbone

• Why Backbone

• Events

• Models

• Collections

• Views

• Routers

• Summary

Page 18: Developing maintainable Cordova applications

Events

Any object communicates with other objects via events

It gives the object the ability to bind and trigger custom named events

It is extremely useful for exchanging data and control among objects

Page 19: Developing maintainable Cordova applications

Events

Basically, each object can:

• listen to events

• trigger events

Page 20: Developing maintainable Cordova applications

Events

Two types of events:

you define your own types of event

built-in

custom

Page 21: Developing maintainable Cordova applications

Events API

object will react to the “alert” event (the “off” function detaches the event)

event parameters

the “alert” event is fired

Page 22: Developing maintainable Cordova applications

Events API

Events methods:

on

object.on(event, callback, [context])

off

object.off([event], [callback], [context])

once

object.once(event, callback, [context])

trigger

object.trigger(event, [*args])

listenTo

object.listenTo(other, event, callback)

stopListening

object.stopListening([other], [event], [callback])

listenToOnce

object.listenToOnce(other, event, callback)

Page 23: Developing maintainable Cordova applications

Events summary

Page 24: Developing maintainable Cordova applications

Backbone

• Why Backbone

• Events

• Models

• Collections

• Views

• Routers

• Summary

Page 25: Developing maintainable Cordova applications

Models

Models represent your data

Each model represents a data type in your app, together with the logic surrounding it, like:

• persistence

• conversions

• validation

• computed properties

• access control

MVC: Notify their observers about state using the Observer pattern

Page 26: Developing maintainable Cordova applications

Models

You extend Backbone.Model with your domain-specific methods, and Model provides a basic set of functionality for managing changes, like:

• getter and setter

• id

• constructor

• REST-based persistence

Page 27: Developing maintainable Cordova applications

Example of model

custom method

setting an attribute

event fired when “color” changes

custom method invocation

Page 28: Developing maintainable Cordova applications

Model constructore and attributes

initialize()

it is triggered every time you create a new instance of a model

it works also for collections and views

it can take a JS object for setting also attributes

get() & set()

they are used to set and retrieve the value of certain attributes

defaults

a property named 'defaults' in your model declaration

Page 29: Developing maintainable Cordova applications

Example

http://goo.gl/UOahsP

Page 30: Developing maintainable Cordova applications

Model persistence

Backbone.sync

is the function that Backbone calls every time it attempts to read or save a model

By default, it uses Ajax to make a REST-ish request to a server

Resources represented as JSON strings

Page 31: Developing maintainable Cordova applications

Sync signature

sync(method, model, [options])

method

the CRUD method ("create“, "read“, "update", or "delete")

model

the model (or collection) to be synced

options

success and error callbacks, and all other jQuery request options

example of overriden sync: http://bit.ly/KWdxNN

Sync returns a jQuery XMLHttpRequest (jqXHR) object It implements the Promise interface

Page 32: Developing maintainable Cordova applications

Sync usage

Normally you will not use the sync method directly, you will do it implicitly when you call one of these methods

Model

• fetch: gets the most up-to-date values of the model instance

• save: persists the model instance

• destroy: deletes the model instance

Collection

• fetch: gets all the models of the collection from the server

• create: creates a model, saves it to the server and adds it to the collection

Page 33: Developing maintainable Cordova applications

Overriding sync

You can override it in order to use a different persistence strategy, such as:

• WebSockets

• Local Storage

• WebSQL

Backbone.sync is the default global function that all models use unless the models have a sync method specifically set

Page 34: Developing maintainable Cordova applications

Models summary

Page 35: Developing maintainable Cordova applications

Backbone

• Why Backbone

• Events

• Models

• Collections

• Views

• Routers

• Summary

Page 36: Developing maintainable Cordova applications

Collections

Collections are ordered sets of models

You can:

• bind change events to be notified when any model in the collection has been modified

• listen for add and remove events

• fetch the collection from the server (or other persistence layers)

• find models or filter collections themeselves

The model attribute of a collection represents the kind of model that can be stored in it

Any event that is triggered on a model in a collection will also be triggered on the collection directly

MVC: Notify their observers about state using the Observer pattern (same as models)

Page 37: Developing maintainable Cordova applications

Collection example

http://goo.gl/UOahsP

Page 38: Developing maintainable Cordova applications

Collections summary

Page 39: Developing maintainable Cordova applications

Backbone

• Why Backbone

• Events

• Models

• Collections

• Views

• Routers

• Summary

Page 40: Developing maintainable Cordova applications

Views

Views represent and manage the visible parts of your application

They are also used to

• listen to interaction events

• and react accordingly

views can be rendered at any time, and inserted into the DOM

you get high-performance UI rendering with as few reflows and repaints as possible

MVC: observe models, and update itself according to the state of the models + manage user inputs (it’s a controller, to this sense)

Page 41: Developing maintainable Cordova applications

Interaction with the DOM

All views refer to a DOM element at all times, even if they are already in the page or not

this.el is a reference to the DOM element, it is created from:

tagName

for example body, ul, span, img

className

class name of some element within the DOM

id

id of an element within the DOM

If none of them is specified, this.el is an empty <div>

Page 42: Developing maintainable Cordova applications

Rendering the view

The render() method is used to update the this.el element with the new HTML

The default implementation of render() is a no-op

you have to override it to update this.el with your HTML code

Backbone is agnostic with respect to your code in render(), however...

you are STRONGLY encouraged to use a Javascript templating library here

Page 43: Developing maintainable Cordova applications

View example

http://goo.gl/UOahsP

Page 44: Developing maintainable Cordova applications

Interaction with the user

Events map “event_name selector”: callback

Events callbacks

Page 45: Developing maintainable Cordova applications

Views summary

Page 46: Developing maintainable Cordova applications

Backbone

• Why Backbone

• Events

• Models

• Collections

• Views

• Routers

• Summary

Page 47: Developing maintainable Cordova applications

The router

Backbone.Router provides methods for routing client-side pages,

and connecting them to actions and events

At a minimum, a router is composed of two main parts:

routes

an hash that pairs routes to actions

actions

JS functions triggered when certain routes are navigated

Page 48: Developing maintainable Cordova applications

Routing

Every router contains an hash that maps routes to functions on your router

URLs fragments can also contain dynamic data via Backbone-specific URL parts:

parameter (:param)

match a single URL component between slashes

splat (*fragment)

match any number of URL components

Page 49: Developing maintainable Cordova applications

Routing

routes map

routing functions

Page 50: Developing maintainable Cordova applications

History

History serves as a global router to

1. handle hashchange events

2. match the appropriate route

3. trigger callbacks

You should never access it directly, you just need call Backbone.history.start() to begin monitoring hashchange events, and dispatching routes in your app

Call Backbone.history.navigate(ROUTE_NAME, {trigger: true}); to activate a specific route of the router

Technically, it uses the HTML5 History API to listen to to its job

For older browsers, it uses URL hash

fragments as fallback

Page 51: Developing maintainable Cordova applications

Router summary

Page 52: Developing maintainable Cordova applications

Backbone

• Why Backbone

• Events

• Models

• Collections

• Views

• Routers

• Summary

Page 53: Developing maintainable Cordova applications

Classical workflow

1. You dig into JSON objects

2. Look up elements in the DOM

3. Update the HTML by hand

Page 54: Developing maintainable Cordova applications

Backbone-based workflow

• You organize your interface into logical views backed by models

• Each view can be updated independently when the model changes, without having to redraw the page

You can bind your view‘s render() function to the model‘s "change” event now everywhere that model data is displayed in the UI, it is always immediately up to date

Page 55: Developing maintainable Cordova applications

Is Backbone real MVC?

Let’s look at the description of the Model-View-Presenter pattern on Wikipedia:

an interface defining the data to be displayed or otherwise acted upon in the user interface

passive interface that displays data (the model) and routes user commands (events) to the presenter to act upon that data

acts upon the model and the view. It retrieves data from repositories (the model), and formats it for display in the view

Model

View

Presenter

Page 56: Developing maintainable Cordova applications

Roadmap

• Introduction

• Backbone

• Require JS

• Handlebars

• Conclusions

Page 57: Developing maintainable Cordova applications

Require JS

• Why Require JS

• Using modules

• Defining modules

• Configuring Require JS

Page 58: Developing maintainable Cordova applications

Why Require JS

We are building apps, not website

We need well-specified and isolated JS files/modules

Code complexity grows as the app gets bigger

we need some sort of #include/import/require

ability to load nested dependencies

Page 59: Developing maintainable Cordova applications

What we want to avoid

uncontrolled scripts

poor control flow understanding

Page 60: Developing maintainable Cordova applications

Require JS

RequireJS is a JavaScript file and module loader

Using a modular script loader like Require JS will improve the modularity of your code

speed in implementing changes

better undestanding of the code

Require JS allows modules to be loaded as fast as possible, even out of order, but evaluated in the correct dependency order

Built on the Module Pattern

JavaScript file and module loader

Page 61: Developing maintainable Cordova applications

The module pattern

A JavaScript code module is some JavaScript code located in a registered location (e.g., a function)

All of the code that runs inside the function lives in a closure, which provides

• privacy

• state

throughout the lifetime of the module

Page 62: Developing maintainable Cordova applications

Module example

Technically, it is simply a function that executes immediately

Page 63: Developing maintainable Cordova applications

Module VS script files

A module is different from a traditional script file in that it defines a well-scoped object that avoids polluting the global namespace its retained objects can be deleted by the GC

It can explicitly list its dependencies and get a handle on those dependencies without needing to refer to global objects, but instead receive the dependencies as arguments to the function that defines the module

VS

Page 64: Developing maintainable Cordova applications

Require JS

• Why Require JS

• Using modules

• Defining modules

• Configuring Require JS

Page 65: Developing maintainable Cordova applications

Using modules

main.js is the entry point of the app

The main HTML:

Page 66: Developing maintainable Cordova applications

The main JS file:

Using modules

This function is called when all dependencies are loaded If a required module calls define(), then this function is not fired until its dependencies have been loaded

Required modules References to required modules

Page 67: Developing maintainable Cordova applications

Require JS

• Why Require JS

• Using modules

• Defining modules

• Configuring Require JS

Page 68: Developing maintainable Cordova applications

Module without dependencies Always one module per files

Public variables

Setup code

the simplest module can be a plain collection of name/value pairs

module with initialization

The returned element can be any valid JS element By convention I always return an object representing the module

Page 69: Developing maintainable Cordova applications

Module with dependencies

Dependency definition

Dependent module reference

Dependent module usage

This function is called when zepto.js is loaded.

If zepto.js calls define(), then this function is not fired until also zepto’s dependencies have loaded

Page 70: Developing maintainable Cordova applications

Require JS under the hoods...

1. loads each dependency as a script tag, using head.appendChild() and waits for all dependencies to load

2. computes the right order in which to call the functions that define the modules

3. calls the module definition functions of each dependency in the right order

main.js

jQuery Backbone

SpinJS

MoviesCollection

MovieModel

MoviesView

1

2

3 4

5

6 7

Page 71: Developing maintainable Cordova applications

Require JS

• Why Require JS

• Using modules

• Defining modules

• Configuring Require JS

Page 72: Developing maintainable Cordova applications

Configuring Require JS

Require refers to a global configuration options

It allows developers to:

• set the paths to all used frameworks in one place

• use older frameworks as modules (shim)

• define configuration params for the modules

• etc.

Page 73: Developing maintainable Cordova applications

Configuring Require JS

Shims for older frameworks

paths to used frameworks

Dependent module usage

Page 74: Developing maintainable Cordova applications

Roadmap

• Introduction

• Backbone

• Require JS

• Handlebars

• Conclusions

Page 75: Developing maintainable Cordova applications

Handlebars

• Why Handlebars

• Handlebars basics

• Usage with Backbone and Require JS

Page 76: Developing maintainable Cordova applications

Why Handlebars

We want to separate presentation from logic

TRANSLATE TO: we don’t want to put any HTML element into JavaScript code

separate logic from presentation

Imagine yourself trying to change how a movie should be rendered in your app...

Page 77: Developing maintainable Cordova applications

Handlebars

• Why Handlebars

• Handlebars basics

• Usage with Backbone and Require JS

Page 78: Developing maintainable Cordova applications

Example of template

A handlebars expression is

{{ something }}

Page 79: Developing maintainable Cordova applications

Escape values

Handlebars HTML-escapes all the values returned by an {{expression}} If you don't want Handlebars to escape a value, use the "triple-stash“ {{{ expression }}}

Page 80: Developing maintainable Cordova applications

Populate your template

The recurrent process of obtaining a populated template is the following:

1. create the template T with its placeholders {{ - }}

2. compile the template into a JavaScript function t

3. create a context CT containing the actual values for placeholders

4. run the compiled template t(CT) to obtain the final HTML fragment

Page 81: Developing maintainable Cordova applications

1. create the template

Templates are defined within a <script> tag or in external files

Page 82: Developing maintainable Cordova applications

2. compile the template

Handlebars.compile is used to compile a template

Compiling = obtaining a JS object representing the template

Page 83: Developing maintainable Cordova applications

3. create a context for the template

A context is a Javascript object used to populate a template

Here the keys of the object must match with the name of the placeholder to be populated

Page 84: Developing maintainable Cordova applications

4. obtain the final HTML fragment

You have to execute a template with a context in order to get its corresponding HTML code

Page 85: Developing maintainable Cordova applications

Expressions

The simplest expression is a simple identifier

This expression means "look up the username property in the current context"

Page 86: Developing maintainable Cordova applications

Expressions with paths

Handlebars expressions can also be dot-separated paths

This expression means

"look up the user property in the current context,

then look up the username property in the result"

Page 87: Developing maintainable Cordova applications

Helpers

Helpers are Javascript functions that return HTML code

You should return a Handlebars SafeString if you don't want it to be escaped by default

Page 88: Developing maintainable Cordova applications

Calling helpers

A Handlebars helper call is a simple identifier, followed by zero or more parameters

Each parameter is a Handlebars expression

es.

{{ test user }}

In this case, test is the name of the Handlebars helper, and user is a parameter to the helper

Page 89: Developing maintainable Cordova applications

Built-in helpers

It shifts the context for a section of a template

with

<div class="entry“> <h1>{{title}}</h1> {{#with author}} <h2>By {{firstName}} {{lastName}}</h2> {{/with}} </div>

{ title: "My first post!", author: { firstName: “Ivano", lastName: “Malavolta" } }

<div class="entry“> <h1>My first post!</h1> <h2>By Ivano Malavolta</h2> </div>

Page 90: Developing maintainable Cordova applications

Built-in helpers

To iterate over a list

each

Inside the block, you can use this

to reference the element being iterated

<ul class="people_list"> {{#each people}} <li>{{this}}</li> {{/each}} </ul>

{ people: [ “Ivano", “Andrea", “Paolo" ] }

<ul class="people_list"> <li>Ivano</li> <li>Andrea</li> <li>Paolo</li> </ul>

Page 91: Developing maintainable Cordova applications

Built-in helpers

It renders the block if its argument is not equal to false, undefined, null, []

If / Else

The unless helper is the inverse of if

<div class="entry“> <h1>{{title}}</h1> {{#if author}} <h2>By {{firstName}} {{lastName}}</h2> {{#else}} <h2>Unknown author</h1> {{/if}} </div>

{ title: "My first post!", author: undefined } }

<div class="entry“> <h1>My first post!</h1> <h2>Unknown author</h2> </div>

Page 92: Developing maintainable Cordova applications

handlebars summary

Each Template can contain Expressions and Helpers operating on them The main helpers are: • with • each • if / else /unless You can define your own Helpers that operate on expressions, they return HTML code A template can be (pre)-compiled and must be executed with a context in order to return the final HTML fragment

Page 93: Developing maintainable Cordova applications

Handlebars

• Why Handlebars

• Handlebars basics

• Usage with Backbone and Require JS

Page 94: Developing maintainable Cordova applications

Usage with Backbone and Require JS

Templates can be seen as special modules

So we can have the following:

• a separate HTML file for each template

• a Backbone view can have a dependency to each template

• the template can be executed by using a JSON object of the Backbone model as context

Page 95: Developing maintainable Cordova applications

Example

Dependency to template HTML file

It contains a string

Compiled template

Execution of the template

Page 96: Developing maintainable Cordova applications

References

http://backbonejs.org http://requirejs.org

http://handlebarsjs.com https://github.com/iivanoo/cordovaboilerplate

Page 97: Developing maintainable Cordova applications

+ 39 380 70 21 600 Contact Ivano Malavolta | DISIM

iivanoo

[email protected]

www.ivanomalavolta.com