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

时间:2014-07-07 13:52:33

标签: javascript angularjs angularjs-scope

我开发了一个具有独立范围的简单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属性,以便来自不同范围的所有变量都存在?有没有办法强制隔离范围从其父级继承?

2 个答案:

答案 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>

这些是我必须解决的问题:

  1. 避免从外部访问隔离的范围变量。由于隔离值模型实际上绑定到父作用域中的某个变量,因此只需使用它。通过这种方式,没有范围的混合,事情变得更加简单。

  2. 绑定变量时,请确保将更改传播到所有绑定模型,方法是调用$apply或类似的,然后再调用调用任何挂钩回调(例如value-changed,在这种情况下)。

  3. 正如@Rafal指出的那样,只需使用单向数据绑定($eval)并将&的属性视为函数即可避免$scope (这就是上面例子中value-changed的工作方式)。

  4. 设置数据绑定到避免无限循环时要小心。在这种情况下,$watch绑定将由UI触发(通过检查event.which),但反之亦然,以打破循环。以编程方式更新值变量时,滑块将会更新,但不会触发其change事件。

  5. 如果您想要在同一范围内有多个指令实例,请不要弄乱transclude

答案 1 :(得分:0)

不要使用eval。 使用隔离范围定义指令时,可以使用单向绑定,如下所示:

app.directive('myDirective', function(){
return{
//...
    scope:{
       functionToCall: '&'
    }
//...
}}

当您的更改事件被触发时,只需在链接函数中调用函数,如下所示:

scope.functionToCall({change:changeVal});

编辑: 当使用指令传递来自控制器的函数时,如:

<my-directive function-to-call="functionInController(change)"></my-directive>