[英]Data-binding for a simple slider with Angular
I have developed a simple UI slider directive that has an isolated scope. 我已经开发了一个具有隔离范围的简单UI滑块指令。 It also supports registering a change
property that is fired when the value of the slider changed. 它还支持注册change
滑块值时触发的change
属性。 The change
property would usually call a function of a parent scope with the slider value from the isolated scope, like this: change
属性通常会使用隔离范围中的滑块值来调用父范围的函数,如下所示:
change="onValueChanged(theValue)"
where change
is an attribute, onValueChanged
is a function declared in a parent controller and theValue
is the current value of the element that is only declared in the isolated scope. 其中change
是属性, onValueChanged
是在父控制器中声明的函数,而theValue
是仅在隔离范围中声明的元素的当前值。
How can I $eval
this correctly? 我该如何正确$eval
? In the slider's link
function I can call $scope.$eval(attrs.change)
but $scope.onValueChanged
is not defined. 在滑块的link
函数中,我可以调用$scope.$eval(attrs.change)
但未定义$scope.onValueChanged
。 The isolated scope did not inherit from its parent. 隔离范围不是从其父级继承的。 At the same time, $scope.$parent.$eval(attrs.change)
correctly calls the onValueChanged
function, but theValue
will not be set because it is isolated in the child scope. 与此同时, $scope.$parent.$eval(attrs.change)
正确调用onValueChanged
功能,但theValue
因为它是在孩子隔离的范围将不会设置。 Since the change
property can reference any number of parent scope properties, I cannot explicitly declare them in the isolated scope. 由于change
属性可以引用任意数量的父范围属性,因此我无法在隔离范围中显式声明它们。
How can I eval
the change
property so that all variables from the different scopes will be present? 如何eval
change
属性,以便显示来自不同作用域的所有变量? Is there a way to force the isolated scope to inherit from its parent? 有没有办法强制隔离范围从其父级继承?
I have found that there were several small issues with my approach. 我发现我的方法存在几个小问题。 My final slider looks a little like this: 我的最终滑块看起来像这样:
/**
* 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
};
});
This is an example instance of a slider: 这是一个滑块的示例:
<slider
value-model="ratingsOwn[category.categoryId]"
value-changed="onRatingsChange(ratingsOwn[category.categoryId])"
range="min"
step="{{category.valueStep}}"
min="{{category.valueMin}}"
max="{{category.valueMax}}"></slider>
These were the issues I had to fix: 这些是我必须解决的问题:
Avoid accessing isolated scope variables from the outside . 避免从外部访问隔离的范围变量 。 Since the isolated value model is actually bound to some variable in the parent scope, simply use that instead. 由于隔离值模型实际上绑定到父作用域中的某个变量,因此只需使用它即可。 This way, there is no mixing of scopes, and things become a whole lot simpler. 这样,范围就不会混合了,事情变得简单得多。
When binding variables, make sure to propagate changes to all bound models, by calling $apply
or similar, before calling any hook callbacks (eg value-changed
, in this case). 绑定变量时,请确保在调用任何钩子回调(例如, value-changed
) 之前 ,通过调用$apply
或类似方法将更改传播到所有绑定的模型。
As @Rafal points out, $eval
can be avoided by simply using uni-directional data-binding ( &
) and treating the $scope
's property as a function (that is how value-changed
works in the above example). 正如@Rafal指出的那样,可以通过简单地使用单向数据绑定( &
)并将$scope
的属性视为函数来避免$eval
(在上面的示例中, value-changed
工作方式)。
Be careful when setting up data-binding to avoid infinite loops . 设置数据绑定时要小心,以避免无限循环 。 In this case, the $watch
binding will be triggered by the UI (by checking event.which
), but vice versa is avoided, to break the loop. 在这种情况下, $watch
绑定将由UI触发(通过检查event.which
),但避免反之亦然,以打破循环。 When the value variable is updated programatically, the slider will be updated but its change
event will not be triggered. 以编程方式更新value变量时,将更新滑块,但不会触发其change
事件。
Do not mess with transclude
if you want multiple directive instances in the same scope. 如果要在同一个作用域中使用多个指令实例,请不要transclude
。
Don't use eval. 不要使用eval。 When defining a directive with an isolate scope you can use one way binding like this: 在使用隔离范围定义指令时,可以使用以下一种方式绑定:
app.directive('myDirective', function(){
return{
//...
scope:{
functionToCall: '&'
}
//...
}}
When your change event is fired simply call the function in the link function like this: 触发您的更改事件时,只需像这样在link函数中调用该函数:
scope.functionToCall({change:changeVal});
edit: When using the directive pass in the function from the controller like this: 编辑:当使用指令从控制器中传入函数时,如下所示:
<my-directive function-to-call="functionInController(change)"></my-directive>
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.