需求说明
表单项的联动、筛选有效数据。
想象在淘宝买 iphone 手机时,当你选颜色,内存,套餐类型,是否有货等 其中一项时, 会在全量数据中 根据选中条件 筛选出有效数据,分别渲染到其他 FormItem 表单项, 所以每个表单项都是一个监听的 action,每次触发选择,都要进行筛选-匹配的流程。
为了方便管理
- 设计一个类,类中实现整个流程的子能力
- 方法间的调用,采用函数式编程思想
- 拆分 + 组合( 组合优于继承 )
- 拆分的价值: 可复用的个体、问题简单化
- 一个入口: 保持纯度
- 实现函数式和面向对象的合体
FormItemDataLinkage-Class的结构
必要的 State 状态
- data - 全量数据
- formItemNames - 参与联动的表单项
- allActions - 要有可写的,外界(调用层)数据源
- defaultActionValue - 初始化的默认选中项
- form - 表单实例
对外暴露的能力
1. initial(): 根据初始值渲染初始状态
initial() {
let that = this;
that.data && this.initialByGrade(
that.data,
that.defaultActionValue,
this.differentiate(
that.formItemNames,
that.allActions,
that.dismantling
)
);
};
2. run(p1, [...actions]): 当日常切换时,监听并调用run方法更新数据状态,更新视图
run(defaultActionValue, [...states]) {
let _monitorChange: MonitorChange = {
envGrade: defaultActionValue,
others: [...states]
};
let that = this;
this.data && this.unInitial(
that.data,
_monitorChange,
that.deleteInvalidProperty,
that.differentiate(
that.formItemNames,
that.allActions,
that.dismantling,
),
that.formItemNames
);
};
内部细分能力
1. initialByDefaultActionValue ()
initial()内部调用的方法,该方法是第一层检查,查找初始值在全量数据中是否有匹配数据
initialByDefaultActionValue(cbs: Data[], facv: string, difn: Function) {
const fld = cbs?.filter((cb: Data) => cb?.envGrade === facv);
if (!fld?.length) {
return message?.error('没有该匹配项');
} else {
difn(fld);
}
return null;
};
2. differentiate()
分化设置,对其他被动联动项,逐个筛选是否有匹配项目
/**
* ---> 3项都为空: 重新 initialByGrade (获取全量数据 - combines)
* |
* 分化设置 --
* |
* | ---> 当前为空: 清除显示项,清空下拉列表
* | |
* ---> 3项不都为空 ->map- ---> 当前存在上次选中的项: 显示默认值(不做任何操作)
* | |
* ---> 当前不为空: 重设下拉数据 --
* |
* ---> 当前不存在上次选中的项: 清空显示项,
*/
differentiate(ns: string[], las: Function[], disFn: Function, cbs: Data[], facv: string) {
let that = this;
return function(fld: Data[]) {
const disd = disFn(fld, ns) as SelectType[][];
// 先判断 如果下面3项都为空,则重新 initialByGrade
if (disd?.every(dis => !dis?.length)) {
cbs && that.initialByGrade(cbs, facv, that.differentiate(ns, las, disFn, cbs, facv));
} else {
// 否则才细化
that.haveExisted(
disd,
that.currentEmpty(ns, las),
that.currentNotEmpty(ns, las)
);
};
};
};
3. haveExisted()
细化分化,对单独一个表单项做判断,用到currentEmpty(), currentNotEmpty()
haveExisted(diss: SelectType[][], ef: Function, nef: Function) {
diss?.forEach((dis: SelectType[], index) => {
// 当前项为空
if (!dis?.length) ef(index);
else nef(index, dis);
});
};
4. deleteInvalidProperty()
去掉无效属性, 值被清空 或 没有值,该属性不参与匹配
deleteInvalidProperty(mc: Monitor) {
let obj: Partial<Monitor> = {};
for (let i in mc) {
if (mc[i]) {
obj[i] = mc[i];
}
}
return obj;
};
5. currentEmpty(), currentNotEmpty()
如果当前筛选出数据为空:清空当前表单项展示与Select.option数据
如果当前筛选出数据不为空:需要判断当前list中,是否有当前项显示的那一项,有则留着,没有则清楚显示项
currentEmpty(ns: string[], las: Function[]) {
return (i: number) => {
this.form.setFieldsValue({ [ns[i]]: '' });
las[i]([]);
};
};
currentNotEmpty(ns: string[], las: Function[]) {
return (i: number, dis: SelectType[]) => {
// 重新赋值下拉项
las[i](dis);
// 当前项不为空,需要判断当前list中,是否有当前项显示的那一项
// 有则留着,没有则清楚显示项
const exised = dis?.some(item => item?.value === this.form?.getFieldValue(ns[i]));
// 如果不存在清空显示项,存在则默认显示
if (!exised) {
this.form.setFieldsValue({ [ns[i]]: '' });
};
};
};
6. dismantling()
拆解,全量数据的类型可能是Data类型,Select.option需要的数据可能是SelectType,中间需要一个format的过程
dismantling(fld: Data[], ns: string[]): SelectType[][] {
let lks: SelectType[][] = [[], [], []];
fld?.forEach((fil: Data) => {
lks?.forEach((lk: SelectType[], i) => {
lk?.push({ value: fil[ns[i]], text: fil[ns[i]] });
});
});
return lks;
};
Usage
useEffect(() => {
// 实例化
const linkage = new FormItemDataLinkage<D, S, M, C>(
combines!,
formItemNames,
allActions,
currentGrade,
form
);
// 启动初始状态
linkage.initial();
setLinker(linkage);
}, [combines]);
// 监听日常操作
useEffect(() => {
// maybe actions is [currentUnit、currentRegion、currentCluster]
linker?.run(defaultActionValue, [...actions]);
}, [defaultActionValue, [...actions]]);