Building Modern Web Apps with

HTML5 and DART

  -  

Of course we're using Geolocation!

Who?

+

aka "drfibonacci"

@

Agenda

How do we create great user experiences?

Make it easy to try & use

Use responsive layout for different form factors

Media Queries for Style Sheets

<link rel="stylesheet" media="all" href="/static/css/base.min.css" />
<link rel="stylesheet" media="only screen and (max-width: 800px)" 
  href="/static/css/mobile.min.css" />

Testing CSS media queries in JavaScript with window.matchMedia()

if (window.matchMedia('only screen and (max-width: 480px)').matches) {
  // Asynchronously provide experience optimized for phone
} else if (window.matchMedia('only screen and (min-width: 481px) and ' +
                             '(max-width: 1024px)').matches) {
  // Asynchronously provide experience optimized for table or smaller screen
} else {
  // Asynchronously provide full screen experience
}

http://formfactorjs.com

Form factors in GWT

Include FormFactor module from mobilewebapp sample

<inherits name='com.google.gwt.sample.mobilewebapp.FormFactor'/>

Use formfactor property to select UI factory impl

  <!-- Use ClientFactoryImplMobile for mobile form factor. -->
  <replace-with class="ClientFactoryImplMobile">
    <when-type-is class="ClientFactory"/>
    <when-property-is name="formfactor" value="mobile"/>
  </replace-with>

Allow for deep linking & single page navigation

function updateHash(push) {
  if (push) {
    var slideNo = curSlide + 1;
    var hash = '#' + slideNo;
    window.history.pushState(slideNo, 'Slide ' + slideNo, hash);
  }
};
window.addEventListener('popstate', handlePopState, false);
function handlePopState(event) {
  if (event.state != null) {
    curSlide = event.state - 1;
    updateSlides(true);
  }
};

http://www.leviroutes.com

Single page navigation in GWT

GWT's History object

Activities and Places

gwt-platform

See also my two talks in Developer Tools track from I/O

How do we build desktop-like apps for the Web?

Make uploading files easier

<input type="file" id="dir-select" webkitdirectory />
document.querySelector('#dir-select').onchange = function(e) {
  var out = [];
  for (var i = 0, f; f = e.target.files[i]; ++i) {
    out.push(f.webkitRelativePath);
  }
  document.querySelector('output').textContent = out.join('/n');
};

Use desktop file handling paradigms

var files = document.querySelectorAll('.dragout');
for (var i = 0, file; file = files[i]; ++i) {
  file.addEventListener('dragstart', function(e) {
    e.dataTransfer.setData('DownloadURL', this.dataset.downloadurl);
  }, false);
}
Drag this file onto your desktop: 3D Glasses [PDF]

( this feature is only available in Google Chrome )

Use desktop file handling paradigms

document.querySelector('#dropzone').
  addEventListener('drop', function(e) {
    var reader = new FileReader();
    reader.onload = function(evt) {
      document.querySelector('img').src = evt.target.result;
    };
    reader.readAsDataURL(e.dataTransfer.files[0]);
}, false);
Drop in images from your desktop

Save data to the local file system

window.requestFileSystem(TEMPORARY, 1048576, initFs, fsError);
function saveFile(arrayBuffer, filename, type, callback) {
  fs.root.getFile(filename, {create: true}, function(fileEntry) {
    fileEntry.createWriter(function(fileWriter) {
      var bb = new BlobBuilder();
      bb.append(arrayBuffer);
      fileWriter.write(bb.getBlob(type));
    }, fsError);
    callback(fileEntry);
  }, fsError);
}
Browse File System

Make it work offline

Application Cache to enable the application

<html manifest="cache.appcache">

GWT can generate for you: see AppCacheLinker in mobilewebapp sample

Store data locally

window.requestFileSystem(PERSISTENT, 1048576, initFs, fsError);
var idbRequest = window.indexedDB.open('Database Name');
localStorage["key"] = "value";

Track online and offline states

window.addEventListener('online', function(e) {
  // Re-sync data with server.
}, false);

Persist users data

var elems = document.querySelectorAll("textarea, input");
var len = elems.length;
for (var i = 0; i < len; i++) {
  var elem = elems[i];
  elem.addEventListener("input", function(item) {
    localStorage[formName + "-" + item.srcElement.id] = 
      item.srcElement.value;
    var debug = document.getElementById("data-persistence-debug");
    debug.innerHTML = "Last auto-saved at: " + new Date();
  }, false);
}

LawnChair - simple json storage

Local storage in GWT

Using the LocalStorage API in GWT 2.3+

import com.google.gwt.storage.client.Storage;

private FlexTable stocksFlexTable = new FlexTable();
private Storage stockstore = null;

stockStore = Storage.getLocalStorageIfSupported();
if (stockStore != null){
  for (int i = 0; i < stockStore.getLength(); i++){
    String key = stockStore.key(i);
    stocksFlexTable.setText(i+1, 0, stockStore.getItem(key));
    stocksFlexTable.setWidget(i+1, 2, new Label());
  }
}
	  

Let the user tell you what they want

<input type="text" x-webkit-speech />
function startSearch(event) {
  if (event.target.results.length > 1) {
    var second = event.target.results[1].utterance;
    document.getElementById("second_best").value = second;
  }
  event.target.form.submit();
}

Keep your user informed

function showNotifications(pic, title, text) {
  if (window.webkitNotifications.checkPermission() == 0) {
    var notificationWindow = 
      window.webkitNotifications.createNotification(pic, title, text);
    notificationWindow.show();
    
    // close notification automatically after a timeout    
    setTimeout(function(popup) { 
      popup.cancel();
    }, 6000, notificationWindow);
  }
}

Treat performance like a feature

Use Application Cache to provide local caching

<html manifest="cache.appcache">

Store data locally

window.requestFileSystem(PERSISTENT, 1048576, initFs, fsError);
var idbRequest = window.indexedDB.open('Database Name');
localStorage["key"] = "value";

Use CSS3 transitions, transforms and animations

transition: all 1s ease-in-out;

Use WebWorkers for non-blocking JavaScript

var worker = new Worker('myworker.js');

More great tips at http://bit.ly/rizNVE

How do we create apps that scale?

MVC Frameworks - Prevent Spaghetti Code

MVC controller
view model

MVC frameworks



CSS frameworks

MVC Frameworks in GWT

Activities and Places (not MVC proper)

gwt-platform (also dispatcher, Eclipse plugin)

mvp4g (event-centric)

gwt-mpv (generate views from ui.xml)

What happens as HTML+JS+CSS programs grow?

A simple and unsurprising OO language

interface Shape {
  perimeter(); // return type optional
}

class Rectangle implements Shape {
  final num height, width; 
  // Compact constructor syntax
  Rectangle(num this.height, num this.width);
  // Terse function syntax
  perimeter() => 2*height + 2*width;
}

class Square extends Rectangle {
  Square(num size) : super(size, size);
}

main() {
  var p = new Square(5).perimeter(); // dynamic type
  print('Perimeter=$p'); // String interpolation
}

Event handling is nice thanks to closures

elem.on.click.add(  (event) => print('click!') );
addHandlers() {
  canvasElement.onmousemove = onMouseMove;
  // No anonymous inner classes!
  canvasElement.onmousedown = (Event e) { window.alert('click'); };
}

onMouseMove(MouseEvent event) {
  int x = event.offsetX;
  int y = event.offsetY - 40;
  if (( y < 0) || (x >= width)) {
    return;
  }
  ctx.setFillStyle(getHexString(getColorIndex(x, y)));
  ctx.fillRect(0, 0, width/2, 30);
}

Getters and setters are built in

String get selectedColor() => _selectedColor;

void set selectedColor(num i) {
  _selectedColor = getHexString(i.floor());
  showSelected();
  fireSelected();
}
newColor = selectedColor;
selectedColor = getColorIndex(x, y);

Isolates

Tools

Sunflower

Demo

Spirodraw

Demo

Total

Demo

What's in it for JS developers?

dartlang.org

What's in it for GWT developers?

dartlang.org

What about GWT?

Step 1. Build web app
Step 2. ???????
Step 3. Profit!
Make it easy for user to discover great apps, extensions, and themes
Make it easy for developers and brands to reach the 200+ million Chrome users
  • Multiple monetization models, including one-time, subscriptions and in-app payments
  • One-time $5 developer registration fee
  • No review or approval process
  • No restrictions on advertising
  • No restrictions on the platform

Available in 41 Languages

Play 23% more and spend 147% more
100% more engagement
Step 1. Create application manifest
Step 2. Upload manifest & graphics
Step 3. Publish

OR

AppMator

Chrome Web Store manifest file

{ "name": "My Chrome Web Store",
  "description": "Discover great apps, games, extensions and...",
  "version": "1.0", 
  "icons": {
    "16": "16.png",
    "128": "128.png"
  },
  "app": {
    "urls": ["*://chrome.google.com/webstore"],
    "launch": { 
      "web_url": "https://chrome.google.com/webstore"
    }
  },
  "permissions": ["unlimitedStorage", "notifications"]
}

5%

In App Payments

  • No per transaction fees
  • No monthly fees
  • No sign up fees
  • PCI compliance

One last thing...

Aim for a great experience, everything else will follow.

chromestatus.com

HTML5Rocks.com

Resources

Developer Guides

Best Practices

Thanks!

Questions?

Slides:

The web is what you make of it.