乐趣区

关于前端:浅谈-Checkbox-Group-的双向数据绑定

前言

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 的计划来说,当初的计划更加灵便,无效缩小了数据操作的代码,不过依然有很大的优化和晋升空间。

如果大家发现本文有不当之处,欢送交换斧正!

退出移动版