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);
  }
});

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?
 

Conversation
  • Patrick Moy says:

    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 Mitchell Johnson says:

      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 says:

        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]
        });

  • Patrick Moy says:

    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 says:

      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);
      }
      });

  • Dinesh says:

    Hai everyone,

    Am very new to UI design. Now trying to learn ember js and build todo application successfully by seeing this tutorial (http://ift.tt/19KrxrC).
    now my aim is to “add a calendar page where user can give calendar date”, to tell which date I need to finish the task. “I mean calendar widget next to each todo to add an target date ”
    am trying to use jquery ui calendar.. but someone kindly tell me how can i add this inside ember js

  • Renzo says:

    Works like a charm :)

  • roslin says:

    What if I want to disable the Sundays alone?.. Any workaround for it?

  • Comments are closed.