Thursday, January 30, 2014

Generalizing My Angular Solution for Polymer


I am undecided on how to present Polymer integration with AngularJS in Patterns in Polymer. As of last night, I have it working nicely in the JavaScript versions of both libraries. The problem with which I am faced is that it is much easier and cleaner to establish double binding in the Dart versions of the libraries.

The ease in Dart is entirely thanks to the angular_node_bind library. If there is an equivalent in the JavaScript world, I have not found it. This leaves me with the dilemma of either writing a similar library myself in JavaScript or describing how to write an equivalent directive in Dart.

To keep my options open, tonight I will try my hand at polymer-bind, which, if I can manage it, would be an Angular directive that establishes a double bound attribute in a Polymer, à la:
<pre ng-bind="pizzaState"></pre>
<p>
  <x-pizza bind-polymer state="{{pizzaState}}"></x-pizza>
</p>
I already have a directive that works specifically for the state attribute on a Polymer and a pizzaState scope variable. Once the Polymer is ready, then the directive establishes a watcher for that attribute / variable pair:
directive('bindPolymer', function($q, $timeout) {
  return {
    restrict: 'A',
    link: function(scope, element) {
      // ...
      // When Polymer is ready, establish the bound variable watch
      onPolymerReady().
        then(function(){
          // Mutation observer here to force Angular $apply when attribute changes...
          scope.$watch(
            function() {return element.attr('state');},
            function(value) {
              scope.pizzaState = value;
            }
          );
        });
    }
  };
});
I need something a bit more generalized than that. Specifically, I need to map all of the attributes with curly brace bindings to the variable names inside the curly braces:
directive('bindPolymer', function($q, $timeout) {
  return {
    restrict: 'A',
    link: function(scope, element, attrs) {
      var attrMap = {};
      for (var prop in attrs.$attr) {
        if (prop != 'bindPolymer') {
          var _attr = attrs.$attr[prop];
          var _match = element.attr(_attr).match(/\{\{\s*(\w+)\s*\}\}/);
          if (_match) {
            attrMap[_attr] = _match[1];
          }
        }
      }
      // ...
    }
  };
});
Then I need to use that map to establish the necessary attribute watch that will update the variable in the current scope:
directive('bindPolymer', function($q, $timeout) {
  return {
    restrict: 'A',
    link: function(scope, element) {
      // ...
      // When Polymer is ready, establish the bound variable watch
      onPolymerReady().
        then(function(){
          // Mutation observer here to force Angular $apply when attribute changes...
          for (var _attr in attrMap) {
            scope.$watch(
              function() {return element.attr(_attr);},
              function(value) {
                scope[attrMap[_attr]] = value;
              }
            );
          }
        });
    }
  };
});
And that works just fine. It is definitely nice starting from a specific, working case.

All that is left from there is to extract this directive out into a separate Bower repository.


Day #1,012

No comments:

Post a Comment