4

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 の「エラー: 10 回の $digest() 反復に達しました。中止します!」関数が何度も何度も生成され、ダイジェストが一致しないためです。

貧弱な解決策は、fieldName を別のスコープ変数として渡すことです。もう 1 つの方法は、関数を文字列 (@) として渡し、必要なときにコールバックを作成することです。

インターフェイスを変更せずにこのディレクティブを機能させる方法について何か提案はありますか? 私は 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

4 に答える 4

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()ディレクティブ内からの呼び出し方に注意!いくつかのギャップ (ファイル名など) があると思いますが、これは良い出発点だと思います。

于 2013-11-07T09:34:09.317 に答える
3

関数への参照を渡す場合は、ディレクティブのスコープ パラメータで「&」を使用する必要があります。

scope: {
  onChange: '&'
},
于 2013-11-07T09:31:21.177 に答える
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) {
  ...
};
于 2014-01-13T11:25:35.837 に答える