After asking on the official angular repository, it turns out to be a simple solution. Instead of passing the service name as a string, you’ll have pass the tokens through the component into the view into another component.
Globally define the injection token
I did this alongside my service itself to make it easier to keep track of.
@Injectable()
export class CustomerService implements ISearchable { ... }
export const CUSTOMER_SERVICE = new InjectionToken<ISearchable>('CustomerService');
Register the injection token in your app providers
import {CUSTOMER_SERVICE, CustomerService} from "./services/customer/customer.service";
@NgModule({
declarations: [ ... ],
imports: [ ... ],
providers: [
{
provide: CUSTOMER_SERVICE, // That's the token we defined previously
useClass: CustomerService, // That's the actual service itself
}
],
bootstrap: [ ... ],
})
export class AppModule { }
Pass the token through the view to your other component
// In your component
import {CUSTOMER_SERVICE} from "./services/customer/customer.service";
@Component({
selector: 'app-root',
template: '<app-search-bar [source]="searcher"></app-search-bar>'
})
export class AppComponent
{
searcher = CUSTOMER_SERVICE;
}
You can now import the service dynamically from your other component
@Component({
selector: 'app-search-bar',
templateUrl: './search-bar.component.html',
styleUrls: ['./search-bar.component.sass'],
})
export class SearchBarComponent implements OnInit
{
@Input()
source: InjectionToken<ISearchable>;
private searcher: ISearchable;
constructor(private injector: Injector) {}
ngOnInit()
{
this.searcher = this.injector.get<ISearchable>(this.source);
}
search(query: string)
{
this.searcher.search(query).subscribe(...);
}
}