表单中的联动下拉框代码思路

问题描述

我们常常在表单中会遇到联动下拉框的需求,最常见的例子是省、市、区的联动。一般联动的特点在于:

  1. 前一级变化时,后面所有层级全部清空:包括下拉框本身的值和下拉框的枚举

  2. 后一级的枚举会带着前一级的某个参数(如id)查询接口

如果只是一个新增表单上面两个注意点在编码过程中,稍加注意即可,用一个 watch 监听事件就可以很好的实现联动。下面我们将以省、市、区的案例简单整理下逻辑。

省、市、区常规案例

下面以vue+伪代码的形式大致梳理下整体思路

定义变量:

// 在vue3中的代码,可以参考思路

const form = reactive({
  province: '',   // 省
  city: '',       // 市
  area: '',       // 区
});

// 枚举
const provinceEnums = ref([]);
const cityEnums = ref([]);
const areaEnums = ref([]);
 

监听省变化:

 watch(
   () => form.province,
   (val) => {
     // 1. 清空市
     form.city = '';
     
     // 2. 如果val有值(选择了新的),根据新选的省查询市的枚举,如果val没有值,则是清空操作,则清空市的枚举
     if (val) {
       // 重新获取市的枚举
       // todo
     } else {
       // 清空市的枚举
       cityEnums.value = [];
     }
   }
 )

监听市变化:

watch(
   () => form.city,
   (val) => {
     // 1. 清空区
     form.area = '';
     
     // 2. 如果val有值(选择了新的),根据新选的市查询区的枚举,如果val没有值,则是清空操作,则清空区的枚举
     if (val) {
       // 重新获取区的枚举
       // todo
     } else {
       // 清空区的枚举
       areaEnums.value = [];
     }
   }
 )

问题描述

如果我们采用了上面的方案,在做新增的时候是没有问题的,但如果我们有重新编辑表单的操作的时候就会出问题了。问题的主要原因在于异步

我们试想一下,还是上面的逻辑,编辑较新增不同的地方在于,表单是有初始值的,这一份数据来自于上一次保存的数据。所以我们的代码会多一步:获取详情数据并赋值

 // 获取详情数据并赋值
 const getDetailData = async () => {
   // 1. 请求接口
   let data = await getDetail();
   
   // 2. 赋值
   form.province = data.province;
   form.city = data.city;
   form.area = data.area;
 }

在实际效果中就可能会出现问题。问题主要是因为 : 使用了watch。我们来梳理这个逻辑:

  1. form.province赋值

  2. form.city赋值

  3. form.area赋值

  4. 触发 city.watch 监听,并在这个时候 清空了 form.area

  5. 触发 province.watch,并在这个时候 清空了 form.city

下面是控制台输出的顺序:

 体现在我们页面上的效果是:只有省有值,市、区的值因 watch执行顺序慢于赋值而被清空

 当然我们可以在watch中添加一些限定条件,比如判断是否是详情进入的,比如在监听省的时候,通过判断是否有 oldVal,来做一些判断,这里就不展开介绍了。

watch(
  () => form.province,
  (val, olaVal) => {
    if (oldVal) {
      // todo
    }
  }

新的思路 onChange

对于这种问题,最后想到的解决方案是,放弃watch监听,因为它的监听模式是自动开启的,只有有变化就会按照固有代码执行,而我们需要的是会根据:

  • 编辑模式第一次进入状态

  • 常规切换状态

来控制代码的逻辑。所以我们不用watch,而用输入框自带的 onChange 事件

这里做一下简单的区别:

  • watch:只要vModel的值变化就会监听到

  • onChange:通过触发输入框改变才会触发事件本身

这就意味着,我们在获取到详情数据赋值时,是不会触发onChange事件的,那这个里面的逻辑就全部由我们自己定制。

获取详情并赋值

// 获取详情数据并赋值
const getDetailData = async () => {
  // 1. 请求接口
  let data = await getDetail();
  
  // 2. 赋值
  form.province = data.province;
  // 获取市枚举
  cityEums.value = await getCityByProvince(form.province);
  // 更新市
  form.city = data.city;
  // 获取区枚举
  cityEums.value = await getAreaByCity(form.area);
  // 更新区
  form.area = data.area;
}

总结

目前遇到这一类问题,我主要用 onChange 代替 watch。如果在比较复杂的业务场景下,一定要用watch来实现的话,可能代码会比较复杂,这个时候不妨考虑下 onChange

以上是本人个人观点,有更好的处理方式可以一起分享。

在 FastAdmin 实现动态下拉表单联动,可以通过以下步骤实现: 1. 在数据库创建两个表,例如表A和表B,其表B的某一字段与表A的另一个字段存在关联关系。 2. 在 FastAdmin 创建两个模型,分别对应表A和表B。 3. 在表A对应的模型,定义一个关联方法,如: ```php public function Bs() { return $this->hasMany(B::class, 'a_id', 'id'); } ``` 其,B::class 是表B对应的模型,'a_id' 是表B与表A关联的字段,'id' 是表A主键字段。 4. 在表B对应的模型,定义一个方法,获取与表A关联的字段的值,如: ```php public static function getAOptions() { $list = A::select('id', 'name')->get(); $options = []; foreach ($list as $item) { $options[$item->id] = $item->name; } return $options; } ``` 其,'id' 和 'name' 分别是表A的主键和需要显示的字段。 5. 在表B对应的模型,定义一个方法,获取与表A关联的选项,如: ```php public static function getASelectOptions($value = '') { $options = self::getAOptions(); if ($value && !isset($options[$value])) { $a = A::find($value); if ($a) { $options[$a->id] = $a->name; } } return $options; } ``` 该方法用于获取下拉框的选项,如果当前值不存在于选项,则尝试从表A获取。 6. 在表B对应的模型的表单,使用下拉框控件,并绑定选项,如: ```php $form->select('a_id', 'A')->options(B::getASelectOptions($form->model()->a_id)); ``` 其,'a_id' 是表B与表A关联的字段,'A' 是需要显示的字段。 7. 在表B对应的模型,定义一个方法,获取与表A关联的数据,如: ```php public static function getAData($a_id) { $a = A::find($a_id); if (!$a) { return null; } return $a->Bs; } ``` 该方法用于获取与表A关联的数据,例如,当用户选择一个 A 的选项后,就可以通过该方法获取与之关联的 B 数据。 8. 在表B对应的模型的表单,使用 Ajax 控件,监听 A 的变化,并根据变化获取关联的 B 数据,如: ```php $form->select('a_id', 'A')->options(B::getASelectOptions($form->model()->a_id)) ->load('b_id', url('admin/b/get-b-data')); $form->select('b_id', 'B')->options([]); ``` 其,'b_id' 是表B需要联动的字段,'url('admin/b/get-b-data')' 是获取与 A 关联的 B 数据的 URL。 9. 在控制器,定义一个方法,获取与表A关联的数据,并返回 JSON 格式的数据,如: ```php public function getBData(Request $request) { $a_id = $request->get('q'); $data = B::getAData($a_id); $options = []; foreach ($data as $item) { $options[$item->id] = $item->name; } return response()->json($options); } ``` 该方法接收一个参数 'q',即 A 的选项值,然后返回与之关联的 B 数据。 完成上述步骤后,就可以实现表单的动态下拉表单联动了。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值