@Input and other decorators and inheritance
Asked Answered
H

6

23

I don't really understand how object binding works, so if anyone could explain if I can use @Input() inside a base class, or better: decorators and inheritance. For example if each form should receive a customer I have a base class:

export class AbstractCustomerForm{

@Input() customer;
...
}

and then I extend this class in an actual component:

export AwesomeCustomerForm extends AbstractCustomerForm implements OnInit{
    ngOnInit(){

        if(this.customer)
            doSomething();

    }
}

but this won't work, customer will never get set :(

Hickory answered 25/2, 2016 at 15:3 Comment(1)
The solution is to add @Directive() as per: https://mcmap.net/q/213150/-can-39-t-bind-input-when-using-abstract-classes-2-level-of-hierarchyConfessor
P
15

update

Inheritance is properly supported since 2.3.0-rc.0

original

Decorators are not inherited. They need to be applied to the class used as component directly. Decorators on subclasses are ignored. I have seen it mentioned that @Input() or @Output() are working if only the super class has them and the sub-class has none.

Phrenology answered 25/2, 2016 at 15:5 Comment(6)
So I have to write each common input in each of the customer forms :(Hickory
Yup, I hope they improve this eventually but it's a complicated topic because it needs to be compatible with Dart and JS and must not prevent compilation for huge applications (Google intern for example). Probably not something that will be fixed for 1.0.Tade
Got burnt by this -- pretty cryptic error messages too, complaining about a field that is undefined. Eventually found out that decorators are not inherited unless there are no decorators on the child class. Seems like the decorators information are just added to the class definition, overriding any in the superclass, and the search for decorators do not follow down the chain. Anyhow, the only way is to COPY all the decorated properties from the superclass to the child class.Grados
I think this is a pity. Inheriting these decorators feels somewhat natural which I feel can prove to be a very good architectural bonusElegant
Perhaps for later versions. I got the impression they just don't consider it a show stopper for 2.0 release.Tade
Consider changing the accepted answer, there is a workaround that is better than duplicating all the @InputsCottingham
P
8

One strategy that I am following is something like this:

@Component({
    selector: 'my-component',
    template: `......`,
    inputs: MyAbstractComponent.genericInputs
})
export class MyComponent extends MyAbstractComponent {

    @Input() width: number = 200;
    ........
}

where:

export abstract class MyAbstractComponent {
    public static genericInputs : string[] = ['base'];
    public base: String;
}

therefore, MyComponent would get base as well as width bindings. In fact, I think there is still room for improvement by using reflection.

Plaint answered 11/9, 2016 at 23:25 Comment(2)
Some tslint confs won't allow use of inputs.Iiette
I tried this and got this error in vscode: "The Angular Language Service server crashed 5 times in the last 3 minutes"Crabber
M
5

Even in Angular 4.2.4 it works fine in dev mode. But when doing a prod build (ng build -prod) it fails:

ERROR in Template parse errors:
Can't bind to 'step' since it isn't a known property of 'app-textarea-step'.
1. If 'app-textarea-step' is an Angular component and it has 'step' input, 
then verify that it is part of this module.
2. If 'app-textarea-step' is a Web Component then add 
'CUSTOM_ELEMENTS_SCHEMA' to the '@NgModule.schemas' of this component to 
suppress this message.

My component looks like:

abstract class StepComponent {
  @Input() step: BaseStep;
  @Output() next = new EventEmitter<string>();
  @Output() answer = new EventEmitter<Answer>();
}

abstract class SingleNextStepComponent extends StepComponent {

  onSubmit(answer: string) {
    // ConfirmStep heeft geen answer.
    if (answer) {
      this.answer.emit({ question: this.step.key, value: answer });
    }
    const step = this.step as SingleNextStep;
    this.next.emit(step.next);
  }
}

// Decorator inheritance works in standard build (ng build) but fails in production build (ng build -prod)
// Workaround: inputs element on @Component that contains the inputs.....
@Component({
  selector: 'app-textbox-step',
  templateUrl: './textbox-step.component.html',
  inputs: ['step']
})
export class TextboxStepComponent extends SingleNextStepComponent { }

@Component({
  selector: 'app-textarea-step',
  templateUrl: './textarea-step.component.html',
})
export class TextareaStepComponent extends SingleNextStepComponent { }

Fortunately the workaround works. The inputs a added to the TextBoxStepComponent have prevented this one to fail, falling through to the next one, not yet provided with 'inputs'.

But 'ng build' works fine without needing inputs on the @Component decorators...

Majka answered 27/6, 2017 at 15:57 Comment(1)
Thanks a ton! Do you know if this has been fixed already or if there is a bug ticket for it?Wreathe
C
3

Decorators are not inherited, but class are. So my solution was this:

@Component({selector: 'a')
class A {
  @Input() field;
}

@Component({selector: 'b', inputs: ['field']}
class B extends A {
}
Chrysolite answered 8/11, 2016 at 18:32 Comment(0)
I
2

I came across this question, and just wanted to point out that as of Angular 2.3.0-rc.0 this is actually possible.

Inheritance Semantics:

Decorators:

1) list the decorators of the class and its parents in the ancestor first order

2) only use the last decorator of each kind (e.g. @Component / ...)

Constructor parameters:

If a class inherits from a parent class and does not declare a constructor, it inherits the parent class constructor, and with it the parameter metadata of that parent class.

Lifecycle hooks:

Follow the normal class inheritance model, i.e. lifecycle hooks of parent classes will be called even if the method is not overwritten in the child class.

https://github.com/angular/angular/commit/f5c8e09

Iormina answered 1/2, 2017 at 11:57 Comment(0)
F
1

I found a possible solution here:

https://medium.com/@ttemplier/angular2-decorators-and-class-inheritance-905921dbd1b7#.ca68alvcz

Basically, it creates a custom Decorator that simply merges parent and child decorators through reflection.

Actually I coudn't make it work on projects baseed on angular-cli.

Franek answered 5/8, 2016 at 1:11 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.