Friday, July 19, 2013

Dart and Sencha Touch for Mobile Web Apps

You can use Dart's JS-Interop to integrate Dart code into your existing Sencha Touch application. This allows you to take advantage of Sencha Touch's mobile-first framework and Dart's productivity, language, and libraries.

Preface

Sencha Touch is a full-featured JavaScript framework for mobile web apps. It supports layouts for multiple devices, touch, lots of widgets, models, a resource framework, and more. I've used Ext JS (a cousin of Sencha Touch) in a previous job, and was quite happy.

I'm also quite happy with Dart, the new scalable web programming platform from Google. Dart compiles to JavaScript, and even interoperates with JavaScript. It's a new language, libraries, package manager, VM (for client AND server apps), and rich tools. I feel really productive in Dart.

I've never used Sencha Touch, so my first question was: how do I insert Dart into an existing Sencha Touch application?



Getting Sencha Touch

Sencha already has great docs, so I won't repeat them here. This post assumes you have a Sencha Touch app already, but if not, check out http://www.sencha.com/products/touch. Make sure to download both the SDK and the sencha command line utility. I needed both to get started.



Getting Dart

Installing Dart is super easy. The best way is to install Dart Editor, which includes the editor, Dart SDK, and Dartium (Chromium + an embedded Dart VM). Even if you don't intent to use the editor, you almost certainly want Dartium for easy development testing (think: edit, reload, there is no step three).


If you are new to Dart, might I recommend the Language Tour and the Tutorials.


The Setup

There are effectively two apps in play:

  1. The Sencha Touch app
  2. The Dart app
Both apps need to fully initialize and load (possibly loading multiple scripts) and there's no guarantee that one app will load before the other.

The Dart app needs a way get a handle on the Sencha App, but only after the Sencha app is loaded and initialized. This scheme needs to work in both Dartium (JS and Dart VM) and across the web (via dart2js, which compiles Dart into JavaScript).

Modify your Sencha Touch app

(Note: I'm a Sencha Touch n00b, so maybe there's a better way. Please let me know in the comments.)

This isn't pretty, but here's what I did:

 var senchaLaunchComplete = false;  
 Ext.application({  
   // ....  
   launch: function() {  
     // ...  
     senchaLaunchComplete = true;  
   },  
 });  

Notice I added a global "senchaLaunchComplete" variable that flips to true when the launch is complete. Again, not proud of this, and I'd like to learn more about how Sencha initializes. I can't fire a postMessage event from the Sencha App because it might get lost before the Dart app has a chance to load and initialize.

Ideas welcome, but let's move on, shall we? :)

Waiting for the Sencha app to load

The Dart app starts by repeatedly polling the senchaLaunchComplete variable from JavaScript. Dart uses JS-Interop to reach into the JavaScript world. JS-Interop works in both Dartium and when compiled with dart2js.

All Dart programs start with a main() function. This makes it very easy to understand where your program actually starts running. Inside main() I setup a Timer to check the variable. (Again, sorry for the ugly code.)


import 'dart:html';
import 'dart:async';
import 'package:js/js.dart' as js;

main() {  
  // TODO: make this better  
  new Timer.periodic(const Duration(milliseconds: 100), (Timer t) {  
   if (js.context.senchaLaunchComplete) {  
    initDart();  
    t.cancel();  
   }  
  });  
 }  


When senchaLaunchComplete is true, initDart() runs and the timer stops. The js.context is a handle into the JavaScript side of the world.

Adding a button and panel with Dart

Now that both apps are loaded, and initialized, Dart code can add new objects to the Sencha Touch app. Let's add a new panel, which contains some text and a button.

Sencha Touch apps can be declared with JSON, or imperatively constructed. I show both methods below. Remember, everything that follows is Dart code:

 initDart() {  
  // Grab the app's main panel  
  var mainPanel = js.context.Ext.Viewport.items.getAt(0);  
    
  // You can create a new object imperatively using a Proxy  
  var button = new js.Proxy(js.context.Ext.Button)  
   ..setText('Click Me')  
   ..setHandler(new js.Callback.many((b, e, _, __) {  
    // Respond to clicks with callbacks  
    js.context.Ext.Msg.alert('Success!', 'This is a Dart function, run from JavaScript.');  
   }));  
    
  // You can also create objects declaratively using ExtJS style.  
  var dartPanel = js.map({  
   'title': 'From Dart',  
   'iconCls': 'star',  
   'html': 'Hello from Dart! This panel was created from Dart code.',  
   'items': [button]  
  });  
    
  // Add the panel from Dart land into JS land  
  mainPanel.add(dartPanel);  
 }  

The main panel was constructed by the main Sencha Touch app over in JavaScript land. We get a handle to the main panel via JS-Interop and the js.context.

Use js.Proxy to construct a new JavaScript object and get its reference. In the code above, a new Ext.Button is created from Dart, and configured. The double-dot syntax (..) is method cascadeshttps://www.dartlang.org/docs/dart-up-and-running/contents/ch02.html#classes

Notice a click handler is installed on the button, as a js.Callback. The callback can be fired multiple times. When the button is clicked, a new Ext.Msg.alert is displayed.

The new panel is created as an example of Sencha's declarative configuration. The js.map is an efficient way to convert a Dart map into a JavaScript object. Notice how the Dart map contains a direct reference to the button (a Proxy object).

The new panel, which contains the button, is then added to the main panel. This combines the two worlds.

Screenshots

The star button comes from Dart. The home button comes for JavaScript. They basically are hugging! :)


When the button is clicked, it calls a Dart function. How cool!



All the code (and details)

The code for a sample Sencha Touch + Dart app is available here: https://github.com/sethladd/dart-sencha-touch-demo.  You'll need the sample code to see how a Dart application is arranged, and how to include both the Sencha JavaScript files and the Dart files. Specifically, check out https://github.com/sethladd/dart-sencha-touch-demo/blob/master/web/index.html

What's next?

This small demo simply shows how to insert some Dart logic into an existing Sencha Touch app. However, Sencha Touch is quite full featured, and there are more areas to explore. For example, how to interact with a Sencha model from Dart? How to create a Dart class and use it as a Sencha model? Good topics for future posts!

Summary

You can extend an existing Sencha Touch app with Dart with Dart's JS-Interop. If you use Sencha Touch for your mobile HTML5 web apps, try using Dart's language, libraries, and tools as you add new features to your app. Go forth and build awesome mobile-first web apps!
Post a Comment

Disclaimer

I'm probably required to say that the views expressed in this blog are my own, and do not necessarily reflect those of my employer. Also, except as otherwise noted, the content of this page is licensed under the Creative Commons Attribution 3.0 License, and code samples are licensed under the BSD License.