Friday, January 31, 2014

Publishing to Bower: a Workflow for an AngularJS Directive


The question for today is, how do you extract a working Angular directive into a published Bower package?

From last night, I have my generalized solution for double binding Polymer variables in AngularJS (pushing changes into Polymer works fine, but seeing changes from Polymer in Angular needs a little help). I can change attributes and variable names in Angular templates:
<pre ng-bind="asdf"></pre>
<p>
  <x-pizza bind-polymer state="{{asdf}}"></x-pizza>
</p>
And, thanks to last night's generalized bind-polymer directive, it just works™:



I can add stuff to my <x-pizza> Polymer and Angular sees the change—even with whatever ridiculous variable name I might put in there. So I am good to go with this approach, except…

It likely will not fit a nice narrative in Patterns in Polymer—at least not a consistent narrative between the Dart and JavaScript versions of Polymer. The problem is that the Dart and JavaScript solutions are at completely different abstraction levels currently. Dart has a package that does this (angular_node_bind) and my JavaScript solution requires the reader to write their own Angular directive. Which means… I have a darn good excuse to publish my first Bower package!

I start with a new, local Git repository. Before publishing this to Bower, I ought to make sure that it works. It seems like Bower will only work with local Git repositories (not directories), I start angular-bind-polymer:
➜  repos  mkdir angular-bind-polymer
➜  repos  cd !$
➜  angular-bind-polymer  git init .
Initialized empty Git repository in /home/chris/repos/angular-bind-polymer/.git/
Next, I initialize this as a Bower project with bower init:
➜  angular-bind-polymer git:(master) bower init
I mostly accept the defaults from the very nice initialization script. The customizations are specific to this being an Angular module (it will depend on AngularJS) and how I organize the package (I will create the code directly in the top-level of the repository). The differences from the defaults in the resulting bower.json are then:
{
  "name": "angular-bind-polymer",
  // ...
  "description": "Angular directive for *double* variable binding of Polymer attributes.",
  "main": "angular_bind_polymer.js",
  // ...
  "dependencies": {
    "angular": "~1.2.9"
  }
}
Now for the main file, angular_bind_polymer.js. I have the Angular directive ready to go, I only need place it in the main file properly. For that, I assume that Angular will already be loaded, which will make the angular global variable available. I need to declare this as a module and the unofficial convention for these things seems to be prefixing the module name with a GitHub ID (eee-c in my case). So angular_bind_polymer.js becomes:
angular.module('eee-c.angularBindPolymer', []).
directive('bindPolymer', function($q, $timeout) {
  // Yesterday's directive definition here...
});
I check those in locally and am ready to try them back in the application from which this is being extracted.

So, back in the bower.json file from the original project, I add my local Git repository in the list of dependencies:
{
  "name": "angular_example",
  // ...
  "dependencies": {
    "angular": "~1.2.9",
    "polymer": "~0.1.3",
    "angular-route": "~1.2.9",
    "angular-bind-polymer": "/home/chris/repos/angular-bind-polymer/"
  }
}
In the application directory, I bower install to get my new module:
➜  js git:(master) ✗ bower install
bower angular-bind-polymer#*       not-cached /home/chris/repos/angular-bind-polymer#*
bower angular-bind-polymer#*          resolve /home/chris/repos/angular-bind-polymer#*
bower angular-bind-polymer#*         checkout master
bower angular-bind-polymer#*         resolved /home/chris/repos/angular-bind-polymer#7bcc2d673d
bower angular#~1.2.9                   cached git://github.com/angular/bower-angular.git#1.2.9
bower angular#~1.2.9                 validate 1.2.9 against git://github.com/angular/bower-angular.git#~1.2.9
bower angular#~1.2.9                      new version for git://github.com/angular/bower-angular.git#~1.2.9
bower angular#~1.2.9                  resolve git://github.com/angular/bower-angular.git#~1.2.9
bower angular#~1.2.9                 download https://github.com/angular/bower-angular/archive/v1.2.11-build.2195+sha.29432ff.tar.gz
bower angular#~1.2.9                  extract archive.tar.gz
bower angular#~1.2.9                 resolved git://github.com/angular/bower-angular.git#1.2.11-build.2195+sha.29432ff
bower angular-bind-polymer#*          install angular-bind-polymer#7bcc2d673d

angular-bind-polymer#7bcc2d673d bower_components/angular-bind-polymer
└── angular#1.2.9
Next, I need to update the web page that holds my Angular application so that, after loading Polymer, Angular, and other related sources, it loads my new package:
    <script src="bower_components/platform/platform.js"></script>
    <link rel="import" href="elements/x-pizza.html">

    <script src="bower_components/angular/angular.min.js"></script>
    <script src="bower_components/angular-route/angular-route.min.js"></script>
    <script src="bower_components/angular-bind-polymer/angular_bind_polymer.js"></script>
Finally, I remove the directive code from my Angular application and, in its place, add my new module:
var pizzaStoreApp = angular.module('pizzaStoreApp', [
  'ngRoute',
  'eee-c.angularBindPolymer'
]);

pizzaStoreApp.config(['$routeProvider',
  // Routing stuff here...
]);
And, with that, I am done! I have successfully created a working Angular module in a Git repository and used that repository to install and use that Angular module.

All that is left at this point is to register the package with bower. First I tag (and push to GitHub):
➜  angular-bind-polymer git:(master) git tag -a v0.0.1 -m "Tag version 0.0.1" 
➜  angular-bind-polymer git:(master) git push --tags
Counting objects: 1, done.
Writing objects: 100% (1/1), 171 bytes, done.
Total 1 (delta 0), reused 0 (delta 0)
To git@github.com:eee-c/angular-bind-polymer.git
 * [new tag]         v0.0.1 -> v0.0.1
Then I register:
➜  angular-bind-polymer git:(master) bower register angular-bind-polymer https://github.com/eee-c/angular-bind-polymer.git
bower                          convert Converted https://github.com/eee-c/angular-bind-polymer.git to git://github.com/eee-c/angular-bind-polymer.git
bower angular-bind-polymer#*   resolve git://github.com/eee-c/angular-bind-polymer.git#*
bower angular-bind-polymer#*  download https://github.com/eee-c/angular-bind-polymer/archive/v0.0.1.tar.gz
bower angular-bind-polymer#*   extract archive.tar.gz
bower angular-bind-polymer#*  resolved git://github.com/eee-c/angular-bind-polymer.git#0.0.1
[?] Registering a package will make it installable via the registry (https://bower.herokuapp.com), continue? Yes
bower angular-bind-polymer    register git://github.com/eee-c/angular-bind-polymer.git

Package angular-bind-polymer registered successfully!
All valid semver tags on git://github.com/eee-c/angular-bind-polymer.git will be available as versions.
To publish a new version, just release a valid semver tag.

Run bower info angular-bind-polymer to list the available versions
And I'm done!

Day #1,013

No comments:

Post a Comment