Angular中用Renderer2克隆Div时ViewChild无法找到选择器的问题
Angular ViewChild无法获取ng-template内元素及克隆元素的解决方案
问题根源
- 元素处于未实例化的ng-template中:
<ng-template>是惰性模板,默认不会渲染到DOM,因此ViewChild无法定位内部的#Champ元素。 - 克隆逻辑错误:当前
Clone()方法用renderer.createElement仅创建空标签,未复制原元素的内容与结构。 - 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
相关产品推荐
相关产品推荐

