You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Angular中如何实现ng-content的动态选择器?

Angular动态切换ng-content投影内容的解决方案

Angular的ng-content是编译时投影机制,select属性的值在组件编译阶段就会被解析并完成内容分配,运行时动态修改select的值无法触发重新投影,所以你尝试的<ng-content select="[id={{activeId}}]"></ng-content>写法不会生效。

以下是两种可行的实现方案:

方案一:使用ng-template和动态模板渲染

这种方式通过将投影内容封装为ng-template,在子组件中动态选择并渲染对应模板,是更符合Angular设计模式的方案。

父组件用法修改

<app-sub #sub>
  <!-- 用ng-template包裹内容,通过模板变量标识 -->
  <ng-template #fullscreen>
    <div>
      <p>Fullscreen</p>
    </div>
  </ng-template>
  <ng-template #test>
    <div>
      <p>Some other content</p>
    </div>
  </ng-template>
</app-sub>

<button (click)="sub.toggle('test')">Switch to test</button>
<button (click)="sub.toggle()">Switch to fullscreen</button>

子组件实现

import { Component, ViewChildren, QueryList, TemplateRef } from '@angular/core';

@Component({
  selector: 'app-sub',
  template: `
    @if (activeId) {
      <p>In Active with: {{activeId}}</p>
      <ng-container *ngTemplateOutlet="activeTemplate"></ng-container>
    } @else {
      <p>In Non Active</p>
      <ng-container *ngTemplateOutlet="fullscreenTemplate"></ng-container>
    }
  `,
})
export class SubComponent {
  protected activeId?: string;
  protected activeTemplate?: TemplateRef<any>;
  protected fullscreenTemplate?: TemplateRef<any>;

  @ViewChildren(TemplateRef) private templates!: QueryList<TemplateRef<any>>;

  ngAfterViewInit() {
    // 初始化时找到fullscreen模板
    this.fullscreenTemplate = this.getTemplateByKey('fullscreen');
  }

  public toggle(id?: string) {
    this.activeId = id;
    this.activeTemplate = id ? this.getTemplateByKey(id) : undefined;
  }

  private getTemplateByKey(key: string): TemplateRef<any> | undefined {
    return this.templates.find(tpl => {
      // 通过模板的本地变量名匹配
      const tplName = (tpl as any)._declarationTContainer?.localNames[0];
      return tplName === key;
    });
  }
}

方案二:收集投影内容并动态渲染

这种方式直接收集所有投影的DOM元素,通过克隆元素的方式动态切换显示内容,父组件用法无需修改。

子组件实现

import { Component, ContentChildren, QueryList, ElementRef, ViewChild, ViewContainerRef, AfterContentInit } from '@angular/core';

@Component({
  selector: 'app-sub',
  template: `
    <p>{{activeId ? 'In Active with: ' + activeId : 'In Non Active'}}</p>
    <ng-container #contentContainer></ng-container>
  `,
})
export class SubComponent implements AfterContentInit {
  protected activeId?: string;
  private contentMap = new Map<string, ElementRef>();

  @ContentChildren(ElementRef, { descendants: false }) 
  private contentElements!: QueryList<ElementRef>;

  @ViewChild('contentContainer', { read: ViewContainerRef }) 
  private contentContainer!: ViewContainerRef;

  ngAfterContentInit() {
    // 建立内容标识与元素的映射
    this.contentElements.forEach(elRef => {
      const el = elRef.nativeElement;
      if (el.hasAttribute('fullscreen')) {
        this.contentMap.set('fullscreen', elRef);
      } else if (el.id) {
        this.contentMap.set(el.id, elRef);
      }
    });
    // 默认渲染fullscreen内容
    this.renderContent('fullscreen');
  }

  public toggle(id?: string) {
    this.activeId = id;
    this.renderContent(id || 'fullscreen');
  }

  private renderContent(key: string) {
    // 清空容器
    this.contentContainer.clear();
    const elRef = this.contentMap.get(key);
    if (elRef) {
      // 克隆DOM元素(原始投影内容只能被使用一次)
      const clonedEl = elRef.nativeElement.cloneNode(true);
      this.contentContainer.element.nativeElement.appendChild(clonedEl);
    }
  }
}

父组件用法保持不变

<app-sub #sub>
  <div fullscreen>
    <p>Fullscreen</p>
  </div>
  <div id="test">
    <p>Some other content</p>
  </div>
</app-sub>

<button (click)="sub.toggle('test')">Switch to test</button>
<button (click)="sub.toggle()">Switch to fullscreen</button>

内容的提问来源于stack exchange,提问作者Link

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 01:53:17