【问题标题】:Retrieve the name of variable associated with TemplateRef检索与 TemplateRef 关联的变量的名称
【发布时间】:2021-07-14 17:14:58
【问题描述】:

假设我有这个模板:

<div class="main__container">
    <ng-template #test></ng-template>
    <ng-template #test2></ng-template>
</div>

我通过以下方式检索到所有TemplateRef 的引用:

@ViewChildren(TemplateRef)
private templates;

我怎样才能为所有这些检索相关变量的名称?因此,第一个模板为“test”,另一个模板为“test2”。我不是绝对想使用变量,如果可以通过使用ng-template 的属性,就可以了。我尝试使用TemplateRef、ViewContainerRef 和ElementRef,但每次都会返回对comment 元素的引用。

【问题讨论】:

    标签: angular


    【解决方案1】:

    我设法使用 TemplateRef 的私有 API 做到了这一点。这是我目前使用的功能:

    @ContentChildren(TemplateRef) templates: QueryList<TemplateRef<any>>;
    
    getTemplate(id: string): TemplateRef<any> {
        // FIXME: Should not use private API...
        return this.templates ? this.templates.find((template: any) => 
        template._def.references[id]) : null;
    }
    

    但这不适用于生产版本,实际上我发现您的帖子正在寻找另一种解决方案,如果我发现某些东西或向 Angular github 提交功能请求,我会更新。 与此同时,我认为它值得分享。

    -- 编辑-- 看起来确实有使用结构指令的可能解决方案。

    您将在本指南中了解到星号 (*) 是一种方便表示法,而字符串是一种微语法,而不是通常的模板表达式。 Angular 将这个符号脱糖成一个标记的&lt;ng-template&gt;,它围绕着宿主元素及其后代。每个结构指令对该模板执行不同的操作。

    Link to documentation

    -- 编辑 2-- 好的,它适用于检索 TemplateRef 的指令,但目前我使用 id 将它注册到服务,我不再使用 ViewChildren/ContentChildren。我的组件使用 *ngTemplateOutlet 指令然后使用服务检索 TemplateRef。

    这是指令源代码:

    @Directive({
         selector: '[formeTemplate]'
    })
    export class FormeTemplateDirective {
        @Input('formeTemplate') templateId;
    
        constructor(private host: TemplateRef<any>, private service: FormeTemplateService) { }
    
        ngOnInit() {
            this.service.register(this.templateId, this.host);
        }
    }
    

    服务:

    @Injectable()
    export class FormeTemplateService {
        private templates: Array<FormeTemplate>;
    
        constructor() {
            this.templates = new Array();
        }
    
        register(id: string, ref: TemplateRef<any>) {
            this.templates.push(new FormeTemplate(id, ref));
        }
    
        getTemplateRef(templateId: string) : TemplateRef<any> {
            let template = this.templates.find((template) => template.id === templateId);
            return template ? template.ref : null;
        }
    }
    

    【讨论】:

      【解决方案2】:

      我还必须能够动态设置 templateRef。目标是根据动态类型选择模板。

      我的解决方案(https://stackblitz.com/edit/angular-dynamic-templateref):

      
      @Directive({
        selector: "ng-template.[typeTemplate]"
      })
      export class TypeTemplateDirective {
        @Input()
        typeTemplate: string;
      
        constructor(public templateRef: TemplateRef<any>) {}
      }
      
      @Component({
        selector: "type-template",
        template: `
          <ng-container *ngTemplateOutlet="(template$ | async)"></ng-container>
      
          <ng-template typeTemplate="typeA">Template for type A</ng-template>
          <ng-template typeTemplate="typeB">Template for type B</ng-template>
        `
      })
      export class TypeTemplateComponent implements AfterViewInit {
        @ViewChildren(TypeTemplateDirective)
        private typeTemplateDirectives: QueryList<TypeTemplateDirective>;
      
        @Input()
        set type(newType: string) {
          this.type$.next(newType);
        }
      
        type$ = new BehaviorSubject<string>(null);
        template$ = this.type$.pipe(
          filter(() => !!this.typeTemplateDirectives),
          map(
            type =>
              this.typeTemplateDirectives.find(
                directive => directive.typeTemplate === type
              ).templateRef
          )
        );
      
        ngAfterViewInit(): void {
          this.type$.next(this.type$.getValue());
        }
      }
      
      

      所以当一个新类型被推送到 时,会使用正确的模板。

      【讨论】:

      • 这应该是公认的答案,因为它是在不利用内部 API 的情况下解决此问题的答案。有了 PrimeNG,pTemplate 就可以做到这一点。
      【解决方案3】:

      您可以通过按名称询问模板变量来访问模板变量,如下所示:

       @ViewChildren('test, test2') templates;
      

      这是一个简单的Plunker 演示行为。

      【讨论】:

      • 是的,我知道这一点,但我希望我的组件更通用。我希望能够放置尽可能多的ng-template 并在代码中检索它们。但是,我还需要一种方法来识别它们,因为我有一个像 { test: { ... }, test2: { ... } } 这样的 json 对象,我想用它来做一些事情。
      • 您不能动态添加模板变量,因此永远不会超过那些创建设计时间的变量。因为如果您可以通过名称引用它们并使用预定义的名称访问对象中的数据,就会出现这种情况。
      • 我不想动态添加模板变量。我想要的可以通过两种方式实现:要么能够从我的模板中动态检索变量,比如ViewChildren,但在运行时(比如let tpl: TemplateRef = this.viewChildren.get(name);),或者能够从模板并有办法获取附加到它们的变量的名称。
      【解决方案4】:

      我遇到了同样的问题。这个简单的例子说明了我的解决方案:

      用法:

      <app-my-component [templates]="{ highlight: highlightTemplate, italic: italicTemplate }" [useTemplate]="'highlight'">
          <ng-template #highlightTemplate let-contentRef="contentRef">
              <span class="highlighted-text"><ng-container *ngTemplateOutlet="contentRef"></ng-container></span>
          </ng-template>
          <ng-template #italicTemplate let-contentRef="contentRef">
              <span class="font-italic"><ng-container *ngTemplateOutlet="contentRef"></ng-container></span>
          </ng-template>
      </app-my-component>
      

      myComponent 的模板:

      <div class="my-component">
          <ng-template #contentRef>The content which is given to the customTemplate</ng-template>
          <ng-container *ngTemplateOutlet="customTemplate; context: { contentRef: contentRef }" >
          </ng-container>
      </div>
      

      类定义:

      export class MyComponent {
      
          @Input() private templates: any = {};
          @Input() private useTemplate: string;
      
          get customTemplate() {
              return this.templates[this.useTemplate];
          }
      
      }
      

      【讨论】:

        猜你喜欢
        • 2014-04-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-12-10
        • 1970-01-01
        • 2020-05-04
        相关资源
        最近更新 更多