I really hoped to finish writing the SVG chapter in Patterns in Polymer last night. As it turns out, AngularJS had other ideas. Like not working at all like I expected.
Last night's question was how can I bind an AngularJS application so that it has access to data inside a Polymer? I have already done this in the Dart versions of both libraries, so I thought this would be an easy question to answer in the JavaScript equivalents. Thanks to angular_node_bind in Dart, I could use square brackets to establish two-way binding between Polymer and Angular:
<h3>Oooh! Let's make beautiful pizza together</h3> <pre>{{ pizza }}</pre> <p> <x-pizza pizzaState="[[ pizza ]]"></x-pizza> </p>That did not work in JavaScript. I note that there was a long discussion on the subject some time ago—and the syntax for Dart's angular_node_bind seems to have been at least partially inspired from that post. But, if there a JavaScript equivalent out there, my Google-fu fails me.
I next tried to write my own directive to accomplish this. Even if it is not a generalized solution (and I still suspect there is already one out there somewhere), this should not be too difficult, right? I write the JavaScript version as:
<h3>Oooh! Let's make beautiful pizza together</h3> <pre ng-bind="state"></pre> <p> <x-pizza polymer-directive state="state"></x-pizza> </p>The
state
variable in both cases can be two-way bound if that polymer-directive
scopes the attribute with =
:var pizzaStoreApp = angular.module('pizzaStoreApp', [ /* Angular modules here... */ ]). directive('polymerDirective', function($interval) { return { restrict: 'A', scope: { state: '=' }, // ... }; });That should make an attribute directive (“A”) that binds the value of
state
in the local scope with the value of the same name in the parent scope. From there, I can establish a simple watch on the state
attribute from the <x-pizza>
Polymer:directive('polymerDirective', function($interval) { return { restrict: 'A', scope: { state: '=' }, link : function(scope, element, attrs) { scope.$watch( function() { // The value being watched.. return element.attr('state'); }, function(value) { // What to do when the value changes ... scope.state = value; } ); } }; });But darn it, that does not work. The
<x-pizza>
Polymer is correctly updating the value of the state
attribute, but Angular is not acknowledging the change:It turns out that Angular is not calling the $digest event loop, and so the $watch is never being evaluated. If I add an
<input ng-model>
—even one that is not tied to a model used anywhere else—then typing in that input field will invoke $digest, which, in turn, calls my $watch:So my ultimate solution is to set an interval in my digest. By default, $interval will evaluate the digest loop, so the interval function does not need to do anything:
directive('polymerDirective', function($interval) { return { restrict: 'A', scope: { state: '=' }, link : function(scope, element, attrs) { scope.$watch( function() { return element.attr('state'); }, function(value) { scope.state = value; } ); $interval(function(){},500); } }; });And that seems to do the trick:
But is that the best way to do this?
Day #1,008
Don't know if you've thought of it, but another way Polymer and Angular can talk to each other is through events. You probably have though, and if it's like D-Bus you want to reduce your use of events otherwise you get a sluggish app.
ReplyDeleteI thought about that, and I'm pretty sure it would work, but... it just doesn't feel like the Angular way. I suppose that doesn't make that much of a difference for a book named Patterns in Polymer, but still :-\
DeleteYeah I can understand that. Also, after posting my last comment, I actually realised, in Dart at least, I think Observable uses events anyway.
Delete