angularjs - 指令中的动态回调

时间:2013-11-07 09:16:19

标签: javascript jquery angularjs

我正在尝试使用angularjs创建一个指令,但我发现了一个问题。

这是指令

的js代码
angular.module('xxxFileUploader', [])
    .directive('xxxFileUploader', function() {
        return {
            restrict: 'E',
            templateUrl: 'fileUploader.html',
            scope: {
                onChange: '='
            },
            link: function(scope, element, attrs) {
                element.find('input[type="file"]').change(function() {
                    var input = this;
                    if (input.files && input.files[0]) {
                        scope.file = input.files[0];
                        scope.onChange(input.files[0]);
                });
            }
        };
    });

指令模板

<span style="position:relative;">
    <a class='btn btn-primary' href='javascript:;'>
        Choose File...
    </a>
    <input type="file" 
           name="{{field.name}}"
           style='position:absolute;z-index:2;top:0;left:0;filter: alpha(opacity=0);opacity:0;background-color:transparent;color:transparent;' 
           />
    &nbsp;
    <span class='label label-info'>{{file.name}}</span>
</span>

我使用指令

的html文件
<xxx-file-uploader on-change="loadFile(field.name)" ></xxx-file-uploader>

和我的控制器的相关部分

$scope.loadFile = function(fieldName) {
    return function(file) {
        // do things with both fieldName and file...
    };
};

此代码应该只定制文件类型输入的外观,并在上载文件时执行回调。

我的问题来自于此时我需要使用为每个fileUploader动态构建的更改回调。正如您所看到的,回调是使用在链接时已知的参数fieldName和在“执行时”已知的参数文件构建的。

使用此代码,我得到了AngularJS“Error:10 $ digest()迭代。正在中止!“因为(我认为)这个函数是一次又一次地生成而且摘要不匹配。

穷人解决方案是将fieldName作为另一个范围变量传递。另一个可能是将函数作为字符串(@)传递,并在需要时构建回调。

关于如何在不改变界面的情况下使该指令工作的任何建议?我对angularjs指令很新(这是我的第一个!)所以如果你在代码中看到任何错误,请指出它。

谢谢。

首先修改:

我试图改变指令的范围,正如我从

建议的那样
scope: {
    onChange: '='
},

scope: {
    onChange: '&'
},

我改变了来自

的回叫电话
scope.onChange(input.files[0]);

scope.onChange()(input.files[0]);

现在它有效,但我对结果并不完全满意。

看起来需要调用onChange函数来获取我的“真实”回调。在这种情况下有没有办法隐式执行它?

我的意思是,如果我写

<xxx-file-uploader on-change="loadFile(field.name)" ></xxx-file-uploader>

应该理解执行函数

如果相反我写

<xxx-file-uploader on-change="doSomething" ></xxx-file-uploader>

它应该认识到doSomething是我希望它执行的“真正的”回调(使用file参数)。

4 个答案:

答案 0 :(得分:6)

您使用onChange: '='的方式并且该功能不正确。以下解决方案可以稍微更改指令的接口,但我认为将onChange重命名为onChangeFactory会更好地反映其预期用途。该名称可以保留onChange但是如果您不介意小的不一致。


loadFile()本身传递为:

<xxx-file-uploader on-change-factory="loadFile(fname)" ></xxx-file-uploader>

修改指令以使用函数绑定:

scope: {
    onChangeFactory: "&"
}

致电工厂并获取link()中的实际回调:

        link: function(scope, element, attrs) {
            // HOW ARE YOU GETTING FIELDNAME???
            var onChange = scope.onChangeFactory({fname: FIELDNAME});
            element.find('input[type="file"]').change(function() {
                var input = this;
                if (input.files && input.files[0]) {
                    scope.file = input.files[0];
                    onChange(input.files[0]); // <--- NOTE CHANGE HERE TOO
                }
            });
        }

请注意从指令内部调用onChangeFactory()的方式!我想会有一些空白(例如文件名),但我相信这是一个很好的起点。

答案 1 :(得分:3)

你应该使用'&amp;'如果您传递对函数的引用

,则在您的指令的范围param中
scope: {
  onChange: '&'
},

答案 2 :(得分:0)

你几乎就在那里......你所缺少的关键部分是$eval。 您可以致电scope.onChangeFactory({fname: FIELDNAME});而不是致电scope.$eval(scope.onChangeFactory)。这意味着您不需要知道控制器传入了什么参数。

以下是我自己的代码的一些片段,其中包含一些带有某些按钮的指令,并且我在单击按钮时添加了一个回调函数。而不是使用link我在我的指令中使用controller来响应按钮点击。

指令模板的一部分,其中包含一个可以单击的按钮:

<button type='button' ng-click='incrementClick()'>

指令代码:

.directive('numberSpinner', function() {
  return {
    restrict: 'EA',
    scope: {
      onIncrement: '&'
    },
    controller: ['$scope', function($scope) {
      $scope.incrementClick = function() {
        // Evaluate the callback
        $scope.$eval($scope.onIncrement); 
      };
    }],
    templateUrl: 'number-spinner.tpl.html'
  };
})

使用该指令的控制器(查看它如何在模型对象中传递item):

<number-spinner on-increment='myCallback(item)'></number-spinner>

在控制器中:

$scope.myCallback= function(item) {
  ...
};

答案 3 :(得分:0)

我自己仍在学习AngularJS,但本文https://stackoverflow.com/a/19890548/1959741提到了使用对象映射语法。我在AngularJS API文档中找不到对象映射,但是通过将{paramName:directiveVar}语法添加到回调事件模板中,它对我有用。