fgonzalez
fgonzalez

Reputation: 3877

Inheritance in Angular 2 components

I'm implemeting dynamic form system in my current project using ANgular 2, and so far is going good but I found the following problem:

I have two components that represent a form control like for example:

    @Component({
  selector: 'app-text-area',
  templateUrl: './text-area.component.html',
  styleUrls: ['./text-area.component.css']
})
export class TextAreaComponent implements OnInit {
  label: string;
  formGroup: FormGroup;
  formControlName: string;
  constructor(private injector: Injector) { }

  ngOnInit() {
    this.label = this.injector.get('label');
    this.formGroup = this.injector.get('formGroup');
    this.formControlName = this.injector.get('formControlName');
  }
}

And:

@Component({
  selector: 'app-input-text',
  templateUrl: './input-text.component.html',
  styleUrls: ['./input-text.component.css']
})
export class InputTextComponent  implements OnInit{
  label: string;
  formGroup: FormGroup;
  formControlName: string;
  constructor(private injector: Injector) { }

  ngOnInit() {
    this.label = this.injector.get('label');
    this.formGroup = this.injector.get('formGroup');
    this.formControlName = this.injector.get('formControlName');
  }
}

As you see both are identical except for the templateUrl, which is displaying different html elements.

So I would like to refactor the code and to create an abstract component to provide the common attributes and logic, and make then, the child classes to inherit the base class (as I would do when using Java). So I have created this class:

export class AbstractFormControl implements OnInit {
  label: string;
  formGroup: FormGroup;
  formControlName: string;
  constructor(private injector: Injector) { }

  ngOnInit() {
    this.label = this.injector.get('label');
    this.formGroup = this.injector.get('formGroup');
    this.formControlName = this.injector.get('formControlName');
  }
}

And I have make the child classes extend the base class like this:

@Component({
  selector: 'app-input-text',
  templateUrl: './input-text.component.html',
  styleUrls: ['./input-text.component.css']
})
export class InputTextComponent  extends AbstractFormControl{

} 

However now I'm getting the following error:

Uncaught Error: Can't resolve all parameters for InputTextComponent: (?).

Can someone explain me what's the right way to do this, or what I'm doing wrong?

Upvotes: 1

Views: 1111

Answers (1)

yurzui
yurzui

Reputation: 214047

Angular dependency injection system should know which type has been passed to constructor. When you inherit component such way typescript won't keep information about parameter private injector. You have two options:

1) Duplicate initializing

@Component({
  ...
})
export class InputTextComponent  extends AbstractFormControl{
    constructor(injector: Injector) { super(injector);}
}

But in your case you have the same number of parameters in your base and inherited classes and this solution seems redundant because we can omit constructor in our derived class.

We can omit constructor in derived class if we want to only use dependencies from parent class. So let's say we have parent class like:

abstract class Parent {
  constructor(private a: string, private b: number) {}
}

we can extend this class either

class Foo extends Parent {
  constructor(a: string, b: number) {
    super(a, b);
  }
}

or

class Foo extends Parent {
}

because the second option will generate code like

function Foo() {
   return _super !== null && _super.apply(this, arguments) || this;
}

Plunker Example

2) Use @Injectable for base class.

@Injectable()
export class AbstractFormControl {

this way typescript will translate the code above into

AbstractFormControl = __decorate([
    core_1.Injectable(),
    __metadata("design:paramtypes", [core_1.Injector])
], AbstractFormControl);

Plunker Example

and angular reflector can easily read this information

3) Use @Inject() for each of parameters

export class AbstractFormControl implements OnInit {
  constructor(@Inject(Injector) private injector: Injector) { }

Upvotes: 5

Related Questions