Javascript 减少AngularJS中已禁用的已评估ng上的事件触发器 用例
我在一个页面上有一组控件,大约60多个。每个控件计算一个字符串表达式,以根据表单本身中输入的数据决定是否禁用该控件。表达式是由最终用户创建的,因此它可以是任何内容 我遇到的问题是表单和评估相当复杂,并且会减慢UI的速度。最终的代码将包含需要查找作用域对象中数据的表达式,并将包含循环等 示范 这是一个模拟我正在努力改进的地方。它不是最终的代码,因为该模块相当大。然而,这应该给出一幅我试图实现(或减少)的图景。这个演示很快,因为表达式相当简单,但我希望能够减少调用函数Javascript 减少AngularJS中已禁用的已评估ng上的事件触发器 用例,javascript,angularjs,Javascript,Angularjs,我在一个页面上有一组控件,大约60多个。每个控件计算一个字符串表达式,以根据表单本身中输入的数据决定是否禁用该控件。表达式是由最终用户创建的,因此它可以是任何内容 我遇到的问题是表单和评估相当复杂,并且会减慢UI的速度。最终的代码将包含需要查找作用域对象中数据的表达式,并将包含循环等 示范 这是一个模拟我正在努力改进的地方。它不是最终的代码,因为该模块相当大。然而,这应该给出一幅我试图实现(或减少)的图景。这个演示很快,因为表达式相当简单,但我希望能够减少调用函数的次数 [编辑]我已经更新了演示
的次数
[编辑]我已经更新了演示,以尽可能多地包含最终生产代码的核心逻辑。这是接近将要完成的事情
模板
小部件#{$index}
控制器
var ctrl=function($scope){
$scope.widgets=[];
/**下面是核心逻辑**/
var getValueByName=函数(名称){
对于(i=0,k=$scope.widgets.length;i var comparators=['=='、'!=='、'>'、'='、'与每次Angular运行摘要循环时为每个小部件调用函数不同,您可以继续使用ngDisabled,方法是让它计算一个简单值,该值仅在指令的链接函数指定的特定条件下更改
以下是一个示例。要执行此操作,您需要:
添加属性受限指令,该指令使用以下行为装饰元素:
- 在父控制器中注册观察者
- 绑定元素上的侦听器,该元素在激发时调用检查所有表达式的函数
将阵列与以下内容一起添加到父控制器:
- 向数组添加观察者的函数(每个指令的作用域)
- 另一种方法是迭代集合,计算每个表达式并为每个小部件设置isDisabled范围变量……这由发生输入或模糊的指令调用
不知道这是否解决了所有的问题,或者如果没有更好的方法来做这样的事情,但希望它至少给你另一种方法来考虑(这就是你所要求的)。< / P>用AngularJs的最新1.3个版本,你可以为NG模型选项设置一个值,用于延迟模型更新。(并依次禁用对ng的呼叫)直到一个设定的时间段之后。@JoseM在另一本小提琴中的建议:这更简单,但我将保留我的原始答案供参考,因为另一种方法debounce解决方案看起来不错,我可能会实现它,或者运行angular的不稳定版本。感谢您在这方面花费时间。我很高兴。我继续回答,主要是为了学习ne我在这里做了很多事情。我能想象到的限制.setViewValue()调用的唯一潜在不利之处是,如果您在ng模型值上有其他观察家,而这些观察家不会从限制中受益。我打赌这对您来说不会是个问题。
<div ng-app ng-controller="ctrl">
<div ng-repeat="widget in widgets">
<label>Widget #{{$index}}
<input type="text" ng-model="widget.value"
ng-disabled="is(widget.expression)" />
</label>
</div>
</div>
var ctrl = function ($scope) {
$scope.widgets = [];
/** Core Logic Below Here **/
var getValueByName = function (name) {
for (i = 0, k = $scope.widgets.length; i < k; i++) {
if ($scope.widgets[i].name == name) {
return $scope.widgets[i].value;
}
}
return 0;
};
var replaceNameWithValue = function () {
var result = this.replace(/( |^)(widget_\d+?)( |$)/g, function ($0, $1, $2, $3) {
return parseInt(getValueByName($2), 10);
});
return result;
};
$scope.is = function (expression) {
console.log('firing');
try {
var exp = replaceNameWithValue.call(expression);
return $scope.$eval(exp);
} catch (e) {}
return false;
};
/** Logic to generate random test cases **/
var numWidgets = 60;
var operators = ['+', '-', '*'];
var comparators = ['===', '!==', '>', '<', '>=', '<='];
var getRandomInt = function (max) {
return Math.floor(Math.random() * max);
};
var makeExpression = function () {
var a = getRandomInt(numWidgets);
var b = getRandomInt(numWidgets);
var c = getRandomInt(20);
var op = operators[getRandomInt(operators.length)];
var cf = comparators[getRandomInt(comparators.length)];
return {
expression: ['widget_' + a, op, 'widget_' + b, cf, c].join(' ')
};
};
var makeRandomObject = function (index) {
var obj = makeExpression();
obj.name = "widget_" + index;
obj.value = 0;
return obj;
};
var preLoad = function (numWidgets) {
for (i = 0; i < numWidgets; i++) {
$scope.widgets.push(makeRandomObject(i));
}
};
preLoad(numWidgets);
};
directive('widgetWatcher', function($timeout){
return {
restrict: 'A',
require: 'ngModel',
scope: true,
link: function(scope, elm, attr) {
// register observer for this directive by passing its scope
scope.addExpressionObserver(scope);
// Check expressions at most once every 1s or immediately when input is blurred
var debounce;
elm.bind('input', function() {
$timeout.cancel(debounce);
debounce = $timeout( function() {
scope.$apply(function() {
scope.checkExpressions();
});
}, 1000);
});
elm.bind('blur', function() {
scope.$apply(function() {
scope.checkExpressions();
});
});
}
}
})
$scope.expressionObservers = [];
$scope.addExpressionObservers = function(dScope) {
$scope.expressionObservers.push(dScope);
};
$scope.checkExpressions = function() {
angular.forEach($scope.expressionWatchers, function(val){
val.isDisabled = $scope.is(val.widget.expression);
});
};