首页 > 网页制作 >Angular获取ngIf渲染DOM元素示例

Angular获取ngIf渲染DOM元素示例

来源:互联网 2026-06-15 08:28:42

在Angular开发中,通过@ViewChild获取*ngIf渲染的DOM元素时需将static显式设为false,否则会返回undefined。利用AngularCDK的拖拽功能实现跨组件元素互拖,需通过全局store与RxJS动态更新可拖拽列表,并在拖拽回调中更新父子节点关系。

Angular获取普通DOM元素的方法

在Angular开发中,获取DOM元素是一个常见需求,但有时一个小细节就可能导致问题。例如,使用模板变量名(#greet)配合@ViewChild获取元素,在大多数场景下可以正常工作。然而,当元素由*ngIf控制显示时,获取到的值往往是undefined。以下是一段典型代码:

import { Component, ViewChild, AfterViewInit } from '@angular/core';
@Component({
  selector: 'my-app',
  template: `
    

Welcome to Angular World

Hello {{ name }}

长期稳定更新的攒劲资源: >>>点此立即查看<<<

`, }) export class AppComponent { name: string = 'Semlinker'; @ViewChild('greet') greetDiv: ElementRef; ngAfterViewInit() { console.log(this.greetDiv.nativeElement); } }

这段代码在普通元素上可以正常运行,但如果换成*ngIf包裹的结构,则会获取失败。具体场景如下:

Angular获取ngIf渲染DOM元素示例

将static设置为false后获取

解决办法很简单:在@ViewChild的配置中,将static显式设置为false。Angular默认的static值为false,但在某些版本中,如果给@ViewChild传入第二个参数,该值可能被误设为true。明确指定static: false可以确保查询行为符合预期——等到结构渲染完毕后再执行查询。改造后的代码如下:

@ViewChild('dropList', { read: CdkDropList, static: false }) dropList: CdkDropList;
ngAfterViewInit(): void {
    if (this.dropList) {
      console.log(this.dropList)
    }
  }

使用该方案,即可成功获取*ngIf渲染后的cdkDropList实例。上述代码来自一个实际功能:buttonGroup中的按钮和某个列表中的按钮可以互相拖拽,底层使用了Angular的cdk/drag-drop模块。

import { CdkDragDrop, CdkDropList, moveItemInArray } from '@angular/cdk/drag-drop';

自实现的关键思路

官方文档的示例较为简单,未涉及跨组件拖拽的整合。以下分享实现时的关键思路。

首先,将所有需要拖拽的元素加入cdkDropList。在组件A和组件B各自初始化时,获取各自的DOM元素,并注册到一个全局Store中,每个元素附带一个唯一的componentId

组件A和组件B通过cdkDropListConnectedTo属性控制跨组件拖拽的目标范围,该属性绑定到一个动态数组_connectableDropLists。在页面初始化时,利用RxJS订阅特定componentId的变化,一旦Store中有新的拖拽列表注册,便更新_connectableDropLists。关键代码片段如下:

const parentId = this.storeService.getProperty(this.pageId, this.componentId, 'parentId');
this.dragDropService.getDragListsAsync(this.pageId, parentId.value)
      .pipe(takeUntil(this.destroy))
      .subscribe(dropLists => {
        this._connectableDropLists = dropLists || [];
      });
this.storeService.getPropertyAsync(this.pageId, this.componentId, 'children')
      .pipe(takeUntil(this.destroy)).subscribe(result => {
        if (!result || result.length === 0) {
          this._children = [];
          this._dragData = [];
          this.changeRef.markForCheck();
        } else {
          const dropbuttonArray = result.filter((item) => {
            const itemType = this.storeService.getProperty(this.pageId, item, 'componentType');
            if (itemType === AdmComponentType.DropdownButton) return item;
          });
          if (dropbuttonArray.length > 0) {
            this._connectableDropLists = [];
            dropbuttonArray.forEach(comId => {
              this.dragDropService.getDragListsAsync(this.pageId, comId)
                .pipe(takeUntil(this.destroy))
                .subscribe(dropLists => {
                  this._connectableDropLists.push(...dropLists);
                });
            });
          }
        }
      });

由于组件A是组件B的父级,因此需要通过当前组件ID获取父级ID,再进一步获取可拖拽的元素列表。

通过cdkDragData传递拖拽元素的value、id等信息

在模板中通过(cdkDropListDropped)="drop($event)"注册拖拽结束的回调。回调中需要处理数据更新——本质上是从旧父级下删除子节点,再将当前组件添加到新父级下,同时更新parentId。另外,buttonGroup内部的按钮之间也允许互相拖拽,因此需增加一层判断做特殊处理。

drop(event: CdkDragDrop) {
    if (event.previousContainer != event.container) {
      const { eventData } = event.item.data;
      const componentId = eventData[event.previousIndex];
      const oldParentId = this.storeService.getProperty(this.pageId, componentId, 'parentId', false)?.value;
      // delete oldParent children
      const oldParent = this.storeService.getProperties(this.pageId, oldParentId);
      const index = oldParent.children.indexOf(componentId);
      oldParent.children.splice(index, 1);
      // add newParent children
      const oldChildren = this.itemDatas.map(x => x.id.value);
      oldChildren.splice(event.currentIndex, 0, componentId);
      this.storeService.setProperty(this.pageId, componentId, 'parentId', { value: this.componentId }, [[this.pageId, componentId]]);
      this.storeService.setProperty(this.pageId, oldParentId, 'children', oldParent.children, [[this.pageId, oldParentId]]);
      this.storeService.setProperty(this.pageId, this.componentId, 'children', oldChildren);
      this.changeDetector.markForCheck();
      return;
    }
    moveItemInArray(this.itemDatas, event.previousIndex, event.currentIndex);
    const children = this.itemDatas.map(x => x.id.value);
    this.storeService.setProperty(this.pageId, this.componentId, 'children', children);
  }

这样,子组件与父组件内部的元素即可互相拖拽,整体交互流程便可顺畅运行。

侠游戏发布此文仅为了传递信息,不代表侠游戏网站认同其观点或证实其描述

热游推荐

更多
湘ICP备14008430号-1 湘公网安备 43070302000280号
All Rights Reserved
本站为非盈利网站,不接受任何广告。本站所有软件,都由网友
上传,如有侵犯你的版权,请发邮件给xiayx666@163.com
抵制不良色情、反动、暴力游戏。注意自我保护,谨防受骗上当。
适度游戏益脑,沉迷游戏伤身。合理安排时间,享受健康生活。