HTML5 Date Inputs and Ember.js

ember_date

My team recently needed a date picker in our web app. We wrote tests and implemented a simple text field first as scaffolding. Call it YAGNI if you want — we really just weren’t excited about choosing from the long list of time-forgotten, unlovable JavaScript date picker widgets out there.

Surprises from Chrome

The next day while we were pairing, we opened up Chrome, hit our app, and noticed that our new “date” field had magically turned into a fully-functional date picker!

We were puzzled. We started looking at commit logs, wondering if another teammate had snuck in a little treat without us noticing. Then I tried the app in Firefox… and the date picker disappeared.

As it turns out, Chrome automatically adds a date picker to input elements with a type attribute of “date.” It’s been in there for a while, but we’d never noticed. It’s one of a longer list of new input types in HTML5 that Chrome has implemented, while Firefox has not.

Why Ember?

My team chose Ember.js to help us build a web app on top of a REST-based JSON+HAL API. Our next task then was to figure out how to integrate this gift horse into our app.

I like Ember.js for two reasons: I trust their team, and I like the decoupled, testable style of their architecture. It’s still a small enough framework that our team can solve most problems by looking through the source code or by running simple examples (or, sometimes, by asking fellow Atoms who’ve been using Ember longer than we have).

As it happens, our app expects a typical (for us weirdos in the US anyway) MM/DD/YYYY date format. We are using moment.js to convert a user’s entry into a preferred ISO8601 format for storage in CouchDB, so we started with a simple Ember view and a template helper to make our date picker and date display easy. The View was kickstarted with the results of this thread on the Ember Discourse page. We used Modernizr to help detect browser support and adjust the expected input format accordingly, so if you’re in Firefox, you’re just going to get a text field. If you’re in Chrome, you can haz a date picker.

The resulting model property is a bonafide Moment object, which in our case converts cleanly when JSON.stringify is called on the way to our API.

Here’s the resulting view class (in CoffeeScript of course.) Good luck with your Ember app!

# Usage:
# {{App.DateField dateBinding="date"}} # Where "date" points to a Moment.js object
App.DateField = Ember.TextField.extend
  type: 'date'
  hasFocus: false
  placeholderBinding: 'dateFormat'
  dateFormat:(->
    if Modernizr.inputtypes.date then 'YYYY-MM-DD' else 'MM/DD/YYYY'
  ).property()

  init: ->
    @_super()
    @updateValue()

  updateDate: (->
    ms = moment.utc(@get('value'), @get('dateFormat'))
    @set('date', ms) if ms and ms.isValid()
  ).observes('value')

  updateValue: (->
    return if @get('hasFocus')
    date = @get('date')
    if date
      @set('value', if moment.isMoment(date) then date.utc().format(@get('dateFormat')) else moment.utc(date))
  ).observes('date')

  focusIn: ->
    @set('hasFocus', true)

  focusOut: ->
    @set('hasFocus', false)
    @updateValue()