AngularJS 1.5表单全局禁用问题:如何扩展form指令实现全元素只读?
嘿,我来帮你搞定AngularJS 1.5里这个表单禁用的问题!
首先得说,你发现的fieldset[disabled]只能禁用原生表单元素(input、button这些)是对的,因为这是HTML原生的行为,非表单元素本来就不受这个属性影响。接下来咱们分两步解决你的需求:既要自动触发禁用状态,又要让所有可点击元素都失效。
方案一:自定义指令+覆盖层(最省心的方式)
我推荐用一个自定义指令来绑定禁用条件,同时用覆盖层挡住所有点击——这种方式不用挨个处理div/span/icon,体验还更直观。
1. 写自定义指令
这个指令会监听你传入的禁用条件,条件满足时自动给表单加禁用的fieldset,同时添加一个半透明覆盖层阻止所有交互:
angular.module('yourApp') .directive('autoFormDisabler', function() { return { require: 'form', // 关联form指令,拿到表单控制器 scope: { disableWhen: '=' // 绑定禁用条件,比如某个加载状态、权限值 }, link: function(scope, formElement, attrs, formCtrl) { // 监听条件变化 scope.$watch('disableWhen', function(shouldDisable) { if (shouldDisable) { // 步骤1:给表单套上禁用的fieldset const fieldset = angular.element('<fieldset disabled></fieldset>'); formElement.children().appendTo(fieldset); formElement.append(fieldset); // 步骤2:添加覆盖层挡住所有点击 const overlay = angular.element('<div class="form-disabled-overlay"></div>'); formElement.append(overlay); } else { // 恢复原样:移除fieldset和覆盖层 const fieldset = formElement.find('fieldset'); fieldset.children().appendTo(formElement); fieldset.remove(); formElement.find('.form-disabled-overlay').remove(); } }); } }; });
2. 加一点CSS样式
给覆盖层加个样式,让它能铺满整个表单区域:
.form-disabled-overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(255, 255, 255, 0.6); z-index: 10; pointer-events: auto; } /* 记得给form元素加相对定位,不然覆盖层会乱跑 */ form { position: relative; }
3. 在HTML里用起来
把指令加到你的表单上,绑定禁用条件就行:
<form name="userForm" auto-form-disabler disable-when="isLoading || !hasPermission"> <!-- 你的表单内容:input、button、带ng-click的div、图标都可以放这儿 --> <input type="text" ng-model="user.name"> <button ng-click="saveUser()">保存</button> <div ng-click="openAvatarPicker()" class="avatar-btn">选择头像</div> <i class="icon-settings" ng-click="openSettings()"></i> </form>
方案二:事件委托(不喜欢覆盖层的话用这个)
如果不想加覆盖层,也可以用事件委托来阻止所有可点击元素的交互:
在指令的link函数里,当禁用条件满足时,给表单容器加点击监听,判断点击目标是不是可点击元素(带ng-click、ui-sref,或者是a标签/自定义可点击类),然后阻止事件:
// 替换方案一里的覆盖层代码 formElement.on('click', function(e) { const target = angular.element(e.target); // 判断是否是可点击元素 const isClickable = target.attr('ng-click') || target.attr('ui-sref') || target.is('a') || target.hasClass('clickable'); if (isClickable) { e.preventDefault(); e.stopPropagation(); return false; } }); // 恢复时移除监听 formElement.off('click');
关于访问form指令的post link
你提到的访问form指令的post link,其实通过require: 'form'就解决了——AngularJS会保证form指令的link函数先执行,然后再执行我们自定义指令的link函数,所以在我们的link里可以安全地操作表单元素、拿到表单控制器的状态。
这样一来,只要你的disableWhen条件变成true,整个表单(包括所有可点击元素)都会被禁用,完全不用手动去加fieldset,非常省心!
内容的提问来源于stack exchange,提问作者Renaud is Not Bill Gates

