We're hiring!

We're actively seeking designers and developers for all three of our locations.

Ember.js Date Pickers – It’s Easy!

Ember.js can be an extremely polarizing framework. If the stars align, you can accomplish astonishing volumes of work in a short period. When you start exploring the boundaries of what’s known practice, sometimes you can get buried under a mountain of yaks that need shaving.

The application I’m currently working on has quite a few form pages. This has lead me to look some of the tougher problems in Ember like radio boxes and modal dialogs. While I’ve had exceptionally poor luck trying to get radio buttons working properly and am not unsatisfied with my current solution for modals, I had a really awesome experience adding a calendar date-picker widget with Ember.

Since we aren’t using Twitter Bootstrap, I couldn’t use the work that other people have done. Instead, I found a nice library called Pikaday that provides a relatively light-weight and dependency-free date picker. It’s even nicely style-able with CSS. I set about integrating it into Ember.js.

Since I want to bind the date picker to a form element, I chose Ember’s TextField View as a base class. This gives me all the binding magic I need, so the only thing that’s needed is to attach the picker. The perfect place for this is the “didInsertElement” event.

App.CalendarDatePicker = Ember.TextField.extend({
  _picker: null,
 
  modelChangedValue: function(){
    var picker = this.get("_picker");
    if (picker){
      picker.setDate(this.get("value"));
    }
  }.observes("value"),
 
  didInsertElement: function(){
    currentYear = (new Date()).getFullYear();
    formElement = this.$()[0];
    picker = new Pikaday({
      field: formElement,
      yearRange: [1900,currentYear+2]
    });
    this.set("_picker", picker);
  },
 
  willDestroyElement: function(){
    picker = this.get("_picker");
    if (picker) {
      picker.destroy();
    }
    this.set("_picker", null);
  }
});

You can play around with the completed example here. Despite the straightforward interface into Ember, it seems to cooperate quite well. You can manually enter dates and even paste dates. Just by adding an observer to the value property, setting properties on the model even works. If you need a custom date format out of the widget, you can use Pikaday’s excellent Moment.js support and add a “format” parameter to the Pikaday initialization.

This method of binding a standard javascript library to an ember-controlled element can be extended to other form elements. Many common jQuery libraries appear to be a glance, it should be easy to use on any number of jQuery libraries. Do you have any neat HTML form tricks to show off in Ember.js?
 

Mitchell Johnson (22 Posts)

Software developer, maker, endlessly curious, and a serial acquirer of skills.

This entry was posted in Ember.js and tagged . Bookmark the permalink. Both comments and trackbacks are currently closed.

5 Comments

  1. Patrick Moy
    Posted November 18, 2013 at 4:44 pm

    Thanks for posting this very useful article. I have some questions:

    1) I can’t seem to find a description of modelChanged value in the Ember API documentation; I looked at Ember.Textfield, Ember.View, Ember.CoreView, and Ember.Object. Is it described somewhere on the Ember site?

    2) Pikaday documentation says definition of “field” is “binds the Datepiker to a form field.” I’m not too clear how “formElement = this.$()[0];” does this. Is there some jquery magic in there somewhere (I don’t know jquery all that well)?

    3) modifying yearRange doesn’t seem to have any effect. But adding minDate and maxDate did worked as described.

    4) Do you happen to know how to change the display format to mm/dd/yyyy?

    • Mitchell Johnson
      Posted November 20, 2013 at 9:30 am

      I’m glad you found the article useful!

      1 – The modelChangedValue function isn’t actually a part of Ember, just something I defined within the date picker. You’ll notice the .observes("value") at the end of the function. That ties into the Ember computed properties system, and calls the function every time the “value” changes.

      2 – There is some jQuery in there. The this.$ is functionality in Ember to return a jQuery element for the view you’re in. The [0] subscript grabs the first element for the array, which is the form field we’re looking for.

      3 – I’m not sure why you might be having problems with the yearRange. What did you try modifying it to?

      4 – To change the display format to mm/dd/yyyy, you’ll need to include the moment.js library. Once it’s been included, you should just be able to provide an extra configuration option to Pikaday of format: 'MM/DD/YYYY'

      • agile
        Posted December 10, 2013 at 3:04 am

        i include the moment.js library, and add the format: ‘MM/DD/YYYY’ into my source ,but it can not format, why?

        picker = new Pikaday({
        field: formElement,
        format: ‘DD-MM-YYYY’,
        yearRange: [1900,currentYear+2]
        });

  2. Patrick Moy
    Posted November 23, 2013 at 10:02 pm

    Thanks for your clarifications.

    Regarding 3:
    I thought by setting a range, Pikaday will preclude selection of a date outside this range. For example, if I set yearRange: [1900, 2013] on your JS Bin, Pikaday still lets me select a date in 2014 and beyond.

    Also, try setting minDate and maxDate in your example to the following and re-run.

    minDate: new Date (“9/30/2013″),
    maxDate: new Date(“10/30/2014″)

    Pikaday will reset ‘date’ from 9/27/2013 to 9/30/2013, which is as expected. But now supposed I want to change the date to Jan 10, 2014 by typing directly into the field. So highlight Oct and type Jan; on my computer, when I type the letter ‘a’, cursor jumps to the end of the field. By defining minDate and maxDate, I seem to have lost the ability to edit the field directly; cursor jumps all over the place. Does that happen on your computer?

    • Patrick Moy
      Posted November 27, 2013 at 9:56 am

      The problem regarding field edit mentioned in my prior comment can be solved by removing the “modelChangedValue” block. With this block removed, I can manually input dates, as well as select dates using the date picker.

      To aid in date validation upon field change, I introduced a mixin similar to that shown below to App.CalendarDatePicker. “isValid” is defined in the appropriate controller.

      App.FocusValidate = Ember.Mixin.create ({
      oldVal: “”,

      focusIn: function() {
      if (this.get(‘isValid’) === true)
      this.set (‘oldVal’, this.get (‘value’));
      },
      focusOut: function() {
      if (this.get(‘isValid’) === true)
      if (this.get(‘oldVal’) !== this.get(‘value’))
      this.set (‘isValid’, false);
      }
      });