Angular component loses its metadata during transpiling

752 Views Asked by At

YET ANOTHER: Uncaught Error: Unexpected value 'UserDialogComponent' declared by the module 'AppModule'. Please add a @Pipe/@Directive/@Component annotation.

Current behavior

summary: Including an external library that uses reflect-metadata causes an error like this one: https://github.com/angular/angular/issues/15890

Expected behavior

Adding an external library that uses the same polyfill doesn't have to cause errors.

Minimal reproduction of the problem with instructions

Error description: There are two components:

AppComponent and UserDialogComponent

As I see UserDialogComponent behave as if was not decorated with @Component but that's not true. The problem relies on reflect metadata polyfills I think. If I remove decorators added by kaop-ts library everything goes well. But it worked before adding UserDialogComponent :\

repo with error: https://github.com/k1r0s/angular2-aop-showcase/ branch: iss-angular-reflection steps to reproduce: clone repo, git checkout origin/iss-angular-reflection, npm install, ng serve

aditional info (images with error trace): https://i.stack.imgur.com/zxxQH.jpg

Attached images: With Chrome dev tools if you set "pause on exception", then you go up on callstack two times and then you evaluate: meta.declarations[1] You should get UserDialogComponent constructor reference, and then you can execute: Reflect.getMetadata("annotations", meta.declarations[1]) in order to retrieve component metadata but it returns undefined whereas Reflect.getMetadata("annotations", meta.declarations[0]) will successfuly return AppComponent's medatada ... this is kinda bizarre..

What is the motivation / use case for changing the behavior?

By removing third party decorators Angular components mantain their metadata

Environment


Angular version: 4.0.0
package.json (project was generated with angular-cli)
  "dependencies": {
    "@angular/animations": "^4.0.0",
    "@angular/cdk": "^2.0.0-beta.8",
    "@angular/common": "^4.0.0",
    "@angular/compiler": "^4.0.0",
    "@angular/core": "^4.0.0",
    "@angular/forms": "^4.0.0",
    "@angular/http": "^4.0.0",
    "@angular/material": "^2.0.0-beta.8",
    "@angular/platform-browser": "^4.0.0",
    "@angular/platform-browser-dynamic": "^4.0.0",
    "@angular/router": "^4.0.0",
    "core-js": "^2.4.1",
    "kaop-ts": "^1.4.0",
    "rxjs": "^5.4.1",
    "zone.js": "^0.8.14"
  },

Browser:
- [x] Chromium (desktop) version `Version 59.0.3071.109 (Developer Build)` 
For Tooling issues:
- Node version: v6.11.2
- Platform: Linux

Thanks!

1

There are 1 best solutions below

0
On

as @yurzui pointed, reflect-metadata package overrides global references so it conflicts with, for example core-js.

The solition, in my case, was replace reflect-metadata package for core-js and then, include in my build an scoped polyfill reflect from core-js.

The correct explanation about why with one component wasn't any problem is because decorators are evaluated when class is declared, so when angular decorators are evaluated, it is painless to evaluate third party decorators, as javascript has already stored metadata within memory and can be accessed. Problem was with another component because after first component was declared, typescript evaluates following method decorators from third party libraries which use reflect-metadata by reasigning current global Reflect object... (polluting global namespace.. ) and then, angular try to load second component but @component can no longer store metadata where it needs to be placed, so angular complains about 2nd component has no @component declaration which is annoying and bizarre in first hand.