Full Code of vaadin/angular2-polymer for AI

master fd8c2d9a9ba6 cached
34 files
143.0 KB
33.7k tokens
59 symbols
1 requests
Download .txt
Repository: vaadin/angular2-polymer
Branch: master
Commit: fd8c2d9a9ba6
Files: 34
Total size: 143.0 KB

Directory structure:
gitextract_9htu4niu/

├── .gitignore
├── .npmignore
├── LICENSE
├── README.md
├── bower.json
├── circle.yml
├── docs/
│   ├── api.adoc
│   ├── best-practices.adoc
│   ├── ng-cli-webpack.adoc
│   ├── overview.adoc
│   ├── tutorial/
│   │   ├── creating-project.adoc
│   │   ├── dependencies.adoc
│   │   ├── hero-editor.adoc
│   │   ├── introduction.adoc
│   │   ├── layout.adoc
│   │   ├── list-heroes.adoc
│   │   ├── polymer.adoc
│   │   └── wrap-up.adoc
│   └── tutorial-index.adoc
├── index.spec.ts
├── index.ts
├── karma.conf.js
├── package.json
├── src/
│   ├── polymer-element.spec.ts
│   ├── polymer-element.ts
│   ├── polymer-module.spec.ts
│   ├── polymer-module.ts
│   ├── renderer/
│   │   ├── polymer-renderer.spec.ts
│   │   ├── polymer-renderer.ts
│   │   └── shared-custom-styles-host.ts
│   └── test-element.html
├── system.config.js
├── tsconfig.json
└── tslint.json

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
*.js
!karma.conf.js
!system.config.js
*.d.ts

# Created by https://www.gitignore.io/api/node,bower,typings

### Node ###
# Logs
logs
*.log
npm-debug.log*

# Runtime data
pids
*.pid
*.seed

# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov

# Coverage directory used by tools like istanbul
coverage

# nyc test coverage
.nyc_output

# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt

# node-waf configuration
.lock-wscript

# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release

# Dependency directories
node_modules
jspm_packages

# Optional npm cache directory
.npm

# Optional REPL history
.node_repl_history


### Bower ###
bower_components
.bower-cache
.bower-registry
.bower-tmp

### IntelliJ ###
.idea
angular-polymer.iml


================================================
FILE: .npmignore
================================================
# Source files
bower.json
tsconfig.json
tslint.json
typings/
typings.json
*.ts
!*.d.ts

# Tests and testing setup
*.html
*.spec.js
*.spec.ts
*.spec.d.ts
system.config.js
karma.conf.js
circle.yml

# Docs
docs/

# Created by https://www.gitignore.io/api/node,bower,typings

### Node ###
# Logs
logs
*.log
npm-debug.log*

# Runtime data
pids
*.pid
*.seed

# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov

# Coverage directory used by tools like istanbul
coverage

# nyc test coverage
.nyc_output

# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt

# node-waf configuration
.lock-wscript

# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release

# Dependency directories
node_modules
jspm_packages

# Optional npm cache directory
.npm

# Optional REPL history
.node_repl_history


### Bower ###
bower_components
.bower-cache
.bower-registry
.bower-tmp


### Typings ###
## Ignore downloaded typings
typings


================================================
FILE: LICENSE
================================================
                                 Apache License
                           Version 2.0, January 2004
                        http://www.apache.org/licenses/

   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

   1. Definitions.

      "License" shall mean the terms and conditions for use, reproduction,
      and distribution as defined by Sections 1 through 9 of this document.

      "Licensor" shall mean the copyright owner or entity authorized by
      the copyright owner that is granting the License.

      "Legal Entity" shall mean the union of the acting entity and all
      other entities that control, are controlled by, or are under common
      control with that entity. For the purposes of this definition,
      "control" means (i) the power, direct or indirect, to cause the
      direction or management of such entity, whether by contract or
      otherwise, or (ii) ownership of fifty percent (50%) or more of the
      outstanding shares, or (iii) beneficial ownership of such entity.

      "You" (or "Your") shall mean an individual or Legal Entity
      exercising permissions granted by this License.

      "Source" form shall mean the preferred form for making modifications,
      including but not limited to software source code, documentation
      source, and configuration files.

      "Object" form shall mean any form resulting from mechanical
      transformation or translation of a Source form, including but
      not limited to compiled object code, generated documentation,
      and conversions to other media types.

      "Work" shall mean the work of authorship, whether in Source or
      Object form, made available under the License, as indicated by a
      copyright notice that is included in or attached to the work
      (an example is provided in the Appendix below).

      "Derivative Works" shall mean any work, whether in Source or Object
      form, that is based on (or derived from) the Work and for which the
      editorial revisions, annotations, elaborations, or other modifications
      represent, as a whole, an original work of authorship. For the purposes
      of this License, Derivative Works shall not include works that remain
      separable from, or merely link (or bind by name) to the interfaces of,
      the Work and Derivative Works thereof.

      "Contribution" shall mean any work of authorship, including
      the original version of the Work and any modifications or additions
      to that Work or Derivative Works thereof, that is intentionally
      submitted to Licensor for inclusion in the Work by the copyright owner
      or by an individual or Legal Entity authorized to submit on behalf of
      the copyright owner. For the purposes of this definition, "submitted"
      means any form of electronic, verbal, or written communication sent
      to the Licensor or its representatives, including but not limited to
      communication on electronic mailing lists, source code control systems,
      and issue tracking systems that are managed by, or on behalf of, the
      Licensor for the purpose of discussing and improving the Work, but
      excluding communication that is conspicuously marked or otherwise
      designated in writing by the copyright owner as "Not a Contribution."

      "Contributor" shall mean Licensor and any individual or Legal Entity
      on behalf of whom a Contribution has been received by Licensor and
      subsequently incorporated within the Work.

   2. Grant of Copyright License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      copyright license to reproduce, prepare Derivative Works of,
      publicly display, publicly perform, sublicense, and distribute the
      Work and such Derivative Works in Source or Object form.

   3. Grant of Patent License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      (except as stated in this section) patent license to make, have made,
      use, offer to sell, sell, import, and otherwise transfer the Work,
      where such license applies only to those patent claims licensable
      by such Contributor that are necessarily infringed by their
      Contribution(s) alone or by combination of their Contribution(s)
      with the Work to which such Contribution(s) was submitted. If You
      institute patent litigation against any entity (including a
      cross-claim or counterclaim in a lawsuit) alleging that the Work
      or a Contribution incorporated within the Work constitutes direct
      or contributory patent infringement, then any patent licenses
      granted to You under this License for that Work shall terminate
      as of the date such litigation is filed.

   4. Redistribution. You may reproduce and distribute copies of the
      Work or Derivative Works thereof in any medium, with or without
      modifications, and in Source or Object form, provided that You
      meet the following conditions:

      (a) You must give any other recipients of the Work or
          Derivative Works a copy of this License; and

      (b) You must cause any modified files to carry prominent notices
          stating that You changed the files; and

      (c) You must retain, in the Source form of any Derivative Works
          that You distribute, all copyright, patent, trademark, and
          attribution notices from the Source form of the Work,
          excluding those notices that do not pertain to any part of
          the Derivative Works; and

      (d) If the Work includes a "NOTICE" text file as part of its
          distribution, then any Derivative Works that You distribute must
          include a readable copy of the attribution notices contained
          within such NOTICE file, excluding those notices that do not
          pertain to any part of the Derivative Works, in at least one
          of the following places: within a NOTICE text file distributed
          as part of the Derivative Works; within the Source form or
          documentation, if provided along with the Derivative Works; or,
          within a display generated by the Derivative Works, if and
          wherever such third-party notices normally appear. The contents
          of the NOTICE file are for informational purposes only and
          do not modify the License. You may add Your own attribution
          notices within Derivative Works that You distribute, alongside
          or as an addendum to the NOTICE text from the Work, provided
          that such additional attribution notices cannot be construed
          as modifying the License.

      You may add Your own copyright statement to Your modifications and
      may provide additional or different license terms and conditions
      for use, reproduction, or distribution of Your modifications, or
      for any such Derivative Works as a whole, provided Your use,
      reproduction, and distribution of the Work otherwise complies with
      the conditions stated in this License.

   5. Submission of Contributions. Unless You explicitly state otherwise,
      any Contribution intentionally submitted for inclusion in the Work
      by You to the Licensor shall be under the terms and conditions of
      this License, without any additional terms or conditions.
      Notwithstanding the above, nothing herein shall supersede or modify
      the terms of any separate license agreement you may have executed
      with Licensor regarding such Contributions.

   6. Trademarks. This License does not grant permission to use the trade
      names, trademarks, service marks, or product names of the Licensor,
      except as required for reasonable and customary use in describing the
      origin of the Work and reproducing the content of the NOTICE file.

   7. Disclaimer of Warranty. Unless required by applicable law or
      agreed to in writing, Licensor provides the Work (and each
      Contributor provides its Contributions) on an "AS IS" BASIS,
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
      implied, including, without limitation, any warranties or conditions
      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
      PARTICULAR PURPOSE. You are solely responsible for determining the
      appropriateness of using or redistributing the Work and assume any
      risks associated with Your exercise of permissions under this License.

   8. Limitation of Liability. In no event and under no legal theory,
      whether in tort (including negligence), contract, or otherwise,
      unless required by applicable law (such as deliberate and grossly
      negligent acts) or agreed to in writing, shall any Contributor be
      liable to You for damages, including any direct, indirect, special,
      incidental, or consequential damages of any character arising as a
      result of this License or out of the use or inability to use the
      Work (including but not limited to damages for loss of goodwill,
      work stoppage, computer failure or malfunction, or any and all
      other commercial damages or losses), even if such Contributor
      has been advised of the possibility of such damages.

   9. Accepting Warranty or Additional Liability. While redistributing
      the Work or Derivative Works thereof, You may choose to offer,
      and charge a fee for, acceptance of support, warranty, indemnity,
      or other liability obligations and/or rights consistent with this
      License. However, in accepting such obligations, You may act only
      on Your own behalf and on Your sole responsibility, not on behalf
      of any other Contributor, and only if You agree to indemnify,
      defend, and hold each Contributor harmless for any liability
      incurred by, or claims asserted against, such Contributor by reason
      of your accepting any such warranty or additional liability.

   END OF TERMS AND CONDITIONS

   Copyright 2016 Vaadin Ltd.

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.


================================================
FILE: README.md
================================================
[![CircleCI](https://img.shields.io/circleci/project/github/platosha/angular-polymer.svg)](https://circleci.com/gh/platosha/angular-polymer) [![Version](https://img.shields.io/npm/v/angular-polymer.svg)](https://www.npmjs.com/package/angular-polymer)

# Angular-Polymer

`angular-polymer` is a directive factory that aims at bridging the gaps between using [Polymer](https://www.polymer-project.org) based Web Components in [Angular](https://angular.io/) applications.

> Note: Currently Angular-Polymer only works with Angular 2.x, or Angular-CLI 1.0.0-rc.2 and lower.
> Work is being done to upgrade the library to work the latest Angular & CLI. [Want to help Contribute?](https://github.com/platosha/angular-polymer/issues/123)

**In case you are using Angular 4+ and Polymer 2+** you might want to check out https://github.com/hotforfeature/origami

---

```typescript
import { NgModule, CUSTOM_ELEMENTS_SCHEMA, Component } from '@angular/core';
import { PolymerModule, PolymerElement } from '@vaadin/angular2-polymer';

@NgModule({
  imports: [ PolymerModule ],
  declarations: [
    AppComponent,
    PolymerElement('paper-input'),
    PolymerElement('vaadin-combo-box')
  ],
  bootstrap: [ AppComponent ],
  schemas: [ CUSTOM_ELEMENTS_SCHEMA ]
})
export class AppModule { }

@Component({
  selector: 'app-component',
  template: `
    <paper-input [(value)]="myValue"></paper-input>
    <vaadin-combo-box [(value)]="myValue" [items]="myItems"></vaadin-combo-box>
  `
})
class AppComponent {
  myValue = 'A';
  myItems = ['A', 'B', 'C'];
}
```

## Getting started

See the overview for a [quick start](https://github.com/platosha/angular-polymer/blob/master/docs/overview.adoc#quick-start).

See the [tutorial](https://github.com/platosha/angular-polymer/blob/master/docs/tutorial-index.adoc) for complete instructions on how to use `angular-polymer` and how to build a working application with Angular data binding and routes.

If you are using [Webpack](https://webpack.github.io/) in your project, see the specific [document](https://github.com/platosha/angular-polymer/blob/master/docs/ng-cli-webpack.adoc) on how to build angular-polymer apps with webpack.

## Demo app

The Expense Manager demo is an example of a real world application built using Angular and Polymer web components.

- [Live demo](http://demo.vaadin.com/expense-manager-ng)
- [Source code](https://github.com/vaadin/expense-manager-ng2-demo)

## Where to get Polymer web components

For high quality Polymer web components, see the [Webcomponents Element Catalog](https://www.webcomponents.org/) and [Vaadin Elements](https://vaadin.com/elements).

## Development

Familiarize yourself with the code and try to follow the same syntax conventions to make it easier for us to accept your pull requests.

Discuss / exchange ideas and ask questions here:
https://polymer.slack.com/messages/polymer-angular/

### Getting the Code

1. Clone the angular-polymer project:

  ```shell
  $ git clone https://github.com/platosha/angular-polymer.git
  $ cd angular-polymer
  ```

2. Install dependencies. We assume that you have already installed `npm` in your system.

  ```shell
  $ npm install
  ```

### Running Tests

For running the tests you need [Bower](http://bower.io) installed.

Then, you can download all bower dependencies needed by the Tests.

  ```shell
  $ bower install
  ```

Finally, you can run the tests by typing:

  ```shell
  $ npm test
  ```

Optionally, you can watch for the source changes and keep the tests running automatically:

  ```shell
  $ npm run test:w
  ```

## License

Apache License 2.0


================================================
FILE: bower.json
================================================
{
  "name": "angular2-polymer",
  "description": "Angular 2 support for Polymer elements",
  "main": "index.js",
  "authors": [
    "Vaadin Ltd"
  ],
  "license": "Apache 2.0",
  "homepage": "https://github.com/vaadin/angular2-polymer",
  "moduleType": [],
  "private": false,
  "ignore": [
    "**/.*",
    "node_modules",
    "bower_components",
    "test",
    "tests"
  ],
  "devDependencies": {
    "polymer": "Polymer/polymer#^1.4.0",
    "iron-form-element-behavior": "PolymerElements/iron-form-element-behavior#^1.0.6",
    "paper-checkbox": "PolymerElements/paper-checkbox#^1.3.0"
  }
}


================================================
FILE: circle.yml
================================================
machine:
  node:
    version: 6.9.1

dependencies:
  override:
    - npm install
    - npm run bower install
  cache_directories:
    - bower_components

compile:
  override:
    - npm run lint


================================================
FILE: docs/api.adoc
================================================
---
title: API Reference
order: 4
layout: page
---

[[vaadin-angular2-polymer.api]]
= Angular2-Polymer API

This package consists of one generic Angular 2 directive factory method named [classname]#PolymerElement#, which is able to handle any Polymer element for easy integrating on Angular apps.

== Features

The [classname]#PolymerElement# is composed of a set of low level directives which would be added to the module dynamically.

`changeEventsAdapterDirective`::
This directive configures all the the public [propertyname]#properties#, that notify for their value change in a Polymer element, to be an Angular 2 link:https://angular.io/docs/js/latest/api/core/DirectiveMetadata-class.html+++#+++!+++#+++outputs-anchor[output].
So as any change made in a property in the polymer side, will be propagated to the Angular 2 parent component.

`notifyForDiffersDirective`::
Configures all the public [propertyname]#properties# in a Polymer element to be an Angular 2 link:https://angular.io/docs/js/latest/api/core/DirectiveMetadata-class.html+++#+++!+++#+++inputs-anchor[input].
It allows to notify changes from the Angular side to Polymer. It supports primitive types, as well as both Array and Object properties, but not its children.
If you want to notify nested object changes, you can still do it by calling the Polymer's [methodname]#notifyPath# method on the element.

`formElementDirective`::
If the element has the Polymer Form Element Behavior, this directive is added to the module, and it configures the element to be compatible with the Angular 2 `ngForm`.

`validationDirective`::
It is another directive added in the case of the element has the Polymer Form Element Behavior. It sets the Polymer [propertyname]#invalid# flag based on the Angular 2 `ngControl` state.

`reloadConfigurationDirective`::
This directive is added to those Polymer elements that need to run a configuration function after they are initialized.
It is added if the element has the [methodname]#isInitialized# and the [methodname]#reloadConfiguration# methods in its prototype.

== Usage

In order to use the [classname]#PolymerElement# in the [propertyname]#declarations# block of your [classname]#NgModule#, you have to import it as is shown in the example below.

The only public API is the [classname]#PolymerElement(tagName: string): Array<Directive># factory method.
The argument [propertyname]#tagName# is mandatory, and must match the tag name of the Polymer element.
The factory returns the set of necessary directives for the given Polymer element.
Hence, you have to add the [classname]#PolymerElement# directive as many times as different Polymer elements you are using in your Angular module.

[source,typescript]
----
import { Component, NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { PolymerElement } from '@vaadin/angular2-polymer';

@Component({
  selector: 'app-root',
  template: `
    <iron-icon icon="attachment"></iron-icon>
    <paper-input [(value)]="myValue"></paper-input>
    <vaadin-combo-box [(value)]="myValue" [items]="myItems"></vaadin-combo-box>
  `
})
class AppComponent {
   myValue = 'A';
   myItems = ['A', 'B', 'C'];
}

@NgModule({
  imports: [ BrowserModule ],
  declarations: [
    AppComponent,
    PolymerElement('iron-icon'),
    PolymerElement('paper-input'),
    PolymerElement('vaadin-combo-box')
  ],
  bootstrap: [ AppComponent ],
  schemas: [ CUSTOM_ELEMENTS_SCHEMA ]
})
export class AppModule { }
----


================================================
FILE: docs/best-practices.adoc
================================================
---
title: Best practices
order: 1
layout: page
---

[[angular-polymer.best-practices]]
= Angular Polymer best practices

This document describes various best practices when using the `angular-polymer` library.

== Lists (with `iron-list`)
Angular has it's own mechanism for dealing with `<template>` elements. That is why something like this:
``` html
<iron-list [items]="data" as="item">
  <template>
    <div>
      Name: {{item.value}}
    </div>
  </template>
</iron-list>
```
Is *not* possible. Here is some more information: https://github.com/PolymerElements/iron-list/issues/291#issuecomment-251354454

=== Best practice
Best practice at the moment is to wrap `<iron-list>` with its template in another Polymer element, that is registered outside Angular templates. For example, create my-list.html:

``` html
<link rel="import" href="bower_components/polymer/polymer.html">
<link rel="import" href="bower_components/iron-list/iron-list.html">

<dom-module id="my-list">
  <template>
    <iron-list items="[[items]]" as="item">
      <template>
        <div>
          Name: [[item.name]]
        </div>
      </template>
    </iron-list>
  </template>

  <script>
    Polymer({
      is: "my-list",
      properties: {
        items: Array
      }
    });
  </script>
</dom-module>
```

Then import it in index.html, just like any other Polymer element:

``` html
...
  <head>
    ...
    <link rel="import" href="my-list.html">
  </head>
...
```

Then you can use the `<my-list>` element in declarations (`PolymerElement('my-list')`), as well as in Angular templates. The `<iron-list>` template was already defined in `<my-list>`, therefore we don't have it in Angular templates:

``` html
<my-list [items]="data"></my-list>
```

It requires a bit of work, though: you have to manually bypass all the properties and methods you use in `<iron-list>` through `<my-list>`. In the example above, only the `items` property is bypassed.

== Forms
As `PolymerElement('x-element')` looks for elements named `<x-element>` it doesn't work with Polymer elements that have been extended from native elements like `<form is="iron-form">`.
For the future, extending native elements will be [dropped in Polymer 2.0](https://github.com/Polymer/polymer/blob/2.0-preview/README.md#v1-custom-elements) so there will be an actual `<iron-form>` element released at some point.

=== Best practice

As a solution in this case you'll have to use Angular form component instead. Another alternative is creating a custom Polymer element that wraps the `<form is="iron-form">`. Same concept like for `iron-lists` in this document.

Here is an approach on how to solve the problem with an angular form:
```
<form [formGroup]="registerForm" (ngSubmit)="onSubmit()">
    <div class="form-group">
        <paper-input
                label="Username"
                type="text"
                id="username"
                class="form-control"
                formControlName="username"
                name="username"
                auto-validate [(errorMessage)]="usernameErrorMessage"></paper-input>
    </div>
    <div class="form-group">
        <paper-input
                label="E-Mail"
                type="email"
                id="email"
                class="form-control"
                formControlName="email"
                name="email"
                autocomplete="on"
                required auto-validate [(errorMessage)]="emailErrorMessage"></paper-input>
    </div>

    <paper-button
            type="submit" raised
            [disabled]="!registerForm.valid"
            (click)="onSubmit()">
        Register User
    </paper-button>
</form>
```

And the typescript file:
```
import {Component, OnInit} from '@angular/core';
import {FormGroup, FormControl, Validators} from '@angular/forms';

@Component({
    selector: 'register',
    templateUrl: './register.component.html'
})
export class RegisterComponent implements OnInit {
    private registerForm: FormGroup;

    private usernameErrorMessage: string;
    private emailErrorMessage: string;

    constructor() {
            this.usernameErrorMessage = '';
            this.emailErrorMessage = '';
    }

    ngOnInit() {
        this.registerForm = new FormGroup({
            username: new FormControl(null, [Validators.required, Validators.minLength(2)]),
            email: new FormControl(null, [
                Validators.required,
                Validators.pattern('[a-z0-9!#$%&\'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&\'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?')
            ])
        });
    }

    onSubmit() {
        console.log(this.registerForm.value.username);
        console.log(this.registerForm.value.email);
    }
}
```


================================================
FILE: docs/ng-cli-webpack.adoc
================================================
---
title: Using in Angular CLI Webpack
order: 3
layout: page
---

:linkattrs:
[[vaadin-angular2-polymer.ng2cli]]
= Using Polymer Elements in Angular CLI Webpack Applications

[[vaadin-angular2-polymer.ng2cliwebpack.introduction]]
== Introduction

https://github.com/angular/angular-cli[Angular CLI] is a command line tool for Angular 2. It is not only a scaffolding tool for creating and modifying a project, but also for other actions like building, running, debugging, and testing the project.

In this document we will describe all the modifications you have to make to your Angular CLI project to use Polymer elements.

[[vaadin-angular2-polymer.ng2cliwebpack.preparation]]
== Preparation

We assume that you already have installed in your system the Angular CLI package and the `ng` command
is available in your PATH. Otherwise run the following command:

[subs="normal"]
----
[prompt]#$# [command]#npm# install -g @angular/cli
----

Then, create a new Angular 2 project.

[subs="normal"]
----
[prompt]#$# [command]#ng# new [replaceable]#my-project#
----

Check that everything works by compiling, testing, and running your new project:

[subs="normal"]
----
[prompt]#$# [command]#cd# [replaceable]#my-project#
[prompt]#$# [command]#ng# build
[prompt]#$# [command]#ng# test
[prompt]#$# [command]#ng# serve
----

When the development server is running you can test the application at http://localhost:4200[http://localhost:4200, role="external", window="_blank"].
Press kbd:[Ctrl+C] to stop the server.

[[vaadin-angular2-polymer.ng2cliwebpack.dependencies]]
== Adding Polymer Elements Dependencies

Polymer uses the http://bower.io/[Bower] package manager. Hence, you first  have to install and initialize Bower before continuing:

[subs="normal"]
----
[prompt]#$# [command]#npm# install bower -g
[prompt]#$# [command]#bower# init
----

By default, Bower installs dependencies to the [filename]#bower_components# folder. But Angular CLI expects static stuff to be in the [filename]#src/assets# directory.
Thus, create the [filename]#.bowerrc# file in the root directory, with the following content:

[source,json]
.&#46;bowerrc
----
{
  "directory" : "src/assets/bower_components"
}
----

Now, you can install all the Polymer elements that you need in your application.

For instance, to install all the elements in the https://elements.polymer-project.org/browse?package=paper-elements[Polymer Paper] collection,
and the [elementname]#https://vaadin.com/elements/-/element/vaadin-combo-box[vaadin-combo-box]# element, run the following:

[subs="normal"]
----
[prompt]#$# [command]#bower# install --save [replaceable]#paper-elements vaadin-combo-box#
----

[TIP]
====
Add the following line to the [filename]#.gitignore# file to prevent the Bower dependencies from being tracked by Git in future:

[source]
----
src/assets/bower_components
----
====

Next we will modify the application index HTML file to load the web components polyfill, and to import the Polymer elements.
Open the [filename]#src/index.html# file and append the following lines to the [elementname]#head# section:

[source,html]
.src/index.html head additions
----
<head>
  ...

  <script src="assets/bower_components/webcomponentsjs/webcomponents.min.js"></script>
  <script>
    window.Polymer = {
      dom: 'shadow'
    };
  </script>
  <link rel="import" href="assets/bower_components/paper-styles/color.html">
  <link rel="import" href="assets/bower_components/paper-styles/typography.html">
  <link rel="import" href="assets/bower_components/vaadin-combo-box/vaadin-combo-box.html">
  <link rel="import" href="assets/bower_components/paper-input/paper-input.html">
</head>
----

In Angular CLI Webpack projects, the main application file is automatically bundled and appended to the end of the [elementname]#body# section of the [filename]#index.html# file.
It means that the Angular application is imported and bootstrapped synchronously.
Meanwhile, Polymer elements are loaded from HTML Imports processed asynchronously in browsers that do not have a native support.

We have to wait for the Polymer elements to be loaded and registered before running the application code.
Therefore, we have to postpone the Angular application import until the [eventname]#WebComponentsReady# event is dispatched.
Create a new file [filename]#src/main-polymer.ts# with the following loader script:

[source,typescript]
.src/main-polymer.ts
----
document.addEventListener('WebComponentsReady', () => {
  require('./main.ts');
});
----

Change the main entry point of the application to load the new loader script.
Edit the [filename]#angular-cli.json# file in the project root and replace the line `"main": "main.ts",` with  `"main": "main-polymer.ts",`.

////
// TODO: `$ ng set` could be used for editing the config, but it is broken nowadays.
Replace the editing instructions above with the following paragraph after this PR is merged: https://github.com/angular/angular-cli/pull/1800

Run the following command to set the new [filename]#src/main-polymer.ts# file as the application entry point:

[subs="normal"]
----
[prompt]#$# [command]#ng# set apps.0.main main-polymer.ts
----
////

Now you can run `ng serve`, open the application in your browser, and everything should work with no errors in the console.

[TIP]
The [filename]#webcomponents.js# polyfill is not necessary for browsers that fully implement the Web Components Spec like Chrome.

[TIP]
====
Optionally, you can move the [filename]#webcomponents.min.js# from the application [filename]#index.html# to the [filename]#angular-cli.json# configuration file.

[source, json]
.angular-cli.json
----
{
  "scripts":[
    "assets/bower_components/webcomponentsjs/webcomponents.min.js"
  ]
}
----
====

[[vaadin-angular2-polymer.ng2cliwebpack.directive]]
== Adding The PolymerElement Package

For using Polymer elements in the Angular 2 application, we need to import the [classname]#PolymerElement#
directive from https://github.com/vaadin/angular2-polymer[@vaadin/angular2-polymer]. Thus we need to install the dependency by typing:

[subs="normal"]
----
[prompt]#$# [command]#npm# install --save @vaadin/angular2-polymer
----


[[vaadin-angular2-polymer.ng2cliwebpack.using]]
== Using Polymer Elements

Now that everything is set, we can add any Polymer elements to our application using their element names in templates,
and the [classname]#PolymerElement# directive in code.
For example, modify the [filename]#src/app/app.component.html# to have the following code:

[source,html]
.src/app/app.component.html
----
<h1>{{title}}</h1>
<vaadin-combo-box [label]="myLabel" [(value)]="myValue" [items]="myItems"></vaadin-combo-box>
<paper-input [(value)]="myValue"></paper-input>
----

In the [filename]#src/app/app.component.ts# file, define the properties bound in the template and specify the initial values:

[source,typescript]
.src/app/app.component.ts
----
import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: 'app.component.html',
  styleUrls: ['app.component.css'],
})
export class AppComponent {
  title = 'app works!';
  myLabel = 'Select a number';
  myValue = '4';
  myItems = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'];
}
----

Then import and add the [classname]#PolymerElement# directives and the [classname]#CUSTOM_ELEMENTS_SCHEMA# to the [classname]#AppModule#.
Open the [filename]#src/app/app.module.ts# file and replace the contents with the following code:

[source,typescript]
.src/app/app.module.ts
----
import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { HttpModule } from '@angular/http';
import { PolymerModule, PolymerElement } from '@vaadin/angular2-polymer';

import { AppComponent } from './app.component';

@NgModule({
  declarations: [
    AppComponent,
    PolymerElement('vaadin-combo-box'),
    PolymerElement('paper-input')
  ],
  imports: [
    PolymerModule,
    FormsModule,
    HttpModule
  ],
  providers: [],
  entryComponents: [AppComponent],
  bootstrap: [AppComponent],
  schemas: [CUSTOM_ELEMENTS_SCHEMA]
})
export class AppModule { }
----

Finally, you can use Polymer custom CSS properties and custom CSS mixins in the [filename]#app.component.css#
file for the scoped styles, and in the [filename]#index.html# file for the global ones.
In the following example we use mixins and properties defined in the Paper [elementname]#color# and [elementname]#typography# elements.

[source,html]
.src/index.html
----
<head>
  ...
  <style is="custom-style">
    body {
      @apply(--paper-font-body1);
    }
  </style>
</head>
----

[source,css]
.src/app/app.component.css
----
paper-input,
vaadin-combo-box {
  background: var(--paper-grey-200);
  padding: 8px;
}
----

[[vaadin-angular2-polymer.ng2cliwebpack.testing]]
== Testing

Angular CLI projects come with https://karma-runner.github.io[Karma] tests.

Since tests are run against the testing module defined in the [filename]#app.component.spec.ts#, instead of
the one defined in the [filename]#app.module.ts#, you need to import the [classname]#CUSTOM_ELEMENTS_SCHEMA#
in the test file.

[source,typescript]
.src/app/app.component.spec.ts
----
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
...

describe('App: NgApp', () => {
  beforeEach(() => {
    TestBed.configureTestingModule({
      declarations: [
        AppComponent,
      ],
      schemas: [CUSTOM_ELEMENTS_SCHEMA]
    });
  });
  ...
});
----

Then, you can test elements API as usual. For example:

[source,typescript]
.src/app/app.component.spec.ts
----
...

it('vaadin-combo-box and paper-input should have an initial value of 4', async(() => {
  let fixture = TestBed.createComponent(AppComponent);
  fixture.detectChanges();
  let compiled = fixture.debugElement.nativeElement;
  let combobox = compiled.querySelector('vaadin-combo-box');
  let input = compiled.querySelector('paper-input');
  expect(combobox.value).toEqual('4');
  expect(input.value).toEqual('4');
}));
----


================================================
FILE: docs/overview.adoc
================================================
---
title: Overview
order: 1
layout: page
---

[[vaadin-angular2-polymer.overview]]
= Angular 2 Integration

The `angular2-polymer` is a directive factory that aims at bridging the gaps between using link:https://www.polymer-project.org[Polymer] based Web Components in link:https://angular.io/[Angular 2] applications.

It scrapes the Polymer element API in order to define Angular 2 `outputs` and `inputs`, to apply the `ngControl` and the `ngForm` directives, and to observe DOM changes updating the element's DOM tree appropriately.

Since Vaadin Elements are built using Polymer, you need the [literal]#https://github.com/vaadin/angular2-polymer[@vaadin/angular2-polymer]# directive to enable seamless usage within Angular 2 applications.

== Quick Start

. Install the `angular2-polymer` package and the Polymer element you want to use.
+
[source,subs="normal"]
----
[prompt]#$# [command]#npm# install @vaadin/angular2-polymer
[prompt]#$# [command]#bower# install [replaceable]#element-name#
----

.  Load the Web Components polyfill for browsers that don’t have native support for the Web Components standards, and configure Polymer to use Shadow DOM. Then you can import the elements you want to use.
+
[source,html,subs="normal"]
.index.html
----
<script src="bower_components/webcomponentsjs/webcomponents.min.js"></script>
<script>
  window.Polymer = {
    dom: 'shadow'
  };
</script>
<link rel="import" href="bower_components/[replaceable]#element-name#/[replaceable]#element-name#.html">
----
+
You also need to wait for the "WebComponentsReady" event before bootstrapping the Angular 2 app. The event is fired after all of the web components have finished registering and are ready to be used.
+
[source,html,subs="normal"]
.index.html
----
<script>
  document.addEventListener('WebComponentsReady', function() {
    System.import('app').catch(function(err){ console.error(err); });
  });
</script>
----
. Import the Angular 2 directives and use them in your code.
+
[source,typescript,subs="normal"]
.app/app.module.ts
----
import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { PolymerModule, PolymerElement } from '@vaadin/angular2-polymer';
import { AppComponent } from './app.component';

@NgModule({
  imports: +++[+++ PolymerModule +++]+++,
  declarations: +++[+++
    AppComponent,
    PolymerElement('[replaceable]#element-name#')
  +++]+++,
  bootstrap: +++[+++ AppComponent +++]+++,
  schemas: +++[+++ CUSTOM_ELEMENTS_SCHEMA +++]+++
})
export class AppModule { }
----
+
[source,typescript,subs="normal"]
.app/app.component.ts
----
import { Component } from '@angular2/core';

@Component({
  selector: 'app-component',
  template: +++`<+++[replaceable]#element-name#></[replaceable]#element-name#>+++`+++
})
export class AppComponent { }
----

Note, that we also added the [classname]#CUSTOM_ELEMENTS_SCHEMA# to the [classname]#AppModule#. This is required to enable usage of non-standard properties on custom elements in the Angular component templates.

== In case you use Angular 2.1 or 2.0
You can replace the `app.module.ts` code with the following. Use the old `BrowserModule`
[source,typescript,subs="normal"]
.app/app.module.ts
----
import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { PolymerElement } from '@vaadin/angular2-polymer';
import { AppComponent } from './app.component';

@NgModule({
  imports: +++[+++ BrowserModule +++]+++,
  declarations: +++[+++
    AppComponent,
    PolymerElement('[replaceable]#element-name#')
  +++]+++,
  bootstrap: +++[+++ AppComponent +++]+++,
  schemas: +++[+++ CUSTOM_ELEMENTS_SCHEMA +++]+++
})
export class AppModule { }
----

link:https://github.com/platosha/angular-polymer/blob/master/docs/tutorial-index.adoc[See the tutorial] for complete instructions how to set up your Angular 2 application to work with Polymer elements.

================================================
FILE: docs/tutorial/creating-project.adoc
================================================
[[vaadin-angular2-polymer.tutorial.creating-project]]
== Creating Project

We begin by creating a new project based on the source code from the Angular 2 QuickStart project. You can either clone it from the GitHub repository or extract it from a ZIP package, as described next.

[NOTE]
====
If you are not yet familiar with Angular 2, please go through the https://angular.io/docs/ts/latest/quickstart.html[Angular 2 QuickStart] tutorial, which explains every step of starting a new Angular 2 application from scratch.
====

=== Starting from the QuickStart Repository

Clone the https://github.com/angular/quickstart[Angular 2 QuickStart repository] into the tutorial project folder:

[subs="normal"]
----
[prompt]#$# [command]#git# clone https://github.com/angular/quickstart [replaceable]#my-project#
[prompt]#$# [command]#cd# [replaceable]#my-project#
----

We are not going to contribute to the quickstart repository itself, so the original repository data is not needed. Remove the [filename]#.git# repository data folder.

=== Starting from the QuickStart ZIP Package

Alternatively, instead of using Git to clone the QuickStart repository, you can download and extract the https://github.com/angular/quickstart/archive/master.zip[QuickStart zip package].

=== Remove the AppComponent Tests

The QuickStart respository contains tests for the [classname]#AppComponent# class. Unfortunately, some changes that will be done to [classname]#AppComponent# in this tutorial would break the compilation of these tests.

Please remove the [filename]#app/app.component.spec.ts# file to prevent compilation errors later in this tutorial.

[NOTE]
.Testing in Angular 2
====
Testing is out of the scope of this tutorial.

See the https://angular.io/docs/ts/latest/guide/testing.html[Testing chapter] in the Angular 2 Developer Guide for more information on the topic.
====

=== Installing npm Packages and Starting the Development Server

Install npm dependencies:

[subs="normal"]
----
[prompt]#$# [command]#npm# install
----

At this point, you should be able to compile the TypeScript source code and launch the development server. You can start the server to check that everything is fine:

[subs="normal"]
----
[prompt]#$# [command]#npm# start
----

Press kbd:[Ctrl+C] to stop the development server.

[TIP]
====
See the Angular 2 https://github.com/angular/quickstart/blob/master/README.md[QuickStart README] for more information about creating a new project and other useful [command]#npm# commands.
====



================================================
FILE: docs/tutorial/dependencies.adoc
================================================
[[vaadin-angular2-polymer.tutorial.dependencies]]
== Adding and Installing Dependencies

After the previous step, we have an empty Angular 2 application source with all the Angular dependencies installed. In this step, we are going to add the Polymer library and some elements as dependencies of our application, and install them.

Vaadin Elements and other Polymer elements are mainly distributed through http://bower.io/[Bower]. We are going to use Bower to declare these dependencies and install them.

=== Adding Bower Dependencies

[IMPORTANT]
====
You should install Bower before we start using it. Use [command]#npm# to install Bower with this command:

[subs="normal"]
----
[prompt]#$# [command]#npm# install -g bower
----
====

Create the [filename]#bower.json# file in your project root with the following contents:

[source,json,subs="verbatim,quotes,macros"]
.[filename]#bower.json#
----
{
  "name": "[replaceable]#my-project#",
  "description": "",
  "main": "",
  "authors": +[+
    "[replaceable]#Your Name#"
  +]+,
  "license": "ISC",
  "homepage": "",
  "private": true,
  "ignore": [
    "**/.*",
    "node_modules",
    "bower_components",
    "test"
  ],
  "dependencies": {
    "polymer": "Polymer/polymer#^1.4.0",
    "iron-flex-layout": "PolymerElements/iron-flex-layout#^1.3.1",
    "iron-icons": "PolymerElements/iron-icons#^1.1.3",
    "app-layout": "PolymerElements/app-layout#^0.9.0",
    "paper-styles": "PolymerElements/paper-styles#^1.1.4",
    "paper-icon-button": "PolymerElements/paper-icon-button#^1.1.1",
    "paper-input": "PolymerElements/paper-input#^1.1.11",
    "vaadin-grid": "Vaadin/vaadin-grid#^1.1.0",
    "vaadin-date-picker": "Vaadin/vaadin-date-picker#^1.1.0"
  }
}
----

The [filename]#bower.json# file declares all Bower dependencies for our application. Now install them with the following command:

[subs="normal"]
----
[prompt]#$# [command]#bower# install
----

After that, you should have a [filename]#bower_components# directory in your project root, with all elements declared in the [filename]#bower.json# file, and their requirements. List the contents of the [filename]#bower_components# directory to verify that it contains the following subdirectories:

.Contents of the [filename]#bower_components# Directory
----
app-layout
font-roboto
iron-a11y-announcer
iron-a11y-keys-behavior
iron-autogrow-textarea
iron-behaviors
iron-checked-element-behavior
iron-dropdown
iron-fit-behavior
iron-flex-layout
iron-form-element-behavior
iron-icon
iron-icons
iron-iconset-svg
iron-input
iron-media-query
iron-meta
iron-overlay-behavior
iron-resizable-behavior
iron-scroll-target-behavior
iron-selector
iron-validatable-behavior
neon-animation
paper-behaviors
paper-button
paper-icon-button
paper-input
paper-material
paper-ripple
paper-styles
polymer
vaadin-date-picker
vaadin-grid
web-animations-js
webcomponentsjs
----

[TIP]
.Ignore the Bower Dependencies in a Version Control System
====
It is usually a good practice to exclude external dependencies from a version control system. In this tip, we assume that you plan to use Git, though the same principle applies to any version control system.

The Angular 2 QuickStart project already contains a [filename]#.gitignore# file, that will exclude the [filename]#node_modules# directory with the npm dependencies.

You can add the following line to the [filename]#.gitignore# file to prevent the Bower dependencies from being tracked by Git in future:

[source]
----
bower_components
----
====

=== The npm Dependency

Alongside with Bower dependencies, we also need to add one npm dependency to the project. The `@vaadin/angular2-polymer` package adds support for Polymer elements in Angular 2 component templates. Run the following command to install the package and save the dependency in [filename]#package.json# at the same time:

[subs="normal"]
----
[prompt]#$# [command]#npm# install @vaadin/angular2-polymer --save
----



================================================
FILE: docs/tutorial/hero-editor.adoc
================================================
[[vaadin-angular2-polymer.tutorial.hero-editor]]
== Hero Editor and Routing

Previously we added the heroes list in our application. In this step, we are going to add the editing feature. After that, the user should be able to navigate to the hero details by clicking a row in the heroes list, edit the details, and get back to the list with the back button in the toolbar.

[NOTE]
.Some Parts are Explained in the Tour of Heroes
====
This step partly follows the Angular 2 Tour of Heroes Tutorial. Therefore, we skip explaining the parts of the code that are similar both in this tutorial and in the Tour of Heroes, such as the routing requirements and configuration.

See https://angular.io/docs/ts/latest/tutorial/[Tour of Heroes] for a detailed explaination of the similar parts.
====

=== Add the Hero Get Method to the Service

Let us add a [methodname]#getHero(id: number)# method to the [classname]#HeroService#. It can be used to retrive a single hero in our application components. Open the [filename]#app/hero.service.ts# file and change its contents to the following code:

[source,typescript]
.[filename]#app/hero.service.ts#
----
import { Injectable } from '@angular/core';

import { Hero } from './hero';
import { HEROES } from './mock-heroes';

@Injectable()
export class HeroService {
  getHeroes() {
    return Promise.resolve(HEROES);
  }

  getHero(id: number) {
    return Promise.resolve(HEROES).then(
      heroes => heroes.filter(hero => hero.id === id)[0]
    );
  }
}
----

=== Add the Hero Editor Component

Create a file named [filename]#app/hero-detail.component.ts# and put the following lines in it:

[source,typescript]
.[filename]#app/hero-detail.component.ts#
----
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { Subscription } from 'rxjs/Subscription';

import { Hero } from './hero';
import { HeroService } from './hero.service';

@Component({
  selector: 'my-hero-detail',
  template: `
    <div *ngIf="hero">
      <paper-input label="Name" [(value)]="hero.name"></paper-input>
      <vaadin-date-picker label="Birthday" [(value)]="hero.birthday"></vaadin-date-picker>
    </div>
  `,
  styles: [`
    :host {
      display: block;
      padding: 16px;
    }
  `]
})
export class HeroDetailComponent implements OnInit {
  hero: Hero;
  private _routeParamsSubscription: Subscription;

  constructor(
    private _route: ActivatedRoute,
    private _heroService: HeroService
  ) { }

  ngOnInit() {
    this._routeParamsSubscription = this._route.params.subscribe(params => {
      let id = +params['id']; // (+) converts string 'id' to a number
      this._heroService.getHero(id).then(hero => this.hero = hero);
    });
  }

  ngOnDestroy() {
    this._routeParamsSubscription.unsubscribe();
  }
}
----

So, here we have just created [classname]#HeroDetailComponent#, the heroes editor for our application. It uses [elementname]#paper-input# bound to the [propertyname]#hero.name# and [vaadinelement]#vaadin-date-picker# bound to the [propertyname]#hero.birthday# property through two-way data binding in both cases (that is, with the `[(value)]` syntax).

[classname]#HeroDetailComponent# gets the hero ID from the [classname]#ActivatedRoute# params and calls [methodname]#getHero(id: number)# method from [classname]#HeroService# with the hero ID argument to retrive the hero object. After the retrieval, the hero object is assigned to the [propertyname]#hero# property of [classname]#HeroDetailComponent#.

Since we use two-way binding, the [propertyname]#hero.name# and the [propertyname]#hero.birthday# sub-property values are automatically displayed in the corresponding elements. When the user edits these values in the elements, the sub-properties of the [propertyname]#hero# property are updated automatically.

[IMPORTANT]
.Use ngIf When Loading Content
====
The hero object is retrived asynchronously after the component initialization. At this time when the retrieval starts, the component template is already rendered, but the [propertyname]#hero# is not loaded yet, so we can not use [propertyname]#hero.name# and [propertyname]#hero.birthday# sub-properties. Using them at this time would result in errors.

That is why we wrap the [elementname]#paper-input# and the [vaadinelement]#vaadin-date-picker# elements with `<div *ngIf="hero"></div>` in the component template. The `ngIf` structural directive not only hides the content, but also stops the hidden part of the template from being evaluated and rendered. This effectively prevents errors of accessing non-existant sub-properties during the loading.
====

Unlike with Vaadin Grid in the heroes list, we do not want our editor contents to touch the edges of the browser window. It is nice to have some spacing around them. For that reason, we add `display: block;` and `padding: 16px;` rules in the styles section of our component metadata.

=== Add Routing

The Angular 2 Component Router uses `history.pushState` API for navigation. This requires us to declare the base `href` for the main document. Add this line to the [filename]#index.html# file in the project root just after the [elementname]#head# opening tag:

[source,html]
.[filename]#index.html#
----
...
<head>
  <base href="/">
  ...
</head>
...
----

Next we create a router configuration file. Add [filename]#app/app.routing.ts# with the following contents:

[source,typescript]
.[filename]#app/app.routing.ts#
----
import { Routes, RouterModule } from '@angular/router';

import { HeroesComponent } from './heroes.component';
import { HeroDetailComponent } from './hero-detail.component';

const appRoutes: Routes = [
  {
    path: '',
    redirectTo: '/heroes',
    pathMatch: 'full',
  },
  {
    path: 'heroes',
    children: [
      {
        path: '',
        component: HeroesComponent,
        data: {
          title: 'All heroes',
          root: true
        }
      },
      {
        path: ':id',
        component: HeroDetailComponent,
        data: {
          title: 'Hero detail'
        }
      }
    ]
  }
];

export const appRoutingProviders: any[] = [

];

export const routing = RouterModule.forRoot(appRoutes);
----

The routes list starts with the default route, which corresponds to the empty path. This route is used when no path is specified, and in our configuration, it redirects users to the `/heroes` path to open the heroes list by default.

After that in our routing configuration, there are two routes grouped as children under the `/heroes` path: one route is for the heroes list ([classname]#HeroesComponent#) and another is for the hero detail editor ([classname]#HeroDetailComponent#). Note, that the second route path features the `:id` parameter. It is received inside [classname]#HeroDetailComponent# and used there to retrive the hero object, as described above in this step.

Next, add a router outlet, the back button, and the navigation reaction to the [classname]#AppComponent#. Edit [filename]#app/app.component.ts# to contain the code below:

[source,typescript]
.[filename]#app/app.component.ts#
----
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute, Router, NavigationEnd } from '@angular/router';
import { Subscription } from 'rxjs/Subscription';

@Component({
  selector: 'my-app',
  template: `
    <app-header-layout has-scrolling-region>
      <app-header fixed>
        <app-toolbar [class.raised]="isInChildView">
          <paper-icon-button icon="arrow-back" *ngIf="isInChildView" (click)="goBack()"></paper-icon-button>
          <div title spacer>{{title}}</div>
        </app-toolbar>
      </app-header>
      <router-outlet></router-outlet>
    </app-header-layout>
  `,
  styles: [`
    app-toolbar {
      background: var(--primary-color);
      color: var(--dark-theme-text-color);
    }

    app-toolbar.raised {
      @apply(--shadow-elevation-4dp);
    }

    paper-icon-button {
      position: absolute;
      top: 12px;
      left: 8px;
    }
  `]
})
export class AppComponent implements OnInit {
  title = '';
  isInChildView = false;
  private _routerSubscription: Subscription;

  constructor(private _route: ActivatedRoute,
              private _router: Router) { }

  ngOnInit() {
    this._routerSubscription = this._router.events.subscribe(event => {
      if (event instanceof NavigationEnd) {
        let route = this._route.snapshot;
        while (route.firstChild) {
          route = route.firstChild;
        }
        this.title = route.data['title'];
        this.isInChildView = !route.data['root'];
      }
    });
  }

  ngOnDestroy() {
    this._routerSubscription.unsubscribe();
  }

  goBack() {
    this._router.navigate(['/heroes']);
  }
}
----

Then we need to update the [classname]#AppModule#, to import and enable the hero detail editor and the routing configuration. Change the [filename]#app/app.module.ts# file contents as follows:

[source,TypeScript]
.[filename]#app/app.module.ts#
----
import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { PolymerModule, PolymerElement } from '@vaadin/angular2-polymer';

import { AppComponent }  from './app.component';
import { HeroService } from './hero.service';
import { HeroesComponent } from './heroes.component';
import { HeroDetailComponent } from './hero-detail.component';
import { routing, appRoutingProviders } from './app.routing';

@NgModule({
  imports: [
    PolymerModule,
    routing
  ],
  declarations: [
    AppComponent,
    PolymerElement('app-header-layout'),
    PolymerElement('app-header'),
    PolymerElement('app-toolbar'),
    PolymerElement('paper-icon-button'),
    HeroesComponent,
    PolymerElement('vaadin-grid'),
    HeroDetailComponent,
    PolymerElement('paper-input'),
    PolymerElement('vaadin-date-picker')
  ],
  providers: [
    HeroService,
    appRoutingProviders
  ],
  bootstrap: [ AppComponent ],
  schemas: [ CUSTOM_ELEMENTS_SCHEMA ]
})
export class AppModule { }
----

=== Navigation to the Hero Details

The last feature to implement in this step is navigation from the heroes list to the hero details. Open [filename]#app/heroes.component.ts# and change it to contain the following code:

[source,typescript]
.[filename]#app/heroes.component.ts#
----
import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';

import { Hero } from './hero';
import { HeroService } from './hero.service';

@Component({
  selector: 'my-heroes',
  template: `
    <vaadin-grid [items]="heroes" (selected-items-changed)="onSelectedItemsChanged($event)">
      <table>
        <colgroup>
          <col name="id">
          <col name="name">
          <col name="birthday">
        </colgroup>
      </table>
    </vaadin-grid>
  `,
  styles: [`
    vaadin-grid {
      height: 100%;
    }
  `]
})
export class HeroesComponent implements OnInit {
  heroes: Hero[];

  constructor(
    private _router: Router,
    private _heroService: HeroService
  ) { }

  getHeroes() {
    this._heroService.getHeroes().then(heroes => this.heroes = heroes);
  }

  ngOnInit() {
    this.getHeroes();
  }

  onSelect(hero: Hero) {
    this._router.navigate(['/heroes', hero.id]);
  }

  onSelectedItemsChanged(event: any) {
    let selectedIndex: number = event.target.selection.selected()[0];
    if (selectedIndex !== undefined) {
      this.onSelect(this.heroes[selectedIndex]);
    }
  }
}
----

Now when the user clicks a row inside the heroes list, [vaadinelement]#vaadin-grid# fires [eventname]#selected-items-changed# event. We bound the event to the [methodname]#onSelectedItemsChanged(event: any)# method of the [classname]#HeroesComponent#. In the listener method, we read the selected item index, find the selected [propertyname]#heroes# array item, and call [methodname]#onSelect(hero: Hero)#, which uses [classname]#Router# to navigate to the hero detail editor for the selected hero.

=== Try It Out

All the changes for this step are done. Now launch your application again and try how the navigation works.

After opening the application, click the first row in the heroes list. You should see the details view like in the following screenshot:

[[figure.vaadin-angular2-polymer.tutorial.hero-detail]]
.The hero detail view
image::img/hero-details.png[width="320"]

Click the back icon in the toolbar to navigate back to the heroes list. If you made any changes in the hero detail editor, they should be shown in the heroes list right away.

=== Nice Touches in the AppComponent

In the folllowing, we go through and explain all the UX-related changes that were made to the [classname]#AppComponent# class earlier.

==== Dynamic Toolbar Title

We added the [propertyname]#title# property to the [classname]#AppComponent# and bound it to the text content of `<div title spacer></div>` inside the toolbar in the template.

Instead of a static title, the title is now updated dynamically. We subscribed to the [classname]#Router# events in [classname]#AppComponent# and used route data in the navigation event callback to get the title value specified for the current route. Each time after user opens the application or navigates inside, the [classname]#Router# [eventname]#NavigationEnd# event is dispatched, so that the [propertyname]#title# property will be updated.

==== The Back Icon in the Toolbar

We added [elementname]#paper-icon-button# to have a back icon inside the [elementname]#app-toolbar# in the template. The icon has a click event binding, which calls the [methodname]#goBack()# method of the [classname]#AppComponent# class. In the method, we invoke the [methodname]#navigate# method of the [classname]#Router# to navigate back to the heroes list from the hero details.

When the heroes list is shown, the back icon is useless, so we need to hide it. To achieve that, we added [propertyname]#isInChildView# property to [classname]#AppComponent#, which is updated from the route data in the navigation event callback. In the template, we added `*ngIf="isInChildView"` for the [elementname]#paper-icon-button#.

We also added a few positioning style rules for the [elementname]#paper-icon-button#.

==== Dynamic Toolbar Shadow

To make the toolbar look better, we made the application toolbar to have a shadow that is shown only for the hero detail view, but not for the heroes list view. For this purpose, we bound the `raised` class of the [elementname]#app-toolbar# to [propertyname]#isInChildView# property and added a style rule which applies the shadow mixin from [elementname]#paper-styles# to the [elementname]#app-toolbar# when it has the `raised` class.



================================================
FILE: docs/tutorial/introduction.adoc
================================================
[[vaadin-angular2-polymer.tutorial.introduction]]
== Introduction

This tutorial explains how to use Polymer elements in Angular 2 applications with the help of the [literal]`https://github.com/vaadin/angular2-polymer[@vaadin/angular2-polymer]` directives.

For this purpose, we show how to create a staff management application similar to the https://angular.io/docs/ts/latest/tutorial/[Tour of Heroes] example. But unlike in the Angular 2 Tour of Heroes, instead of implementing the UI elements in place, we will use the ready-made UI elements that are built on top of the Web Components technology stack. The resulting application will have a nice-looking mobile-first Material designed UI.

The https://github.com/vaadin/angular2-polymer-quickstart[resulting application source] is published on GitHub.

=== The Start Point

This tutorial is based on the https://angular.io/docs/ts/latest/quickstart.html[5 min QuickStart] for Angular 2 and the https://angular.io/docs/ts/latest/tutorial/[Angular 2 Tour of Heroes]. We start from the QuickStart application source.

If you are not yet familiar with Angular 2, we advice you to first walk through the Angular 2 QuickStart and the Tour of Heroes. In this tutorial, we skip the explanations that are already given in the Angular 2 tutorials.

=== The Goal

In our heroes management application, there will be two UI screens. The first screen, as illustrated in <<figure.vaadin-angular2-polymer.tutorial.result-heroes-list>>, uses https://vaadin.com/elements/-/element/vaadin-grid[Vaadin Grid] to show a list of all the heroes.

[[figure.vaadin-angular2-polymer.tutorial.result-heroes-list]]
.The first screen with the list of heroes
image::img/heroes-list-default.png[width="320"]

By clicking on a grid row, the user can open the hero details editor screen. It is made using [elementname]#paper-input# and [vaadinelement]#vaadin-date-picker#:

[[figure.vaadin-angular2-polymer.tutorial.result-hero-detail]]
.The second screen with the hero details view
image::img/hero-details.png[width="320"]

Both screens have a context-sensitive toolbar at the top, containing the title and the optional back icon button.



================================================
FILE: docs/tutorial/layout.adoc
================================================
[[vaadin-angular2-polymer.tutorial.layout]]
== Building the Application Layout with Paper Elements

After the previous step, we have some Polymer elements imported in the [filename]#index.html# file of our application. In this step, we are going to use them to create an application layout with a toolbar in [classname]#AppComponent#.

=== Updating SystemJS Configuration

For using Polymer elements in our Angular components, we need to import the [classname]#PolymerElement# directives from `@vaadin/angular2-polymer`. Therefore, we need to make the module loader (SystemJS, in our case) aware of how to load the `@vaadin/angular2-polymer` package.

Angular 2 TypeScript QuickStart contains the SystemJS configuration in the [filename]#systemjs.config.js# file in the project root. Please edit this file and add a mapping for the `@vaadin` scope and the `@vaadin/angular2-polymer` package there as follows:

[source,javascript]
.[filename]#systemjs.config.js#
----
/**
 * System configuration for Angular 2 samples
 * Adjust as necessary for your application needs.
 */
(function(global) {
  System.config({
    paths: {
      // paths serve as alias
      'npm:': 'node_modules/'
    },
    // map tells the System loader where to look for things
    map: {
      // our app is within the app folder
      app: 'app',

      // angular bundles
      '@angular/core': 'npm:@angular/core/bundles/core.umd.js',
      '@angular/common': 'npm:@angular/common/bundles/common.umd.js',
      '@angular/compiler': 'npm:@angular/compiler/bundles/compiler.umd.js',
      '@angular/platform-browser': 'npm:@angular/platform-browser/bundles/platform-browser.umd.js',
      '@angular/platform-browser-dynamic': 'npm:@angular/platform-browser-dynamic/bundles/platform-browser-dynamic.umd.js',
      '@angular/http': 'npm:@angular/http/bundles/http.umd.js',
      '@angular/router': 'npm:@angular/router/bundles/router.umd.js',
      '@angular/forms': 'npm:@angular/forms/bundles/forms.umd.js',

      // other libraries
      'rxjs':                       'npm:rxjs',
      'angular2-in-memory-web-api': 'npm:angular2-in-memory-web-api',
      '@vaadin/angular2-polymer':   'npm:@vaadin/angular2-polymer'
    },
    // packages tells the System loader how to load when no filename and/or no extension
    packages: {
      app: {
        main: './main.js',
        defaultExtension: 'js'
      },
      rxjs: {
        defaultExtension: 'js'
      },
      'angular2-in-memory-web-api': {
        main: './index.js',
        defaultExtension: 'js'
      },
      '@vaadin/angular2-polymer': {
        main: './index.js',
        defaultExtension: 'js'
      }
    }
  });
})(this);
----

=== Extending AppModule with Polymer Elements

Now we import the [classname]#PolymerElement# directives and attach them to the [classname]#AppModule#, in order to allow using the listed Polymer elements in this Angular module. Edit [filename]#app/app.module.ts#, append the [classname]#PolymerElement# directives to the [propertyname]#declarations# list, and add the [classname]#CUSTOM_ELEMENTS_SCHEMA#, as follows:

[source,typescript]
.[filename]#app/app.module.ts#
----
import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { PolymerModule, PolymerElement } from '@vaadin/angular2-polymer';

import { AppComponent }  from './app.component';

@NgModule({
  imports: [ PolymerModule ],
  declarations: [
    AppComponent,
    PolymerElement('app-header-layout'),
    PolymerElement('app-header'),
    PolymerElement('app-toolbar')
  ],
  bootstrap: [ AppComponent ],
  schemas: [ CUSTOM_ELEMENTS_SCHEMA ]
})
export class AppModule { }
----

=== AppComponent Changes

Open [filename]#app/app.component.ts# and replace the contents with the following code:

[source,typescript]
.[filename]#app/app.component.ts#
----
import { Component } from '@angular/core';

@Component({
  selector: 'my-app',
  template: `
    <app-header-layout has-scrolling-region>
      <app-header fixed>
        <app-toolbar>
          <div title spacer>All heroes</div>
        </app-toolbar>
      </app-header>
      <div>My application content</div>
    </app-header-layout>
  `,
  styles: [`
    app-toolbar {
      background: var(--primary-color);
      color: var(--dark-theme-text-color);
    }
  `]
})
export class AppComponent { }
----

Save the changes and launch the development server to see the results in your browser. After loading, your application should look as follows:

[[figure.vaadin-angular2-polymer.tutorial.app-layout]]
.The empty application layout
image::img/app-layout.png[width="320"]

Now your application has a layout made by using the [elementname]#app-header-layout#, [elementname]#app-header#, and [elementname]#app-toolbar# elements.

=== Elements Used in This Step

[elementname]#app-header-layout#:: The application layout that consists of the [elementname]#app-header# element and the main contents. In our case, it adds a scrollable container for the application contents as well.

[elementname]#app-header#:: Acts as a header in the application layout. The header is fixed in our application.

[elementname]#app-toolbar#:: Provides a toolbar wrapper.

[NOTE]
.The [elementname]#app-layout# Elements are Design-Agnostic
====
Polymer elements from the [elementname]#app-layout# set, including [elementname]#app-toolbar# that we use, are design-agnostic. They do not have Material Design look by default. We need to adjust [elementname]#app-toolbar# styles a bit.

Therefore, we added color rules for the [elementname]#app-toolbar# in the styles of the [classname]#AppComponent#. We reuse the color values of default theme from [elementname]#paper-styles#.

Apart from the colors, it inherits the font family declared for the body. We have already declared our font settings for the body in the [filename]#index.html# file earlier during this step.
====

[IMPORTANT]
.The [classname]#PolymerElement# Directives
====
In order to enable all features of Polymer elements used inside your Angular component templates, remember to import [classname]#PolymerElement# in the module file and add `PolymerElement('element-name')` line for each Polymer element that you use to the [propertyname]#declarations# array of your Angular module metadata.
====



================================================
FILE: docs/tutorial/list-heroes.adoc
================================================
[[vaadin-angular2-polymer.tutorial.list-heroes]]
== List Heroes with Vaadin Grid

In the previous step, we added the application layout with [elementname]#app-layout# elements. Next, we are going add actual application content. Our plan is to use Vaadin Grid to list Heroes.

[NOTE]
.Some Parts are Explained in the Tour of Heroes
====
This step partly follows the Angular 2 Tour of Heroes Tutorial. Therefore, we skip explaining the parts of the code that are similar in both this tutorial and the Tour of Heroes, such as the [classname]#Hero# class and the [classname]#HeroService#.

See https://angular.io/docs/ts/latest/tutorial/[Tour of Heroes] for a detailed explaination of such similar parts.
====

=== Hero Class

Let us start by creating the [classname]#Hero# class. Create a [filename]#app/hero.ts# file with the following contents:

[source,typescript]
.[filename]#app/hero.ts#
----
export class Hero {
  id: number;
  name: string;
  birthday: string; // Using strings for simplicity
}
----

Unlike in Angular 2 Tour of Heroes, in our application we store and expose the birthday of each hero for the user. Here we add the `birthday: string;` property to our [classname]#Hero# class.

[NOTE]
.Using Strings to Store Dates
====
Why are we using the [classname]#string# type and not [classname]#Date# to store dates? There are two reasons:

. The built-in JavaScript [classname]#Date# type is always stored as a timestamp, so it always contains the exact time information. This is not only redundant, but also harder to use than a plain string in case of storing just a date. It requires extra care about the correct time and timezone when storing the value and displaying it to the user; otherwise we might get incorrect dates because of timezone mismatches.

. [vaadinelement]#vaadin-date-picker#, as well as native HTML5 `<input type="date">`, gives the date value as an ISO-formatted [classname]#string#. To preserve the simplicity in our application, we also store dates as strings, avoiding conversions.
====

=== Mock Heroes Data

Create a [filename]#app/mock-heroes.ts# file with some heroes data:

[source,typescript]
.[filename]#app/mock-heroes.ts#
----
import { Hero } from './hero';

export var HEROES: Hero[] = [
  { "id": 11,  "name": "Mr. Nice",   "birthday": "1980-01-11" },
  { "id": 12,  "name": "Narco",      "birthday": "1980-01-12" },
  { "id": 13,  "name": "Bombasto",   "birthday": "1980-01-13" },
  { "id": 14,  "name": "Celeritas",  "birthday": "1980-01-14" },
  { "id": 15,  "name": "Magneta",    "birthday": "1980-01-15" },
  { "id": 16,  "name": "RubberMan",  "birthday": "1980-01-16" },
  { "id": 17,  "name": "Dynama",     "birthday": "1980-01-17" },
  { "id": 18,  "name": "Dr IQ",      "birthday": "1980-01-18" },
  { "id": 19,  "name": "Magma",      "birthday": "1980-01-19" },
  { "id": 20,  "name": "Tornado",    "birthday": "1980-01-20" }
];
----

=== The Hero Service

We also need a [classname]#HeroService# to be able to retrive the heroes list in our Angular application. Create a [filename]#app/hero.service.ts# file:

[source,typescript]
.[filename]#app/hero.service.ts#
----
import { Injectable } from '@angular/core';

import { Hero } from './hero';
import { HEROES } from './mock-heroes';

@Injectable()
export class HeroService {
  getHeroes() {
    return Promise.resolve(HEROES);
  }
}
----

=== Heroes List Component

Add the heroes list component file [filename]#app/heroes.component.ts# with the following code:

[source,typescript]
.[filename]#app/heroes.component.ts#
----
import { Component, OnInit } from '@angular/core';

import { Hero } from './hero';
import { HeroService } from './hero.service';

@Component({
  selector: 'my-heroes',
  template: `
    <vaadin-grid [items]="heroes">
      <table>
        <colgroup>
          <col name="id">
          <col name="name">
          <col name="birthday">
        </colgroup>
      </table>
    </vaadin-grid>
  `,
  styles: [`
    vaadin-grid {
      height: 100%;
    }
  `]
})
export class HeroesComponent implements OnInit {
  heroes: Hero[];

  constructor(private _heroService: HeroService) { }

  getHeroes() {
    this._heroService.getHeroes().then(heroes => this.heroes = heroes);
  }

  ngOnInit() {
    this.getHeroes();
  }
}
----

Here in the [classname]#HeroesComponent#, we have the [vaadinelement]#vaadin-grid# element in the template. In the styles, we have a `height: 100%;` rule for the [vaadinelement]#vaadin-grid# element. In the template, there are three columns inside the [vaadinelement]#vaadin-grid#, specified with their corresponding item property names.

Also in the template, the [propertyname]#items# property of the [vaadinelement]#vaadin-grid# is bound to the [propertyname]#heroes# array property of [classname]#HeroesComponent#. At the same time, we import and use [classname]#HeroService# to get the list of heroes and assign the [propertyname]#heroes# property. Data binding of the Angular component takes care of updating the [propertyname]#items# property of [vaadinelement]#vaadin-grid# with the list of heroes for us.

=== Displaying Heroes List

Next, we need to edit the [filename]#app/app.component.ts# file to display the heroes list in the [classname]#AppComponent# template. Replace `<div>My application content</div>` with `<my-heroes></my-heroes>`, as in the following code:

[source,typescript]
.[filename]#app/app.component.ts#
----
import { Component } from '@angular/core';

@Component({
  selector: 'my-app',
  template: `
    <app-header-layout has-scrolling-region>
      <app-header fixed>
        <app-toolbar>
          <div title spacer>All heroes</div>
        </app-toolbar>
      </app-header>
      <my-heroes></my-heroes>
    </app-header-layout>
  `,
  styles: [`
    app-toolbar {
      background: var(--primary-color);
      color: var(--dark-theme-text-color);
    }
  `]
})
export class AppComponent { }
----

=== Updating AppModule

Finally in this step, we update the [classname]#AppModule#. Change the contents of [filename]#app/app.module.ts# as follows:

[source,typescript]
.[filename]#app/app.module.ts#
----
import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { PolymerModule, PolymerElement } from '@vaadin/angular2-polymer';

import { AppComponent }  from './app.component';
import { HeroService } from './hero.service';
import { HeroesComponent } from './heroes.component';

@NgModule({
  imports: [ PolymerModule ],
  declarations: [
    AppComponent,
    PolymerElement('app-header-layout'),
    PolymerElement('app-header'),
    PolymerElement('app-toolbar'),
    PolymerElement('paper-icon-button'),
    HeroesComponent,
    PolymerElement('vaadin-grid')
  ],
  providers: [ HeroService ],
  bootstrap: [ AppComponent ],
  schemas: [ CUSTOM_ELEMENTS_SCHEMA ]
})
export class AppModule { }
----

We did the following changes in the [filename]#app/app.module.ts# file:

* We imported [classname]#HeroService# and listed it in [propertyname]#providers#
* We also imported [classname]#HeroesComponent#, and appended it the [propertyname]#declarations#, alongside with the [classname]#PolymerElement# directives for the [vaadinelement]#vaadin-grid# element, which is used in the [classname]#HeroesComponent# template.

Now it is again time to look in the browser window, to see how the heroes list looks in our application. It should look about as in <<figure.vaadin-angular2-polymer.tutorial.heroes-list>>:

[[figure.vaadin-angular2-polymer.tutorial.heroes-list]]
.The list of heroes
image::img/heroes-list.png[width="320"]



================================================
FILE: docs/tutorial/polymer.adoc
================================================
[[vaadin-angular2-polymer.tutorial.polymer]]
== Adding Polymer Elements to Our Application

In the previous step, we downloaded the needed elements to the [filename]#bower_components# directory. Next, we will import these elements in our application.

In your project root, edit the [filename]#index.html# file and replace the contents with the following lines:

[source,html]
----
<!DOCTYPE html>
<html>
  <head>
    <title>Angular 2 with Polymer Elements QuickStart</title>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <!-- Polyfills -->
    <script src="bower_components/webcomponentsjs/webcomponents.min.js"></script>
    <script src="node_modules/core-js/client/shim.min.js"></script>

    <script>
      window.Polymer = {
        dom: 'shadow'
      };
    </script>

    <!-- JavaScript libraries -->
    <script src="node_modules/zone.js/dist/zone.js"></script>
    <script src="node_modules/reflect-metadata/Reflect.js"></script>
    <script src="node_modules/systemjs/dist/system.src.js"></script>

    <!-- Styles -->
    <link rel="import" href="bower_components/iron-flex-layout/iron-flex-layout.html">
    <link rel="import" href="bower_components/paper-styles/color.html">
    <link rel="import" href="bower_components/paper-styles/default-theme.html">
    <link rel="import" href="bower_components/paper-styles/typography.html">
    <link rel="import" href="bower_components/paper-styles/shadow.html">
    <style is="custom-style">
      body {
        @apply(--layout-fullbleed);
        @apply(--paper-font-body1);
        background: var(--primary-background-color);
        color: var(--primary-text-color);
      }
    </style>

    <!-- Polymer Elements -->
    <link rel="import" href="bower_components/iron-icons/iron-icons.html">
    <link rel="import" href="bower_components/app-layout/app-layout.html">
    <link rel="import" href="bower_components/paper-icon-button/paper-icon-button.html">
    <link rel="import" href="bower_components/paper-input/paper-input.html">
    <link rel="import" href="bower_components/vaadin-grid/vaadin-grid.html">
    <link rel="import" href="bower_components/vaadin-date-picker/vaadin-date-picker.html">

    <!-- SystemJS Configuration -->
    <script src="systemjs.config.js"></script>
    <script>
      document.addEventListener('WebComponentsReady', function() {
        System.import('app').catch(function(err){ console.error(err); });
      });
    </script>
  </head>

  <body>
    <my-app>Loading...</my-app>
  </body>
</html>
----

We have made the following important changes to the original Angular 2 tutorial file:

The Doctype Declaration::
We added the `<!DOCTYPE html>` declaration in the first line of the HTML file. It switches document to use the Standards mode, as required by the internals of [vaadinelement]#vaadin-grid#.

The Web Components Polyfill::
The technology stack behind Web Components (namely, HTML Imports, Shadow DOM, and Custom Elements) is not yet natively supported in all browsers. Therefore, we added the [filename]#webcomponents.min.js# polyfill.

Configure Polymer DOM::
By default, Polymer uses shady DOM, which is not compatible with Angular. We created the Polymer global settings object and set it to use shadow DOM instead.

Importing Polymer Elements::
We added imports of Polymer elements that we are going to use in our application to the head section of the [filename]#index.html# file.

SystemJS App Import Change::
In some browsers, HTML Imports are loaded asynchronously, but we need them to be completely loaded before we import our Angular application. Hence we wrapped the `System.import('app')...` call in the listener callback of the [eventname]#WebComponentsReady# event, which is fired by the polyfill after all imports are loaded and elements have been registered.
+
[IMPORTANT]
.Load Order
====
The order in which the Polymer elements and the rest of the Angular application code are loaded does matter. It is required that the Polymer elements are loaded and registered before importing the Angular application. The `@vaadin/angular2-polymer` package strictly depends on that.
====

Style Changes::
Polymer elements come with nice built-in styles in the way of Material Design. Angular 2 also provides style encapsulation mechanisms for our application components.
+
Therefore, the global styles are not needed anymore. We removed the external [filename]#styles.css# stylesheet and replaced it with [elementname]#iron-flex-layout# and [elementname]#paper-styles# style mixin imports and an embedded global style rule for the body element.
+
The body style rule is the only global style rule that remains in our application. The body element needs to be styled to take the full height of the browser viewport, and also to specify default font styles, line height, background, and text colors.
+
[TIP]
====
Instead of figuring out the exact rules and values for the body style, we import and reuse CSS mixins and CSS custom properties declared in [elementname]#iron-flex-layout# and [elementname]#paper-styles#.
====
+
[IMPORTANT]
====
When using custom CSS mixins and custom CSS properties in your main document styles, you need to wrap your styles inside a `<style is="custom-style"></style>` tag.

See the https://www.polymer-project.org/1.0/docs/devguide/styling.html[Styling section of the Polymer Developer Guide] for more information on styling Polymer elements and the document, custom CSS mixins and properties usage and limitations.
====

Delete the [filename]#styles.css# file from your project directory since it is no longer in use.

[NOTE]
.Duplicated Imports
====
The HTML imports we have in the [filename]#index.html# file also import any possible dependencies. Imports having the same dependencies result in multiple imports of the same file. In such case, browsers prevent fetching the same file multiple times by checking the file location.

See http://w3c.github.io/webcomponents/spec/imports/#fetching-import[Fetching Import section of the HTML Imports Spec] for more detailed information about the fetching algorithm.
====


================================================
FILE: docs/tutorial/wrap-up.adoc
================================================
[[vaadin-angular2-polymer.tutorial.wrap-up]]
== Wrap Up

You have now finished all the tutorial steps and know how to use all the powers of Polymer elements in your Angular 2 applications.

You can see the https://github.com/vaadin/angular2-polymer-quickstart[resulting application source] on GitHub.

=== Further Steps

There are several ways to improve the application you created in this tutorial. For example, you might want to add an explicit [guibutton]#Save# button to the hero detail editor and make the user able to intentionally submit or discard their changes. To do that, you can use the `ngForm` directive. See the https://angular.io/docs/ts/latest/guide/forms.html[Forms Chapter] in the Angular 2 Basics guide for the detailed instructions.

In the tutorial, we did not consider the topic of storing your application data. For simplicity, our application uses mock in-memory data and relies on data binding to make temporary changes, which are not saved anywhere. You might want to move the heroes data to a server and add some HTTP API calls in your application. See the Angular 2 https://angular.io/docs/ts/latest/guide/server-communication.html[Http Client] documentation to know how to do that.

Read the https://www.polymer-project.org/1.0/[Polymer Project] website to know about other features that Polymer provides you with. There is also the https://www.polymer-project.org/1.0/start/[Getting Started] guide, where you can learn how to create your own elements and apps based on Polymer.

Don’t forget to find out abouth other elements that you can use for building your applications from the https://vaadin.com/elements[Vaadin Elements] page and the https://elements.polymer-project.org/[Polymer Catalog].



================================================
FILE: docs/tutorial-index.adoc
================================================
---
title: Tutorial
order: 2
layout: page
subnav_auto_list_numbers: true
subnav:
  - title: Introduction
    href: '#vaadin-angular2-polymer.tutorial.creating-project'
  - title: Creating Project
    href: '#vaadin-angular2-polymer.tutorial.creating-project'
  - title: Adding and Installing Dependencies
    href: '#vaadin-angular2-polymer.tutorial.dependencies'
  - title: Adding Polymer Elements to Our Application
    href: '#vaadin-angular2-polymer.tutorial.polymer'
  - title: Building the Application Layout with Paper Elements
    href: '#vaadin-angular2-polymer.tutorial.layout'
  - title: List Heroes with Vaadin Grid
    href: '#vaadin-angular2-polymer.tutorial.list-heroes'
  - title: Hero Editor and Routing
    href: '#vaadin-angular2-polymer.tutorial.hero-editor'
  - title: Wrap Up
    href: '#vaadin-angular2-polymer.tutorial.wrap-up'
---

[[vaadin-angular2-polymer.tutorial]]
= Angular 2 with Polymer Elements Tutorial
:linkattrs:
:experimental:
:sectnums:
:imagesdir: tutorial

include::tutorial/introduction.adoc[]

include::tutorial/creating-project.adoc[]

include::tutorial/dependencies.adoc[]

include::tutorial/polymer.adoc[]

include::tutorial/layout.adoc[]

include::tutorial/list-heroes.adoc[]

include::tutorial/hero-editor.adoc[]

include::tutorial/wrap-up.adoc[]



================================================
FILE: index.spec.ts
================================================
import './src/polymer-element.spec';
import './src/polymer-module.spec';


================================================
FILE: index.ts
================================================
export {PolymerElement} from './src/polymer-element';
export {PolymerModule} from './src/polymer-module';


================================================
FILE: karma.conf.js
================================================
// Karma configuration
// Generated on Fri Mar 10 2017 15:33:36 GMT+0200 (EET)

module.exports = function(config) {
  config.set({

    // base path that will be used to resolve all patterns (eg. files, exclude)
    basePath: '',


    // frameworks to use
    // available frameworks: https://npmjs.org/browse/keyword/karma-adapter
    frameworks: ['jasmine'],


    customLaunchers: {
      ChromeHeadless: {
        base: 'Chrome',
        flags: [
          '--no-sandbox',
          // See https://chromium.googlesource.com/chromium/src/+/lkgr/headless/README.md
          '--headless',
          '--disable-gpu',
          // Without a remote debugging port, Google Chrome exits immediately.
          ' --remote-debugging-port=9222'
        ]
      }
    },


    // list of files / patterns to load in the browser
    files: [
      'bower_components/webcomponentsjs/webcomponents-lite.min.js',
      'src/test-element.html',

      'node_modules/systemjs/dist/system.src.js',
      'node_modules/core-js/client/shim.js',
      'node_modules/zone.js/dist/zone.js',
      'node_modules/zone.js/dist/long-stack-trace-zone.js',
      'node_modules/zone.js/dist/proxy.js',
      'node_modules/zone.js/dist/sync-test.js',
      'node_modules/zone.js/dist/jasmine-patch.js',
      'node_modules/zone.js/dist/async-test.js',
      'node_modules/zone.js/dist/fake-async-test.js',

      'system.config.js',

      { pattern: 'node_modules/**/*.js', included: false, watched: false },
      { pattern: 'bower_components/**/*.+(html|css|js)', included: false, watched: false },
      { pattern: 'node_modules/rxjs/**/*.js', included: false, watched: false },
      { pattern: 'node_modules/rxjs/**/*.js.map', included: false, watched: false },
      { pattern: '**/*.js', included: false, watched: true },

      // Serve the sources for debugging
      { pattern: '**/*.ts', included: false, watched: false, served: true }
    ],


    // list of files to exclude
    exclude: [
    ],


    // preprocess matching files before serving them to the browser
    // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
    preprocessors: {
    },


    // test results reporter to use
    // possible values: 'dots', 'progress'
    // available reporters: https://npmjs.org/browse/keyword/karma-reporter
    reporters: ['progress'],


    // web server port
    port: 9876,


    // enable / disable colors in the output (reporters and logs)
    colors: true,


    // level of logging
    // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
    logLevel: config.LOG_INFO,


    // enable / disable watching file and executing tests whenever any file changes
    autoWatch: true,


    // start these browsers
    // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
    browsers: ['Chrome'],


    // Continuous Integration mode
    // if true, Karma captures browsers, runs the tests and exits
    singleRun: false,

    // Concurrency level
    // how many browser should be started simultaneous
    concurrency: Infinity
  })
}


================================================
FILE: package.json
================================================
{
  "name": "angular-polymer",
  "version": "1.1.0",
  "description": "Angular 2 support for Polymer elements",
  "repository": "https://github.com/platosha/angular-polymer.git",
  "main": "index.js",
  "scripts": {
    "bower": "bower",
    "test": "tsc && npm run karma -- start --single-run --browsers ChromeHeadless",
    "test:w": "tsc && concurrently \"npm run tsc:w\" \"npm run karma start\"",
    "karma": "karma",
    "tsc": "tsc",
    "tsc:w": "tsc -w",
    "prepublish": "tsc",
    "lint": "tslint './**/*.ts' --exclude './node_modules/**'",
    "fix-lint": "tslint './**/*.ts' --exclude './node_modules/**' --fix"
  },
  "typescript": {
    "definition": "index.d.ts"
  },
  "author": "Vaadin Ltd",
  "license": "Apache-2.0",
  "peerDependencies": {
    "@angular/common": "^2.0.0",
    "@angular/core": "^2.0.0",
    "@angular/forms": "^2.0.0",
    "@angular/platform-browser": "^2.0.0",
    "rxjs": "^5.0.0",
    "zone.js": "^0.7.2"
  },
  "devDependencies": {
    "@angular/compiler": "^2.0.0",
    "@angular/platform-browser-dynamic": "^2.0.0",
    "@types/core-js": "^0.9.34",
    "@types/jasmine": "^2.5.36",
    "@types/node": "^6.0.46",
    "@types/selenium-webdriver": "^2.53.33",
    "bower": "latest",
    "concurrently": "^2.2.0",
    "core-js": "^2.4.1",
    "jasmine-core": "^2.4.1",
    "karma": "^1.5.0",
    "karma-chrome-launcher": "^2.0.0",
    "karma-jasmine": "^1.1.0",
    "live-server": "1.0.0",
    "reflect-metadata": "^0.1.3",
    "systemjs": "^0.19.27",
    "tslint": "^4.4.2",
    "typescript": "^2.1.6"
  }
}


================================================
FILE: src/polymer-element.spec.ts
================================================
import {
    async,
    TestBed,
    ComponentFixture
} from '@angular/core/testing';
import {PolymerElement} from './polymer-element';
import {Component, CUSTOM_ELEMENTS_SCHEMA} from '@angular/core';
import {ReactiveFormsModule, FormGroup, FormControl} from '@angular/forms';

const Polymer: any = (<any>window).Polymer;

describe('PolymerElement', () => {

    beforeEach(async(() => {
        TestBed.configureTestingModule({
            imports: [
                ReactiveFormsModule
            ],
            declarations: [
                TestComponent,
                TestComponentForm,
                TestComponentCheckboxForm,
                PolymerElement('test-element'),
                PolymerElement('paper-checkbox')
            ],
            schemas: [CUSTOM_ELEMENTS_SCHEMA]
        });
        TestBed.compileComponents();
    }));

    let testElement: any;
    let testComponent: TestComponent;
    let fixture: ComponentFixture<any>;

    function createTestComponent(type: any) {
        fixture = TestBed.createComponent(type);
        testElement = fixture.debugElement.query((el) => el.name === 'test-element').nativeElement;
        testComponent = fixture.componentInstance;
    }

    it('is defined', () => {
        expect(PolymerElement).toBeDefined();
    });

    it('is function', () => {
        expect(typeof PolymerElement).toBe('function');
    });

    describe('Developer experience', () => {

        it('should throw an error for non-registered elements', () => {
            try {
                PolymerElement('non-registered');
            } catch (error) {
                expect(error.message).toContain('element "non-registered" has not been registered');
            }
        });

    });

    describe('Two-way data binding', () => {

        beforeEach(() => {
            createTestComponent(TestComponent);
        });

        it('should have initial bound value', () => {
            fixture.detectChanges();
            expect(testElement.value).toEqual('foo');
        });

        it('should change value on bound value change', () => {
            testComponent.value = 'bar';
            fixture.detectChanges();
            expect(testElement.value).toEqual('bar');
        });

        it('should change bound value on value change', () => {
            testElement.value = 'bar';
            expect(testComponent.value).toEqual('bar');
        });

        it('should reflect change to a nested value (object)', () => {
            testComponent.nestedObject.value = 'foo';
            fixture.detectChanges();
            let nested = Polymer.dom(testElement.root).querySelector('#nested');
            expect(nested.getAttribute('nested-object-value')).toEqual('foo');
        });

        it('should reflect change to a nested value (array)', () => {
            testComponent.arrayObject.push('foo');
            fixture.detectChanges();
            let nested = Polymer.dom(testElement.root).querySelector('#nested');
            expect(nested.getAttribute('array-object-value')).toEqual('foo');
        });

    });

    describe('Form field', () => {

        let form: any;

        function formTests(): void {

            describe('Initial state', () => {

                it('should be initially pristine', () => {
                    expect(testElement.classList.contains('ng-pristine')).toEqual(true);
                });

                it('should be initially untouched', () => {
                    expect(testElement.classList.contains('ng-untouched')).toEqual(true);
                });

                it('should be invalid', () => {
                    expect(testElement.classList.contains('ng-invalid')).toEqual(true);
                });

                it('should be an invalid form', () => {
                    expect(form.valid).toEqual(false);
                });

                it('should not reflect invalid state to element initially', () => {
                    expect(testElement.invalid).toBeFalsy();
                });

            });

            describe('after value has changed', () => {

                beforeEach(() => {
                    testElement.value = 'qux';
                    fixture.detectChanges();
                });

                it('should be dirty on value change', () => {
                    expect(testElement.classList.contains('ng-dirty')).toEqual(true);
                });

                it('should be a valid form', () => {
                    expect(form.valid).toEqual(true);
                });

                it('should have correct value', () => {
                    expect(form.value.value).toEqual('qux');
                });

                it('should be valid', () => {
                    expect(testElement.classList.contains('ng-valid')).toEqual(true);
                });

                it('should reflect invalid state to testElement when value changed', () => {
                    testElement.value = '';
                    fixture.detectChanges();
                    expect(testElement.invalid).toEqual(true);
                });

            });

        }

        describe('Forms API', () => {

            beforeEach(() => {
                createTestComponent(TestComponentForm);
                form = new FormGroup({value: new FormControl()});
                fixture.debugElement.componentInstance.form = form;
                fixture.detectChanges();
            });

            formTests();
        });

    });


    describe('Checked Element inside Form', () => {

        let form: FormGroup;
        let checkedElement: any;

        describe('initially false', () => {
            beforeEach(() => {
                createTestComponent(TestComponentCheckboxForm);
                form = new FormGroup({value: new FormControl(false)});
                fixture.debugElement.componentInstance.form = form;
                fixture.detectChanges();
                checkedElement = fixture.debugElement.query((el) => el.name === 'paper-checkbox').nativeElement;
            });

            it('should set default value', () => {
                expect(checkedElement.checked).toEqual(false);
            });

            it('should set form value', () => {
                checkedElement.checked = true;
                expect(form.value.value).toEqual(true);
            });
        });

        describe('initially true', () => {
            beforeEach(() => {
                createTestComponent(TestComponentCheckboxForm);
                form = new FormGroup({value: new FormControl(true)});
                fixture.debugElement.componentInstance.form = form;
                fixture.detectChanges();
                checkedElement = fixture.debugElement.query((el) => el.name === 'paper-checkbox').nativeElement;
            });

            it('should set default value', () => {
                expect(checkedElement.checked).toEqual(true);
            });

            it('should set form value', () => {
                checkedElement.checked = false;
                expect(form.value.value).toEqual(false);
            });
        });
    });
});


@Component({
    template: `<test-element [(value)]="value" [(nestedObject)]="nestedObject" [(arrayObject)]="arrayObject"></test-element>`
})
class TestComponent {
    value = 'foo';
    nestedObject = {value: undefined};
    arrayObject = [];
    barVisible = false;
}

@Component({
    template: `
    <form [formGroup]="form">
      <test-element formControlName="value" required></test-element>
    </form>`
})
class TestComponentForm {
    value = 'foo';
}

@Component({
    // test-element added to make the global test setup not crash.
    template: `
    <form [formGroup]="form">
      <paper-checkbox formControlName="value"></paper-checkbox>
    </form>
    <test-element></test-element>`
})
class TestComponentCheckboxForm {
}


================================================
FILE: src/polymer-element.ts
================================================
import {
    Injector,
    Directive,
    ElementRef,
    EventEmitter,
    forwardRef,
    Renderer,
    NgZone,
    KeyValueDiffers,
    IterableDiffers,
    DefaultIterableDiffer
} from '@angular/core';
import {FormControlName, NG_VALUE_ACCESSOR} from '@angular/forms';

const Polymer: any = (<any>window).Polymer;


export function PolymerElement(name: string): any[] {
    const propertiesWithNotify: Array<any> = [];
    const arrayAndObjectProperties: Array<any> = [];

    const proto: any = Object.getPrototypeOf(document.createElement(name));
    if (proto.is !== name) {
        throw new Error(`The Polymer element "${name}" has not been registered. Please check that the element is imported correctly.`);
    }
    const isFormElement: boolean = Polymer && Polymer.IronFormElementBehavior && proto.behaviors.indexOf(Polymer.IronFormElementBehavior) > -1;
    const isCheckedElement: boolean = Polymer && Polymer.IronCheckedElementBehaviorImpl && proto.behaviors.indexOf(Polymer.IronCheckedElementBehaviorImpl) > -1;
    proto.behaviors.forEach((behavior: any) => configureProperties(behavior.properties));
    configureProperties(proto.properties);

    function configureProperties(properties: any) {
        if (properties) {
            Object.getOwnPropertyNames(properties)
                .filter(name => name.indexOf('_') !== 0)
                .forEach(name => configureProperty(name, properties));
        }
    }

    function configureProperty(name: string, properties: any) {
        let info = properties[name];
        if (typeof info === 'function') {
            info = {
                type: info
            };
        }

        if (info.type && !info.readOnly && (info.type === Object || info.type === Array)) {
            arrayAndObjectProperties.push(name);
        }

        if (info && info.notify) {
            propertiesWithNotify.push(name);
        }
    }

    const eventNameForProperty = (property: string) => `${property}Change`;

    const changeEventsAdapterDirective = Directive({
        selector: name,
        outputs: propertiesWithNotify.map(eventNameForProperty),
        host: propertiesWithNotify.reduce((hostBindings, property) => {
            hostBindings[`(${Polymer.CaseMap.camelToDashCase(property)}-changed)`] = `_emitChangeEvent('${property}', $event);`;
            return hostBindings;
        }, {})
    }).Class({
        constructor: function () {
            propertiesWithNotify
                .forEach(property => this[eventNameForProperty(property)] = new EventEmitter<any>(false));
        },

        _emitChangeEvent(property: string, event: any) {
            // Event is a notification for a sub-property when `path` exists and the
            // event.detail.value holds a value for a sub-property.

            // For sub-property changes we don't need to explicitly emit events,
            // since all interested parties are bound to the same object and Angular
            // takes care of updating sub-property bindings on changes.
            if (!event.detail.path) {
                this[eventNameForProperty(property)].emit(event.detail.value);
            }
        }
    });

    const validationDirective = Directive({
        selector: name
    }).Class({
        constructor: [ElementRef, Injector, function (el: ElementRef, injector: Injector) {
            this._element = el.nativeElement;
            this._injector = injector;
        }],

        ngDoCheck: function () {
            const control = this._injector.get(FormControlName, null);

            if (control) {
                this._element.invalid = !control.pristine && !control.valid;
            }
        }
    });

    const formElementDirective: any = Directive({
        selector: name,
        providers: [
            {
                provide: NG_VALUE_ACCESSOR,
                useExisting: forwardRef(() => formElementDirective),
                multi: true
            }
        ],
        host: (isCheckedElement ? {'(checkedChange)': 'onValueChanged($event)'} : {'(valueChange)': 'onValueChanged($event)'})
    }).Class({
        constructor: [Renderer, ElementRef, function (renderer: Renderer, el: ElementRef) {
            this._renderer = renderer;
            this._element = el.nativeElement;
            this._element.addEventListener('blur', () => this.onTouched(), true);
        }],

        onChange: (_: any) => {
        },
        onTouched: () => {
        },

        writeValue: function (value: any): void {
            this._renderer.setElementProperty(this._element, (isCheckedElement ? 'checked' : 'value'), value);
        },

        registerOnChange: function (fn: (_: any) => void): void {
            this.onChange = fn;
        },
        registerOnTouched: function (fn: () => void): void {
            this.onTouched = fn;
        },

        onValueChanged: function (value: any) {
            this.onChange(value);
        }
    });

    const notifyForDiffersDirective = Directive({
        selector: name,
        inputs: arrayAndObjectProperties,
        host: arrayAndObjectProperties.reduce((hostBindings, property) => {
            hostBindings[`(${Polymer.CaseMap.camelToDashCase(property)}-changed)`] = `_setValueFromElement('${property}', $event);`;
            return hostBindings;
        }, {})

    }).Class({

        constructor: [ElementRef, IterableDiffers, KeyValueDiffers, function (el: ElementRef, iterableDiffers: IterableDiffers, keyValueDiffers: KeyValueDiffers) {
            this._element = el.nativeElement;
            this._iterableDiffers = iterableDiffers;
            this._keyValueDiffers = keyValueDiffers;
            this._differs = {};
            this._arrayDiffs = {};
        }],

        ngOnInit() {
            let elm = (<any>this)._element;
            // In case the element has a default value and the directive doesn't have any value set for a property,
            // we need to make sure the element value is set to the directive.
            arrayAndObjectProperties.filter(property => elm[property] && !this[property])
                .forEach(property => {
                    this[property] = elm[property];
                });
        },

        _setValueFromElement(property: string, event: Event) {
            // Properties in this directive need to be kept synced manually with the element properties.
            // Don't use event.detail.value here because it might contain changes for a sub-property.
            let target: any = event.target;
            if (this[property] !== target[property]) {
                this[property] = target[property];
                (<any>this)._differs[property] = this._createDiffer(this[property]);
            }
        },

        _createDiffer(value: string) {
            let differ = Array.isArray(value) ? (<any>this)._iterableDiffers.find(value).create(null) : (<any>this)._keyValueDiffers.find(value || {}).create(null);

            // initial diff with the current value to make sure the differ is synced
            // and doesn't report any outdated changes on the next ngDoCheck call.
            differ.diff(value);

            return differ;
        },

        _handleArrayDiffs(property: string, diff: any) {
            if (diff) {
                diff.forEachRemovedItem((item: any) => this._notifyArray(property, item.previousIndex));
                diff.forEachAddedItem((item: any) => this._notifyArray(property, item.currentIndex));
                diff.forEachMovedItem((item: any) => this._notifyArray(property, item.currentIndex));
            }
        },

        _handleObjectDiffs(property: string, diff: any) {
            if (diff) {
                let notify = (item: any) => this._notifyPath(property + '.' + item.key, item.currentValue);
                diff.forEachRemovedItem(notify);
                diff.forEachAddedItem(notify);
                diff.forEachChangedItem(notify);
            }
        },

        _notifyArray(property: string, index: number) {
            this._notifyPath(property + '.' + index, this[property][index]);
        },

        _notifyPath(path: string, value: any) {
            (<any>this)._element.notifyPath(path, value);
        },

        ngDoCheck() {
            arrayAndObjectProperties.forEach(property => {
                let elm = (<any>this)._element;
                let _differs = (<any>this)._differs;
                if (elm[property] !== this[property]) {
                    elm[property] = this[property];
                    _differs[property] = this._createDiffer(this[property]);
                } else if (_differs[property]) {

                    // TODO: these differs won't pickup any changes in need properties like items[0].foo
                    let diff = _differs[property].diff(this[property]);
                    if (diff instanceof DefaultIterableDiffer) {
                        this._handleArrayDiffs(property, diff);
                    } else {
                        this._handleObjectDiffs(property, diff);
                    }
                }
            });
        }
    });

    const reloadConfigurationDirective = Directive({
        selector: name
    }).Class({
        constructor: [ElementRef, NgZone, function (el: ElementRef, zone: NgZone) {
            el.nativeElement.async(() => {
                if (el.nativeElement.isInitialized()) {
                    // Reload outside of Angular to prevent unnecessary ngDoCheck calls
                    zone.runOutsideAngular(() => {
                        el.nativeElement.reloadConfiguration();
                    });
                }
            });
        }],
    });

    let directives = [changeEventsAdapterDirective, notifyForDiffersDirective];

    if (isFormElement) {
        directives.push(formElementDirective);
        directives.push(validationDirective);
    }

    // If the element has isInitialized and reloadConfiguration methods (e.g., Charts)
    if (typeof proto.isInitialized === 'function' &&
        typeof proto.reloadConfiguration === 'function') {
        directives.push(reloadConfigurationDirective);
    }

    return directives;
}


================================================
FILE: src/polymer-module.spec.ts
================================================
import './renderer/polymer-renderer.spec';

import {RootRenderer} from '@angular/core';
import {NgModuleResolver} from '@angular/compiler';
import {PolymerRootRenderer} from './renderer/polymer-renderer';

import {POLYMER_RENDER_PROVIDERS, PolymerModule} from './polymer-module';

describe('POLYMER_RENDER_PROVIDERS', () => {
    it('contains PolymerRootRenderer', () => {
        expect(POLYMER_RENDER_PROVIDERS).toContain(PolymerRootRenderer);
    });

    it('provides RootRenderer with PolymerRootRenderer', () => {
        const item: any = POLYMER_RENDER_PROVIDERS.find((d: any) => d.provide === RootRenderer);
        expect(item.useExisting).toBe(PolymerRootRenderer);
    });
});

describe('PolymerModule', () => {
    let resolver: NgModuleResolver;

    beforeEach(() => {
        resolver = new NgModuleResolver();
    });

    it('is NgModule', () => {
        expect(resolver.isNgModule(PolymerModule)).toBe(true);
    });

    it('has POLYMER_RENDER_PROVIDERS', () => {
        const metadata = resolver.resolve(PolymerModule);
        expect(metadata.providers).toContain(POLYMER_RENDER_PROVIDERS);
    });
});


================================================
FILE: src/polymer-module.ts
================================================
import {NgModule, Provider, RootRenderer} from '@angular/core';
import {BrowserModule} from '@angular/platform-browser';

import {SharedCustomStylesHost} from './renderer/shared-custom-styles-host';
import {PolymerRootRenderer} from './renderer/polymer-renderer';

export const POLYMER_RENDER_PROVIDERS: Provider[] = [
    SharedCustomStylesHost,
    PolymerRootRenderer,
    {provide: RootRenderer, useExisting: PolymerRootRenderer}
];

@NgModule({
    exports: [BrowserModule],
    providers: [POLYMER_RENDER_PROVIDERS]
})
export class PolymerModule {
}


================================================
FILE: src/renderer/polymer-renderer.spec.ts
================================================
import {async, TestBed, ComponentFixture} from '@angular/core/testing';
import {Component, Renderer, RootRenderer, CUSTOM_ELEMENTS_SCHEMA, ViewEncapsulation} from '@angular/core';

import {PolymerModule} from '../polymer-module';
import {DefaultPolymerRenderer, EmulatedEncapsulationPolymerRenderer, ShadowDomPolymerRenderer, PolymerRootRenderer} from './polymer-renderer';

const Polymer: any = (<any>window).Polymer;

@Component({
    template: `<test-element [(value)]="value" [(nestedObject)]="nestedObject" [(arrayObject)]="arrayObject" boolean-value></test-element>`
})
class RendererTestComponent {
    constructor(public renderer: Renderer,
                public rootRenderer: RootRenderer) {
    }

    value = 'foo';
    nestedObject = {value: undefined};
    arrayObject = [];
    barVisible = false;
}

@Component({
    template: `<p>encapsulation: none</p>`,
    styles: [`p { order: 1; }`],
    encapsulation: ViewEncapsulation.None
})
class StyleEncapsulationNoneComponent {}

@Component({
    template: `<p>encapsulation: native</p>`,
    styles: [`p { order: 2; }`],
    encapsulation: ViewEncapsulation.Native
})
class StyleEncapsulationNativeComponent {}

@Component({
    template: `<p>encapsulation: emulated</p>`,
    styles: [`p { order: 3; }`],
    encapsulation: ViewEncapsulation.Emulated
})
class StyleEncapsulationEmulatedComponent {}

describe('DefaultPolymerRenderer', () => {
    beforeEach(async(() => {
        TestBed.configureTestingModule({
            imports: [PolymerModule],
            declarations: [
                RendererTestComponent,
                StyleEncapsulationNoneComponent
            ],
            schemas: [CUSTOM_ELEMENTS_SCHEMA]
        });
        TestBed.compileComponents();
    }));

    let testElement: Element;
    let testDomApi: any;
    let renderer: DefaultPolymerRenderer;
    let rootRenderer: PolymerRootRenderer;

    beforeEach(() => {
        const fixture = TestBed.createComponent(RendererTestComponent);
        const testComponent = fixture.componentInstance;
        testElement = fixture.nativeElement.firstElementChild;
        renderer = <DefaultPolymerRenderer> testComponent.renderer;
        testDomApi = Polymer.dom(testElement);
        rootRenderer = <PolymerRootRenderer> testComponent.rootRenderer;
    });

    it('is in use', () => {
        expect(renderer instanceof DefaultPolymerRenderer).toBe(true);
    });

    describe('selectRootElement method', () => {
        let expectedRoot: Element;

        beforeEach(() => {
            expectedRoot = document.createElement('div');
            expectedRoot.classList.add('test-root'); // should be selected as root
            document.body.appendChild(expectedRoot);
        });

        afterEach(() => {
            document.body.removeChild(expectedRoot);
        });

        it('respects shadow DOM boundaries', () => {
            (<any> testElement).$.nested.classList.add('test-root'); // should not be selected as root

            const testRoot = renderer.selectRootElement('.test-root');

            expect(testRoot).toBe(expectedRoot);
        });

        it('clears previous content using Polymer.dom API', () => {
            const spy = jasmine.createSpy('textContentSetterSpy');
            const domApi: any = Polymer.dom(expectedRoot);
            Object.defineProperty(domApi, 'textContent', {set: spy});

            renderer.selectRootElement('.test-root');

            expect(spy).toHaveBeenCalledWith('');
        });
    });

    describe('tree manipulation', () => {
        beforeEach(() => {
            spyOn(testDomApi, 'appendChild').and.callThrough();
            spyOn(testDomApi, 'insertBefore').and.callThrough();
            spyOn(testDomApi, 'removeChild').and.callThrough();
        });

        it('implements createElement method', () => {
            const el = renderer.createElement(testElement, 'foo-element');

            expect(el instanceof Element).toBe(true);
            expect(el.localName).toBe('foo-element');
            expect(testDomApi.appendChild).toHaveBeenCalledWith(el);
        });

        it('implements createViewRoot method', () => {
            const viewRoot = renderer.createViewRoot(testElement);
            expect(viewRoot).toBe(testElement);
        });

        it('implements createTemplateAnchor method', () => {
            const anchor = renderer.createTemplateAnchor(testElement);

            expect(anchor instanceof Node).toBe(true);
            expect(testDomApi.appendChild).toHaveBeenCalledWith(anchor);
        });

        it('implements createText method', () => {
            const node = renderer.createText(testElement, 'foo');

            expect(node instanceof Node).toBe(true);
            expect(node.textContent).toBe('foo');
            expect(testDomApi.appendChild).toHaveBeenCalledWith(node);
        });

        it('implements projectNodes method', () => {
            const nodeFoo = document.createTextNode('foo');
            const nodeBar = document.createTextNode('bar');

            renderer.projectNodes(testElement, [nodeFoo, nodeBar]);

            expect(Polymer.dom(nodeFoo).parentNode).toBe(testElement);
            expect(Polymer.dom(nodeBar).parentNode).toBe(testElement);
            expect(testDomApi.appendChild).toHaveBeenCalledWith(nodeFoo);
            expect(testDomApi.appendChild).toHaveBeenCalledWith(nodeBar);
        });

        it('implements attachViewAfter method, target node is last child case', () => {
            const nodeFoo = document.createTextNode('foo');
            Polymer.dom(testElement).appendChild(nodeFoo);

            const viewRootBaz = document.createTextNode('baz');
            const viewRootQux = document.createTextNode('qux');

            renderer.attachViewAfter(nodeFoo, [viewRootBaz, viewRootQux]);

            expect(Polymer.dom(viewRootBaz).parentNode).toBe(testElement);
            expect(Polymer.dom(viewRootQux).parentNode).toBe(testElement);
            expect(testDomApi.appendChild).toHaveBeenCalledWith(viewRootBaz);
            expect(testDomApi.appendChild).toHaveBeenCalledWith(viewRootQux);
        });

        it('implements attachViewAfter method, target node is non-last child case', () => {
            const nodeFoo = document.createTextNode('foo');
            const nodeBar = document.createTextNode('bar');
            Polymer.dom(testElement).appendChild(nodeFoo);
            Polymer.dom(testElement).appendChild(nodeBar);

            const viewRootBaz = document.createTextNode('baz');
            const viewRootQux = document.createTextNode('qux');

            renderer.attachViewAfter(nodeFoo, [viewRootBaz, viewRootQux]);

            expect(Polymer.dom(viewRootBaz).parentNode).toBe(testElement);
            expect(Polymer.dom(viewRootQux).parentNode).toBe(testElement);
            expect(testDomApi.insertBefore).toHaveBeenCalledWith(viewRootBaz, nodeBar);
            expect(testDomApi.insertBefore).toHaveBeenCalledWith(viewRootQux, nodeBar);
        });

        it('implements detachView method', () => {
            const nodeFoo = document.createTextNode('foo');
            Polymer.dom(testElement).appendChild(nodeFoo);
            const viewRootBaz = document.createTextNode('baz');
            const viewRootQux = document.createTextNode('qux');
            renderer.attachViewAfter(nodeFoo, [viewRootBaz, viewRootQux]);

            renderer.detachView([viewRootBaz, viewRootQux]);

            expect(Polymer.dom(viewRootBaz).parentNode).toBe(null);
            expect(Polymer.dom(viewRootQux).parentNode).toBe(null);
            expect(testDomApi.removeChild).toHaveBeenCalledWith(viewRootBaz);
            expect(testDomApi.removeChild).toHaveBeenCalledWith(viewRootQux);
        });
    });

    describe('listen', () => {
        const callbacks = {
            returnTrue: () => true,
            returnFalse: () => false
        };

        let fakeClickEvent: CustomEvent;

        beforeEach(() => {
            spyOn(callbacks, 'returnTrue').and.callThrough();
            spyOn(callbacks, 'returnFalse').and.callThrough();

            fakeClickEvent = new CustomEvent('click', {
                bubbles: true,
                cancelable: true
            });
            spyOn(fakeClickEvent, 'preventDefault').and.callThrough();
        });

        it('implements listen method, with default action', () => {
            renderer.listen(testElement, 'click', callbacks.returnTrue);

            testElement.dispatchEvent(fakeClickEvent);
            expect(callbacks.returnTrue).toHaveBeenCalledWith(fakeClickEvent);
            expect(fakeClickEvent.returnValue).not.toBe(false);
            expect(fakeClickEvent.preventDefault).not.toHaveBeenCalled();
        });

        it('implements listen method, without default action', () => {
            renderer.listen(testElement, 'click', callbacks.returnFalse);

            testElement.dispatchEvent(fakeClickEvent);
            expect(callbacks.returnFalse).toHaveBeenCalledWith(fakeClickEvent);
            expect(fakeClickEvent.returnValue).toBe(false);
            expect(fakeClickEvent.preventDefault).toHaveBeenCalled();
        });

        it('implements listenGlobal method, with default action', () => {
            renderer.listenGlobal('window', 'click', callbacks.returnTrue);

            testElement.dispatchEvent(fakeClickEvent);
            expect(callbacks.returnTrue).toHaveBeenCalledWith(fakeClickEvent);
            expect(fakeClickEvent.returnValue).not.toBe(false);
            expect(fakeClickEvent.preventDefault).not.toHaveBeenCalled();
        });

        it('implements listenGlobal method, without default action', () => {
            renderer.listenGlobal('window', 'click', callbacks.returnFalse);

            testElement.dispatchEvent(fakeClickEvent);
            expect(callbacks.returnFalse).toHaveBeenCalledWith(fakeClickEvent);
            expect(fakeClickEvent.returnValue).toBe(false);
            expect(fakeClickEvent.preventDefault).toHaveBeenCalled();
        });
    });

    it('implements setElementProperty method', () => {
        renderer.setElementProperty(testElement, 'foo', 'bar');
        expect(testElement['foo']).toBe('bar');
    });

    it('implements setElementAttribute method', () => {
        spyOn(testDomApi, 'setAttribute');
        spyOn(testDomApi, 'removeAttribute');

        renderer.setElementAttribute(testElement, 'foo', 'bar');
        renderer.setElementAttribute(testElement, 'baz', '');
        renderer.setElementAttribute(testElement, 'qux', null);

        expect(testDomApi.setAttribute).toHaveBeenCalledWith('foo', 'bar');
        expect(testDomApi.setAttribute).toHaveBeenCalledWith('baz', '');
        expect(testDomApi.removeAttribute).toHaveBeenCalledWith('qux');
    });

    it('implements setBindingDebugInfo method', () => {
        spyOn(testDomApi, 'setAttribute');
        spyOn(testDomApi, 'removeAttribute');

        renderer.setBindingDebugInfo(testElement, 'foo', 'bar');
        renderer.setBindingDebugInfo(testElement, 'baz', '');
        renderer.setBindingDebugInfo(testElement, 'qux', null);

        expect(testDomApi.setAttribute).toHaveBeenCalledWith('foo', 'bar');
        expect(testDomApi.setAttribute).toHaveBeenCalledWith('baz', '');
        expect(testDomApi.removeAttribute).toHaveBeenCalledWith('qux');
    });

    it('implements setElementClass method', () => {
        spyOn(testDomApi.classList, 'add');
        spyOn(testDomApi.classList, 'remove');

        renderer.setElementClass(testElement, 'foo', true);
        renderer.setElementClass(testElement, 'bar', false);

        expect(testDomApi.classList.add).toHaveBeenCalledWith('foo');
        expect(testDomApi.classList.remove).toHaveBeenCalledWith('bar');
    });

    it('implements setElementStyle method', () => {
        const testHtmlElement = testElement as HTMLElement;
        spyOn(testHtmlElement.style, 'setProperty');
        spyOn(testHtmlElement.style, 'removeProperty');

        renderer.setElementStyle(testHtmlElement, '--foo', 'none');
        renderer.setElementStyle(testHtmlElement, '--bar', '');

        expect(testHtmlElement.style.setProperty).toHaveBeenCalledWith('--foo', 'none');
        expect(testHtmlElement.style.removeProperty).toHaveBeenCalledWith('--bar');
    });

    it('implements invokeElementMethod method', () => {
        spyOn(testElement, (<any> 'click'));
        const callArgs = [1, 'foo'];

        renderer.invokeElementMethod(testElement, 'click', callArgs);

        const calls = testElement['click'].calls.all();
        expect(calls.length).toBe(1);
        expect(calls[0].object).toBe(testElement);
        expect(calls[0].args).toEqual(callArgs);
        expect(calls[0].returnValue).toBeUndefined;
    });

    it('implements setText method', () => {
        const textNode = renderer.createText(testElement, '');
        renderer.setText(textNode, 'foo');
        expect(textNode.nodeValue).toBe('foo');
    });

    it('implements animate method', () => {
        spyOn(rootRenderer.animationDriver, 'animate');

        const animateArgs = [
            testElement,
            {}, // startingStyles: any
            [], // keyframes: any[]
            0,  // duration: number
            0,  // delay: number
            '', // easing: string
            []  // previousPlayers: AnimationPlayer[]
        ];

        renderer.animate.apply(renderer, animateArgs);

        expect(
            (<any>rootRenderer.animationDriver.animate).calls.mostRecent().args
        ).toEqual(animateArgs);
    });

    describe('PolymerRootRenderer', () => {
        it('is injectable', () => {
            expect(rootRenderer instanceof PolymerRootRenderer).toBe(true);
        });
    });

    describe('setElementAttribute', () => {
        it('supports boolean attributes with no value', () => {
            expect((<any> testElement).booleanValue).toBe(true);
        });
    });

    describe('setElementProperty', () => {
        it('updates the value of angular component and polymer component', () => {
            (<any> testElement).value = 'Should change';
            expect((<any> testElement).value).toBe('Should change');
        });
    });

    it('applies component styles', () => {
        const fixture = TestBed.createComponent(StyleEncapsulationNoneComponent);
        const testElement = fixture.nativeElement.firstElementChild;
        expect(window.getComputedStyle(testElement).order).toBe('1');
    });
});

describe('ShadowDomPolymerRenderer', () => {
    beforeEach(async(() => {
        TestBed.configureTestingModule({
            imports: [PolymerModule],
            declarations: [
                StyleEncapsulationNativeComponent
            ],
            schemas: [CUSTOM_ELEMENTS_SCHEMA]
        });
        TestBed.compileComponents();
    }));

    let testElement: Element;

    beforeEach(() => {
        const fixture = TestBed.createComponent(StyleEncapsulationNativeComponent);
        testElement = fixture.nativeElement.shadowRoot.querySelector('p');
    });

    it('applies component styles', () => {
        expect(window.getComputedStyle(testElement).order).toBe('2');
    });

    it('encapsulates component styles', () => {
        const p = document.createElement('p');
        document.body.appendChild(p);
        expect(window.getComputedStyle(p).order).not.toBe('2');
    });
});

describe('EmulatedEncapsulationPolymerRenderer', () => {
    beforeEach(async(() => {
        TestBed.configureTestingModule({
            imports: [PolymerModule],
            declarations: [
                StyleEncapsulationEmulatedComponent
            ],
            schemas: [CUSTOM_ELEMENTS_SCHEMA]
        });
        TestBed.compileComponents();
    }));

    let testElement: Element;

    beforeEach(() => {
        const fixture = TestBed.createComponent(StyleEncapsulationEmulatedComponent);
        testElement = fixture.nativeElement.firstElementChild;
    });

    it('applies component styles', () => {
        expect(window.getComputedStyle(testElement).order).toBe('3');
    });

    it('encapsulates component styles', () => {
        const p = document.createElement('p');
        document.body.appendChild(p);
        expect(window.getComputedStyle(p).order).not.toBe('3');
        document.body.removeChild(p);
    });
});


================================================
FILE: src/renderer/polymer-renderer.ts
================================================
import {APP_ID, Injectable, Inject, Renderer, RootRenderer, RenderComponentType, AnimationPlayer, ViewEncapsulation} from '@angular/core';
import {EventManager, AnimationDriver, DOCUMENT} from '@angular/platform-browser';
import {SharedCustomStylesHost} from './shared-custom-styles-host';

const Polymer: any = (<any>window).Polymer;

const COMPONENT_REGEX = /%COMP%/g;
const COMPONENT_VARIABLE = '%COMP%';
const HOST_ATTR = `_nghost-${COMPONENT_VARIABLE}`;
const CONTENT_ATTR = `_ngcontent-${COMPONENT_VARIABLE}`;

function flattenStyles(
        styleShimId: string,
        styles: Array<any|any[]>,
        target: string[]): string[] {
    for (let i = 0; i < styles.length; i++) {
        let style = styles[i];
        if (Array.isArray(style)) {
            flattenStyles(styleShimId, style, target);
        } else {
            style = style.replace(COMPONENT_REGEX, styleShimId);
            target.push(style);
        }
    }
    return target;
}

function decoratePreventDefault(handler: Function) {
    return (event: any) => {
        const allowDefault = handler(event);
        if (allowDefault === false) {
            event.preventDefault();
            event.returnValue = false;
        }
    };
}

/**
 * The polymer renderer takes care of supporting angular > 2.2 shady DOM
 *
 * The problem:
 * Starting from v2.2, Angular uses direct DOM rendering in browser. BrowserDomAdapter is not invoked by Renderer.
 * Therefore, changing the default BrowserDomAdapter to PolymerDomAdapter trick is not helpful anymore.
 * The issue breaks setting Light DOM for Polymer elements in Shady DOM mode of Polymer v1.0.
 *
 * The solution:
 * Instead of PolymerDomAdapter, we created the PolymerRenderer by implementing the Renderer interface.
 * The PolymerRenderer calls Polymer.dom APIs instead of DOM methods. In order to make Angular use the PolymerRenderer,
 * we need to define and export custom platforms, i.e., platformPolymer and platformPolymerDynamic.
 * In practice, developers will have to switch the imports in their main.ts files to use our custom Polymer platforms
 * instead of the default platformBrowser and platformBrowserDynamic.
 *
 * DefaultPolymerRenderer used for ViewEncapsulation.None, other style incapsulation modes are implemented in subclasses.
 */
export class DefaultPolymerRenderer implements Renderer {
    constructor(
            private _eventManager: EventManager,
            private _animationDriver: AnimationDriver
    ) { }

    selectRootElement(selectorOrElement: string|Element): Element {
        let el: Element;
        if (typeof selectorOrElement === 'string') {
            el = Polymer.dom(document).querySelector(selectorOrElement);
            if (!el) {
                throw new Error(`Root element for selector "${selectorOrElement}" was not found`);
            }
        } else {
            el = selectorOrElement;
        }
        Polymer.dom(el).textContent = '';
        return el;
    }

    createElement(parent: Element|DocumentFragment, name: string): Element {
        const el: Element = document.createElement(name);
        if (parent) {
            Polymer.dom(parent).appendChild(el);
        }
        return el;
    }

    createViewRoot(hostElement: Element): Element|DocumentFragment {
        return hostElement;
    }

    createTemplateAnchor(parent: Element|DocumentFragment): Element {
        const anchor = document.createElement('template-anchor');
        anchor.setAttribute('hidden', 'hidden');
        if (parent) {
            Polymer.dom(parent).appendChild(anchor);
        }
        return anchor;
    }

    createText(parent: Element|DocumentFragment, value: string): Text {
        const node = document.createTextNode(value);
        if (parent) {
            Polymer.dom(parent).appendChild(node);
        }
        return node;
    }

    projectNodes(parent: Element|DocumentFragment, nodes: Node[]) {
        if (!parent) return;
        const parentDomApi: any = Polymer.dom(parent);
        for (let i = 0; i < nodes.length; i++) {
            parentDomApi.appendChild(nodes[i]);
        }
    }

    attachViewAfter(node: Node, viewRootNodes: Node[]) {
        const parent: Element = Polymer.dom(node).parentNode;
        if (!parent || viewRootNodes.length === 0) return;
        const parentDomApi = Polymer.dom(parent);
        const nextSibling: Node = Polymer.dom(node).nextSibling;
        if (nextSibling) {
            for (let i = 0; i < viewRootNodes.length; i++) {
                parentDomApi.insertBefore(viewRootNodes[i], nextSibling);
            }
        } else {
            for (let i = 0; i < viewRootNodes.length; i++) {
                parentDomApi.appendChild(viewRootNodes[i]);
            }
        }
    }

    detachView(viewRootNodes: Node[]) {
        for (let i = 0; i < viewRootNodes.length; i++) {
            const node: Node = viewRootNodes[i];
            const parent: Element = Polymer.dom(node).parentNode;
            if (parent) {
                Polymer.dom(parent).removeChild(node);
            }
        }
    }

    destroyView(hostElement: Element|DocumentFragment, viewAllNodes: Node[]) {
    }

    listen(renderElement: any, name: string, callback: Function): Function {
        return this._eventManager.addEventListener(
            renderElement,
            name,
            decoratePreventDefault(callback)
        );
    }

    listenGlobal(target: string, name: string, callback: Function): Function {
        return this._eventManager.addGlobalEventListener(
            target,
            name,
            decoratePreventDefault(callback)
        );
    }

    setElementProperty(renderElement: Element|DocumentFragment, propertyName: string, propertyValue: any): void {
        (renderElement as any)[propertyName] = propertyValue;
    }

    setElementAttribute(renderElement: Element|DocumentFragment, attributeName: string, attributeValue: string): void {
        if (attributeValue != null) {
            Polymer.dom(renderElement).setAttribute(attributeName, attributeValue);
        } else {
            Polymer.dom(renderElement).removeAttribute(attributeName);
        }
    }

    setBindingDebugInfo(renderElement: Element, propertyName: string, propertyValue: string): void {
        this.setElementAttribute(renderElement, propertyName, propertyValue);
    }

    setElementClass(renderElement: Element, className: string, isAdd: boolean) {
        if (isAdd) {
            Polymer.dom(renderElement).classList.add(className);
        } else {
            Polymer.dom(renderElement).classList.remove(className);
        }
    }

    setElementStyle(renderElement: HTMLElement, styleName: string, styleValue: string): void {
        if (styleValue) {
            renderElement.style.setProperty(styleName, styleValue);
        } else {
            renderElement.style.removeProperty(styleName);
        }
    }

    invokeElementMethod(renderElement: Element, methodName: string, args: any[]) {
        (renderElement as any)[methodName].apply(renderElement, args);
    }

    setText(renderNode: Text, text: string): void {
        renderNode.nodeValue = text;
    }

    animate(element: any,
            startingStyles: any,
            keyframes: any[],
            duration: number,
            delay: number,
            easing: string,
            previousPlayers: AnimationPlayer[] = []): AnimationPlayer {
        if (!element.domHost && document.body.contains(element)) {
            return this._animationDriver.animate(element, startingStyles, keyframes, duration, delay, easing, previousPlayers);
        }
    }
}

/**
 * Polymer renderer for ViewEncapsulation.Emulated styles encapsulation mode (defaultmode)
 */
export class EmulatedEncapsulationPolymerRenderer extends DefaultPolymerRenderer {
    private _contentAttr: string;
    private _hostAttr: string;

    constructor(
            eventManager: EventManager,
            animationDriver: AnimationDriver,
            sharedCustomStylesHost: SharedCustomStylesHost,
            componentType: RenderComponentType,
            styleShimId: string
    ) {
        super(eventManager, animationDriver);
        const styles = flattenStyles(styleShimId, componentType.styles, []);
        sharedCustomStylesHost.addStyles(styles);
        this._contentAttr = CONTENT_ATTR.replace(COMPONENT_REGEX, styleShimId);
        this._hostAttr  = HOST_ATTR.replace(COMPONENT_REGEX, styleShimId);
    }

    createViewRoot(hostElement: Element): Element|DocumentFragment {
        super.setElementAttribute(hostElement, this._hostAttr, '');
        return hostElement;
    }

    createElement(parent: Element|DocumentFragment, name: string): Element {
        const el: Element = super.createElement(parent, name);
        super.setElementAttribute(el, this._contentAttr, '');
        return el;
    }
}

/**
 * Polymer renderer for ViewEncapsulation.Native styles encapsulation mode
 */
export class ShadowDomPolymerRenderer extends DefaultPolymerRenderer {
    private _shadowRoot: DocumentFragment;
    private _styles: string[];

    constructor(
            eventManager: EventManager,
            animationDriver: AnimationDriver,
            private sharedCustomStylesHost: SharedCustomStylesHost,
            componentType: RenderComponentType,
            styleShimId: string
    ) {
        super(eventManager, animationDriver);
        this._styles = flattenStyles(styleShimId, componentType.styles, []);
    }

    createViewRoot(hostElement: Element): Element|DocumentFragment {
        super.createViewRoot(hostElement);
        this._shadowRoot = <DocumentFragment> (hostElement as any).createShadowRoot();
        this.sharedCustomStylesHost.addHost(this._shadowRoot);
        this._styles.forEach(style => {
            const styleEl: Element = document.createElement('style');
            styleEl.textContent = style;
            this._shadowRoot.appendChild(styleEl);
        });
        return this._shadowRoot;
    }

    destroyView(hostElement: Element|DocumentFragment, viewAllNodes: Node[]) {
        this.sharedCustomStylesHost.removeHost(this._shadowRoot);
    }
}

@Injectable()
export class PolymerRootRenderer implements RootRenderer {
    protected registeredComponents: Map<string, Renderer> = new Map<string, Renderer>();
    private defaultRenderer: Renderer;

    constructor(
            @Inject(DOCUMENT) public document: any,
            public eventManager: EventManager,
            public sharedCustomStylesHost: SharedCustomStylesHost,
            public animationDriver: AnimationDriver,
            @Inject(APP_ID) public appId: string
    ) {
        this.defaultRenderer = new DefaultPolymerRenderer(eventManager, animationDriver);
    }

    renderComponent(componentType: RenderComponentType): Renderer {
        const styleShimId = `${this.appId}-${componentType.id}`;
        switch (componentType.encapsulation) {
            case ViewEncapsulation.Emulated: {
                let renderer = this.registeredComponents.get(componentType.id);
                if (!renderer) {
                    renderer = new EmulatedEncapsulationPolymerRenderer(
                            this.eventManager,
                            this.animationDriver,
                            this.sharedCustomStylesHost,
                            componentType,
                            styleShimId
                    );
                    this.registeredComponents.set(componentType.id, renderer);
                }
                return renderer;
            }
            case ViewEncapsulation.Native: {
                return new ShadowDomPolymerRenderer(
                        this.eventManager,
                        this.animationDriver,
                        this.sharedCustomStylesHost,
                        componentType,
                        styleShimId
                );
            }
            default: {
                if (!this.registeredComponents.has(componentType.id)) {
                    const styles = flattenStyles(styleShimId, componentType.styles, []);
                    this.sharedCustomStylesHost.addStyles(styles);
                    this.registeredComponents.set(componentType.id, this.defaultRenderer);
                }
                return this.defaultRenderer;
            }
        }
    }
}


================================================
FILE: src/renderer/shared-custom-styles-host.ts
================================================
import {Inject, Injectable, OnDestroy} from '@angular/core';
import {DOCUMENT} from '@angular/platform-browser';

const Polymer: any = (<any>window).Polymer;

@Injectable()
export class SharedCustomStylesHost implements OnDestroy {
    private _stylesSet = new Set<string>();
    private _hostNodes = new Set<Node>();
    private _customStyleNodes = new Set<Node>();

    constructor(@Inject(DOCUMENT) private _doc: any) {
        this._hostNodes.add(_doc.head);
    }

    addStyles(styles: string[]): void {
        styles.forEach(style => {
            if (!this._stylesSet.has(style)) {
                this._stylesSet.add(style);
                this._hostNodes.forEach(hostNode => {
                    this._addStyleToHost(style, hostNode);
                });
            }
        });
    }

    private _addStyleToHost(style: string, host: Node): void {
        const customStyleEl = <Element>(<any>this._doc).createElement('style', 'custom-style');
        customStyleEl.textContent = style;
        Polymer.dom(host).appendChild(customStyleEl);
        Polymer.dom.flush();
        Polymer.updateStyles();
        this._customStyleNodes.add(customStyleEl);
    }

    addHost(hostNode: Node): void {
        this._stylesSet.forEach(style => this._addStyleToHost(style, hostNode));
        this._hostNodes.add(hostNode);
    }

    removeHost(hostNode: Node): void {
        this._hostNodes.delete(hostNode);
    }

    ngOnDestroy(): void {
        this._customStyleNodes.forEach(styleNode => Polymer.dom(Polymer.dom(styleNode).parentNode).removeChild(styleNode));
    }

    getAllStyles(): string[] {
        return Array.from(this._stylesSet);
    }
}


================================================
FILE: src/test-element.html
================================================
<link rel="import" href="../bower_components/polymer/polymer.html">
<link rel="import" href="../bower_components/iron-form-element-behavior/iron-form-element-behavior.html">
<link rel="import" href="../bower_components/paper-checkbox/paper-checkbox.html">

<dom-module id="test-element">
    <template>
        <div id="nested" nested-object-value$=[[nestedObject.value]] array-object-value$=[[arrayObject.0]]></div>
        <content id="selected" select=".selected"></content>
        <content id="all"></content>
    </template>
</dom-module>

<script>
    Polymer({
        is: 'test-element',

        behaviors: [
            Polymer.IronFormElementBehavior
        ],

        properties: {
            value: {
                value: '',
                notify: true
            },

            booleanValue: {
                type: Boolean
            },

            nestedObject: {
                value: {
                    value: ''
                }
            },

            arrayObject: {
                value: []
            }
        }
    });
</script>


================================================
FILE: system.config.js
================================================
System.config({
    baseURL: '/base',
    paths: {
        'npm:': 'node_modules/'
    },
    map: {
        '@angular/common': 'npm:@angular/common/bundles/common.umd.js',
        '@angular/common/testing': 'npm:@angular/common/bundles/common-testing.umd.js',
        '@angular/core': 'npm:@angular/core/bundles/core.umd.js',
        '@angular/core/testing': 'npm:@angular/core/bundles/core-testing.umd.js',
        '@angular/compiler': 'npm:@angular/compiler/bundles/compiler.umd.js',
        '@angular/compiler/testing': 'npm:@angular/compiler/bundles/compiler-testing.umd.js',
        '@angular/forms': 'npm:@angular/forms/bundles/forms.umd.js',
        '@angular/platform-browser': 'npm:@angular/platform-browser/bundles/platform-browser.umd.js',
        '@angular/platform-browser/testing': 'npm:@angular/platform-browser/bundles/platform-browser-testing.umd.js',
        '@angular/platform-browser-dynamic': 'npm:@angular/platform-browser-dynamic/bundles/platform-browser-dynamic.umd.js',
        '@angular/platform-browser-dynamic/testing': 'npm:@angular/platform-browser-dynamic/bundles/platform-browser-dynamic-testing.umd.js',
        'rxjs': 'npm:rxjs',
    },
    packages: {
        '': {defaultExtension: 'js'},
        rxjs: {defaultExtension: 'js'}
    }
});

(function() {
    var originalKarmaLoaded = window.__karma__.loaded.bind(window.__karma__);
    window.__karma__.loaded = function() {};

    document.addEventListener('WebComponentsReady', function () {
        System.import('index.spec')
            .then(function () {
                return Promise.all([
                    System.import('@angular/core/testing'),
                    System.import('@angular/platform-browser-dynamic/testing')
                ]).then(function (providers) {
                    var testing = providers[0];
                    var testingBrowser = providers[1];

                    testing.TestBed.initTestEnvironment(
                        testingBrowser.BrowserDynamicTestingModule,
                        testingBrowser.platformBrowserDynamicTesting());
                });
            })
            .then(originalKarmaLoaded)
            .catch(console.error.bind(console));
    });
})();


================================================
FILE: tsconfig.json
================================================
{
  "compilerOptions": {
    "declaration": true,
    "target": "es5",
    "module": "commonjs",
    "moduleResolution": "node",
    "inlineSourceMap": true,
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "removeComments": false,
    "noImplicitAny": false,
    "suppressImplicitAnyIndexErrors": true,
    "typeRoots": [
      "./node_modules/@types/"
    ],
    "lib": [
      "es6",
      "dom"
    ]
  },
  "exclude": [
    "bower_components",
    "node_modules"
  ]
}


================================================
FILE: tslint.json
================================================
{
    "jsRules": {
        "class-name": true,
        "comment-format": [
            true,
            "check-space"
        ],
        "indent": [
            true,
            "spaces"
        ],
        "no-duplicate-variable": true,
        "no-eval": true,
        "no-trailing-whitespace": true,
        "no-unsafe-finally": true,
        "one-line": [
            true,
            "check-open-brace",
            "check-whitespace"
        ],
        "quotemark": [
            true,
            "single"
        ],
        "semicolon": [
            true,
            "always"
        ],
        "triple-equals": [
            true,
            "allow-null-check"
        ],
        "variable-name": [
            true,
            "ban-keywords"
        ],
        "whitespace": [
            true,
            "check-branch",
            "check-decl",
            "check-operator",
            "check-separator",
            "check-type"
        ]
    },
    "rules": {
        "class-name": true,
        "comment-format": [
            true,
            "check-space"
        ],
        "indent": [
            true,
            "spaces"
        ],
        "no-eval": true,
        "no-internal-module": true,
        "no-trailing-whitespace": true,
        "no-unsafe-finally": true,
        "no-var-keyword": true,
        "one-line": [
            true,
            "check-open-brace",
            "check-whitespace"
        ],
        "quotemark": [
            true,
            "single"
        ],
        "semicolon": [
            true,
            "always"
        ],
        "triple-equals": [
            true,
            "allow-null-check"
        ],
        "typedef-whitespace": [
            true,
            {
                "call-signature": "nospace",
                "index-signature": "nospace",
                "parameter": "nospace",
                "property-declaration": "nospace",
                "variable-declaration": "nospace"
            }
        ],
        "variable-name": [
            true,
            "ban-keywords"
        ],
        "whitespace": [
            true,
            "check-branch",
            "check-decl",
            "check-operator",
            "check-separator",
            "check-type"
        ],
        "no-console": [
            true,
            "log",
            "error"
        ]
    }
}
Download .txt
gitextract_9htu4niu/

├── .gitignore
├── .npmignore
├── LICENSE
├── README.md
├── bower.json
├── circle.yml
├── docs/
│   ├── api.adoc
│   ├── best-practices.adoc
│   ├── ng-cli-webpack.adoc
│   ├── overview.adoc
│   ├── tutorial/
│   │   ├── creating-project.adoc
│   │   ├── dependencies.adoc
│   │   ├── hero-editor.adoc
│   │   ├── introduction.adoc
│   │   ├── layout.adoc
│   │   ├── list-heroes.adoc
│   │   ├── polymer.adoc
│   │   └── wrap-up.adoc
│   └── tutorial-index.adoc
├── index.spec.ts
├── index.ts
├── karma.conf.js
├── package.json
├── src/
│   ├── polymer-element.spec.ts
│   ├── polymer-element.ts
│   ├── polymer-module.spec.ts
│   ├── polymer-module.ts
│   ├── renderer/
│   │   ├── polymer-renderer.spec.ts
│   │   ├── polymer-renderer.ts
│   │   └── shared-custom-styles-host.ts
│   └── test-element.html
├── system.config.js
├── tsconfig.json
└── tslint.json
Download .txt
SYMBOL INDEX (59 symbols across 6 files)

FILE: src/polymer-element.spec.ts
  function createTestComponent (line 35) | function createTestComponent(type: any) {
  function formTests (line 103) | function formTests(): void {
  class TestComponent (line 223) | @Component({
  class TestComponentForm (line 233) | @Component({
  class TestComponentCheckboxForm (line 243) | @Component({

FILE: src/polymer-element.ts
  function PolymerElement (line 18) | function PolymerElement(name: string): any[] {

FILE: src/polymer-module.ts
  constant POLYMER_RENDER_PROVIDERS (line 7) | const POLYMER_RENDER_PROVIDERS: Provider[] = [
  class PolymerModule (line 17) | class PolymerModule {

FILE: src/renderer/polymer-renderer.spec.ts
  class RendererTestComponent (line 9) | @Component({
    method constructor (line 13) | constructor(public renderer: Renderer,
  class StyleEncapsulationNoneComponent (line 23) | @Component({
  class StyleEncapsulationNativeComponent (line 30) | @Component({
  class StyleEncapsulationEmulatedComponent (line 37) | @Component({

FILE: src/renderer/polymer-renderer.ts
  constant COMPONENT_REGEX (line 7) | const COMPONENT_REGEX = /%COMP%/g;
  constant COMPONENT_VARIABLE (line 8) | const COMPONENT_VARIABLE = '%COMP%';
  constant HOST_ATTR (line 9) | const HOST_ATTR = `_nghost-${COMPONENT_VARIABLE}`;
  constant CONTENT_ATTR (line 10) | const CONTENT_ATTR = `_ngcontent-${COMPONENT_VARIABLE}`;
  function flattenStyles (line 12) | function flattenStyles(
  function decoratePreventDefault (line 28) | function decoratePreventDefault(handler: Function) {
  class DefaultPolymerRenderer (line 55) | class DefaultPolymerRenderer implements Renderer {
    method constructor (line 56) | constructor(
    method selectRootElement (line 61) | selectRootElement(selectorOrElement: string|Element): Element {
    method createElement (line 75) | createElement(parent: Element|DocumentFragment, name: string): Element {
    method createViewRoot (line 83) | createViewRoot(hostElement: Element): Element|DocumentFragment {
    method createTemplateAnchor (line 87) | createTemplateAnchor(parent: Element|DocumentFragment): Element {
    method createText (line 96) | createText(parent: Element|DocumentFragment, value: string): Text {
    method projectNodes (line 104) | projectNodes(parent: Element|DocumentFragment, nodes: Node[]) {
    method attachViewAfter (line 112) | attachViewAfter(node: Node, viewRootNodes: Node[]) {
    method detachView (line 128) | detachView(viewRootNodes: Node[]) {
    method destroyView (line 138) | destroyView(hostElement: Element|DocumentFragment, viewAllNodes: Node[...
    method listen (line 141) | listen(renderElement: any, name: string, callback: Function): Function {
    method listenGlobal (line 149) | listenGlobal(target: string, name: string, callback: Function): Functi...
    method setElementProperty (line 157) | setElementProperty(renderElement: Element|DocumentFragment, propertyNa...
    method setElementAttribute (line 161) | setElementAttribute(renderElement: Element|DocumentFragment, attribute...
    method setBindingDebugInfo (line 169) | setBindingDebugInfo(renderElement: Element, propertyName: string, prop...
    method setElementClass (line 173) | setElementClass(renderElement: Element, className: string, isAdd: bool...
    method setElementStyle (line 181) | setElementStyle(renderElement: HTMLElement, styleName: string, styleVa...
    method invokeElementMethod (line 189) | invokeElementMethod(renderElement: Element, methodName: string, args: ...
    method setText (line 193) | setText(renderNode: Text, text: string): void {
    method animate (line 197) | animate(element: any,
  class EmulatedEncapsulationPolymerRenderer (line 213) | class EmulatedEncapsulationPolymerRenderer extends DefaultPolymerRenderer {
    method constructor (line 217) | constructor(
    method createViewRoot (line 231) | createViewRoot(hostElement: Element): Element|DocumentFragment {
    method createElement (line 236) | createElement(parent: Element|DocumentFragment, name: string): Element {
  class ShadowDomPolymerRenderer (line 246) | class ShadowDomPolymerRenderer extends DefaultPolymerRenderer {
    method constructor (line 250) | constructor(
    method createViewRoot (line 261) | createViewRoot(hostElement: Element): Element|DocumentFragment {
    method destroyView (line 273) | destroyView(hostElement: Element|DocumentFragment, viewAllNodes: Node[...
  class PolymerRootRenderer (line 279) | class PolymerRootRenderer implements RootRenderer {
    method constructor (line 283) | constructor(
    method renderComponent (line 293) | renderComponent(componentType: RenderComponentType): Renderer {

FILE: src/renderer/shared-custom-styles-host.ts
  class SharedCustomStylesHost (line 7) | class SharedCustomStylesHost implements OnDestroy {
    method constructor (line 12) | constructor(@Inject(DOCUMENT) private _doc: any) {
    method addStyles (line 16) | addStyles(styles: string[]): void {
    method _addStyleToHost (line 27) | private _addStyleToHost(style: string, host: Node): void {
    method addHost (line 36) | addHost(hostNode: Node): void {
    method removeHost (line 41) | removeHost(hostNode: Node): void {
    method ngOnDestroy (line 45) | ngOnDestroy(): void {
    method getAllStyles (line 49) | getAllStyles(): string[] {
Condensed preview — 34 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (153K chars).
[
  {
    "path": ".gitignore",
    "chars": 816,
    "preview": "*.js\n!karma.conf.js\n!system.config.js\n*.d.ts\n\n# Created by https://www.gitignore.io/api/node,bower,typings\n\n### Node ###"
  },
  {
    "path": ".npmignore",
    "chars": 991,
    "preview": "# Source files\nbower.json\ntsconfig.json\ntslint.json\ntypings/\ntypings.json\n*.ts\n!*.d.ts\n\n# Tests and testing setup\n*.html"
  },
  {
    "path": "LICENSE",
    "chars": 10756,
    "preview": "                                 Apache License\n                           Version 2.0, January 2004\n                   "
  },
  {
    "path": "README.md",
    "chars": 3598,
    "preview": "[![CircleCI](https://img.shields.io/circleci/project/github/platosha/angular-polymer.svg)](https://circleci.com/gh/plato"
  },
  {
    "path": "bower.json",
    "chars": 596,
    "preview": "{\n  \"name\": \"angular2-polymer\",\n  \"description\": \"Angular 2 support for Polymer elements\",\n  \"main\": \"index.js\",\n  \"auth"
  },
  {
    "path": "circle.yml",
    "chars": 194,
    "preview": "machine:\n  node:\n    version: 6.9.1\n\ndependencies:\n  override:\n    - npm install\n    - npm run bower install\n  cache_dir"
  },
  {
    "path": "docs/api.adoc",
    "chars": 3522,
    "preview": "---\ntitle: API Reference\norder: 4\nlayout: page\n---\n\n[[vaadin-angular2-polymer.api]]\n= Angular2-Polymer API\n\nThis package"
  },
  {
    "path": "docs/best-practices.adoc",
    "chars": 4756,
    "preview": "---\ntitle: Best practices\norder: 1\nlayout: page\n---\n\n[[angular-polymer.best-practices]]\n= Angular Polymer best practices"
  },
  {
    "path": "docs/ng-cli-webpack.adoc",
    "chars": 10004,
    "preview": "---\ntitle: Using in Angular CLI Webpack\norder: 3\nlayout: page\n---\n\n:linkattrs:\n[[vaadin-angular2-polymer.ng2cli]]\n= Usin"
  },
  {
    "path": "docs/overview.adoc",
    "chars": 3906,
    "preview": "---\ntitle: Overview\norder: 1\nlayout: page\n---\n\n[[vaadin-angular2-polymer.overview]]\n= Angular 2 Integration\n\nThe `angula"
  },
  {
    "path": "docs/tutorial/creating-project.adoc",
    "chars": 2507,
    "preview": "[[vaadin-angular2-polymer.tutorial.creating-project]]\n== Creating Project\n\nWe begin by creating a new project based on t"
  },
  {
    "path": "docs/tutorial/dependencies.adoc",
    "chars": 3931,
    "preview": "[[vaadin-angular2-polymer.tutorial.dependencies]]\n== Adding and Installing Dependencies\n\nAfter the previous step, we hav"
  },
  {
    "path": "docs/tutorial/hero-editor.adoc",
    "chars": 14580,
    "preview": "[[vaadin-angular2-polymer.tutorial.hero-editor]]\n== Hero Editor and Routing\n\nPreviously we added the heroes list in our "
  },
  {
    "path": "docs/tutorial/introduction.adoc",
    "chars": 2162,
    "preview": "[[vaadin-angular2-polymer.tutorial.introduction]]\n== Introduction\n\nThis tutorial explains how to use Polymer elements in"
  },
  {
    "path": "docs/tutorial/layout.adoc",
    "chars": 6254,
    "preview": "[[vaadin-angular2-polymer.tutorial.layout]]\n== Building the Application Layout with Paper Elements\n\nAfter the previous s"
  },
  {
    "path": "docs/tutorial/list-heroes.adoc",
    "chars": 7544,
    "preview": "[[vaadin-angular2-polymer.tutorial.list-heroes]]\n== List Heroes with Vaadin Grid\n\nIn the previous step, we added the app"
  },
  {
    "path": "docs/tutorial/polymer.adoc",
    "chars": 6128,
    "preview": "[[vaadin-angular2-polymer.tutorial.polymer]]\n== Adding Polymer Elements to Our Application\n\nIn the previous step, we dow"
  },
  {
    "path": "docs/tutorial/wrap-up.adoc",
    "chars": 1730,
    "preview": "[[vaadin-angular2-polymer.tutorial.wrap-up]]\n== Wrap Up\n\nYou have now finished all the tutorial steps and know how to us"
  },
  {
    "path": "docs/tutorial-index.adoc",
    "chars": 1295,
    "preview": "---\ntitle: Tutorial\norder: 2\nlayout: page\nsubnav_auto_list_numbers: true\nsubnav:\n  - title: Introduction\n    href: '#vaa"
  },
  {
    "path": "index.spec.ts",
    "chars": 73,
    "preview": "import './src/polymer-element.spec';\nimport './src/polymer-module.spec';\n"
  },
  {
    "path": "index.ts",
    "chars": 106,
    "preview": "export {PolymerElement} from './src/polymer-element';\nexport {PolymerModule} from './src/polymer-module';\n"
  },
  {
    "path": "karma.conf.js",
    "chars": 3144,
    "preview": "// Karma configuration\n// Generated on Fri Mar 10 2017 15:33:36 GMT+0200 (EET)\n\nmodule.exports = function(config) {\n  co"
  },
  {
    "path": "package.json",
    "chars": 1550,
    "preview": "{\n  \"name\": \"angular-polymer\",\n  \"version\": \"1.1.0\",\n  \"description\": \"Angular 2 support for Polymer elements\",\n  \"repos"
  },
  {
    "path": "src/polymer-element.spec.ts",
    "chars": 7891,
    "preview": "import {\n    async,\n    TestBed,\n    ComponentFixture\n} from '@angular/core/testing';\nimport {PolymerElement} from './po"
  },
  {
    "path": "src/polymer-element.ts",
    "chars": 10161,
    "preview": "import {\n    Injector,\n    Directive,\n    ElementRef,\n    EventEmitter,\n    forwardRef,\n    Renderer,\n    NgZone,\n    Ke"
  },
  {
    "path": "src/polymer-module.spec.ts",
    "chars": 1127,
    "preview": "import './renderer/polymer-renderer.spec';\n\nimport {RootRenderer} from '@angular/core';\nimport {NgModuleResolver} from '"
  },
  {
    "path": "src/polymer-module.ts",
    "chars": 556,
    "preview": "import {NgModule, Provider, RootRenderer} from '@angular/core';\nimport {BrowserModule} from '@angular/platform-browser';"
  },
  {
    "path": "src/renderer/polymer-renderer.spec.ts",
    "chars": 16355,
    "preview": "import {async, TestBed, ComponentFixture} from '@angular/core/testing';\nimport {Component, Renderer, RootRenderer, CUSTO"
  },
  {
    "path": "src/renderer/polymer-renderer.ts",
    "chars": 12340,
    "preview": "import {APP_ID, Injectable, Inject, Renderer, RootRenderer, RenderComponentType, AnimationPlayer, ViewEncapsulation} fro"
  },
  {
    "path": "src/renderer/shared-custom-styles-host.ts",
    "chars": 1667,
    "preview": "import {Inject, Injectable, OnDestroy} from '@angular/core';\nimport {DOCUMENT} from '@angular/platform-browser';\n\nconst "
  },
  {
    "path": "src/test-element.html",
    "chars": 1076,
    "preview": "<link rel=\"import\" href=\"../bower_components/polymer/polymer.html\">\n<link rel=\"import\" href=\"../bower_components/iron-fo"
  },
  {
    "path": "system.config.js",
    "chars": 2211,
    "preview": "System.config({\n    baseURL: '/base',\n    paths: {\n        'npm:': 'node_modules/'\n    },\n    map: {\n        '@angular/c"
  },
  {
    "path": "tsconfig.json",
    "chars": 500,
    "preview": "{\n  \"compilerOptions\": {\n    \"declaration\": true,\n    \"target\": \"es5\",\n    \"module\": \"commonjs\",\n    \"moduleResolution\":"
  },
  {
    "path": "tslint.json",
    "chars": 2375,
    "preview": "{\n    \"jsRules\": {\n        \"class-name\": true,\n        \"comment-format\": [\n            true,\n            \"check-space\"\n "
  }
]

About this extraction

This page contains the full source code of the vaadin/angular2-polymer GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 34 files (143.0 KB), approximately 33.7k tokens, and a symbol index with 59 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!