2

When I want to reuse the same base component in angular 2 (rc6), I need to use the same declaration of dependencies in all constructors that extends the base component, otherwise the dependency injection breaks because it tries to inject always the same dependencies declaration (I think that is the last imported component that extends the base component).

For example, I have the base component "AnimalComponent":

@Component({
    selector: '#animalComponent',
    template: `I'm a animal component`
})
export class AnimalComponent
{
    constructor(
        @Inject('AnimalData') protected _data: any
    ) {}
}

I extend the base component in two components "CatComponent" and "DogComponent"

@Component({
    selector: '#catComponent',
    template: `I'm a cat component`
})
export class CatComponent extends AnimalComponent
{
    constructor(
        @Inject('CatData') data: any
    ) {
        super(
            data
        );
    }
}


@Component({
    selector: '#dogComponent',
    template: `I'm a dog component`
})
export class DogComponent extends AnimalComponent
{
    constructor(
        @Inject('DogData') data: any
    ) {
        super(
            data
        );
    }
}

then I wrap both the component in modules, so I can load them at run-time

@NgModule({
    imports: [CommonModule],
    declarations: [CatComponent],
    exports: [CatComponent]
})
export class CatModule {}


@NgModule({
    imports: [CommonModule],
    declarations: [DogComponent],
    exports: [DogComponent]
})
export class DogModule {}

and finally I will load the two components in my final component

import {DogModule} from './dog.module';
import {CatModule} from './cat.module';

@Component({
    selector: '#mainComponent',
    template: `
        I'm a main component
        <template #catComponent></template>
        <template #dogComponent></template>
    `
})
export class MainComponent
{
    @ViewChild('catComponent', {read: ViewContainerRef}) catView: ViewContainerRef;
    @ViewChild('dogComponent', {read: ViewContainerRef}) dogView: ViewContainerRef;

    constructor(
        protected _injector: Injector,
        protected _compiler: Compiler
    ) {
        let catInjector = ReflectiveInjector.fromResolvedProviders(
            ReflectiveInjector.resolve({provide: 'CatData', useValue: 'Some cat information'}),
            this._injector
        );

        this._compiler.compileModuleAndAllComponentsAsync(CatModule).then(
            moduleFactory => {
                let compFactory = moduleFactory.componentFactories.find(tmpCompFactory => tmpCompFactory.componentType.name === 'CatComponent');
                let componentRef = this.catView.createComponent(compFactory, 0, catInjector, []);
            }
        );

        let dogInjector = ReflectiveInjector.fromResolvedProviders(
            ReflectiveInjector.resolve({provide: 'DogData', useValue: 'Some dog information'}),
            this._injector
        );

        this._compiler.compileModuleAndAllComponentsAsync(DogModule).then(
            moduleFactory => {
                let compFactory = moduleFactory.componentFactories.find(tmpCompFactory => tmpCompFactory.componentType.name === 'DogComponent');
                let componentRef = this.dogView.createComponent(compFactory, 0, dogInjector, []);
            }
        );
    }
}

In this dummy case, the component injection will be broken when try to resolve the dependency injection of the "DogComponent" whit the message: "No provider for CatData!" However if I change both the names of providers for the same name ("AnyAnimalData" instead of "CatData" and "DogData") all works fine.

Someone else with the same problem? Am I doing something wrong?

1 Answer 1

1

Today I updated my angular2 version to the newest 2.0.1, and unfortunately the problem persists. In order to reuse the same base component multiple times avoiding this problem of angular injection, I left here my solution in case of being useful to someone.

1- Create a copy of the component that you can extend and rename to “[my-component].extension-component.ts” for example, then declare all variables out of constructor and rename the constructor to “init”, in the init method receive all variables as parameter and assign each one to the correspondent declared variable.

@Component({
    selector: 'myComponent',
    template: ''
})
export abstract class MyComponentExtensionComponent
{
    // Contructor vars
    protected _var1: type1;
    protected _var2: type2;
    protected _varN: typeN;

    // Local vars
    protected _varZ: typeZ;

    /**
     * Initialization of component (replace the original constructor to avoid angular injection inheritance problem)
     * @param var1
     * @param var2
     * @param varN
     */
    public init(
        var1: type1,
        var2: type2,
        varN: typeN
    ) {
        // Constructor vars
        this._var1 = var1;
        this._var2 = var2;
        this._varN = varN;

        // Local vars
        this._varZ = "I'm a var Z";
    }

    //...
}

2- Now you can extend this component multiple times without any problem in this way:

//...
import {MyComponentExtensionComponent} from './my-component.extension-component';

//...

export class MyChildComponent extends MyComponentExtensionComponent
{
    constructor(
        var1: type1,
        var2: type2,
        varN: typeN
    ) {
        // Call parent
        super();
        super.init(
            var1,
            var2,
            varN
        );

        //...
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.