简体   繁体   English

带有Angular的简单滑块的数据绑定

[英]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: 这些是我必须解决的问题:

  1. 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. 这样,范围就不会混合了,事情变得简单得多。

  2. 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或类似方法将更改传播到所有绑定的模型。

  3. 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工作方式)。

  4. 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事件。

  5. 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM