Angular | 利用 `ChangeDetectorRef` 解决 Angular 动态显示输入框的聚焦问题

在 Angular 应用开发中,实现用户点击按钮后,原地切换显示一个输入框并自动获取焦点的功能,是一个常见的交互模式。例如,搜索图标点击后变为搜索框,用户可以直接输入。然而,由于 Angular 的变更检测和 DOM 更新机制的异步性,直接在改变显示状态后立即调用元素的 focus() 方法,往往会因为元素尚未完全渲染或准备就绪而失败。

本文将探讨这一问题,并介绍如何利用 ChangeDetectorRef 来更同步地更新视图,从而实现对动态出现元素的精确聚焦。

问题再现:按钮到输入框的切换与聚焦

考虑以下 Angular 模板结构:

<main class="columns-content-area" *ngIf="DictManagerStack.empty() || DictManagerStack.exists('EntriesManagement')"><div class="columns-content-header"><h1 class="columns-content-title">{{ 'EntriesManagement' | translate }}</h1><button style="margin-left: auto; margin-right: 0.5rem;" *ngIf="!isClickGlassInDictManager"(click)="onDictManagerButtonClick('SearchButton', 'search')"class="normal-toolbar-button"><i class="fa-solid fa-magnifying-glass"></i></button><input #searchInput *ngIf="isClickGlassInDictManager" style="margin-left: auto; margin-right: 0.5rem; border: 1px solid #ccc; border-radius: 4px; padding: 1px;"(keyup.enter)="onDictManagerButtonClick('SearchButton', 'EnterSearch')" type="text"(keyup.esc)="onDictManagerButtonClick('SearchButton', 'cancelSearch')"(focusout)="isClickGlassInDictManager = false"[(ngModel)]="searchWordInDictManager" placeholder="搜索"/></div><p class="columns-content-description">{{ 'EntriesManagementDescription' | translate }}</p></main>

我们希望当点击搜索按钮时,isClickGlassInDictManager 变量变为 true,导致按钮隐藏,输入框显示。同时,我们希望新出现的输入框立即获得焦点。

一个直观的想法可能是在处理按钮点击的方法中直接改变变量并调用聚焦:

import { Component, ViewChild, ElementRef } from '@angular/core';// ... other imports@Component({ /* ... */ })
export class YourComponent {isClickGlassInDictManager = false;// 使用 @ViewChild 获取输入框的引用@ViewChild('searchInput') searchInputElement!: ElementRef;onDictManagerButtonClick(itemType: 'SearchButton' | 'EntriesManagementContent', item: any) {if (itemType === 'SearchButton') {this.isClickGlassInDictManager = !this.isClickGlassInDictManager;// 问题所在:此时输入框可能还未完全呈现在 DOM 中或未准备好if (this.isClickGlassInDictManager && this.searchInputElement) {const searchInput = this.searchInputElement.nativeElement as HTMLInputElement;searchInput.focus(); // 可能失败}}}
}

之所以会失败,是因为 Angular 的变更检测通常不会在你的事件处理函数执行的 同一刻 立即更新 DOM。当 isClickGlassInDictManager 的值改变时,Angular 会安排一次视图更新,但这个更新过程可能发生在当前 JavaScript 任务完成之后。因此,在 focus() 被调用时,@ViewChild('searchInput') 对应的元素可能还不存在于 DOM 中,或者虽然存在,但浏览器还没有完成其布局和渲染,导致 focus() 方法没有效果。

解决方案:手动触发变更检测 cdr.detectChanges()

为了解决这个问题,我们可以利用 Angular 提供的 ChangeDetectorRef 服务来手动触发一次变更检测,强制 Angular 立即更新视图,从而确保输入框在调用 focus() 之前已经出现在 DOM 中。

以下是使用 ChangeDetectorRef 的解决方案:

import { Component, ViewChild, ElementRef, ChangeDetectorRef } from '@angular/core';// ... other imports@Component({ /* ... */ })
export class YourComponent {isClickGlassInDictManager = false;@ViewChild('searchInput') searchInputElement!: ElementRef;constructor(private cdr: ChangeDetectorRef) {} // 注入 ChangeDetectorRefonDictManagerButtonClick(itemType: 'SearchButton' | 'EntriesManagementContent', item: any) {if (itemType === 'SearchButton') {// 1. 改变状态,隐藏按钮,准备显示输入框this.isClickGlassInDictManager = !this.isClickGlassInDictManager;// 2. 手动触发变更检测// 这会强制 Angular 立即根据 isClickGlassInDictManager 的新值更新 DOMthis.cdr.detectChanges();// 3. 在 DOM 更新后,尝试聚焦输入框// 此时,由于 detectChanges() 已经执行,@ViewChild 应该已经获取到新创建的元素引用if (this.isClickGlassInDictManager && this.searchInputElement) {const searchInput = this.searchInputElement.nativeElement as HTMLInputElement;searchInput.focus(); // 现在聚焦操作应该能够成功}}}
}

方案解释

  1. this.isClickGlassInDictManager = !this.isClickGlassInDictManager;: 这一步改变组件状态,指示输入框应该显示(如果之前是隐藏的)。
  2. this.cdr.detectChanges();: 这是关键步骤。它告诉 Angular 立即执行一次变更检测。Angular 会检查所有绑定,发现 isClickGlassInDictManager 的变化,并根据 *ngIf="isClickGlassInDictManager" 的条件,同步地在 DOM 中创建并插入搜索输入框元素。
  3. if (this.isClickGlassInDictManager && this.searchInputElement): 在 detectChanges() 执行后,@ViewChild('searchInput') 应该已经成功获取到了新创建的输入框元素的引用。这个条件确保只有当输入框确实应该显示且引用可用时,才执行聚焦操作。
  4. searchInput.focus();: 由于 detectChanges() 已经保证了输入框元素的存在,这里的 focus() 调用现在能够成功地作用于实际的 DOM 元素,使其获得焦点。

通过 cdr.detectChanges(),我们将原本可能稍后发生的 DOM 更新提前到当前方法的执行流程中,从而解决了状态改变与 DOM 准备好进行交互之间的时序不同步问题。

总结

利用 ChangeDetectorRefdetectChanges() 方法,我们可以强制 Angular 立即更新视图,确保目标元素在 DOM 中存在且可供操作,从而实现对动态出现元素的精确控制,例如自动聚焦。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/82830.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

CSP认证准备第三天-差分及第36次CCF认证(BFS)

基础知识参考&#xff1a; csp突击前两题常用算法代码_ccf csp常用优化算法-CSDN博客 差分 什么是差分数组&#xff1f; 差分数组是原数组相邻元素之间的差值构成的数组。对于原数组 a&#xff0c;其差分数组 b 定义为&#xff1a; b[1] a[1] (假设 a[0] 0) b[i] a[i] …

[案例四] 智能填写属性工具(支持装配组件还有建模实体属性的批量创建、编辑)

论文盲审结果要出来了,渣渣超没有心情继续写了,过一段时间再说吧,今天宣布五一结束,哈哈哈。写完这篇博客开始搞科研了,有时间再进NX开发学习。本次案例主要是对上次导出自动导出BOM的一个前处理,要想导出属性,首先的有属性。于是本着学习的态度进行制作,可能有些功能有…

四核RK3566多媒体控制板技术分享(RK3566如何实现7个串口同时进行)

四核RK3566多媒体控制板技术分享: 今天分享一款近期接触到的四核RK3566多媒体控制板&#xff08;产品型号&#xff1a;ZK-R36A&#xff09;&#xff0c;这款产品在工业控制和智能设备领域有不错的表现&#xff0c;特此整理了一些技术参数供大家参考。 产品概述: 这款控制板采用…

多线程代码案例-1 单例模式

单例模式 单例模式是开发中常见的设计模式。 设计模式&#xff0c;是我们在编写代码时候的一种软性的规定&#xff0c;也就是说&#xff0c;我们遵守了设计模式&#xff0c;代码的下限就有了一定的保证。设计模式有很多种&#xff0c;在不同的语言中&#xff0c;也有不同的设计…

【计算机组成原理】第二部分 存储器--分类、层次结构

文章目录 分类&层次结构0x01 分类按存储介质分类按存取方式分类按在计算机中的作用分类 0x02 层次结构 分类&层次结构 0x01 分类 按存储介质分类 半导体存储器磁表面存储器磁芯存储器光盘存储器 按存取方式分类 存取时间与物理地址无关&#xff08;随机访问&#…

迅为RK3588开发板安卓GPIO调用APP运行测试

将网盘上的安卓工程文件复制到 Windows 电脑上。确保工程路径中使用英文字符&#xff0c;不包含中文。接着&#xff0c;启动 Android Studio&#xff0c;点击“Open”按钮选择应用工程文件夹&#xff0c;然后点击“OK”。由于下载 Gradle 和各种 Jar 包可能需要一段时间&#x…

BFS算法篇——打开智慧之门,BFS算法在拓扑排序中的诗意探索(下)

文章目录 引言一、课程表1.1 题目链接&#xff1a;https://leetcode.cn/problems/course-schedule/description/1.2 题目分析&#xff1a;1.3 思路讲解&#xff1a;1.4 代码实现&#xff1a; 二、课程表||2.1 题目链接&#xff1a;https://leetcode.cn/problems/course-schedul…

计数循环java

import java.util.Scanner;public class Hello {public static void main(String[] args) {Scanner in new Scanner(System.in);int count 10;while(count > 0) {count count -1;System.out.println(count);}System.out.println(count);System.out.println("发射&am…

11. CSS从基础样式到盒模型与形状绘制

在前端开发中&#xff0c;CSS&#xff08;层叠样式表&#xff09;是控制网页样式和布局的核心技术。整理了关于 CSS 基础样式、文本样式、盒模型以及形状绘制的一些心得。以下是详细的学习笔记。 一、基础样式设置 1. 字体样式 字体样式是网页视觉呈现的重要组成部分&#xf…

双种群进化算法:动态约束处理与资源分配解决约束多目标优化问题

双种群进化算法&#xff1a;动态约束处理与资源分配解决约束多目标优化问题 一、引言 约束多目标优化问题&#xff08;CMOPs&#xff09;在工程设计、资源分配等领域广泛存在&#xff0c;其核心是在满足多个约束条件的同时优化多个目标函数。传统方法往往难以平衡约束满足与目…

【Qt】pro工程文件转CMakeLists文件

1、简述 Qt6以后默认使用cmake来管理工程,之前已经一直习惯使用pro,pro的语法确实很简单、方便。 很多项目都是cmake来管理,将它们加入到Qt项目中,cmake确实是大势所趋。比如,最近将要开发的ROS项目,也是使用的cmake语法。 以前总结的一些Qt代码,已经编写成pro、pri等…

手机换地方ip地址会变化吗?深入解析

在移动互联网时代&#xff0c;我们经常带着手机穿梭于不同地点&#xff0c;无论是出差旅行还是日常通勤。许多用户都好奇&#xff1a;当手机更换使用地点时&#xff0c;IP地址会随之改变吗&#xff1f;本文将深入解析手机IP地址的变化机制&#xff0c;帮助您全面了解这一常见但…

【Canda】常用命令+虚拟环境创建到选择

目录 一、conda常用命令 二、conda 环境 2.1 创建虚拟环境 2.2 conda环境切换 2.3 查看conda环境 2.4 删除某个conda环境 2.5 克隆环境 三、依赖包管理 3.1 安装命令 3.2 更新包 3.3 卸载包 3.4 查看环境中所有包 3.5 查看某个包的版本信息 3.6 搜索包 四、环境…

目标检测任务常用脚本1——将YOLO格式的数据集转换成VOC格式的数据集

在目标检测任务中&#xff0c;不同框架使用的标注格式各不相同。常见的框架中&#xff0c;YOLO 使用 .txt 文件进行标注&#xff0c;而 PASCAL VOC 则使用 .xml 文件。如果你需要将一个 YOLO 格式的数据集转换为 VOC 格式以便适配其他模型&#xff0c;本文提供了一个结构清晰、…

Python作业练习2

任务简述 if_name__main_的含义&#xff0c;why? 问题解答 在Python中&#xff0c;if __name__ __main__:是一种常见的惯用法&#xff0c;用于检查当前模块是否是主程序入口点。要理解其含义和用途&#xff0c;首先需要了解两个概念&#xff1a; 1. __name__: 这是一个特…

ppy/osu构建

下载 .NET (Linux、macOS 和 Windows) | .NET dotnet还行 构建&#xff1a;f5 运行&#xff1a;dotnet run --project osu.Desktop -c Debug

NY182NY183美光固态颗粒NY186NY188

NY182NY183美光固态颗粒NY186NY188 在存储技术的竞技场上&#xff0c;美光科技&#xff08;Micron&#xff09;始终扮演着革新者的角色。其NY系列固态颗粒凭借前沿的3D NAND架构和精准的工艺控制&#xff0c;成为企业级存储和数据中心的关键支柱。本文将围绕NY182、NY183、NY1…

C++的历史与发展

目录 一、C 的诞生与早期发展 &#xff08;一&#xff09;C 语言的兴起与局限 &#xff08;二&#xff09;C 的雏形&#xff1a;C with Classes &#xff08;三&#xff09;C 命名与早期特性丰富 二、C 的主要发展历程 &#xff08;一&#xff09;1985 年&#xff1a;经典…

DedeCMS-Develop-5.8.1.13-referer命令注入研究分析 CVE-2024-0002

本次文章给大家带来代码审计漏洞挖掘的思路&#xff0c;从已知可控变量出发或从函数功能可能照成的隐患出发&#xff0c;追踪参数调用及过滤。最终完成代码的隐患漏洞利用过程。 代码审计挖掘思路 首先flink.php文件的代码执行逻辑&#xff0c;可以使用php的调试功能辅助审计 …

计算机网络|| 常用网络命令的作用及工作原理

1.hostname 作用&#xff1a;显示计算机的完整计算机名的主机名部分。仅当 Internet 协议 (TCP/IP) 协议作为组件安装在网络的网络适配器的属性中时&#xff0c;此命令才可用。 2.ping 作用&#xff1a; 1.用来检测网络的连通情况和分析网络速度 2.根据域名得到服务器 IP …