0

I have an abstract component AbstractPageComponent (an abstract class describing a component), the template of an instance of which can contain an instance of another abstract component. Specifically, an instance of AbstractPageComponent will contain a component with a list of entities (people, for example). The component with the list of entities is itself an instance of another abstract component, AbstractListComponent. I want to access this child component with a @ViewChild() decorator. However, simply writing @ViewChild(AbstractListComponent) doesn't work. So, right now I have to manually declare the ViewChild properties for each instance of AbstractPageComponent:

// task page component
@ViewChild(TaskListComponent) private readonly listComponent!: TaskListComponent;

// employee page component
@ViewChild(EmployeeListComponent) private readonly listComponent!: EmployeeListComponent;

This seems really excessive. I was wondering if there's a way to "provide" a different component to each instance of AbstractPageComponent, similar to a service? Something like:

import { AbstractListComponent } from '...';

@Component({
    template: ''
})
export abstract class AbstractPageComponent {
    @ViewChild(AbstractListComponent) public readonly listComponent!: AbstractListComponent;

    ...
}
import { TaskListComponent } from '...';
import { AbstractListComponent } from '...';

// TaskListComponent extends AbstractListComponent

@Component({
    ...
    providers: [
        {
            provide: AbstractListComponent,
            useClass: TaskListComponent
        }
    ]
})
export class TaskPageComponent extends AbstractPageComponent { ... }
akrick
  • 53
  • 4
  • Yeah if you provide it, it become available to the injector (if all the constructor parameters are injectable). – Matthieu Riegler Aug 01 '23 at 16:06
  • I'm not really trying to inject it like a dependency, I just want some universal way to be able to pass different instances of an abstract component to another component. The last code snippet is there just to make what I want clearer for everybody reading this. Injecting a component like a dependency will only work if I make it `@Injectable()`, which (as far as I know) makes it a singleton, which is a whole other story. – akrick Aug 01 '23 at 16:15

2 Answers2

1

yes, it is possible. Just use useExisting for the provider piece. Just remember that there providers would be available in the components subtree only.

providers: [
        {
            provide: AbstractListComponent,
            useExisting: TaskListComponent
        }
    ]

in this example existing instance of TaskListComponent would be injected when something would try to inject AbstractListComopnent

@Injectable() without provideIn: 'root' rather tells angular to precompile the service, so it's constructor would inject some services that you are injecting there. You can provide random class somewhere and then use it as a service even without Injectable annotation if it has no dependencies (there is however no reasons to do it)

Andrei
  • 10,117
  • 13
  • 21
0
What about (I don't check)?
export abstract class AbstractPageComponent<T> {
    @ViewChild(T) public readonly listComponent!: T;

    ...
}

and

export class TaskPageComponent extends 
             AbstractPageComponent<TaskListComponent> { ... }
Eliseo
  • 50,109
  • 4
  • 29
  • 67