easyui根据select下拉框内容更新表单内容_Ant Design 4.0 的一些杂事儿 - Select 篇

前几篇:

  • Ant Design 4.0 的一些杂事儿 - Table 篇
  • Ant Design 4.0 的一些杂事儿 - Form 篇

聊完了 Table 和 Form 两个重型组件,我们来继续聊聊看起来不那么重的 Select 组件。它在 Ant Design 4.0 中有哪些变化。如果你读过 《Ant Design 4.0 进行时》,那你应该已经有了大概的印象。当然,即便没有读过也不用担心。之后会为你细细道来 Select 背后的一些故事~

“滚滚的车轮”

如果你关注过底层实现,你会发现 rc-selectrc-tree-select 在选择框部分有大量相似而又有一点点不同的地方。这有一段有趣的历史,当年先有的 Select 组件之后结合了 Tree 做了一个 TreeSelect 组件。由于这两个组件有少部分代码相同,所以 TreeSelect 复制了一部分 Select 的代码,继续开发。但是随着时间推移,两者自分裂之日起,来自社区的 issue 以及人类的忘性。两者新增的一些 API 开始出现了矛盾。

一个典型例子就是 inputValuesearchValue 之争:

最早,Select 和 TreeSelect 对于搜索框内的文字都不支持受控模式。之后社区提了 issue 希望对其进行控制,于是乎 rc-select 就多了一个 inputValue

然后又过了一段时间,社区同学提出 TreeSelect 的搜索框内容在选择一个值后会被清空。这在单选没有问题,但是如果是多选的话,就会遇到需要重复输入搜索内容的情况。嗯嗯,这的确不好。于是我们就在 3.7.0 版本中添加了 autoClearSearchValue

那么,既然 TreeSelect 有了。Select 也应该有啊。于是在 3.10.0 里我们给 Select 也添加了 autoClearSearchValue

你们发现问题了吗?rc-select 把搜索框的值叫做 inputValue 但是自动清除却叫做 autoClearSearchValue。因为两个 API 各自发展最后又合到了一块儿。

于是,我们在之后的版本进行了调整,统一使用 searchValue ,而遗留的 inputValue 在之后的版本会收到 warning 信息。

样式谁为先?

如果你是一个设计师,你会发现 v3 版本的 Select 和 TreeSelect 在单选开启搜索的时候样子有点不太一样:

35592014e864cc4bb4dc06d64b8f0803.png
TreeSelect 搜索框在下面

d03212b51db92cf633fe5ab10f7cdf88.png
Select 搜索框在同级

于是我就去做了一下考古。发现在 0.12.x 版本中,Select 的输入框是在弹窗内部的:

b5fb4a10560b44c4dfbd9bae705d3d40.png

而在之后 1.x 版本中,Select 改成了现在的设计,但是 TreeSelect 却没有更新。 于是乎,TreeSelect 随着 0.12.x 的尾巴一直保留了这个设计。

我们在 issue 中对此有所讨论。从 v3 的设计角度来说,下拉框内包含搜索框更容易让用户明白这个输入框是用于搜索而非当前的值。但是在 v4 中,我们经过讨论认为 Select 作为一个更加常用的组件。用户已经培养了使用习惯,搜索框位置已经不再是一个会让人纠结的问题。所以新版设计中,改为 TreeSelect 的搜索框移出于 Select 保持一致。

多米诺骨牌

Select 底层的 rc-select 有着非常古老的历史,追溯到第一次提交是 2015 年 1 月。 当年,React 还是 0.12.x 的版本。大家对生命周期的理解也不像现在拥有大量文章进行介绍。所以一些陈年代码片段因为一直运行良好就保留至今,其中一个困扰大家很久的是一段不知道为什么直接设置 state 的代码。 @Mack 在编写测试的时候,看到它毅然决然写下了一个注释:

6f8f678a29f80206c1a2c4f5207050ef.png
对啊,为啥不用 `setState`?

@陈帅等13w人 在重构成 Typescript 的时候,看到它,毅然决然的改成了 setState

c1b95e4c970046b17166d2d2de77f0ef.png

事后,我们获得了一个 bug:

996835cf7434a8fd65c6ef19d06abeef.png
https://github.com/ant-design/ant-design/issues/14262

于是, @陈帅等13w人 默默进行了回滚并添加了额外的 comment:

d31fefe7bc95bd5e368b1da96d63c62c.png

当然,我们在重写代码的时候已经抛开了这些历史包袱。在 v4 版本中,Select 没有这些直接操作 state or DOM or style 的脏代码。转而使用纯粹的生命周期来进行管理,以准备迎接未来的 concurrent 模式。

谁先谁后

在日常维护生活中,我们还收到过一个 issue:

d5d72061b34da17ac2ba2f4dda6b6bd1.png
https://github.com/ant-design/ant-design/issues/17630

第一反应估计大家都是 onChange 然后清空一下 value,但是正如我们上面所说的。AutoComplete 实际上是一个输入框。所以用户输入的时候是会触发 onChange 的,所以在 AutoComplete 中如果要触发选择应该使用 onSelect 事件。 但是,我们的顺序是先 onSelectonChange 。这导致,如果用户用 onSelect 清空了值,又会被 onChange 给赋值回来。于是,我们做了调整,onChange 变成了最先触发的。小伙伴本终于可以轻松的通过其他事件来修改 value 啦~

原生的不香吗?

原生的 select 组件简单好用。不过如果你想要实现一些额外的效果,它却又不尽如人意。我们暂时不提 Option 不能接受 ReactNode 这类问题。先来一个简单的例子,如果你为原生的 select 设置一个空值,你会期望页面中是一个空选项:

<select value=""><option>one</option>
</select>

而实际上却是:

f792fe815103d6df588d9172df1f9436.png

更麻烦的是,原生的 select 由于会默认设置一个值到选项里。所以如果你的 select 只有一个 option 时,你就无法触发 onChange 了。

为此,大部分使用原生 select 组件为了保留空值不得不提供一个补位:

c594b1eae318b76eeb3fac726ce081ba.png

此外,如果你的 select 需要多选,select 会从一行变成多行展示。其对应的 onChange 事件获得的 value 也不是你所预期的。

c8789001820f9da2836510177d8eb19a.png

为此,几乎每一个组件库总是会着手解决 Select 组件的问题。 将其进行提炼、抽象变得简单、好看且“好用”。

value 的同步问题

在我们的日常维护中,一个关于 value 的常见提问就是:“为什么 value 不在 options 中却可以展示。这是一个 bug!”

的确,它的行为和原生的 select 不太一样,但是这其实是有意为之。从交互角度看,如果你给一个组件赋值。那么无论是用户还是开发者,都会存在一个预期。即:

  • 用户:我看到了 Select 的值为 a,我不需要打开这个 Select 看一圈 options ,我也知道它的值是 a
  • 开发者:我为这个 Select 设置了值为 a,那么我应该对我赋的值负责。组件不应该在我的预期外修改这个值。

这是一个很有趣的问题,试想一下有个 Select ,它的 options 是异步加载的。那么在其 value 加载完成且 options 还没有准备好的时候。你预期 Select 的值应该是什么?很显然,如果是没有值,那么受控的 value 就和展示的值不同步了。更有甚者,如果你的 options 是依赖于另一个控件来动态加载。那么你的 Select 值就会反复处于 value => null => value 的状态。对于用户而言,它处于即是有值又是无值的状态。 即便我并不是想设置这个 Select,我只是打开这个页面看看配置而已。

所以,无论 options 是什么状态。Select 的展示值与其内部值都应该跟随 value 受控。

combobox 是不是一个 Select?

在 v3 初期,我们的 Select 提供了一个 combobox 模式,你可以理解为现在的 AutoComplete 组件。combobox 模式下,Select 相当于一个 Input 组件,只是它提供了一个自动完成的下拉选项卡,其输入框内的值就是 value

但是,这也正是让人困扰的地方。开发者很难分清 combobox 模式到底和其他的模式有什么区别:

e6b699a0fec5ff5437ca06e1cf2f21c4.png

5353388c148e9d531aaea773e799f690.png

c89c986241f31337120293e59627395c.png

4a959eb0150acf32561d2183c46c59b7.png

于是,我们不得不反复告知 combobox 设计上的区别:

a8172a6bb41493aa445627419bf51179.png
然后收获了

为此,我们最终决定将 combobox 从 Select 中抽离出来。转成一个新的组件 AutoComplete。它虽然在内部与 Select 共享了大量代码,但是仍然毅然决然的与 Select 分道扬镳以降低开发者的理解成本。

杂谈:我们推荐你可以看一下今年 SEE Conf 中 @林外 关于 《Ant Design 4.0:创造快乐工作》的演讲。Ant Design 对于组件设计会保持克制,因而我们会从设计与实现的角度共同去探索一个组件所代表的意义。也正是于此,AutoComplete 应该与 Select 进行拆分以达到开发者更好的开发体验。减少困扰就是减少工作量,减少工作量就是早点下班~

选择生成器

上面我们已经描述过了 TreeSelect 和 Select 在选择框部分非常相似,而 API 更是几乎 70% 都相同。在解决了布局样式统一的问题后,我们终于可以将这一部分进行提炼。

在新版的 rc-select 中,我们抽取了一个 generate 方法。它主要接收一个 OptionList 的自定义组件用于渲染下拉框部分。这样我们就可以直接复用选择框部分的代码,而自定义 Select 和 TreeSelect 对应的列表或者树形结构了。

键盘的小把戏

为了让 Selct 更贴近原生组件,我们需要考虑到一些键盘交互的问题。它应该有且唯一有一个能获取焦点的元素来进行交互处理,否则就会遇到 Tab 到 Select 时需要多次 Tab 才能切换到下一个组件的问题。也会遇到 v3 时期,DatePicker 中 onFocusonBlur 多次触发的问题:

bab03b5509be32fb519c9cb9f1ca37fe.png

bddca3c90d5c73a6f902cdea8e366f03.png

因而在设计之初,我们给 Select 预留了一个全组件唯一的焦点入口 input。这个 input 在设置 showSearch 的时候,是那个搜索框;在未设置的时候,是一个 opacity: 0 的幽灵元素;在禁用时,它只需要一个 disabled 就可以略过键盘交互。 从而我们将一些组合的键盘操作简化为了对 input 的操作,我们不再需要面对多个焦点组件之间进行焦点的切换。

a37b3ede14fec89319051166ca95581a.gif
v3 中打开面板按 ESC 会导致焦点无法还原的问题

此外,我们还对键盘交互进行了一些细节调整。

比如在 v3 版本中,通过 tab 选中 Select 后必须通过 SpaceEnter 打开下拉框才能进行输入过滤。在 v4 版本中,利用 input 总是存在的特性。获取焦点的 Select 可以直接输入触发过滤,减少了用户额外的操作步骤。

又如 v3 中,多选模式下长按删除键会出现把已经选中的值误删的情况。在新版中,我们为此添加了一个额外的状态来确定你是否正在输入。如果是输入状态,删除至最左时不会触发删除选中值的操作。当停止输入一段时间后,释放该状态以允许用户删除:

cf2e0c04b0c02aa80088effb285fc2f8.gif
v3 的手抖误删

3c5a79b7fc785d0bdddb2799cb13b0c9.gif
v4 的输入检测防止误删

同样的,我们将这套规则也应用于 Tree 组件上。v4 版本的 Tree 也拥有一个隐藏且唯一的焦点组件用于处理键盘交互。更 Cool 的是,由于这个焦点组件是无法被鼠标点击的。从而我们可以精确的知道,其交互是来自于用户点击还是键盘聚焦:

5defe89db7c4de42bfe31944eb2d5009.gif
Tree 拥有键盘 only 的交互样式

看到这里,你或许会想到一个问题。既然下拉内容是自定义的,那么如何确定自定义内容支持哪些操作呢?其中的奥秘在于,我们规定的 OptionList 需要提供一个键盘交互接口。在上层 Select 消费完毕后仍有未处理的键盘事件则会通过该接口传递给 OptionList。也因此,我们可以完全复用 rc-tree 组件自身已经完成的键盘交互。

差不多了~

是的,又到了结尾的时候。这次文章本来还想写一写 Tree 更多的故事,不过只是作为一部分的话又略为可惜。其中 Ant Design 支持动画的虚拟滚动技术可以额外写一篇大文章,让大家理解我们为了一些交互细节额外做了什么事情。而它和 Tree 也非常有关系。所以,这里就不做重复功。下次有机会我们再细聊吧。

PS:虽然这里不提虚拟滚动。但是 Select 也已经接入了虚拟滚动,你可以通过这个例子体验一下 v3 与 v4 版本的性能差异。

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

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

相关文章

避免代码冗余,使用接口和泛型重构Java代码

转载自 避免代码冗余&#xff0c;使用接口和泛型重构Java代码在使用动态语言和.NET工作了若干年后&#xff0c;我又回到老本行–Java开发。在Ruby中&#xff0c;清除代码冗余是非常方便的&#xff0c;而在Java中则需要结合接口和泛型实现类似的功能。 原始代码 以下是这个类中的…

一文理类加载相关知识:类加载器、双亲委派、SPI

思维导图 类加载的时机 类加载的流程 类从被加载到内存中开始&#xff0c;直到被从内存中卸载为止&#xff0c;它的整个生命周期包括&#xff1a;验证、准备、解析、初始化、使用和卸载7 个阶段。 其中验证、准备、解析 3 个部分统称为连接&#xff08;Linking&#xff09; …

可以搜python编程答案的软件_python实现百万答题自动百度搜索答案

用python搭建百万答题、自动百度搜索答案。 使用平台 windows7 python3.6 MIX2手机 代码原理 手机屏幕内容同步到pc端 对问题截图 对截图文字分析 用浏览器自动搜索文本 使用教程 1、使用Airdroid 将手机屏幕显示在电脑屏幕上。也可使用360手机助手实现。不涉及任何代码。实现效…

intellij idea设置主题、字体样式和背景色

转自&#xff1a; https://blog.csdn.net/fanrenxiang/article/details/80598895 点击这里查看 <intellij idea使用教程汇总篇> 引言&#xff1a;所谓工欲善其事必先利其器&#xff0c;idea就是这样的利器&#xff0c;刚装好的intellij idea主题样式是白的&#xff0c;…

MySQL优化(四):count()

count()不同写法的区别 COUNT(字段名)&#xff1a;返回SELECT语句检索的行中值不为NULL的行数 COUNT(1)&#xff1a;表示的是直接查询符合条件的数据库表的行数&#xff08;会包含值为NULL的行数&#xff09;。其中1指的是表中的第一个字段&#xff0c;如有表 table(id, colu…

图像sobel梯度详细计算过程_数字图像处理(第十章)

点、线、边缘检测背景知识。书中主要介绍了图像的一阶导数与二阶导数&#xff0c;这个之前的文章中有过介绍这里在复习一遍。对于函数 ,对于点 在x方向的一阶偏导为&#xff1a;,二阶偏导为&#xff1a;之后书中总结了一阶导与二阶导对于图像求取边缘的结论&#xff1a;孤立点检…

idea部署maven+javaweb项目到jboss

小编习惯使用eclipse对jboss跑的项目部署,第一次使用idea进行jboss部署项目,遇到很多问题,做此文章以帮助更多人. 图中涂鸦的是项目名,对应上自己的项目名即可 1.导入项目,这一步不多说 2.配置项目: a>点击file-->Project-Stucture-->Project 3.配置Modules 配置…

Java8-本地缓存

转载自 Java8-本地缓存这里我将会给大家演示用ConcurrentHashMap类和lambda表达式实现一个本地缓存。因为Map有一个新的方法可以在key为Null的时候自动计算一个新的value值。非常完美的实现cache。来看下代码&#xff1a;12345678910111213141516publicstatic void main(String…

Integer和Int的比较,谈谈拆卸和装箱

示例代码 public static void main(String[] args) {Integer a new Integer(10111);int b 10111;boolean equal1 a b;//自动拆箱&#xff0c;xxxValue()boolean equal2 a.equals(b);//自动装箱, valueOf()System.out.println(equal1);System.out.println(equal2); }反编译…

python调用webservice接口实例_python调用webservice接口的实现

使用suds这个第三方模块 from suds.client import Client url http://ip:port/?wsdl cilentClient(url) print cilent 查看webservice接口的具体信息&#xff1a; 调用接口方法&#xff0c;通常 client.service.methodname 实际测试过程中遇到的坑&#xff1a; 1、tns 值为Lo…

idea2021部署maven+javaweb项目到jboss(diy)

【README】 我为什么要写这个文章&#xff0c;看了这位老哥的博文 https://blog.csdn.net/PacosonSWJTU/article/details/118074604 部署成功了&#xff0c;很感谢&#xff0c;所以也想照做一下&#xff1b; 【1】创建web项目module &#xff08;Project02 是一个空项目&…

Java对象内存结构

转载自 Java对象内存结构学C/C出身的我&#xff0c;对Java有一点非常困惑&#xff0c;那就是缺乏计算对象占用内存大小的机制。而在C中就可以通过sizeof运算符来获得基本类型以及类实例的大小。C和C中的这个操作符对于指针运算、内存拷贝和IO操作都非常有用。 Java中并没有一个…

Java版大顶堆的实现

堆的概念 堆是一棵完全二叉树&#xff0c;一般使用数组来存储。通俗来讲堆其实就是利用数组来维护一个完全二叉树。 按照堆的特点可以把堆分为大顶堆和小顶堆 大顶堆&#xff1a;堆的每个结点的值都大于或等于其左右孩子结点的值 小顶堆&#xff1a;堆的每个结点的值都小于或…

Java 8新特性探究(二)深入解析默认方法

转载自 Java 8新特性探究&#xff08;二&#xff09;深入解析默认方法 什么是默认方法&#xff0c;为什么要有默认方法 简单说&#xff0c;就是接口可以有实现方法&#xff0c;而且不需要实现类去实现其方法。只需在方法名前面加个default关键字即可。 为什么要有这个特性&am…

把本地库推送到github远程库

【1】 github上创建远程库 注意 &#xff0c;远程库的名字要与本地库相同 【2】新建github远程库别名origin 【3】 代码提交 git add ./* &#xff1a; 把修改内容添加到暂存区 &#xff1b; git commit -m msg &#xff1a; 提交暂存区的修改内容到本地库&#xff1b; g…

react antd confirm content list_React造轮系列:对话框组件 - Dialog 思路

React造轮系列&#xff1a;对话框组件 - Dialog 思路对话框一般是我们点击按钮弹出的这么一个东西&#xff0c;主要类型有 Alter, Confirm 及 Modal, Modal 一般带有半透明的黑色背景。当然外观可参考 AntD 或者 Framework 等。确定 APIAPI 方面主要还是要参考同行&#xff0c;…

Spring IOC 和 AOP 概览

IOC&#xff08;控制反转&#xff09; IoC&#xff08;Inversion of Control&#xff0c;控制倒转&#xff09;。所谓IoC&#xff0c;对于spring框架来说&#xff0c;就是由spring来负责控制对象的生命周期和对象间的关系。 在没有IOC时&#xff0c;我们通过new 等关键字等方…

Java 并发实践 — ConcurrentHashMap 与 CAS

转载自 Java 并发实践 — ConcurrentHashMap 与 CAS最近在做接口限流时涉及到了一个有意思问题&#xff0c;牵扯出了关于concurrentHashMap的一些用法&#xff0c;以及CAS的一些概念。限流算法很多&#xff0c;我主要就以最简单的计数器法来做引。先抽象化一下需求&#xff1a;…

git rebase命令(转)

转自&#xff1a; https://www.yiibai.com/git/git_rebase.html git rebase命令在另一个分支基础之上重新应用&#xff0c;用于把一个分支的修改合并到当前分支。 使用语法 git rebase [-i | --interactive] [options] [--exec <cmd>] [--onto <newbase>][<u…

python tkinter计算器实例_python -Tkinter 实现一个小计算器功能

原博文 2017-03-25 22:08 − 文章来源&#xff1a;http://www.cnblogs.com/Skyyj/p/6618739.html 本代码是基于python 2.7的 如果是对于python3.X 则需要将 tkinter 改为Tkinter 将tkMessagebox&... 相关推荐 2019-12-10 15:59 − python GUI编程(Tkinter) Python 提供了多…