前言

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

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

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

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

着急的同学可以直接看最终的实现方案: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 的方案来说,现在的方案更加灵活,有效减少了数据操作的代码,不过仍然有很大的优化和提升空间。

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

浅谈 Checkbox Group 的双向数据绑定的更多相关文章

  1. 【Vue】浅谈Vue(一):从模板语法数据绑定、指令到计算属性

    写在前面 今年前端届比较有意思,从大漠穷秋发表文章比较angular和vue,继而致歉vue作者.社区,从谷歌辞去Angular Developer PM in China一职并且呼吁大家停止各种无谓 ...

  2. 浅谈Vue之双向绑定

    VUE实现双向数据绑定的原理就是利用了 Object.defineProperty() 这个方法重新定义了对象获取属性值(get)和设置属性值(set)的操作来实现的.那么Object.defineP ...

  3. 关于AngularJS学习整理---浅谈$scope(作用域) 新手必备!

    作为初次接触 AngularJS的新手,想要深层理解里面的内容短时间还是不可能的,所以标题写了浅谈字样,以下内容是参考各位大神以及相关书籍整理加个人理解,出现错误的地方请大家指正. $scope(作用 ...

  4. 视频基础知识:浅谈视频会议中H.264编码标准的技术发展

    浅谈视频会议中H.264编码标准的技术发展 浅谈视频会议中H.264编码标准的技术发展 数字视频技术广泛应用于通信.计算机.广播电视等领域,带来了会议电视.可视电话及数字电视.媒体存储等一系列应用,促 ...

  5. iOS开发之浅谈MVVM的架构设计与团队协作

    今天写这篇博客是想达到抛砖引玉的作用,想与大家交流一下思想,相互学习,博文中有不足之处还望大家批评指正.本篇博客的内容沿袭以往博客的风格,也是以干货为主,偶尔扯扯咸蛋(哈哈~不好好工作又开始发表博客啦 ...

  6. Linux特殊符号浅谈

    Linux特殊字符浅谈 我们经常跟键盘上面那些特殊符号比如(?.!.~...)打交道,其实在Linux有其独特的含义,大致可以分为三类:Linux特殊符号.通配符.正则表达式. Linux特殊符号又可 ...

  7. python浅谈正则的常用方法

    python浅谈正则的常用方法覆盖范围70%以上 上一次很多朋友写文字屏蔽说到要用正则表达,其实不是我不想用(我正则用得不是很多,看过我之前爬虫的都知道,我直接用BeautifulSoup的网页标签去 ...

  8. 浅谈Vue.js

    作为一名Vue.js的忠实用户,我想有必要写点文章来歌颂这一门美好的语言了,我给它的总体评价是“简单却不失优雅,小巧而不乏大匠”,下面将围绕这句话给大家介绍Vue.js,希望能够激发你对Vue.js的 ...

  9. 浅谈HTML5单页面架构(一)——requirejs + angular + angular-route

    心血来潮,打算结合实际开发的经验,浅谈一下HTML5单页面App或网页的架构. 众所周知,现在移动Webapp越来越多,例如天猫.京东.国美这些都是很好的例子.而在Webapp中,又要数单页面架构体验 ...

  10. 浅谈HTML5单页面架构(二)——backbone + requirejs + zepto + underscore

    本文转载自:http://www.cnblogs.com/kenkofox/p/4648472.html 上一篇<浅谈HTML5单页面架构(一)--requirejs + angular + a ...

随机推荐

  1. node代码片段

    /** * Created by Administrator on 2016/8/22 0022. * chat */ var net=require('net'); var chatServer=n ...

  2. Navicat 回复 psc 文件 Mysql

    在mysql 中回复 psc文件 的时候 只能一步步来,先在navicat中建一个空数据库,然后点击有上角的备份==>回复备份==> 找到psc文件==> 注意此时不要急于点击 开始 ...

  3. extjs Combobox动态加载数据问题,mode:local 还是remote

    var fabircTypeDs = new Ext.data.Store({ proxy: new Ext.data.HttpProxy({ url: 'province.do' }), reade ...

  4. 第一百九十五天 how can I 坚持

    晚上回来又肚子疼,拉肚子,咋搞的呢. 小米.华为.感觉虽然现在华为有些许优势,哎,还是不说了,感觉小米手机信号好像有问题. 中午吃的刀削面好像不熟,其实,怎么说呢,像开面馆,做的面顾客都吃不完,很明显 ...

  5. 六款最佳Linux教育应用

    导读 对教育行业的用户来说,有好几款专门的Linux发行版是专门面向教育行业的.本文将介绍适合教育领域的几款顶级发行版. 1.Edubuntu 位居榜首的是Edubuntu.顾名思义,Edubuntu ...

  6. 关于csrss.exe和winlogon.exe进程多、占用CPU高的解决办法

    原地址 http://blog.sina.com.cn/s/blog_912e77480101nuif.html   最近VPS的CPU一直处在100%左右,后台管理上去经常打不开,后来发现上远程都要 ...

  7. (转)Bat Command

    1.Echo 命令 打开回显或关闭请求回显功能,或显示消息.如果没有任何参数,echo 命令将显示当前回显设置.语法 echo [{on|off}] [message] Sample篅echo off ...

  8. python random从集合中随机选择元素

    1.使用python random模块的choice方法随机选择某个元素 from random import choice foo = ['a', 'b', 'c', 'd', 'e'] print ...

  9. Linux系统bash shell之历史命令

    1.相关变量: HISTSIZE: 定义命令历史记录的条数 HISTFILE: 定义命令储存的文件,一般是 ~/.bash_history HISTFILESIZE: 定义了历史文件记录历史的条数 H ...

  10. CSS使用小记

    1. 省略显示 max-width: 200px; white-space: nowrap; text-overflow: ellipsis; overflow: hidden; 固定宽度,nowra ...