As a preamble on how I came across these bugs, I have a form that uses a variety of components, from string inputs to selects, etc. Some of the components may be more custom than others, but they all share a set of input/output bindings and may share some @Host components.
I tried this with both core-js and reflect-metadata with the same results.
The two issues I found with Plunkr and examples on how to recreate:
@Input (and potentially @Output) decorators are not inherited if the child component class specifies it's own @Input.
The code below has:
- an AbstractComponent with
@Input a,
- a child class AComponent which correctly inherits that
@Input a
- a child class BComponent which creates
@Input b, and does not inherit @Input a
The expected output of this code should be a string, a a b. The actual output is just a b
Here is this bugs Plunkr example (all code is in app.module.ts).
import {Component, Input, NgModule} from 'ng-metadata/core';
import {platformBrowserDynamic} from 'ng-metadata/platform-browser-dynamic';
abstract class AbstractComponent {
@Input('@') public a: string;
}
@Component({
selector: 'aComponent',
template: '<span>{{$ctrl.a}}</span>'
})
export class AComponent extends AbstractComponent { }
@Component({
selector: 'bComponent',
template: '<span>{{$ctrl.a}} {{$ctrl.b}}</span>'
})
export class BComponent extends AbstractComponent {
@Input('@') public b: string;
}
@Component({
selector: 'app',
template: `
<a-component a="a"></a-component>
<b-component a="a" b="b"></b-component>
`
})
export class AppComponent { }
@NgModule({
declarations: [AComponent, BComponent, AppComponent],
exports: [AppComponent],
bootstrap: [AppComponent]
})
export default class AppModule { }
platformBrowserDynamic().bootstrapModule(AppModule);
@Host (and potentially @Self/@SkipSelf) constructor decorators incorrectly get applied to the parent class, adding parameter information to all child classes.
The code below has:
- an AbstractComponent with constructor
@Inject('form') @Host of an IFormController,
- a child class AComponent which also
@Inject('$http'), the IHttpService
- a child class BComponent which also
@Inject('app') @Host the parent AppComponent.
I believe the BComponent's @Host on the second parameter is getting applied to every second parameter of any child of AbstractComponent and thus the $http services becomes a required directive.
Here is this bugs Plunkr example (all code is in app.module.ts).
This actually breaks Angular with an error logged as:
VM71 angular.js:14525 Error: [$compile:ctreq] Controller '$http', required by directive 'aComponent', can't be found!
http://errors.angularjs.org/1.6.4/$compile/ctreq?p0=%24http&p1=aComponent
at eval (VM71 angular.js:66)
at getControllers (VM71 angular.js:9896)
at getControllers (VM71 angular.js:9903)
at nodeLinkFn (VM71 angular.js:9796)
at compositeLinkFn (VM71 angular.js:9055)
at nodeLinkFn (VM71 angular.js:9809)
at compositeLinkFn (VM71 angular.js:9055)
at nodeLinkFn (VM71 angular.js:9809)
at compositeLinkFn (VM71 angular.js:9055)
at nodeLinkFn (VM71 angular.js:9809)
import {IFormController, IHttpService} from 'angular';
import {Component, Host, Inject, NgModule} from 'ng-metadata/core';
import {platformBrowserDynamic} from 'ng-metadata/platform-browser-dynamic';
abstract class AbstractComponent {
private formController: IFormController;
constructor (@Inject('form') @Host() formController: IFormController) {
this.formController = formController;
}
}
@Component({
selector: 'aComponent'
})
export class AComponent extends AbstractComponent {
private $http: IHttpService;
constructor (@Inject('form') @Host() formController: IFormController,
@Inject('$http') $http: IHttpService) {
super(formController);
this.$http = $http;
}
}
@Component({
selector: 'bComponent'
})
export class BComponent extends AbstractComponent {
private app: AppComponent;
constructor (@Inject('form') @Host() formController: IFormController,
@Inject('app') @Host() app: AppComponent) {
super(formController);
this.app = app;
}
}
@Component({
selector: 'app',
template: `
<form>
<a-component></a-component>
<b-component></b-component>
</form>
`
})
export class AppComponent { }
@NgModule({
declarations: [AComponent, BComponent, AppComponent],
exports: [AppComponent],
bootstrap: [AppComponent]
})
export default class AppModule { }
platformBrowserDynamic().bootstrapModule(AppModule);
As a preamble on how I came across these bugs, I have a form that uses a variety of components, from string inputs to selects, etc. Some of the components may be more custom than others, but they all share a set of input/output bindings and may share some
@Hostcomponents.I tried this with both core-js and reflect-metadata with the same results.
The two issues I found with Plunkr and examples on how to recreate:
@Input(and potentially@Output) decorators are not inherited if the child component class specifies it's own@Input.The code below has:
@Input a,@Input a@Input b, and does not inherit@Input aThe expected output of this code should be a string,
a a b. The actual output is justa bHere is this bugs Plunkr example (all code is in app.module.ts).
@Host(and potentially@Self/@SkipSelf) constructor decorators incorrectly get applied to the parent class, adding parameter information to all child classes.The code below has:
@Inject('form') @Hostof anIFormController,@Inject('$http'), theIHttpService@Inject('app') @Hostthe parentAppComponent.I believe the BComponent's
@Hoston the second parameter is getting applied to every second parameter of any child ofAbstractComponentand thus the$httpservices becomes a required directive.Here is this bugs Plunkr example (all code is in app.module.ts).
This actually breaks Angular with an error logged as: