angular - Tar emot ett rör från Injector när endast namnet är känt
Jag planerar att ha en tjänst som kan använda pipes som mallar. För detta syfte behöver jag få de registrerade pipes.
Koden bör se ut så här i slutändan:
@Injectable()
class MyService {
construct(private injector: Injector) {}
// text could be something like 'myDate | date' or 'aVariable | uppercase'
public interpolate(text: string, params: object = {}): string {
let p: number = text.lastIndexOf('|');
if (p > -1) {
let args = [this.interpolate(text.substring(0, p))];
let pipeName = text.substr(p+1).trim();
// how ever we get the rest of the args from pipeName
pipe = this.getPipe(pipeName)
pipe.transform.apply(pipe, args);
} else {
// how ever we interpolate the base
}
}
private pipeInstances: any = {}
private getPipe(pipeName) {
if (!this.pipeInstances[pipeName]) {
// how to get the pipe?
this.pipeInstances[pipeName] = this.injector.get(PipesContainer).get(pipeName);
}
return this.pipeInstances[pipeName];
}
}
Problemet är att du inte kan få rör från injektorn. Du måste tillhandahålla dem först (en gång för riktlinjer och en gång för leverantörer). Jag letar efter ett sätt att få dem från Angular (kompilator, core - vad som helst. någonstans måste det finnas en lista - kanske för rootmodulen) istället för att definiera en ny lista.
Svar #1
Det finns inget rent idiomatiskt sätt i Angular att få en pipe på det här sättet. Bara för att de används internt av kompilatorn och inte exponeras för injektion. Om pipes ska injiceras med injector, bör de definieras som providers.
Eftersom pipes är injicerbara är det enda korrekta sättet att få deras instanser med injektorn. Detta kan implementeras med en karta över tillgängliga pipes.
export const pipesMap = {
some: SomePipe
}
export const pipesList = Object.values(pipesMap);
@Injectable();
export class Pipes {
protected pipesMap = pipesMap;
constructor(private injector: Injector) {}
get(pipeName) {
return this.injector.get(this.pipesMap[pipeName]);
}
}
...
providers: [pipesList, Pipes, ...],
...
Att fylla i kartan kan automatiseras genom att specificera en array av rörklasser och använda PipeResolver för att få deras namn:
import {PipeResolver} from '@angular/compiler';
const pipeResolver = new PipeResolver();
export const pipesList = [
SomePipe
];
export const pipesMap = pipesList.reduce((pipesMap, pipeClass) => {
const pipeName = pipeResolver.resolve(pipeClass, true).name;
pipesMap[pipeName] = pipeClass;
return pipesMap;
}, {});
...
Eftersom pipelines förväntas instansieras av kompilatorn en gång per bindning, kanske injector.get(...) inte fungerar för vissa pipelines. AsyncPipe är illustrativt i detta fall, det är tillståndsberoende och använder också ChangeDetectorRef-beroendet, vilket inte är tillgängligt för injektion utanför kompilatorn.
Så i slutändan bör detta lösas på varje rörbasis, beroende på utvecklarens behov och rörens interna strukturer. Välutformade rör är ofta tunna omslag för de relevanta dokumenterade tjänsterna, en god praxis är att använda dessa tjänster direkt om möjligt.
