Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
rstpv opened this issue Sep 13, 2016 · 27 comments
Open
Labels

Comments

@rstpv
Copy link

rstpv commented Sep 13, 2016

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
Copy link

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
Copy link

asaph26 commented Oct 17, 2016

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

@mbeckenbach
Copy link

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
Copy link

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
Copy link

asaph26 commented Oct 21, 2016

@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
Copy link

sobvan commented Oct 22, 2016

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
Copy link

mbeckenbach commented Oct 22, 2016

@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
Copy link
Author

rstpv commented Oct 22, 2016

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
Copy link

asaph26 commented Oct 22, 2016

@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
Copy link

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
Copy link
Contributor

jouni commented Nov 7, 2016

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
Copy link

sobvan commented Nov 7, 2016

Best news today so far :)

@sobvan
Copy link

sobvan commented Nov 11, 2016

@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 samiheikki self-assigned this Nov 16, 2016
@samiheikki samiheikki removed their assignment Nov 21, 2016
@samiheikki
Copy link
Contributor

We finished our research. Highlights below. Currently #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: Router: AoT compilation fails when using a function with loadChildren angular/angular#10789 (comment)
  2. Generate Source Codes for each element: Ahead of Time (AoT) compile doesn't work with PolymerElement in declarations #86 (comment)
  3. CLI-tool to generate source code for each element

@axtlotic
Copy link

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
Copy link

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

@BorntraegerMarc
Copy link
Collaborator

BorntraegerMarc commented Dec 14, 2016

Any ideas when this will be fixed?

@axtlotic
Copy link

@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
Copy link

@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
Copy link

@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
Copy link

tomzi8 commented Jan 10, 2017

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

@hydraslay
Copy link

@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
Copy link

tomzi8 commented Jan 11, 2017

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
Copy link

DDOUP commented Jan 19, 2017

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
Copy link

hydraslay commented Jan 21, 2017

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
Copy link

hydraslay commented Jun 14, 2017

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
Copy link
Owner

@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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests