PhoneJS - HTML5 JavaScript Mobile Development Framework
As you know, there are many frameworks for mobile app development and a growing number of them are based on HTML5. These next-generation tools help developers create mobile apps for phones and tablets without the steep learning curve associated with native SDKs and other programming languages like Objective-C or Java. For countless developers throughout the world, HTML5 represents the future for cross-platform mobile app development.
But the question is why? Why has HTML5 become so popular?
The widespread adoption of HTML5 involves the emergence of the bring your own device (BYOD) movement. BYOD means that developers can no longer limit application usage to a single platform because consumers want their apps to run on the devices they use every day. HTML5 allows developers to target multiple devices with a single codebase and deliver experiences that closely emulate native solutions, without writing the same application multiple times, using multiple languages or SDKs. The evolution of modern web browsers means that HTML5 can deliver cross-platform, multi-device solutions that mirror behaviors and experiences of “native” apps to the point that it’s often difficult to distinguish between an app written using native development tools and those using HTML.
Multiple platform support, time to market and lower maintenance costs are just a few of the advantages inherent in HTML/JavaScript. It’s advantages don’t stop there. HTML’s ability to mitigate long-term risks associated with emerging technologies such as WinRT, ChromeOS, FirefoxOS, and Tizen is unmatched. Simply said, the only code that will work on all these platforms is HTML/JavaScript.
Is there a price? Yes, certainly native app consumes less memory and will have faster or more responsive user experience. But in all cases where a web app would work, you can make a step further and create a mobile web app or even packaged application for the store, for multiple platforms, from single codebase. PhoneJS lets you get started fast.
PhoneJS for Your Next Mobile Project
PhoneJS is a cross-platform HTML5 mobile app development framework that was built to be versatile, flexible and efficient. PhoneJS is a single page application (SPA) framework, with view management and URL routing. Its layout engine allows you to abstract navigation away from views, so the same app can be rendered differently across different platforms or form factors. PhoneJS includes a rich collection of touch-optimized UI widgets with built-in styles for today’s most popular mobile platforms including iOS, Android, and Windows Phone 8.
To better understand the principles of PhoneJS development and how you can create and publish applications in platform stores, let’s take a look at a simple demo app called TipCalculator. This application calculates tip amounts due based on a restaurant bill. The complete source code for this app is available here.
The app can be found in the AppStore, Google Play, and Windows Store.
PhoneJS Application Layout and Routing
TipCalculator is a Single-Page Application (SPA) built with HTML5. The start page is index.html, with standard meta tags and links to CSS and JavaScript resources. It includes a script reference to the JavaScript file index.js, where you’ll find the code that configures PhoneJS app framework logic:
TipCalculator.app = new DevExpress.framework.html.HtmlApplication({ namespace: TipCalculator, defaultLayout: "empty" });
Within this section, we must specify the default layout for the app. In this example, we’ll use the simplest option, an empty layout. More advanced layouts are available with full support for interactive navigation styles described in the following images:
PhoneJS uses well-established layout methodologies supported by many server-side frameworks, including Ruby on Rails and ASP.NET MVC. Detailed information about Views and Layouts can be found in our online documentation.
To configure view routing in our SPA, we must add an additional line of code in index.js:
TipCalculator.app.router.register(":view", { view: "home" });
This registers a simple route that retrieves the view name from the URL (from the hash segment of the URL). The home view is used by default. Each view is defined in its own HTML file and is linked into the main application page index.html like this:
<link rel="dx-template" type="text/html" href="views/home.html" />
PhoneJS ViewModel
A viewmodel is a representation of data and operations used by the view. Each view has a function with the same base name as the view itself and returns the viewmodel for the view. For the home view, the views/home.js script defines the function home which creates the corresponding viewmodel.
TipCalculator.home = function(params) { ... };
Three input parameters are used for the tip calculation algorithm: bill total, the number of people sharing the bill, and a tip percentage. These variables are defined as observables, which will be bound to corresponding UI widgets.
Note: Observables functionality is supplied by Knockout.js, an important foundation for viewmodels used in PhoneJS. You can learn more about Knockout.js here.
This is the code used in the home function to initialize the variables:
var billTotal = ko.observable(), tipPercent = ko.observable(DEFAULT_TIP_PERCENT), splitNum = ko.observable(1);
The result of the tip calculation is represented by four values: totalToPay, totalPerPerson, totalTip, tipPerPerson. Each value is a dependent observable (a computed value), which is automatically recalculated when any of the observables used in its definition change. Again, this is standard Knockout.js functionality.
var totalTip = ko.computed(...); var tipPerPerson = ko.computed(...); var totalPerPerson = ko.computed(...); var totalToPay = ko.computed(...);
For an example of business logic implementation in a viewmodel, let’s take a closer look at the observable totalToPay.
The total sum to pay is usually rounded. For this purpose, we have two functions roundUp and roundDown that change the value of roundMode (another observable). These changes cause recalculation of totalToPay, because roundMode is used in the code associated with the totalToPay observable.
var totalToPay = ko.computed(function() { var value = totalTip() + billTotalAsNumber(); switch(roundMode()) { case ROUND_DOWN: if(Math.floor(value) >= billTotalAsNumber()) return Math.floor(value); return value; case ROUND_UP: return Math.ceil(value); default: return value; } });
When any input parameter in the view changes, rounding should be disabled to allow the user to view precise values. We subscribe to the changes of the UI-bound observables to achieve this:
billTotal.subscribe(function() { roundMode(ROUND_NONE); }); tipPercent.subscribe(function() { roundMode(ROUND_NONE); }); splitNum.subscribe(function() { roundMode(ROUND_NONE); });
The complete viewmodel can be found in home.js. It represents a simple example of a typical viewmodel.
Note: In a more complex app, it may be useful to implement a structure that modularizes your viewmodels separate from view implementation files. In other words, a file like home.js need not contain the code to implement the viewmodel and instead call a helper function elsewhere for this purpose. In this walkthrough we’re trying to keep things structurally simple.
PhoneJS Views
Let’s now turn to the markup of the view located in the view/home.html file. The root div element represents a view with the name ‘home’. Within it is a div containing markup for a placeholder called ‘content’.
<div data-options="dxView : { name: 'home' }"> <div data-options="dxContent : { targetPlaceholder: 'content' }"> ... </div> </div>
A toolbar is located at the top of the view:
<div data-bind="dxToolbar: { items: [ { align: 'center', text: 'Tip Calculator' } ] }"></div>
dxToolbar is a PhoneJS UI widget. It’s defined in the markup using Knockout.js binding.
A fieldset appears below the toolbar. To display a fieldset, we use two special CSS classes understood by PhoneJS: dx-fieldset and dx-field. The fieldset contains a text field for the bill total and two sliders for the tip percentage and the number of diners.
<div data-bind="dxNumberBox: { value: billTotal, placeholder: 'Type here...', valueUpdateEvent: 'keyup',min: 0 }"> </div> <div data-bind="dxSlider: { min: 0, max: 25, step: 1, activeStateEnabled: true, value: tipPercent }"></div> <div data-bind="dxSlider: { min: 1, step: 1, max: 10, activeStateEnabled: true, value: splitNum }"></div>
Two buttons (dxButton) are displayed below the editors, allowing the user to round the total sum to pay. The remaining view displays fieldsets used for calculated results.
<div class="round-buttons"> <div data-bind="dxButton: { text: 'Round Down', clickAction: roundDown }"></div> <div data-bind="dxButton: { text: 'Round Up', clickAction: roundUp }"></div> </div> <div id="results" class="dx-fieldset"> <div class="dx-field"> <span class="dx-field-label">Total to pay</span> <span class="dx-field-value" style="font-weight: bold" data-bind="text: Globalize.format(totalToPay(), 'c')"></span> </div> <div class="dx-field"> <span class="dx-field-label">Total per person</span> <span class="dx-field-value" data-bind="text: Globalize.format(totalPerPerson(), 'c')"></span> </div> <div class="dx-field"> <span class="dx-field-label">Total tip</span> <span class="dx-field-value" data-bind="text: Globalize.format(totalTip(), 'c')"></span> </div> <div class="dx-field"> <span class="dx-field-label">Tip per person</span> <span class="dx-field-value" data-bind="text: Globalize.format(tipPerPerson(), 'c')"></span> </div> </div>
This completes the description of the files required to create a simple app using PhoneJS. As you’ve seen, the process is simple, straightforward and intuitive.
Start, Debug and Build for Stores
Starting and debugging a PhoneJS app is just like any other HTML5 based app. You must deploy the folder containing HTML and JavaScript sources, along with any other required file to your web server. Because there is no server-side component to the architectural model, it doesn’t matter which web server you use as long as it can provide file access through HTTP. Once deployed, you can open the app on a device, in an emulator or a desktop browser by simply navigating app’s start page URL.
If you want to view the app as it will appear in a phone or tablet within a desktop browser, you will have to override the UserAgent in the browser. Fortunately, this is easy to do with the developer tools that ship as part of today’s modern browsers:
If you prefer not to modify UserAgent settings, you can use the Ripple Emulator to emulate multiple device types.
At this point you have a web application that will work in the browser on the mobile device and look like native app. Modern mobile browsers provide access to local storage, location api, camera, so good chances are that your app already has anything it needs.
Creating Store Ready Applications Using PhoneJS and PhoneGap
But what if you need access to device features that browser does not provide? What if you want an app in the app store, not just a webpage. Then you’ll have to create a hybrid application and de-facto standard for such an app is Apache Cordova aka PhoneGap.
PhoneGap project for each platform is a native app project that contains WebView (browser control) and a “bridge” that lets your JavaScript code inside WebView access native functions provided by PhoneGap libraries and plugins.
To use it, you need to have SDK for each platform you are targeting, but you don’t need to know details of native development, you just need to put your HTML, CSS, JS files into right places and specify your app’s properties like name, version, icons, splashcreens and so on.
To be able to publish your app, you will need to register as developer in the respective development portal. This process is well documented for each store and beyond the scope of this article. After that you’ll be able to receive certificates to sign your app package.
The need to have SDK for each platform installed sounds challenging - especially after “write one, run everywhere” promise of HTML5/JS approach. This is a small price to pay for building hybrid application and have everything under control. But still there are several services and products that solves this problem for you.
One is Adobe’s online service - PhoneGap Build which allows you to build one app for free (to build more, you’ll need a paid account). If you have all the required platform certificate files, the service can build your app for all supported platforms with a few mouse clicks. You only need to prepare app descriptions, promotional and informational content and icons in order to submit your app to an individual store.
For Visual Studio developers, DevExpress offers a product called DevExtreme (it includes PhoneJS), which can build applications for iOS, Android and Windows Phone 8 directly within the Microsoft Visual Studio IDE.
To summarize, if you need a web application that looks and feels like native on a mobile device, you need PhoneJS - it contains everything required to build touch-enabled, native-looking web application. If you want to go further and access device features, like the contact list or camera, from JavaScript code, you will need Cordova aka PhoneGap. PhoneGap also lets you compile your web app into a native app package. If you don’t want to install an SDK for each platform you are targeting, you can use the PhoneGapBuild service to build your package. Finally, if you have DevExtreme, you can build packages right inside Visual Studio.