Angular获取ngIf条件渲染DOM元素示例
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包裹的结构,就会扑空。具体场景如下:

将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';
自己实现的思路
官方文档中的demo比较简单,并未涉及跨组件的拖拽整合。这里分享实现时的核心思路。
首先,将所有需要拖拽的元素都加入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); }
这样一来,子组件和父组件内部的元素就能互相拖拽,整个交互流程也就顺畅起来。
游乐网为非赢利性网站,所展示的游戏/软件/文章内容均来自于互联网或第三方用户上传分享,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系youleyoucom@outlook.com。
同类文章
如何用拖放在Flask看板应用中更新数据库状态
在Flask看板应用中,通过HTML5拖放API结合前端fetch异步POST请求,将拖拽任务的task_id和target_state发送至后端路由;后端接收JSON后更新数据库并提交事务,实现跨列拖拽时实时更新状态。需注意将事件绑定到容器而非子元素,并在ondragover中阻止默认行为以确保drop触发。
如何用CSS :has()选择器快速实现悬停span显示相邻div
利用CSS的:has()伪类选择器,通过` bar:has(> baz:hover)+ qux`实现悬停内联元素时显示相邻块级容器,解决了传统相邻兄弟选择器无法跨层级匹配的局限,可用于悬停图标显示详情框等交互场景。需注意浏览器兼容性和性能优化,避免过度使用影响渲染效率。
display:block居中段落文本的响应式实现方法
通过`display:block`与`max-width`限制宽度,配合`margin:auto`使段落块水平居中并实现响应式布局;同时用CSS统一控制段落间距,避免脱离文档流,提升可读性与维护性。
JavaScript代码去重:高效避免重复编写的技巧
将重复代码中的变化点提取为参数,利用ES6模板字符串动态生成选择器,可消除重复、保持一致性。注意参数安全性,必要时进行白名单校验;批量化场景可扩展为数组形式;现代框架中更推荐数据驱动或组件化封装替代手动选择器。
使用容器查询单位cqw实现表单输入框相对于祖父容器百分比宽度
利用CSS容器查询单位cqw,表单输入框宽度可直接基于祖父容器计算,绕过中间包装器尺寸干扰,实现简洁响应式布局,无需硬编码或JavaScript,显著降低维护成本,提升嵌套结构下的布局灵活性。
- 日榜
- 周榜
- 月榜
相关攻略
2026-07-08 06:57
2026-07-08 06:56
2026-07-08 06:56
2026-07-08 06:56
2026-07-08 06:56
2026-07-08 06:56
2026-07-08 06:56
2026-07-08 06:55
热门教程
- 游戏攻略
- 安卓教程
- 苹果教程
- 电脑教程
热门话题

