解决AngularJS中,使用ng-if使得rule.json校验不生效

本文探讨了Angular中三种条件渲染方式:ng-if、ng-show与ng-hide的区别与应用场景。重点介绍了ng-if通过添加或删除DOM元素实现条件渲染可能导致的规则校验失效问题,并给出了相应的解决方案。

问题描述:

在使用ng-if时,rule.json对其节点的校验不生效。

解决方法:

将ng-if替换为ng-show


以下为ng-if、ng-show、ng-hide的区别:

1.ng-if
  根据变量的bool值决定要不要显示DOM;

  true添加dom,并显示

  false删除dom,从而隐藏

注意:ng-if是通过添加删除dom而进行的显示或隐藏,若为了隐藏而删除了dom,则使得规则校验无法生效。


2.ng-show

  根据变量的bool值决定要不要显示DOM
  只是显示,隐藏,dom无删除

3.ng-hide

  根据变量的bool值决定要不要隐藏DOM
  只是显示,隐藏,dom无删除


<template> <aui-dialog-box :visible="visible" :title="title" width="500px" append-to-body draggable :close-on-click-modal="false" @close="onClose" > <aui-form ref="form" label-position="top" validate-position="bottom" :model="formData" :rules="formRules" :append-to-body="false" > <aui-form-item :label="mainOptionsLabel" prop="selectMainKey"> <aui-checkbox-group v-if="isMultiMain" v-model="formData.selectMainKey" :options="mainOptions"> </aui-checkbox-group> <aui-radio-group v-else v-model="formData.selectMainKey" :options="mainOptions"> </aui-radio-group> </aui-form-item> <div v-for="rule in ruleList" :key="rule.id"> <!-- <aui-form-item :prop="rule.id" :required="rule.fieldIsRequired === 'yes'"> --> <!-- <span>{{ rule.id }}</span> --> <aui-form-item :prop="rule.id"> <template #label> <div class="flex-wrap"> {{ rule[nameKey] }} <aui-tooltip v-if="rule[commentKey]" effect="light" placement="top" popper-class="commentTippoper"> <template #content> {{ rule[commentKey] }} </template> <IconHelpCircle class="iconHelp" /> </aui-tooltip> </div> </template> <!-- <span>{{ ruleValueList[rule.id] }}</span> --> <aui-select :ref="handleRef(rule)" v-model="ruleValueList[rule.id]" v-show-all-checked-tag="getShowAllCheckedTagBindingValue(rule, selectOptionLabels, ruleValueList)" :multiple="isMultiple(rule)" :query-debounce="300" :collapse-tags="isMultiple(rule)" :remote-method="searchText => remoteMethod(rule, searchText)" :remote-config="{ autoSearch: true, clearData: true, showIcon: true }" remote filterable clearable reserve-keyword :loading="loadingList[rule.id]" loading-text="Loading..." @change="selected => onSelectChange(rule, selected)" @clear="onClearSelection(rule)" is-drop-inherit-width > <template #dropdown> <li v-if="isMultiple(rule) && hasOptions(rule)" class="aui-select-dropdown__item all-checked-option" data-tag="aui-select-dropdown-item" :class="[{ selected: isAllChecked(rule) }]" @mouseenter="onAllCheckedOptionMouseOver" > <aui-checkbox :indeterminate="isIndeterminate(rule)" :checked="isAllChecked(rule)" @change="allChecked => onAllCheckedChange(rule, allChecked)" > {{ $t('semantic.material.MySelect.label.allChecked') }} </aui-checkbox> </li> </template> <template v-for="item in selectOptionLabels[rule.id]"> <aui-option v-if="item" :key="item.value || item" :label="item.label || item" :value="item.value || item" /> </template> </aui-select> </aui-form-item> </div> </aui-form> <template #footer> <aui-button @click="onCancel"> {{ $t('semantic.material.common.button.cancel') }} </aui-button> <aui-button type="primary" @click="onConfirm"> {{ $t('semantic.material.common.button.confirm') }} </aui-button> </template> </aui-dialog-box> </template> <script lang="ts"> import { defineComponent } from '@vue/composition-api'; import { DialogBox, Radio, Form, FormItem, Select, Option, Button, CheckboxGroup, RadioGroup, Tooltip, Checkbox, } from '@aurora/vue'; import { IconHelpCircle } from '@aurora/vue-icon'; import { getQueryParams, getFieldDataFromContent, showSelectLabel } from '../../query-select/QueryDataHelper'; import { queryDetailData } from '../../../service/detailDataService'; import { isEmptyStringArray, isLocaleZh } from '../../../utils/PageContextUtils'; import * as Export from '../common/export'; import { createDirective as createShowAllCheckedTagDirective } from '../../../directives/show-all-checked-tag'; type FormState = { selectMainKey: string[] | string; }; let promise: Promise<FormState> | null; let resolve: ((v: FormState) => void) | null; let reject: ((err: Error) => void) | null; export default defineComponent({ components: { AuiDialogBox: DialogBox, AuiForm: Form, AuiFormItem: FormItem, AuiRadio: Radio, AuiOption: Option, AuiSelect: Select, AuiButton: Button, AuiCheckboxGroup: CheckboxGroup, AuiRadioGroup: RadioGroup, AuiTooltip: Tooltip, AuiCheckbox: Checkbox, IconHelpCircle: IconHelpCircle(), }, directives: { showAllCheckedTag: createShowAllCheckedTagDirective(), }, setup() {}, data() { return { formData: { selectMainKey: [], }, mainOptions: [], configData: [], visible: false, ruleList: [], ruleValueList: {}, loadingList: {}, selectOptionLabels: {}, searchValue: '', remoteSearched: {}, isMultiMain: false, mainOptionsLabel: '', cascadeRuleMap: new Map(), title: '', defaultAllChecked: {}, }; }, computed: { nameKey() { return isLocaleZh() ? 'nameCn' : 'nameEn'; }, commentKey() { return isLocaleZh() ? 'commentCn' : 'commentEn'; }, configDataMapById() { return new Map(this.configData.map(item => [item.id, item])); }, formRules() { const rules: any = {}; // 为 selectMainKey 添加必填规则 rules['selectMainKey'] = [ { required: true, trigger: 'change', }, ]; // 动态生成其他规则 this.ruleList.forEach(rule => { if (rule.fieldIsRequired === 'yes') { rules[rule.id] = [ { required: true, trigger: 'change', }, ]; } }); console.log(rules, '添加必填规则'); return rules; }, }, watch: { 'formData.selectMainKey': { handler(newVal) { this.changeRuleList(newVal); }, }, }, methods: { /** 全选选项的切换选中状态回调 */ onAllCheckedChange(rule, allChecked: string[]) { const options = this.selectOptionLabels[rule.id]; const oldChecked = this.ruleValueList[rule.id]; let newChecked; if (allChecked) { // 追加 newChecked = [...new Set([...oldChecked, ...options])]; } else { // 剔除列表中选中的项 newChecked = oldChecked.filter(checked => !options.includes(checked)); } this.$set(this.ruleValueList, rule.id, newChecked); this.updateDefaultAllCheckedOnSelect(rule, allChecked); // 触发级联查询 下级字段 if (this.cascadeRuleMap.get(rule.id)?.length) { // 下级字段 this.setSelectChangeCascaded(rule); } }, /** 移除兄弟节点的 hover 样式 */ onAllCheckedOptionMouseOver(e) { [...e.target.parentNode.children].forEach(ele => { if (ele !== e.target) { ele.classList.remove('hover'); } }); }, /** 是否有下拉选项 */ hasOptions(rule) { return this.selectOptionLabels?.[rule.id]?.length > 0; }, isDefaultAllCheckedRule(rule) { return this.defaultAllChecked[rule.id] === true; }, /** 是否有选中项 */ hasSelected(rule) { const value = this.ruleValueList?.[rule.id]; if (typeof value === 'number' && !isNaN(value)) { return true; } return value?.length > 0; }, /** * 远程搜索扩展的半选逻辑。 */ isIndeterminate(rule) { if (!this.hasSelected(rule) || !this.hasOptions(rule)) { return false; } // 只要某个选项未选中(即选中列表未包含所有选项),就是半选 return !this.isSelectedContainsAllOptions(rule); }, isAllChecked(rule) { if (!this.hasSelected(rule) || !this.hasOptions(rule)) { return false; } // 选中列表未包含所有选项就是全选 return this.isSelectedContainsAllOptions(rule); }, /** 更新全选标记 */ updateDefaultAllChecked(rule: typeof this.ruleList[number], allChecked) { this.$set(this.defaultAllChecked, rule.id, allChecked); }, isRemoteSearchedRule(rule) { return this.remoteSearched[rule.id] === true; }, isSelectedContainsAllOptions(rule, checkedList = this.ruleValueList[rule.id]) { const allSelect = this.selectOptionLabels[rule.id]?.every(ruleId => checkedList?.includes(ruleId)); // 模糊搜索后,列表项会发生变化,如果所有列表项被选中,全部也要选中 // 避免在存在重复value的场景时,全选后,无法直接取消用于重复value项的勾选 const lengthValid = this.selectOptionLabels[rule.id]?.length <= checkedList.length; return allSelect && lengthValid; }, updateDefaultAllCheckedOnSelect(rule, allCheckedOrCheckList) { if (!this.isMultiple(rule)) { return; } const forAllCheck = typeof allCheckedOrCheckList === 'boolean'; let ret = false; // 只要触发了远程搜索,就不会打上 全选 标记 if (this.isRemoteSearchedRule(rule)) { ret = false; } else if (forAllCheck) { // 如果是全选选项触发的选中,全选标记的值与 全选选项的状态一致 ret = allCheckedOrCheckList; } else { // 如果是非全选选项触发的选中,则判断所有选项都被选中,即 选中列表包含所有选项 // 为什么要用 判断选中列表是否包含了所有列表,是因为有需求: // 模糊搜索后,列表项会发生变化,如果所有列表项被选中,全部也要选中 ret = this.isSelectedContainsAllOptions(rule, allCheckedOrCheckList); } this.updateDefaultAllChecked(rule, ret); }, isAllCheckedTagShow(rule) { return this.isMultiple(rule) && !this.isRemoteSearchedRule(rule) && this.isDefaultAllCheckedRule(rule); }, getShowAllCheckedTagBindingValue(rule, selectOptionLabels, ruleValueList) { return { isAllChecked: this.isAllCheckedTagShow(rule), options: selectOptionLabels[rule.id], checked: ruleValueList[rule.id], label: this.$t('semantic.material.MySelect.label.allChecked'), updateAllCheckedStatus: allChecked => { this.updateDefaultAllChecked(rule, allChecked); }, check: options => { this.$set(this.ruleValueList, rule.id, options); }, showLog: false, }; }, setPromise() { return (promise = new Promise((rawResolve, rawReject) => { resolve = rawResolve; reject = rawReject; })); }, clearPromise() { promise = null; resolve = null; reject = null; }, changeRuleList(newVal) { const checked = newVal ? [newVal].flat() : []; // 使用 this.$set 确保 ruleList 的更新是响应式的 this.$set(this, 'ruleList', checked.flatMap(id => { return this.configDataMapById.get(id).customOptions ?? []; })); }, isMultiple(rule) { return rule.functionalOperator?.includes('IN'); }, // 因为select是循环渲染出来的,所以绑定的ref也是动态的 handleRef(rule) { if (this.isMultiple(rule)) { return `multiSelect${rule.id}`; } else { return `select${rule.id}`; } }, remoteMethod(rule, selectValue) { this.processData(rule, selectValue, false); const select = this.$refs[this.handleRef(rule)][0]; select.state.cachedOptions.forEach(item => { item.state.visible = !selectValue || showSelectLabel(rule, selectValue, item.label); }); }, normalizeQueryParams(rule, selectValue) { const { modelId, tableId, tableName } = rule; let params = getQueryParams(modelId, tableId, tableName, rule, selectValue); params.resultFilter.having = ''; // 分组筛选器,这里传空 return this.normalizeCascadeRuleQueryParams(rule, params); }, normalizeProcessDataFetcher(rule, selectValue, useCache = false) { if (rule.cascadeRuleList.length) { const params = this.normalizeQueryParams(rule, selectValue); return queryDetailData({ ...params, useCache }); } else { return this.getData(rule, selectValue, useCache); } }, setFieldValue(rule, content) { const obj = getFieldDataFromContent(rule, content); this.$set(this.selectOptionLabels, rule.id, obj.valueFieldData); }, getParamsOptions(rule) { const list = rule.field.valueList.map(item => ({ value: item.value, label: item[this.nameKey] })); this.$set(this.selectOptionLabels, rule.id, list); }, // 数据处理 async processData(rule, selectValue, useCache = false) { this.searchValue = selectValue; if (rule.tableId === 'params') { this.getParamsOptions(rule); return; } this.$set(this.loadingList, rule.id, true); return this.normalizeProcessDataFetcher(rule, selectValue, useCache) .then( data => { if (selectValue === this.searchValue) { this.setFieldValue(rule, data); this.$set(this.remoteSearched, rule.id, Boolean(selectValue)); } }, e => { this.$set(this.remoteSearched, rule.id, false); return Promise.reject(e); }, ) .finally(() => { this.$set(this.loadingList, rule.id, false); }); }, getData(rule, searchText, useCache) { const { modelId, tableId, tableName } = rule; const params = getQueryParams(modelId, tableId, tableName, rule, searchText); return queryDetailData({ ...params, useCache }); }, normalizeCondition(rule, ruleValue) { const field = { ...rule.field }; return { functionalOperator: rule.functionalOperator, field: { id: field.id, }, args: { type: field.customDataType || field.dataType, values: [...ruleValue].flat(), }, tableId: rule.tableId, }; }, /** 已拼查询条件中的 conditions */ conditionsOfParams(params) { try { return JSON.parse(params.resultFilter.where)?.conditions ?? []; } catch (e) { return []; } }, /** 拼级联条件 */ normalizeCascadeRuleQueryParams(rule, params) { if (!rule.cascadeRuleList.length) { return params; } const cascadeRuleList = rule.cascadeRuleList; // 条件操作符,带入已拼的搜索关键字及其他 const conditions = this.conditionsOfParams(params); // 多个上级 this.ruleList.forEach(item => { if (cascadeRuleList.includes(item.id)) { // 上级已选值 const value = this.ruleValueList[item.id]; if (!isEmptyStringArray(value)) { conditions.push(this.normalizeCondition(item, value)); } } }); // 如果父级有部分是非全选的,conditions 一定是非空数组 if (conditions.length) { params.resultFilter.where = JSON.stringify({ logicalOperator: 'and', // 只有一层默认传and conditions, }); } return params; }, /** 非全选选项切换选中状态回调 */ onSelectChange(rule, checkedList) { this.$set(this.ruleValueList, rule.id, checkedList); console.log(this.ruleValueList, rule.id, this.ruleValueList[rule.id],'选中的值'); this.updateDefaultAllCheckedOnSelect(rule, checkedList); // 触发级联查询 if (this.cascadeRuleMap.get(rule.id)?.length) { // 下级字段 this.setSelectChangeCascaded(rule); } }, // 级联字段清空 setSelectChangeCascaded(rule) { this.ruleList.forEach(item => { if (this.cascadeRuleMap.get(rule.id)?.includes(item.id)) { // 清空已选 this.$set(this.ruleValueList, item.id, ''); // 清空下拉框值 this.$set(this.selectOptionLabels, item.id, []); // 查询下拉框值 this.processData(item, '', false); } }); }, /** 清除选中项时执行的回调 */ onClearSelection(rule) { this.remoteMethod(rule, ''); }, initDefaultAllChecked() { this.ruleListData?.forEach(rule => { this.defaultAllChecked[rule.id] = false; }); }, getChildRuleId(rule, ruleList) { const childRuleId = ruleList.filter(item => item.cascadeRuleList.includes(rule.id)).map(item => item.id); childRuleId.length && this.cascadeRuleMap.set(rule.id, childRuleId); }, async show(buttonConfig) { this.title = buttonConfig[this.nameKey]; const serviceConfig = buttonConfig.service; this.cascadeRuleMap.clear(); this.initDefaultAllChecked(); this.configData = serviceConfig.list.map(item => ({ ...item, customOptions: item.customOptions?.map(rule => { this.getChildRuleId(rule, item.customOptions || []); return { ...rule, parentId: item.id, }; }) || [], })); this.isMultiMain = serviceConfig.isMultiple ?? false; this.mainOptionsLabel = serviceConfig[this.nameKey]; this.mainOptions = this.configData.map(item => ({ text: item?.servicePath?.[this.nameKey], label: item.id })); if (this.isMultiMain) { this.formData.selectMainKey = []; } else { this.formData.selectMainKey = ''; } this.ruleValueList = {}; this.selectOptionLabels = {}; this.visible = true; await this.$nextTick(); this.$refs.form?.clearValidate?.(); // 返回 promise,将 show 方法设计成异步方法,点确定的时候,兑现 return this.setPromise(); }, reset() { this.remoteSearched = {}; this.defaultAllChecked = {}; this.ruleValueList = {}; this.selectOptionLabels = {}; }, hide(success = true) { if (reject) { this.visible = false; this.reset(); if (!success) { reject(new Error(Export.ErrorMsg.CANCEL)); } this.clearPromise(); } }, // 获取所有的选中值 getOptions(id) { const selectRule = this.ruleList.filter(item => item.parentId === id); const options: unknown[] = []; selectRule.forEach(rule => { let selectValues = []; if ((this.isMultiple(rule) && this.ruleValueList[rule.id].length) || this.ruleValueList[rule.id]) { selectValues.push(this.ruleValueList[rule.id]); } selectValues = selectValues.flat(); if (selectValues.length) { options.push({ key: rule.exportType, value: selectValues, }); } }); return options; }, // 数据拼装 handleParams() { const selectServiceList = this.configData.filter(item => { if (this.isMultiMain) { return this.formData.selectMainKey.includes(item.id); } return this.formData.selectMainKey === item.id; }); const params = selectServiceList.map(item => ({ config: item, options: this.getOptions(item.id), })); return params; }, async onConfirm() { await this.$refs.form?.validate?.(); // resolve 一定不能是 null 或 undefined, // 如果是 null 或 undefined,一定是 setPromise 的调用时机不对 // 需要纠正 setPromise 调用时机,而不是对 resolve 进行空值判断 resolve!(this.handleParams()); this.hide(); }, onClose() { this.hide(false); }, onCancel() { this.hide(false); }, }, }); </script> <style lang="less" scoped> .iconHelp { margin-left: 5px; font-size: 1em; fill: #666; &:hover { fill: #0067d1ed; } } .flex-wrap { display: flex; align-items: center; display: inline-block } </style> aui-form 标签上 :model 绑定要用来验证的数据对象,:rules 绑定用来验证表单的规则。 aui-form-item的 prop 属性值需要和 rules 中校验字段对应,prop 属性值和表单组件绑定的变量名一定要相同。 由于<aui-form-item :prop="rule.id"> 和v-model="ruleValueList[rule.id]"对不上,所以导致校验有问题,该怎么修复
最新发布
12-05
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值