Ember CLI recently introduced the concept of addons, they make it possible to easily share common code between applications without copy pasting. A datepicker component is a good example for common code, a lot of applications need one and the implementation is usually identical. Therefore I wrote the ember-pikaday addon last weekend and want to use it as an example to show you what steps it takes to write an addon.
My datepicker component uses, as the addon name suggests, the Pikaday library for the datepicker functionality. In addition it includes Moment.js so the output format of a date can be adapted. In case you are wondering why I created another datepicker addon the reason is pretty simple: As far as I can tell none of the existing addons is fully tested.
Before you start following along my steps, make sure you have an up-to-date version of Ember CLI. As of this writing the most recent version is
0.0.44 which I also recommend you to install.
I started by creating the addons directory structure, as you can see in the listing, Ember CLI gives you a command to do exactly that. After creating the structure I installed the Pikaday and Moment.js dependencies via Bower. To make them available in the dummy application and tests I modified
Brocfile.js and added three import lines for the relevant files. It is necessary to import Moment.js before Pikaday so it gets picked up correctly.
An addon consists of multiple important files and directories.
package.jsonis needed to allow publishing and installing via npm. The keyword attribute has to contain
ember-addonso Ember CLI recognizes the npm package as an addon.
index.jsis the default entry point of the addon. There are various hooks available which get called by Ember CLI with the consuming application. I use it to make sure the third party dependencies are imported into the consuming application.
tests/dummy/is a dummy application within the addon, it can be used to manually try and test whatever the addon is providing.
addon/is the place for your addons code, everything within is available in the consuming application under the addons namespace.
app/and its contents gets merged into the consuming applications namespace.
In the beginning the difference and purpose of the addon and app directories was not clear to me, thanks to Robert Jacksons patience and answers it is now. If I put a component named
pikaday-input in the addon directory I would have to import it from
ember-pikaday/components/pikaday-input wherever I want to use it in the consuming application. In this case
ember-pikaday is the namespace of my addon which is also the name of the addon.
When I put the component in the app directory I don't have to manually import it in the consuming application. Since everything within the app directory is merged with the consuming application my component is available without additional work. Putting the components code under the app directory is still a bad idea, it prevents users of your addon from modifying the component.
Thankfully it is easy to solve both problems. In order to allow the consuming application to use the component without manual import statements I put the component under the app directory. But the components actual code is not in this file. Instead it just imports the component from the addon directory and exports it again. This setup allows others to modify the component by extending it while making the component available in the consuming applications namespace.
At this point I started developing the actual
pikaday-input component. You can look at the relevant files
tests/unit/components/pikaday-input-test.js on GitHub. This article is not about developing a component so I won't explain the development process step by step. But if you embark on this way I can give you a few pointers.
The tests include a few workarounds to get them working, they make sure the acceptance test helpers are available in the component test and that the component is correctly destroyed after each test. I suspect this will not be necessary with future Ember CLI releases. Testing the Pikaday popup took me a lot of time.
jQuery allows you to trigger events manually with
$('selector').trigger('event'). But this method has a big catch: It only triggers events added through the jQuery interface. Since Pikaday does not use jQuery I have to dispatch native events, events triggered via jQuery will not work with Pikaday. Keep that in mind and it will save you countless hours debugging.
The components code itself is pretty short, key points are setting up Pikaday and correctly removing it whenever the component is destroyed. The rest of the code is making sure to properly handle the
Before being able to publish my addon I had to make sure the two dependencies will be installed and available in the consuming application. There are multiple ways to do this, the most straightforward way is to vendor the required files with the addon. This means putting the files into the
vendor directory and importing them from there.
Since this is completely sidestepping Bower or having your addon depend on Bower which results in an inflated package size I opted for the blueprint method. Ember CLI has a concept called blueprints which can be used to generate files. Blueprints recently got the ability to add Bower dependencies to
bower.json. This is exactly what I need to make sure my addons dependencies are installed.
Creating a blueprint is relatively easy, I created a
index.js file in the appropiate directory and used
addBowerPackageToProject in the
afterInstall hook to add my dependencies to the project. The downside of using the blueprint method is having to run two commands instead of one to install the addon. But since there is no perfect method for adding dependencies I can live with that flaw.
Now I only had to make sure the dependencies files are imported into the consuming application. As mentioned above the entry point of my addon is the right place to do this. I used the
included hook to import the three files in the correct order.
Having written the tests and the component itself and making sure everything is correctly installed and imported in a consuimg application it was time for me to publish the addon. This meant making sure the
package.json has all the relevant information. I changed the repository attribute to the correct link, added a few keywords and set my name as the author. After committing I was ready to publish my addon. With npms help I updated the version of my addon and created the corresponding git tag. Then I pushed my code to the git remote and published the new version of the addon.
Done, now everybody can use the component in their application by installing the addon with two simple commands:
npm install --save-dev ember-pikaday and
ember g ember-pikaday.
I appreciate any feedback you migth got, you can reach me @stravid or write an email to david.strauss[at]edgycircle.com