我有一些input
s,当某些属性发生变化时(例如,购物车被检出),所有这些都应该变为readonly
。
我可以为每个这样的输入添加ng-readonly=cart.checkedOut
,这很无聊,容易遗忘,并且因为有部分人无权访问cart
这一事实而变得复杂
所以我正在考虑修改input
(以及textarea
和select
,它们只是错误的输入),以便它在其DOM的祖先中查找类{{ 1}}并且可能使自己只读。我的问题是与readonly
的互动,这可能会导致ng-readonly
只读用于其他原因。看起来我必须修改input
,但AFAIK无法修改现有指令,只能为同一个名称添加其他行为。
不知何故,我感到迷茫;我确信这是一个简单的解决方案,但随着我的潜入,一切都变得复杂起来。向我展示正确的方向就足够了。或者可能是针对原始问题的不同解决方案。
答案 0 :(得分:0)
请注意,对于多个表单输入元素,将忽略readonly
属性。
因此,ng-readonly
可能无法正常使用某些组件(即,它不会阻止用户交互编辑值)。
也就是说,解决问题的一种方法是制作一个监视签出状态的指令,并动态添加或删除适当的输入元素属性,使它们看起来被锁定以进行编辑。
就像我上面提到的,您需要关注的属性取决于您使用的元素类型。对于readonly
不适用的用户,例如select
,您可以选择使用disable
。但是,无法提交已禁用的元素,这就是hidden
输入类型通常用于补充select
元素的原因。在您的情况下,它似乎也是不受欢迎的,因为您希望不要触摸您的DOM。
要仍然使用disable
并解决后一个问题,在提交表单时,您可以制作另一个指令,从具有它们的输入元素中去除所有disabled
属性,确保所有输入数据得到提交。
这是JSBin关于如何动态管理输入元素属性的基本演示。
答案 1 :(得分:0)
这是我制作的解决方案,它扩展了input
,select
和textarea
指令。
在我们的应用程序的配置块中使用$provide.decorator
,我们可以在不触及其内部实现的情况下扩充这些指令行为。
我建议你做的是为你从包含$scope
广播的事件设置一个监听器。附加回调,检查当前输入指令的nodeName
- 并关闭用户输入(使用readOnly
或disabled
)。
这些方面的东西:
app.config(function ($provide) {
var directives = [
'input',
'select',
'textarea'
];
var eventName = '__disableInputs';
angular.forEach(directives, function (directive) {
$provide.decorator(directive + 'Directive', function ($delegate, $controller) {
// Decorating directives returns an array. Select the first index.
var directive = $delegate[0];
// If a controller is tied to the directive, store a reference to it.
if (directive.controller) {
var origCtrl = directive.controller;
}
// Define a new controller on the directive.
directive.controller = function ($scope, $element, $attrs) {
if (origCtrl) {
// Extend this new controller with the old behaviour of the
// original controller.
angular.extend(this, $controller(origCtrl, {
$scope: $scope,
$element: $element,
$attrs: $attrs
}));
}
// Add an event listener to the directive controller.
var unlisten = $scope.$on(eventName, function () {
if ($element[0].nodeName === 'SELECT') {
$element[0].disabled = true;
} else {
$element[0].readOnly = true;
}
});
$scope.$on('$destroy', unlisten);
};
// Return the original (augmented) directive.
return $delegate;
});
});
});
然后,我们可以简单地从控制器(或其他一些地方)触发行为:
app.controller('ctrl', function ($scope, $timeout) {
$timeout(function () {
$scope.$broadcast('__disableInput', {});
}, 5000);
});
现在,每个input
,select
和textarea
目前都在广播中
范围应该应用其重复回调,而不是通过常规编辑
用户意味着了。
还有可能从$ rootScope中触发这个 - 但是;使用内置的ng
指令时,
我想说添加我们自己的自定义标志是一个很好的做法,该标志决定指令是否监听
在我们的配置块(__disableInput
)中定义的eventName与否。
如果我们要添加此自定义标志,
我们应该将新的directive.controller
函数更改为:
if (origCtrl) {
angular.extend(this, $controller(origCtrl, {
$scope: $scope,
$element: $element,
$attrs: $attrs
}));
}
// Only add our eventlistener if canBeDisabled is set.
if ($attrs.canBeDisabled !== undefined) {
var unlisten = $scope.$on(eventName, function (e, data) {
if ($element[0].nodeName === 'SELECT') {
$element[0].disabled = true;
} else {
$element[0].readOnly = true;
}
});
$scope.$on('$destroy', unlisten);
}
然后在我们看来,我们会这样做:
<input ng-model="someModel.property" can-be-disabled type="text">
现在,这是一个粗略的例子,可能还有一些我尚未探索过的问题。
但我确实认为如果你想要一个简单的方式,这是朝着正确方向迈出的一步
扩展ng
指令而不必触及其内部和/或创建
新指令。
我还没做的一件事是将eventName
定义为app.value
(或app.constant
),
这样我们就不必在配置块中定义eventName或担心拼写错误
在设立此活动的广播员时。我们只是将价值注入我们的背景中
并将其用作活动的名称。
这是给你的JSBin:http://jsbin.com/xipokuyi/5/edit
更深入地探讨探索此类方法的文章的链接: http://angular-tips.com/blog/2013/09/experiment-decorating-directives/
答案 2 :(得分:0)
辅助服务用作指令和控制器之间的通信机制。服务登记册&amp;取消注册元素(在指令链接功能中)并启用&amp;通过函数调用(在控制器中)禁用所有已注册的元素。
HERE是我的解决方案的种子 1 ,其中元素可以位于DOM树中的任何位置。
app.factory("demoReadonlyState", function(){
var elements = [];
var state = false;
return {
enable : function(){
state = false;
for (var i = 0, len = elements.length; i < len; i++){
elements[i].removeAttribute('readonly');
}
},
disable : function(){
state = true;
for (var i = 0, len = elements.length; i < len; i++){
elements[i].setAttribute('readonly', "True");
}
},
addElement : function(domEl) {
elements.push(domEl);
if(state){
domEl.setAttribute('readonly', state);
}
else {
domEl.removeAttribute('readonly');
}
console.log("add -- # elements: ", elements.length);
},
removeElement : function(el) {
var index = elements.indexOf(el);
if (index > -1) {
elements.splice(index, 1);
}
console.log("remove -- # elements: ", elements.length);
}
}
})
app.directive("demoReadonly", function(demoReadonlyState){
return {
link : function(scope, element){
console.log(demoReadonlyState);
demoReadonlyState.addElement(element[0]);
element.on('$destroy', function(){
demoReadonlyState.removeElement(element);
})
}
}
})
在JS
:
app.controller("MainCtrl", function($scope, demoReadonlyState){
$scope.roState = demoReadonlyState;
$scope.roState.enable();
//$scope.roState.disable();
});
在HTML
:
<body ng-controller="MainCtrl">
<input demo-readonly />
<input />
<input demo-readonly />
<input />
<input demo-readonly />
<input />
<div>
<button ng-click="roState.enable()">enable</button>
<button ng-click="roState.disable()">disable</button>
</div>
<div>
<button ng-click="arr.push([])">add element</button>
<button ng-click="arr.length && arr.splice(0,1)">remove element</button>
</div>
<div ng-repeat="x in arr">
<input demo-readonly/>
</div>
</body>
1 通过种子我的意思是,它是不可配置的。在正常的解决方案中,指令可以传递一个字符串(demo-readonly="type_X"
),它指定一组应该enabled/disabled
的元素,保持所有其他组完整。
HERE是一种处理带有单个指令的子树的方法。它可以独立处理多个子树。在这种形式下,它不适用于动态ng-repeat
井 2 ,但可以轻松实现更强大的机制(尽管需要使用附加指令)。
app.factory("demoReadonlyState", function(){
var elements = {};
return {
enable : function(type){
element = elements[type];
var inputs = element.querySelectorAll('input');
for (var i = 0, len = inputs.length; i < len; i++){
inputs[i].removeAttribute('readonly');
}
},
disable : function(type){
element = elements[type];
var inputs = element.querySelectorAll('input');
for (var i = 0, len = inputs.length; i < len; i++){
inputs[i].setAttribute('readonly', "True");
}
},
addElement : function(type, domEl) {
elements[type] = domEl;
},
removeElement : function(type) {
delete elements[type];
}
}
})
app.directive("demoReadonly", function(demoReadonlyState){
return {
scope : {
type : "@demoReadonly"
},
link : function(scope, element){
demoReadonlyState.addElement(scope.type, element[0]);
element.on('$destroy', function(){
demoReadonlyState.removeElement(scope.type);
})
}
}
})
在JS
:
app.controller("MainCtrl", function($scope, demoReadonlyState){
$scope.roState = demoReadonlyState;
$scope.roState.enable('XXX');
//$scope.roState.disable('XXX');
});
在HTML
:
<body ng-controller="MainCtrl">
<div>
<button ng-click="roState.enable('XXX')">enable</button>
<button ng-click="roState.disable('XXX')">disable</button>
</div>
<div demo-readonly="XXX">
<input/>
<input/>
<div>
<input/>
<input/>
</div>
</div>
</body>
1 基本上使用动态ng-repeat
需要在每次更新集合后手动调用enable/disable
。