Fork me on GitHub

ng-polymer-elements

AngularJS two-way binding for Polymer Web Components

Out of the box support for Iron, Paper, Gold and Google elements

View on GitHub Download (0.3.0)

Why do I need it?

AngularJS and Web Components both provide binding mechanisms, and for some cases you can bind the scope properties to Web Components attributes. For example, the following code works fine:

$scope.buttonLabel = 'Click me';

$scope.buttonAction = function() {
   alert('Button clicked');
};
<paper-button ng-click="buttonAction()" raised>
  {{buttonLabel}}
</paper-button>
{{ctrl.buttonLabel}}

There are two cases where you can't get Web Components to work with your AngularJS models:

Enters ng-polymer-elements.


What does it do?

ng-polymer-elements creates directives for existing custom elements, allowing you to use them as you would any directive. The following example shows how you can use iron-input and paper-input with a model as if they were "plain" input:

<input type="text" ng-model="text"/>
<input is="iron-input" ng-model="text"/>
<paper-input ng-model="text""></paper-input>

How do I install it?

Either install it with bower using bower install ng-polymer-elements or download the release.

Add ng-polymer-elements to your module's dependencies.

ng-polymer-elements uses Object.observe() with properties of type Object and Array. To support this on browsers that don't support Object.observe(), add a polyfill.

The generated directives may be applied only after the custom elements have been registered. This means that in browsers that don't support custom elements and rely on the polyfill, the generated directives can be used only after the WebComponentsReady event has been fired. If the initial view of the Angular application contains these directives, you may want to consider manual bootstrap like so:

window.addEventListener('WebComponentsReady ', function() {
  angular.bootstrap(wrap(document), ['ng-polymer-elements-example']);
});

Show me the magic!

Out of the box ng-polymer-elements supports the following Polymer elements:

Custom Element Custom Element Property/Event Mapped Directive Attribute
paper-input
paper-textarea
gold-email-input
gold-phone-input
gold-cc-expiration-input
gold-cc-cvc-input
gold-zip-input
value ng-model
disabled ng-disabled
focused ng-focused
iron-selector
paper-menu
if "multi" - selectedValues , otherwise - selected ng-model
paper-radio-group
paper-tabs
selected ng-model
paper-checkbox
paper-toggle-button
checked ng-model
disabled ng-disabled
iron-change ng-change
paper-slider value ng-model
value-change ng-change
disabled ng-disabled
paper-dialog opened ng-opened
iron-overlay-opened ng-overlay-opened
iron-overlay-closed ng-overlay-closed
gold-cc-input value ng-model
cardType ng-card-type
disabled ng-disabled
focused ng-focused
google-feeds results ng-model
loading ng-loading
google-feeds-error ng-error
google-feeds-queryerror ng-query-error
google-feeds-queryresponse ng-query-response
google-feeds-response ng-response
google-multi-feeds-response ng-multi-response
google-map latitude ng-latitude
longitude ng-longitude
map ng-map
google-sheets rows ng-rows
sheet ng-sheet
tab ng-tab

The following example shows the binding of a paper-radio-group selected value:

<paper-radio-group ng-model="radio">
   <paper-radio-button name="one" label="One"></paper-radio-button>
   <paper-radio-button name="two" label="Two"></paper-radio-button>
   <paper-radio-button name="three" label="Three"></paper-radio-button>
</paper-radio-group>
<span>Selected: {{radio}}</span>
Selected: {{radio}}

The following example shows the binding of iron-input, paper-tabs, paper-slider, paper-menu and iron-selector to a numeric value:

<input type="number" is="iron-input" ng-model="number" min="0" max="5"/>

<paper-slider ng-model="number" max="5"></paper-slider>

<paper-tabs ng-model="number">
   <paper-tab>0</paper-tab>
   <paper-tab>1</paper-tab>
   <paper-tab>2</paper-tab>
   <paper-tab>3</paper-tab>
   <paper-tab>4</paper-tab>
   <paper-tab>5</paper-tab>
</paper-tabs>

<paper-menu ng-model="number">
   <paper-item>Zero</paper-item>
   <paper-item>One</paper-item>
   <paper-item>Two</paper-item>
   <paper-item>Three</paper-item>
   <paper-item>Four</paper-item>
   <paper-item>Five</paper-item>
</paper-menu>

<iron-selector ng-model="number">
  <div>Zero</div>
  <div>One</div>
  <div>Two</div>
  <div>Three</div>
  <div>Four</div>
  <div>Five</div>
</iron-selector>
0 1 2 3 4 5 Zero One Two Three Four Five
Zero
One
Two
Three
Four
Five

The following example shows the binding of paper-checkbox and paper-toggle-button to a boolean value:

<paper-checkbox ng-model="boolean" ng-change="onChange()">
</paper-checkbox>

<paper-toggle-button ng-model="boolean" ng-change="onChange()">
</paper-toggle-button>

Boolean values can be used to toggle paper-dialog on and off. The two way binding means that hiding the dialog will also set the bound value to false.

<paper-button ng-click="opened=true">Dialog</paper-button>
<span>Dialog opened: {{opened}}</span>
<paper-dialog ng-opened="dialog
  ng-overlay-opened="onOpened()" ng-overlay-closed="onClosed()">
   <!-- dialog content -->
</paper-dialog>
Dialog Dialog opened: {{ctrl.opened}}

Some content goes here


When iron-selector and paper-menu are set with the multi attribute, ng-model is bound to the array of selected values:

<iron-selector ng-model="values" multi attr-for-selected="name">
  <div name="one">One</div>
  <div name="two">Two</div>
  <div name="three">Three</div>
  <div name="four">Four</div>
  <div name="five">Five</div>
</iron-selector>

<paper-menu ng-model="values" multi attr-for-selected="name">
  <paper-item name="one">One</paper-item>
  <paper-item name="two">Two</paper-item>
  <paper-item name="three">Three</paper-item>
  <paper-item name="four">Four</paper-item>
  <paper-item name="five">Five</paper-item>
</paper-menu>
One
Two
Three
Four
Five
One Two Three Four Five
Values: {{values}}

Gold elements work similarly to paper-input, and gold-cc-input also exposes the read-only ng-card-type.

<gold-email-input ng-model="email"></gold-email-input>
<gold-phone-input ng-model="phone"></gold-phone-input>
<gold-cc-input ng-model="cc" ng-card-type="ct"></gold-cc-input>
<gold-cc-expiration-input ng-model="exp"></gold-cc-expiration-input>
<gold-cc-cvc-input ng-model="cvc" card-type="{{ct}}"></gold-cc-cvc-input>
<gold-zip-input ng-model="zip"></gold-zip-input>
{{gold}}

In google-feeds , ng-model gets populated with the feed results.

<google-feeds feed="http://www.engadget.com/rss-full.xml"
  ng-model="result"></google-feeds>

<pre>{{result|json}}</pre>
{{result|json}}

Google Map element supports binding of the center latitude and longitude, and the Maps API object.

<google-map ng-latitude="lat" ng-longitude="long"></google-map>
<input type="number" ng-model="lat"/>
<input type="number" ng-model="long"/>
{{ctrl.map}}

google-sheet

<google-sheets key="0Anye-JMjUkZZdDBkMVluMEhZMmFGeHpYdDJJV1FBRWc"
  published ng-rows="rows" ng-sheet="sheet" ng-tab="tab">
</google-sheets>

<pre>{{rows|json}}</pre>
<pre>{{sheet|json}}</pre>
<pre>{{tab|json}}</pre>
Rows
{{rows|json}}
Sheet
{{sheet|json}}
Tab
{{tab|json}}

How do I use it with other custom elements?

ng-polymer-elements uses a simple structure to map directive attributes to custom element properties. You can extend the mapping by setting the $ngPolymerMappings constant in the ng-polymer-elements module with your mappings.

The mapping structure is an object where the keys are the directive names, and their values are objects that contain a key for each directive attribute you want to map. The values for the directives represent the custom element property or event, and can be constant or defined in runtime for each instance of the directive.

For constant mapping, use a string prefixed with "=" for property or "&" for event, and the property/event name.

For dynamic mapping, use a function named "property" or "event". The function received the element as a single argument during the directive linking and returns a property/event name as string (without "="/"&" prefix).

angular.module('ng-polymer-elements').constant('$ngPolymerMappings', {
  myElement: {
    ngModel: function property(element) {
      return element.hasAttribute('multi') ? 'selectedValues' : 'selected';
    },
    ngChange: '&iron-change'
  }
});

In this example we add support for my-element and map the ng-model attribute in runtime to either selectedValue or selected property, depending on the existence of the multi attribute, and we map the ng-change attribute to the iron-change event.

When mapping events, the original event will be available to AngularJS handlers as the $event parameter.

See allMappings in ng-polymer-elements.js for default mappings.