angular - รับ pipe จาก Injector เมื่อรู้เพียงชื่อเท่านั้น
ฉันวางแผนที่จะมีบริการที่สามารถใช้ท่อเหมือนแม่แบบ สำหรับวัตถุประสงค์นี้ฉันจำเป็นต้องได้ท่อที่ลงทะเบียนแล้ว
โค้ดควรมีลักษณะดังนี้ในตอนท้าย:
@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];
}
}
ปัญหาคือคุณไม่สามารถรับ pipe จาก injector ได้ คุณต้องจัดเตรียมให้ก่อน (ครั้งหนึ่งสำหรับ directives และอีกครั้งสำหรับ providers) ผมกำลังหาวิธีที่จะรับพวกมันจาก angular (compiler, core - อะไรก็ได้ ที่ไหนสักแห่งต้องมีรายการ - อาจจะสำหรับ root module) แทนการกำหนดรายการใหม่
คำตอบ #1
ไม่มีวิธีสำนวนที่สะอาดใน Angular ที่จะรับ pipe แบบนี้ เพียงเพราะพวกมันถูกใช้ภายในโดยคอมไพเลอร์และไม่ได้เปิดเผยสำหรับการฉีด หาก pipe ควรถูกฉีดด้วย injector พวกมันควรถูกกำหนดเป็นผู้ให้บริการ (providers)
เนื่องจาก pipe เป็นสิ่งที่ฉีดเข้าไป การได้ตัวอย่างของพวกมันด้วย injector เป็นวิธีเดียวที่เหมาะสมในการทำสิ่งนี้ ซึ่งสามารถทำได้โดยใช้แผนที่ของ pipe ที่มีอยู่
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, ...],
...
การกรอกแผนที่สามารถทำให้อัตโนมัติโดยระบุอาร์เรย์ของคลาสท่อและใช้ PipeResolver เพื่อรับชื่อของพวกมัน:
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;
}, {});
...
เนื่องจากท่อ (pipes) ควรถูกสร้างโดยคอมไพเลอร์เพียงครั้งเดียวต่อการผูก (binding) การเรียกใช้ injector.get(...) อาจไม่ทำงานสำหรับท่อบางตัว AsyncPipe เป็นตัวอย่างในกรณีนี้ เพราะมันมีสถานะ (stateful) และยังใช้การขึ้นต่อ ChangeDetectorRef ซึ่งไม่สามารถฉีดได้จากภายนอกคอมไพเลอร์
ดังนั้นในที่สุดสิ่งนี้ควรได้รับการแก้ไขตามแต่ละท่อ ขึ้นอยู่กับความต้องการของนักพัฒนาและภายในของท่อ ท่อที่ออกแบบมาอย่างดีมักเป็นเพียงส่วนหุ้มบางสำหรับบริการที่มีเอกสารที่เกี่ยวข้อง วิธีปฏิบัติที่ดีคือการใช้บริการเหล่านี้โดยตรงหากเป็นไปได้
