ค้นหาเว็บไซต์

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 ซึ่งไม่สามารถฉีดได้จากภายนอกคอมไพเลอร์

ดังนั้นในที่สุดสิ่งนี้ควรได้รับการแก้ไขตามแต่ละท่อ ขึ้นอยู่กับความต้องการของนักพัฒนาและภายในของท่อ ท่อที่ออกแบบมาอย่างดีมักเป็นเพียงส่วนหุ้มบางสำหรับบริการที่มีเอกสารที่เกี่ยวข้อง วิธีปฏิบัติที่ดีคือการใช้บริการเหล่านี้โดยตรงหากเป็นไปได้