Wrapping a jQuery plugin with Ember.js Views
Whenever I’m working with Ember I often find myself wanting the functionality that a jQuery plugin provides. It may not be obvious, but we can wrap jQuery plugins inside of Ember views and use these plugins just like any other Ember view.
Here I’ll be wrapping the PickADate jQuery plugin. This plugin allows you to attach to a text input field and whenever it is focused it shows a date popup for you to select. This Ember view will allow us to embed the PickADate view into our templates and bind the value selected to a property just like Ember.TextField
does.
So we have our Ember app set up and we have included the PickADate plugin we can start writing our view.
App.PickADate = Ember.View.extend({
attributes: ['monthsFull', 'monthsShort', 'weekdaysFull', 'weekdaysShort',
'monthPrev', 'monthNext', 'showMonthsFull', 'showWeekdaysShort', 'today',
'clear', 'format', 'formatSubmit', 'hiddenSuffix', 'firstDay', 'monthSelector',
'yearSelector', 'dateMin', 'dateMax', 'datesDisabled', 'disablePicker'],
events: ['onOpen', 'onClose', 'onSelect', 'onStart'],
type: 'text',
tagName: 'input',
classNames: 'pickadate',
});
We’re just extending Ember’s view and setting the attributes
and events
properties to be the properties and events that the PickADate plugin accepts. We set the tagName
of our view to be an input tag instead of the default div and set the classNames
attribute of our view to be pickadate.
In order to hook our plugin up to our view we will need to implement the didInsertElement
function which as implied, is called when the element is inserted.
didInsertElement: function() {
var options = {};
var self = this;
this.get('events').forEach(function(event) {
if (self[event]) {
options[event] = self[event];
}
});
this.get('attributes').forEach(function(attr) {
if (self[attr] !== undefined) {
options[attr] = self[attr];
}
});
},
We set up the options variable to be an empty object because this will be what we eventually pass to PickADate when we call it on our element. We then loop over all of our events and check if we have a callback set for that event and if we do, we assign it to our options object. We do the same thing with attributes but ensure that they’re defined.
This next part is slightly odd because we actually implement a callback ourself but then call the callback that we were provided if it exists. We do this so that we can set the value
property which is what our valueBinding depends on.
var onSelectCallback = options.onSelect;
options.onSelect = function() {
Ember.set(self, 'value', this.getDate(true));
if (onSelectCallback) {
onSelectCallback.call(this)
}
}
Again, we’re setting our provided onSelect
callback to the onSelectCallback
variable so we can use it later. We then set the onSelect
callback to a function that sets the value
property on our view and then calls our callback if one was provided. If we didn’t implement this method our valueBinding
property on the view would never update and we would have a datepicker that doesn’t set a value.
The last piece of our code is calling the pickadate
function on our views element. We can access this via the $
method, which returns a jQuery object of this views element.
this.$().pickadate(options);
All we’re doing is calling pickadate
on our view and passing it the options object that we created. Here’s an example of how you can use the PickADate view in your application.
App.NewIssueView = Ember.View.extend({
datePicker: App.PickADate.extend({
format: 'dddd, mmmm d, yyyy',
firstDay: 1,
clear: false,
onStart: function() {
var date = new Date();
this.setDate(date.getFullYear(), date.getMonth() + 1, date.getDate());
},
}),
})
And in our template we set the placeholder and pass the valueBinding
.
This is just a simple example of wrapping a single jQuery plugin but can be applied to a lot more than a simple date picker. Here is a gist of the full source for the pickadate Ember view.