我开发了一个具有独立范围的简单UI滑块指令。它还支持注册滑块值更改时触发的change
属性。 change
属性通常使用来自隔离范围的滑块值调用父作用域的函数,如下所示:
change="onValueChanged(theValue)"
其中change
是一个属性,onValueChanged
是在父控制器中声明的函数,theValue
是仅在隔离范围内声明的元素的当前值。
我怎样才能正确$eval
这个?在滑块的link
功能中,我可以调用$scope.$eval(attrs.change)
,但未定义$scope.onValueChanged
。孤立的范围不会从其父级继承。同时,$scope.$parent.$eval(attrs.change)
正确调用onValueChanged
函数,但不会设置theValue
,因为它在子范围内被隔离。由于change
属性可以引用任意数量的父作用域属性,因此我无法在隔离的作用域中显式声明它们。
我如何eval
change
属性,以便来自不同范围的所有变量都存在?有没有办法强制隔离范围从其父级继承?
答案 0 :(得分:1)
我发现我的方法有几个小问题。我的最终滑块看起来有点像这样:
/**
* A jquery-UI slider with databinding.
* @see http://jsfiddle.net/vNfsm/50/
*/
app.directive('slider', function() {
var linkFun = function($scope, element, attrs) {
var $slider = jQuery(element);
var option = attrs;
var readIntOption = function(key, option) {
if (option[key]) {
option[key] = parseInt(option[key]);
}
};
// read default options
readIntOption("min", option);
readIntOption("max", option);
readIntOption("step", option);
// add `value` and `change` properties to slider for data-binding
option = jQuery.extend({
change: function(event, ui) {
if (!event.which) return; // only trigger on UI events
if (ui.value != $scope.valueModel) {
// update value
$scope.valueModel = ui.value;
// update the value of the variable that is bound to `valueModel`
if (!$scope.$$phase) {
$scope.$apply();
}
// raise callback
if ($scope.valueChanged) {
$scope.valueChanged();
}
}
}
}, option);
// data binding in the other direction
$scope.$watch("valueModel", function(val) {
if ($scope.valueModel != $slider.slider("value")) {
// update slider value
// this will not raise the `change` event above
$slider.slider("value", $scope.valueModel);
}
});
// create slider
$slider.slider(option);
};
return {
restrict: 'E',
replace: true,
transclude: false,
template: '<div />',
scope: {
valueModel: '=',
valueChanged: '&'
},
link: linkFun
};
});
这是滑块的示例实例:
<slider
value-model="ratingsOwn[category.categoryId]"
value-changed="onRatingsChange(ratingsOwn[category.categoryId])"
range="min"
step="{{category.valueStep}}"
min="{{category.valueMin}}"
max="{{category.valueMax}}"></slider>
这些是我必须解决的问题:
避免从外部访问隔离的范围变量。由于隔离值模型实际上绑定到父作用域中的某个变量,因此只需使用它。通过这种方式,没有范围的混合,事情变得更加简单。
绑定变量时,请确保将更改传播到所有绑定模型,方法是调用$apply
或类似的,然后再调用调用任何挂钩回调(例如value-changed
,在这种情况下)。
正如@Rafal指出的那样,只需使用单向数据绑定($eval
)并将&
的属性视为函数即可避免$scope
(这就是上面例子中value-changed
的工作方式)。
设置数据绑定到避免无限循环时要小心。在这种情况下,$watch
绑定将由UI触发(通过检查event.which
),但反之亦然,以打破循环。以编程方式更新值变量时,滑块将会更新,但不会触发其change
事件。
如果您想要在同一范围内有多个指令实例,请不要弄乱transclude
。
答案 1 :(得分:0)
不要使用eval。 使用隔离范围定义指令时,可以使用单向绑定,如下所示:
app.directive('myDirective', function(){
return{
//...
scope:{
functionToCall: '&'
}
//...
}}
当您的更改事件被触发时,只需在链接函数中调用函数,如下所示:
scope.functionToCall({change:changeVal});
编辑: 当使用指令传递来自控制器的函数时,如:
<my-directive function-to-call="functionInController(change)"></my-directive>