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

Angular中用Renderer2克隆Div时ViewChild无法找到选择器的问题

Angular ViewChild无法获取ng-template内元素及克隆元素的解决方案

问题根源

  1. 元素处于未实例化的ng-template中:<ng-template>是惰性模板,默认不会渲染到DOM,因此ViewChild无法定位内部的#Champ元素。
  2. 克隆逻辑错误:当前Clone()方法用renderer.createElement仅创建空标签,未复制原元素的内容与结构。
  3. ngIf的潜在影响:即使模板被实例化,*ngIf="Next3"会在条件为false时移除元素,导致ViewChild获取失败。

修复步骤

1. 确保模板元素被渲染

若无需<ng-template>的惰性特性,可直接移除;若需保留模板,用*ngTemplateOutlet将其实例化:

<!-- 在组件模板合适位置添加,触发模板渲染 -->
<ng-container *ngTemplateOutlet="effacer4"></ng-container>

<ng-template #effacer4>
  <!-- 原内容不变 -->
</ng-template>

2. 修正ViewChild配置

显式声明{ static: false },确保在视图初始化后获取元素(Angular 8+默认值为false,显式声明更清晰):

@ViewChild('Champ', { static: false }) ChampViewChild!: ElementRef;

3. 正确实现元素克隆

使用原生cloneNode(true)复制元素完整结构,再通过Renderer2将克隆元素添加到目标父节点:

修改后的完整代码

HTML代码

<ng-container *ngTemplateOutlet="effacer4"></ng-container>

<ng-template #effacer4>
  <div *ngIf="Next3">
    <div #Champ>
      <div class="Champ">
        <h1 class="title"> Prêt(s) immobilier à assurer </h1><br>
        
        <h2>Type de prêt</h2>
          <select class="form-select" aria-label="Default select example">
            <option value="1">Prêt classique (amortissable)</option>
            <option value="2">In fine</option>
            <option value="3">Prêt à taux Zéro</option>
            <option value="4">Prêt relais</option>
          </select> <br>
          <h2>Montant du prêt</h2>
          <div class="input-group mb-3">
            <input type="text" class="form-control" aria-label="Amount (to the nearest dollar)">
            <span class="input-group-text">.00 €</span>
          </div>
          <h2>Taux du prêt</h2>
          <div class="input-group mb-3">
            <input type="text" class="form-control" aria-label="Amount (to the nearest dollar)">
            <span class="input-group-text">%</span>
          </div>
    
      <h2>Durée totale</h2>
          <div class="input-group mb-3">
            <input type="text" class="form-control" aria-label="Amount (to the nearest dollar)">
            <span class="input-group-text">an(s)</span>
            <input type="text" class="form-control" aria-label="Amount (to the nearest dollar)">
            <span class="input-group-text">mois</span>
          </div>
          <h2>Dont différé</h2>
          <select class="form-select" aria-label="Default select example">
            <option value="1">1 mois</option>
            <option value="2">2 mois</option>
            <option value="3">3 mois</option>
            <option value="4">4 mois</option>
            <option value="5">5 mois</option>
            <option value="6">6 mois</option>
            <option value="7">7 mois</option>
            <option value="8">8 mois</option>
            <option value="9">9 mois</option>
            <option value="10">10 mois</option>
            <option value="11">11 mois</option>
            <option value="12">12 mois</option>
            <option value="13">13 mois</option>
            <option value="14">14 mois</option>
            <option value="15">15 mois</option>
            <option value="16">16 mois</option>
            <option value="17">17 mois</option>
            <option value="18">18 mois</option>
            <option value="19">19 mois</option>
            <option value="20">20 mois</option>
            <option value="21">21 mois</option>
            <option value="22">22 mois</option>
            <option value="23">23 mois</option>
            <option value="24">24 mois</option>
          </select>
         </div>
        </div>
      </div>
<button (click)="Clone()" class="btn btn-secondary btn-lg"><fa-icon [icon]="faPlusSquare"></fa-icon>  Ajouter un prêt à assurer</button>
</ng-template>

TypeScript代码

import { Component, ViewChild, ElementRef, Renderer2, AfterViewInit } from '@angular/core';
import { faFileContract, faChartLine, faCalculator, faBuilding, faHome, faMapMarkedAlt, faChess, faChessKing, faPlusSquare } from '@fortawesome/free-solid-svg-icons';

@Component({
  selector: 'app-questionnaire',
  templateUrl: './questionnaire.component.html',
  styleUrls: ['./questionnaire.component.css'],  
})

export class QuestionnaireComponent implements AfterViewInit{
  faFileContract = faFileContract;
  faChartLine = faChartLine;
  faCalculator = faCalculator;
  faBuilding = faBuilding;
  faHome = faHome;
  faMapMarkedAlt = faMapMarkedAlt;
  faChessKing = faChessKing;
  faChess = faChess;
  faPlusSquare = faPlusSquare;
  button: boolean = true;
  SUIVANT: boolean = true;
  Next: boolean = true;
  Next2: boolean = true;
  Next3: boolean = true;
  dynamic = 0;
  
  @ViewChild('Champ', { static: false }) ChampViewChild!: ElementRef;
  
  constructor(private _renderer: Renderer2) {}

  Clone() {
    console.log("Button clicked!");
    if (!this.ChampViewChild) return;

    // 深度克隆原元素的完整结构
    const clonedElement = this.ChampViewChild.nativeElement.cloneNode(true) as HTMLElement;
    // 为克隆元素添加新类
    this._renderer.addClass(clonedElement, 'new-Champ-class');
    // 将克隆元素添加到原元素的父节点,避免嵌套克隆
    this._renderer.appendChild(this.ChampViewChild.nativeElement.parentElement, clonedElement);
  }
  
  ngAfterViewInit() {
    // 若需初始化克隆,需确保Next3为true,否则元素未渲染
    // this.Clone();
  }

  incrementProgress() {
    this.dynamic += 5;
  };
}

额外说明

  • 移除了原代码中[ngClass]="Champ"的错误绑定:Champ是元素引用,不能直接作为ngClass的参数。
  • 克隆时将元素添加到原元素的父节点,避免嵌套克隆导致结构混乱。
  • 若Next3初始值为false,需在条件变为true后调用Clone方法,可通过ngAfterViewChecked或监听Next3的变化实现。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 16:20:23