日志

浅谈 Checkbox Group 的双向数据绑定

 来源    2021-01-13    0  

前言

不曾想在忙碌的工作面前,写一篇技术博客也成了奢求。

Checkbox 作为表单中最常见的一类元素,使用方式分为单值和多值,其中单值的绑定很简单,就是 truefalse,但是多值(Checkbox Group)的绑定就有一点复杂了。在实际工作中发现很多组件库关于 checkbox-group 的双向绑定一直很别扭,或者说多多少少都有一些瑕疵。

开始本文之前,我们先假定有如下需求:

数据列表和输出值都是对象数组。能否只用一个双向绑定就完成数据的输入输出,而不是在得到绑定的数据之后再使用数组的 filtermap 这些方法去过滤和筛选。

着急的同学可以直接看最终的实现方案:Checkbox Group

现有组件库的实现及缺陷

调研一下市面上的组件库会发现,checkbox-group 并不是一个通用组件,很多组件库并没有这个组件,其中 Ant Design 的 checkbox-group 的设计方案算是比较完善的。简单看一下 Ant Design 是如何设计这个组件的。

1、Ant Design React 版的实现:

<Checkbox.Group options={options} defaultValue={['Pear']} onChange={onChange} />

optionsdefaultValue 的类型定义如下:

interface Option {
  label: string;
  value: string;
  disabled?: boolean;
}

defaultValue: string[];

2、Ant Design Angular 版的实现:

<nz-checkbox-group [(ngModel)]="options" (ngModelChange)="log(checkOptions)">
</nz-checkbox-group>

其中双向绑定的数据类型如下:

options : Array<{ label: string; value: string; checked?: boolean; disabled?: boolean; }>

问题剖析

不管是 React 版还是 Angular 版,它们的 checkbox-group 都有一个共同点或者说缺陷,那就是 Option 的类型是固定的,假设需要绑定的数据如下:

cars = [
    { id: 1, name: 'Ford' },
    { id: 2, name: 'Chevrolet' },
    { id: 3, name: 'Dodge' },
];

那我们必须先将这个 cars 数组 map 成 Option 类型,然后才能绑定渲染。

另外,React 版和 Angular 版的输出值类型也是固定的,其中 React 版输出的是一个关于 value 的字符串数组,Angular 版是则是一个双向绑定 checked 的原数组(个人觉得 Angular 版的绑定比 React 版的要灵活,至少从原数组取值更容易一点)。

还是以上面的 cars 数组为例,如果后端同事告诉我们想要一个完整的对象数组,比如下面这样:

selectedCars = [
    { id: 2, name: 'Chevrolet' }
];

那我们就必须再遍历一次 selectedCars 数组才能得到需要的数据。也就是说,对于上面展示的这种情况,我们必须要做一些额外的数据处理工作才能完成目标,但是这对于双向绑定功能来说显得有些繁琐。

那到底应该怎样设计 checkbox-group 的双向数据绑定才能更灵活的使用呢?

如何设计 Checkbox Group

在介绍如何设计之前,我们先尝试能否从其它组件设计中找到灵感。

Checkbox 与 Select 的共性

Checkbox Group 和 Multiple Select 除了很细小的交互差异之外,几乎看不出太大的不同。大多数情况下两者可以相互替换,所以很多人总是困惑两种组件到底应该如何选择。这里 有篇文章 专门对比了两种组件的交互场景,甚至使用 A/B test 去分析用户的偏好。

好像有点跑题了,言归正传,基于这种相似性,我们完全可以仿照 Select 的双向绑定机制去设计 Checkbox Group。

Select 的双向数据绑定

下面我们看一下 Material Select 和 Ng-Select 是如何设计双向绑定的,数据就以上面的 cars 为例。

cars = [
    { id: 1, name: 'Ford' },
    { id: 2, name: 'Chevrolet' },
    { id: 3, name: 'Dodge' },
];

selectedCars = [
    { id: 2, name: 'Chevrolet' }
];

1、Material Select

<mat-select multiple [(ngModel)]="selectedCars" [compareWith]="compareWith">
  <mat-option *ngFor="let car of cars" [value]="car">{{car.name}}</mat-option>
</mat-select>

2、Ng-Select

<ng-select [multiple]="true" [items]="cars" bindLabel="name" 
           [(ngModel)]="selectedCars" [compareWith]="compareWith">
</ng-select>

Material Select 和 Ng-Select 在设计上稍微有一些差别。Material Select 完全基于模板渲染,Ng-Select 则是属性配置优先,两者的数据回显都是通过 compareWith。它们的双向绑定都非常简单,我们没有写任何多余的代码就按规定的格式完成了数据的输入输出,这种设计思路同样可以用在 Checkbox Group 上面。

Checkbox Group 的设计实现

看完上面关于 Select 的两个例子,或许已经不需要我再多说什么了,最终我设计的 Checkbox Group 代码如下:

<mtx-checkbox-group [items]="cars"
                    bindLabel="name"
                    [(ngModel)]="selectedCars"
                    [compareWith]="compareWith">
</mtx-checkbox-group>

线上 DEMO

上面的代码没有任何多余的过滤筛选就完成了开篇提出的需求,对数据的操作全都隐藏在双向绑定的内部。

总结

这篇文章拖沓了非常久,一方面是自己工作很忙,另一方面做开源项目占据了大部分时间。

从最开始考虑 Checkbox Group 的重构方案到最终实现差不多用了半年多的时间,不过实际开发时间大概也就一周吧。相比之前借鉴 Ant Design 的方案来说,现在的方案更加灵活,有效减少了数据操作的代码,不过仍然有很大的优化和提升空间。

如果大家发现本文有不当之处,欢迎交流指正!

相关文章
浅谈用户需求的挖掘 – 脑图
问答浅谈用户需求的挖掘::
1
c# – 与转换器的双向数据绑定不更新源
问答我有一个数据绑定设置与转换器,以将一个笨拙的XML源转换为显示和编辑方便的内部类树.一切都非常适合从XML源读取,但我有一段时间试图对内部类进行更改以传播回XML源. 这是使用网站的XAML: < ...
2
c# – 使用jQuery驱动的“选择”下拉的双向数据绑定
问答好吧,我有一个jQuery版本的Chosen应用于我的页面上正确显示的选择,我已经使用以下代码完成了.首先,我有一个BaseController,它设置一个列出所有可能类别的ViewBag属性: pr ...
2
javascript – 如何使用ng-bind-html与双向数据绑定?
问答我目前正在研究一个用angular构建的简单编辑器.主文本框只是一个div,其中contenteditable设置为true和ng-bind-html属性,如下所示: <div contente ...
1
windows-phone-7 – 当单击ApplicationBar中的按钮时,TextBox的双向数据绑定不会更新
问答我的应用程序中有一个TextBox,ApplicationBar中有一个ApplicationBarIconButton,它充当TextBox内容的"提交". 使用虚拟键盘编辑Te ...
1
angularjs – Angular-UI-Bootstrap自定义工具提示/弹出窗口,具有双向数据绑定功能
问答我在我目前的项目中使用angular-ui-bootstrap,而且我需要一个popover,这将允许用户对给定的元素(重命名/编辑/删除/ etc -)采取一些行动.由于angle-ui的引导弹出窗 ...
1
angular – 为自定义属性实现双向数据绑定
问答我在primeng组件中看到像这样的某些属性使用类似ngModel(双向数据绑定)样式的东西 [(selection)]="selectedItem"; 它看起来像 @Input( ...
2
数据绑定 – Angular2双向数据绑定
问答我知道Angular2没有双向数据绑定,但有没有办法模仿Angular1.x的双向数据绑定行为?::注意 – 向下滚动ng-model绑定的答案 你实际上可以这样做,只需要调用内部changelist ...
1
数据绑定 – Angular2:使用DynamicComponentLoader动态插入的组件上的双向数据绑定
问答我正在开发一个Angular2应用程序,我遇到了一个问题: 我有一组可以使用UI选择的不同对象.每个对象都有一组可以使用UI编辑的选项(不同的对象).现在,我正在使用DynamicComponentL ...
1
javascript – AngularJS – 使用controllerAs语法和服务属性进行DRY双向数据绑定
问答我偶然发现了一个应该是常见且明显的问题,但我似乎无法绕过它. 我正在研究一个小型原型应用程序.我的后端开发人员在JSON对象中为我提供了配置文件数据.让我们说,它看起来像这样: profile = { ...
1
angularjs – Angular js双向数据绑定问题,用于管理“文本框”到“标签”的更改
问答我正在使用具有"编辑"字段的数据模型.并基于"编辑"值我使用文本框或标签.我希望一旦用户在编辑后点击"确定"然后文本框应该更改为标签.但不知 ...
1
angularjs – 具有Ajax功能的Angular-UI Select2的双向数据绑定
问答使用具有Ajax功能的AngularJS Angular-UI Select2的双向数据绑定. 我创建了一个指令,以通用的方式实现Select2下拉式Ajax行为: – (它需要很少的属性来获取for ...
1
javascript – AngularJS:如何在两个隔离控制器和共享服务之间创建双向数据绑定?
问答我正在尝试在两个隔离控制器和共享服务之间创建双向数据绑定(其提供另一个隔离的范围): app.factory("sharedScope", function($rootScope) ...
1
数据绑定 – 组件输入属性上的双向数据绑定
问答我试图在angular2上做一些工作,我无法找到关于这种行为的东西. 我有一个实现自定义组件的应用程序,如下所示: import {Component,Input} from 'angular2/co ...
1
javascript – Angular 2中双向数据绑定的共享RxJS主题
问答我有应用程序设置的单件服务 class Setting { get foo() { return storage.get('foo'); } set foo(val) storage.set('foo ...
1
普通Javascript双向数据绑定
问答出于好奇并增加了我的知识,我想在dom元素和javascript变量之间实现某种双向数据绑定. 我很幸运地找到了一个很好的答案,我的一半问题在这里@ stackoverflow导致我这个要点https ...
1
c# – ASP.NET中的双向数据绑定
问答假设我们有一个对象 class Entity { public string ID {get; set;} public string Name {get; set;} } 我想将属性绑定到页面上的两 ...
2
理解AngularJS中的双向数据绑定
问答我是AngularJS的新手.很长一段时间我试图滥用它,就像我一直使用Javascript或Mootools这样的Javascript-Frameworks.现在我明白它不再那样了--但是我遇到了一些 ...
1
angular – @Input:双向数据绑定不起作用
问答我试图复制这个:http://plnkr.co/edit/OB2YTMJyeY3h9FOaztak?p=preview(这个plunker是有效的例子,我希望得到相同的结果,但我的代码,这是行不通的) ...
1
Angularjs:双向数据绑定和控制器重新加载
问答如果使用路由和控制器,那么模型不会在控制器重新加载之间保存其状态. Angular在每个路由负载上创建控制器实例和新范围. 例如,我输入的内容有ng-model ="something&qu ...
1