platosha / angular-polymer

Angular 2 support for Polymer elements
https://www.npmjs.com/package/angular-polymer
Apache License 2.0
220 stars 44 forks source link

Ahead of Time (AoT) compile doesn't work with PolymerElement in declarations #86

Open rstpv opened 8 years ago

rstpv commented 8 years ago

When using PolymerElement function inside the @NgModule, the AoT compiles complains with the following message:

Error encountered resolving symbol values statically. Calling function 'PolymerElement', function calls are not supported. Consider replacing the function or lambda with a reference to an exported function

@NgModule({
...
declarations: [
 PolymerElement('paper-input') //This is what doesn't work with AoT
],
...
})

Solutions to this error message have been provided in:

angular issue#10789

But almost all use the useFactory attribute as a workaround, but, declarations don't have such a feature.

DanielGarciaMandillo commented 8 years ago

Hello,

Same problem here:

Error: Error encountered resolving symbol values statically. Calling function 'PolymerElement', function calls are not supported. Consider replacing the function or lambda with a reference to an exported function

Any solution? Thanks

asaph26 commented 8 years ago

It would be really good to know if there is an alternative that will be provided

mbeckenbach commented 8 years ago

I get the same message like @DanielGarciaMandillo As far as i understood the i18n of ng2, AOT will be required to compile apps with multiple languages.

dcoblentz commented 8 years ago

I spent the last couple of days playing with this, and the only way that I could come up with was to generate source code for each element ahead of time.

For instance, write a base class that looks something like this: export class ChangeEventsAdapterDirective {

protected _eventNameForProperty = (property: string) => `${property}Change`;

constructor(props: string[]) {
    props
        .forEach(property => this[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[this._eventNameForProperty(property)].emit(event.detail.value);
    }
}

}

And then add an implementation for each element, along these lines:

@Directive({ selector: 'paper-tabs', host: { '(selected-changed)': '_emitChangeEvent("selected", $event)', '(selected-item-changed)': '_emitChangeEvent("selectedItem", $event)', '(items-changed)': '_emitChangeEvent("items", $event)', '(selected-values-changed)': '_emitChangeEvent("selectedValues", $event)', '(selected-items-changed)': '_emitChangeEvent("selectedItems", $event)' } }) export class PaperTabsChangeEventsAdapterDirective extends ChangeEventsAdapterDirective { @Output() selectedChange: any; @Output() selectedItemChange: any; @Output() itemsChange: any; @Output() selectedValuesChange: any; @Output() selectedItemsChange: any;

constructor() {
    super(['selectedChange', 'selectedItemChange', 'itemsChange', 'selectedValuesChange', 'selectedItemsChange']);
}

}

I did this for all of the elements in my project, and they all seem to work, but it's definitely a hassle. I don't know of a cleaner way to manage it at this point though.

asaph26 commented 8 years ago

@dcoblentz Had a similar thought but didn't want to go down that route. Awesome work on your part. If this is the only way to achieve this for now, what do you think about creating a separate repo and sharing the definitions that you have and we could collaborate to bring all paperElements into it

sobvan commented 7 years ago

I think AoT is essential with Angular 2. The 2+ sec parse time (which seems to be 10 sec on a tablet) just really not lives up to the promise of Angular 2. On the other hand, Material 2 will be soon there where Polymer elements are now (IMHO). So it is a good question if this is worth the effort. Do you guys think that it is of added value to maintain Polymer Angular 2 bridge in the long term? If so, what are your reasons?

mbeckenbach commented 7 years ago

@istvanszoboszlai

  1. AOT is a must have when dealing with i18n in angular2
  2. I don't think Material 2 will be comming "soon" when I take a look at their roadmap
  3. The Angular Material 1 Datepicker was crap, I don't really trust them for V2.
  4. Material 2 has no file upload component in their roadmap
  5. Since the first day of Material 1 they promise a grid, but they have never delivered one
  6. Their IE support was never really good. Somehow I don't trust them here too.
  7. The vaadin web components are awesome. :-)

So yes, please continue this. At least until Material 2 will maybe become mature.

rstpv commented 7 years ago

One thing to note about dcoblentz solution, it does enable two way data binding. But, it will require shadow dom fully enabled if you want to use components with content/slot. For example, using shady dom, something like:

<paper-input><span suffix>$</span></paper-input>

Won't work, because of the Polymer.dom dependency on web components with shady dom (As far as I saw, it won't be required on Polymer 2 as it uses dom functions directly). Angular2-Polymer solves this with the classes PolymerShadyDomAdapter: https://github.com/vaadin/angular2-polymer/blob/master/src/polymer-element.ts#L27

Or, have you find any solution that doesn't require that class or shadow dom fully enabled?

asaph26 commented 7 years ago

@rstpv I believe we could still continue to use a combination of both. The solution provided by @dcoblentz makes the directive declarations for each component static(which is required for AOT to work). We could still use the PolymerShadyDomAdapter provided by angular2-polymer as it is not directly linked to the creation of the directives but rather sets the Root DOM Adapter.

dcoblentz commented 7 years ago

This is pretty rough, but here's a gist with the code I wrote, it's all pretty rough still, but I copied the output from my browser console to a new ts file, but it would of course be better to turn it into a cli tool that can generate each file directly.

https://gist.github.com/dcoblentz/b16e97bc5671a8c31ab65add8c88b041

jouni commented 7 years ago

We’re going to do a quick round of prototyping (2d timebox) to get a better understanding about this, what would be a viable fix for this issue.

sobvan commented 7 years ago

Best news today so far :)

sobvan commented 7 years ago

@dcoblentz, this is really awesome what you did. I do not get the whole process, however. How do you generate output.ts? Can this be applied to vaadin polymer elements, too?

samiheikki commented 7 years ago

We finished our research. Highlights below. Currently https://github.com/vaadin/angular2-polymer/issues/104 is prioritized as the next fix in the future, but after that AoT support will probably be the next one. ETA 2017Q1.

Problem

When following the Angular’s AoT guide (https://angular.io/docs/ts/latest/cookbook/aot-compiler.html) the development with angular2-polymer will fail when trying to compile the application. You can reproduce the issue in angular2-polymer-quickstart AoT branch: https://github.com/vaadin/angular2-polymer-quickstart/tree/aot Error: Error encountered resolving symbol values statically. Calling function 'PolymerElement', function calls are not supported.

Cause of Problem

Feature in Angular: https://github.com/angular/angular/blob/491d5a22a9dc1082e80aa5ca481aa02b49b9bfe8/modules/%40angular/compiler/src/aot/static_reflector.ts#L695 The reason for this limitation is that the AoT compiler needs to generate the code that calls the factory and there is no way to import a lambda from a module and you can only import an exported symbol. Meaning you can’t call functions in metadata. Code should be statically analyzable.

Possible Solutions

1) Extend useFactory: https://github.com/angular/angular/issues/10789#issuecomment-242220591 2) Generate Source Codes for each element: https://github.com/vaadin/angular2-polymer/issues/86#issuecomment-255398864 3) CLI-tool to generate source code for each element

axtlotic commented 7 years ago

if some one needs, I used polymer-element.ts to implement a class for directives generation, it is not a CLI-tool, but generates directives for a component, I hope helps you:

polymer-directive-generator.ts https://gist.github.com/axtlotic/06f8715a6ea0223af6fbd6cbf1266be7

app.component.ts https://gist.github.com/axtlotic/36764dc3747526aaa6254b39dc78a106

app.component.html https://gist.github.com/axtlotic/8ada0ade7244b412190a1d66e4c4abac

app.component.css https://gist.github.com/axtlotic/1113f933aee2a5e588161d6a8d0797e8

app.module.ts (example) https://gist.github.com/axtlotic/fddb1527f861c8fe8ad626d5bf3ff440

mpartipilo commented 7 years ago

@axtlotic Could this be used to create a pre-built public package for all elements?

BorntraegerMarc commented 7 years ago

Any ideas when this will be fixed?

axtlotic commented 7 years ago

@mpartipilo thanks, if this is possible, I would have to install all the elements in my project and modify it to generate the directives, I hope to get the complete list of elements and give me some time to do it

hydraslay commented 7 years ago

@axtlotic Thanks for the generator. but when I use the generated code in my component. the ng build gave me this message:

chunk {0} main.bundle.js, main.bundle.map (main) 26.1 kB {2} [initial] [rendered] chunk {1} styles.bundle.js, styles.bundle.map (styles) 9.99 kB {3} [initial] [rendered] chunk {2} vendor.bundle.js, vendor.bundle.map (vendor) 2 MB [initial] [rendered] chunk {3} inline.bundle.js, inline.bundle.map (inline) 0 bytes [entry] [rendered]

ERROR in ./src/app/app.component.ts Module not found: Error: Can't resolve 'app.component.html' in '/Users/xukai/git/angular2/my-project/src/app' @ ./src/app/app.component.ts 21:22-51 @ ./src/app/index.ts @ ./src/main.ts @ multi main

ERROR in ./src/app/app.component.ts Module not found: Error: Can't resolve 'app.component.css' in '/Users/xukai/git/angular2/my-project/src/app' @ ./src/app/app.component.ts 22:21-49 @ ./src/app/index.ts @ ./src/main.ts @ multi main

what have I missed?
my module is like this:

import { BrowserModule } from '@angular/platform-browser';
import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { HttpModule } from '@angular/http';
import { PolymerElement } from '@vaadin/angular2-polymer';

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

import { PaperInput } from './paper-input-directives';
import { VaadinComboBox } from './vaadin-combo-box-directives';

@NgModule({
  declarations: [
    AppComponent,
    PaperInput,
    VaadinComboBox
  ],
  imports: [
    BrowserModule,
    FormsModule,
    HttpModule
  ],
  providers: [],
  entryComponents: [AppComponent],
  bootstrap: [AppComponent],
  schemas: [CUSTOM_ELEMENTS_SCHEMA]
})
export class AppModule { }

and my component

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'];
}

and my template

<h1>{{title}}</h1>
<h2>{{title}}</h2>
<vaadin-combo-box [label]="myLabel" [(value)]="myValue" [items]="myItems"></vaadin-combo-box>
<paper-input [(value)]="myValue"></paper-input>
hydraslay commented 7 years ago

@axtlotic ok that was a path mistake I solved it by change templateUrl: 'app.component.html', styleUrls: ['app.component.css'], to templateUrl: './app.component.html', styleUrls: ['./app.component.css'],

thank you for your generator , it's good enough for me now.

tomzi8 commented 7 years ago

How to use this generator? How to implement this solution?

hydraslay commented 7 years ago

@tomaszcysewski I created a generator project in Github here: https://github.com/hydraslay/ng2-polymer-static-gen And use the generated xxx.directives.ts file just like this project: https://github.com/hydraslay/ng2-polymer-static

There is still something need to be adjusted in vaadin-date-picker and vaadin-grid while other paper-xxx works well.

tomzi8 commented 7 years ago

Those are most used elements I think. I want to install this in order to use vaadin-grid. Let's make a request for this adjusting, who develop this generator generally?

DDOUP commented 7 years ago

Unfortunately it is not working every time. If I am using a paper-dialog which has a vaadin-date-picker the dialog will not open. If I delete the containing vaadin-date-picker the dialog is working again. The vaadin-date-picker is working, but not with the dialog.

And I have some problems with paper-input in the paper-dialog. They do not show their actual value.

Do you may have some suggestions?

hydraslay commented 7 years ago

Already turn to another UI Component "PrimeNG" because the commercial project won't wait. But the primeNG is not a material design, sad. And the same time, keeping another eye on the schedule of material2. This is the thing I really want.

hydraslay commented 7 years ago

AOT is available (but first you should generated static directives). Check this repository

sounds like I have done twice AOT compile, One for Polymer, One for Angular...

platosha commented 7 years ago

@hydraslay thanks for sharing your solution. Yeah, looks like we can’t avoid some generation step for Polymer in general.

My idea on this — to try making a nicer generator using the polymer-analyzer output, so that you don’t have to run the browser with elements and copy the generated directives code. This can be well automated, I believe. Prototyping this idea nowadays.

I would avoid publishing the generated directives, however, because then updating them for all the changes in all the elements is likely to turn into a big maintenance problem.