AngularJs:ファイル入力フィールドの変更を確認する方法は?


281

Angularは初めてです。このフィールドで「変更」が発生するたびに、HTMLの「ファイル」フィールドからアップロードされたファイルパスを読み取ろうとしています。「onChange」を使用すると機能しますが、「ng-change」を使用して角度を付けて使用すると機能しません。

<script>
   var DemoModule = angular.module("Demo",[]);
   DemoModule .controller("form-cntlr",function($scope){
   $scope.selectFile = function()
   {
        $("#file").click();
   }
   $scope.fileNameChaged = function()
   {
        alert("select file");
   }
});
</script>

<div ng-controller="form-cntlr">
    <form>
         <button ng-click="selectFile()">Upload Your File</button>
         <input type="file" style="display:none" 
                          id="file" name='file' ng-Change="fileNameChaged()"/>
    </form>  
</div>

fileNameChaged()が呼び出されることはありません。Firebugでもエラーは表示されません。


1
参考になるかもしれません:stackoverflow.com/q/17063000/983992
Marcel Gwerder 2013

回答:


240

ファイルアップロードコントロールのバインディングサポートなし

https://github.com/angular/angular.js/issues/1375

<div ng-controller="form-cntlr">
        <form>
             <button ng-click="selectFile()">Upload Your File</button>
             <input type="file" style="display:none" 
                id="file" name='file' onchange="angular.element(this).scope().fileNameChanged(this)" />
        </form>  
    </div>

の代わりに

 <input type="file" style="display:none" 
    id="file" name='file' ng-Change="fileNameChanged()" />

試して頂けますか

<input type="file" style="display:none" 
    id="file" name='file' onchange="angular.element(this).scope().fileNameChanged()" />

注:これには、角度アプリケーションが常にデバッグモードであることが必要です。デバッグモードが無効になっている場合、これは製品コードでは機能しません。

そしてあなたの関数の代わりに

$scope.fileNameChanged = function() {
   alert("select file");
}

試して頂けますか

$scope.fileNameChanged = function() {
  console.log("select file");
}

以下は、ドラッグドロップによるファイルアップロードの実用的な例です。ファイルアップロードが役立つ場合があります http://jsfiddle.net/danielzen/utp7j/

Angular File Upload Information

ASP.NetでのAngularJSファイルアップロードのURL

http://cgeers.com/2013/05/03/angularjs-file-upload/

NodeJSで進行中のAngularJsネイティブマルチファイルアップロード

http://jasonturim.wordpress.com/2013/09/12/angularjs-native-multi-file-upload-with-progress/

ngUpload-iframeを使用してファイルをアップロードするためのAngularJSサービス

http://ngmodules.org/modules/ngUpload


2
onchange = "angular.element(this).scope()。fileNameChaged(this)" $ scope.fileNameChaged = function(element){console.log( 'files:'、element.files); } 2箇所で交換して確認できますか?ブラウザに応じて、ファイルのフルパスを取得します。以下のフィドルを更新すると、URLアップロードファイルが表示され、コンソールjsfiddle.net/utp7j/260
JQuery Guru

13
このメソッドはデフォルトのAngularJS処理を回避するため、$ scope。$ digest()は呼び出されません(バインディングは更新されません)。後で 'angular.element(this).scope()。$ digest()'を呼び出すか、$ applyを使用するスコープメソッドを呼び出します。
Ramon de Klein

113
これは恐ろしい答えです。angular.element(blah).scope()の呼び出しは、デバッグにのみ使用すべきデバッグ機能です。Angularは.scope機能を使用しません。開発者がデバッグするのを助けるためだけにあります。アプリをビルドして本番環境にデプロイするときは、デバッグ情報をオフにする必要があります。これはあなたのすべての多くをスピードアップします!!! したがって、element.scope()呼び出しに依存するコードを作成することはお勧めできません。これを行わないでください。カスタムディレクティブの作成に関する答えは、これを行う正しい方法です。@JQueryGuruあなたの答えを更新する必要があります。最も投票数が多いため、人々はこの悪いアドバイスに従います。
凍えるような

7
このソリューションは、デバッグ情報が無効になっている場合にアプリを破壊します。本番環境でこれを無効にすることは、公式の推奨事項docs.angularjs.org/guide/productionです。blog.thoughtram.io/angularjs/2014/12/22/…
wiherek

4
悪いsqren解決策... ... ' デバッグをオフにする'に関する他のコメントを読んでください... ... ...の解決策を参照してください... ... ... @ 0MV1
dsdsdsdsd

477

ファイル入力の変更をリッスンする小さなディレクティブを作成しました。

JSFiddleを表示

view.html:

<input type="file" custom-on-change="uploadFile">

controller.js:

app.controller('myCtrl', function($scope){
    $scope.uploadFile = function(event){
        var files = event.target.files;
    };
});     

directive.js:

app.directive('customOnChange', function() {
  return {
    restrict: 'A',
    link: function (scope, element, attrs) {
      var onChangeHandler = scope.$eval(attrs.customOnChange);
      element.on('change', onChangeHandler);
      element.on('$destroy', function() {
        element.off();
      });

    }
  };
});

4
これは、毎回異なるファイルが選択される場合にうまく機能します。同じファイルが選択されているときにも聞くことができますか?
JDawg 2015

12
これには非常に残念な「バグ」が1つあります。コントローラーはcustomOnChangeで「this」としてバインドされていませんが、ファイル入力です。それは長い間私を失望させ、私は実際のバグを見つけることができませんでした。「これ」を正しく設定して実際に呼び出す方法はまだわかりません。
KarelBílek15年

4
今日の時点では、フィドルはChromeでもFirefoxでも機能しません。
seguso

2
@KarelBílekが述べているように、この回答には欠陥があります。私はそれを回避することができず、angular-file-uploadを使用することにしました。
Gunar Gessner、2015

2
これは絶対にそれを行う方法であり、魅力のように機能しました。さらに、本番環境でデバッグ機能を使用する理由は何ですか?
ジャスティンクルーゼ

42

これは他のいくつかの改良版であり、データはng-modelになり、通常はこれが必要です。

マークアップ(属性データファイルを作成して、ディレクティブが検出できるようにする)

<input
    data-file
    id="id_image" name="image"
    ng-model="my_image_model" type="file">

JS

app.directive('file', function() {
    return {
        require:"ngModel",
        restrict: 'A',
        link: function($scope, el, attrs, ngModel){
            el.bind('change', function(event){
                var files = event.target.files;
                var file = files[0];

                ngModel.$setViewValue(file);
                $scope.$apply();
            });
        }
    };
});

1
される$scope.$apply();必要?ng-changeそれがなければ、私のイベントはまだ発生しているようです。
row1、2015年

1
@ row1は、その操作中にそれについて知る必要がある他の角度のあるものがある場合にのみ、手動で適用を起動する必要があります。
Scott Sword

27

クリーンな方法は、「変更」イベントにバインドする独自のディレクティブを記述することです。IE9はFormDataをサポートしていないため、変更イベントからファイルオブジェクトを実際に取得することはできません。

あなたは使用することができますngのファイル・アップロードすでにFileAPIのポリフィルでIEをサポートライブラリをポスティングにサーバにファイルを簡素化します。これを実現するためにディレクティブを使用します。

<script src="angular.min.js"></script>
<script src="ng-file-upload.js"></script>

<div ng-controller="MyCtrl">
  <input type="file" ngf-select="onFileSelect($files)" multiple>
</div>

JS:

//inject angular file upload directive.
angular.module('myApp', ['ngFileUpload']);

var MyCtrl = [ '$scope', 'Upload', function($scope, Upload) {
  $scope.onFileSelect = function($files) {
    //$files: an array of files selected, each file has name, size, and type.
    for (var i = 0; i < $files.length; i++) {
      var $file = $files[i];
      Upload.upload({
        url: 'my/upload/url',
        data: {file: $file}
      }).then(function(data, status, headers, config) {
        // file is uploaded successfully
        console.log(data);
      }); 
    }
  }
}];

この例は古くなっています。こちらのドキュメントから新しいものを入手してください
Gunar Gessner、2015

26

@Stuart Axonのアイデアを拡張して、ファイル入力に双方向バインディングを追加しました(つまり、モデル値をnullにリセットして入力をリセットできるようにします)。

app.directive('bindFile', [function () {
    return {
        require: "ngModel",
        restrict: 'A',
        link: function ($scope, el, attrs, ngModel) {
            el.bind('change', function (event) {
                ngModel.$setViewValue(event.target.files[0]);
                $scope.$apply();
            });

            $scope.$watch(function () {
                return ngModel.$viewValue;
            }, function (value) {
                if (!value) {
                    el.val("");
                }
            });
        }
    };
}]);

デモ


@JLRisheありがとうございます。1つのメモ、実際にdata-ng-click="vm.theFile=''"はうまく機能し、コントローラのメソッドに書き込む必要はありません
Sergey

20

ここで他のいくつかの良い答えと同様に、私はこの問題を解決するためのディレクティブを書きましたが、この実装はイベントをアタッチする角度の方法をより厳密に反映しています。

次のようなディレクティブを使用できます。

HTML

<input type="file" file-change="yourHandler($event, files)" />

ご覧のとおり、$ eventオブジェクトを任意のngイベントハンドラーに挿入するのと同じように、選択したファイルをイベントハンドラーに挿入できます。

JavaScript

angular
  .module('yourModule')
  .directive('fileChange', ['$parse', function($parse) {

    return {
      require: 'ngModel',
      restrict: 'A',
      link: function ($scope, element, attrs, ngModel) {

        // Get the function provided in the file-change attribute.
        // Note the attribute has become an angular expression,
        // which is what we are parsing. The provided handler is 
        // wrapped up in an outer function (attrHandler) - we'll 
        // call the provided event handler inside the handler()
        // function below.
        var attrHandler = $parse(attrs['fileChange']);

        // This is a wrapper handler which will be attached to the
        // HTML change event.
        var handler = function (e) {

          $scope.$apply(function () {

            // Execute the provided handler in the directive's scope.
            // The files variable will be available for consumption
            // by the event handler.
            attrHandler($scope, { $event: e, files: e.target.files });
          });
        };

        // Attach the handler to the HTML change event 
        element[0].addEventListener('change', handler, false);
      }
    };
  }]);

私があなたの答えを試すまで、これで数日間苦労しました。ありがとう!
Michael Tranchida、2016

fileChange式に追加のパラメーターを渡すにはどうすればよいですか?私は内部でこのディレクティブを使用していますng-repeatし、$scopeに渡される変数は、attrHandler各レコードの同じです。最良の解決策は何ですか?

16

このディレクティブは、選択したファイルも渡します。

/**
 *File Input - custom call when the file has changed
 */
.directive('onFileChange', function() {
  return {
    restrict: 'A',
    link: function (scope, element, attrs) {
      var onChangeHandler = scope.$eval(attrs.onFileChange);

      element.bind('change', function() {
        scope.$apply(function() {
          var files = element[0].files;
          if (files) {
            onChangeHandler(files);
          }
        });
      });

    }
  };
});

HTML、それを使用する方法:

<input type="file" ng-model="file" on-file-change="onFilesSelected">

私のコントローラーでは:

$scope.onFilesSelected = function(files) {
     console.log("files - " + files);
};

$ scope.onFilesSelectedはコントローラーでどのように表示されますか?
インガハム2017年

ファイルが開かれていて、Microsoft Edgeブラウザーでファイルをアップロードしようとした場合。何も起こりません。手伝ってくれますか?
Naveen Katakam 2017

はい、他のブラウザで正常に動作しています。私はマイクロソフトエッジで問題に直面しています@ingaham
Naveen

8

ディレクティブを作成することをお勧めします

<input type="file" custom-on-change handler="functionToBeCalled(params)">

app.directive('customOnChange', [function() {
        'use strict';

        return {
            restrict: "A",

            scope: {
                handler: '&'
            },
            link: function(scope, element){

                element.change(function(event){
                    scope.$apply(function(){
                        var params = {event: event, el: element};
                        scope.handler({params: params});
                    });
                });
            }

        };
    }]);

このディレクティブは何度も使用でき、独自のスコープを使用し、親スコープに依存しません。ハンドラー関数にいくつかのパラメーターを与えることもできます。ハンドラー関数は、入力を変更したときにアクティブだったスコープオブジェクトで呼び出されます。$ applyは、変更イベントが呼び出されるたびにモデルを更新します


4

最も単純なAngular jqLit​​eバージョン。

JS:

.directive('cOnChange', function() {
    'use strict';

    return {
        restrict: "A",
        scope : {
            cOnChange: '&'
        },
        link: function (scope, element) {
            element.on('change', function () {
                scope.cOnChange();
        });
        }
    };
});

HTML:

<input type="file" data-c-on-change="your.functionName()">

ファイルが開かれていて、Microsoft Edgeブラウザーでファイルをアップロードしようとした場合。何も起こりません。手伝ってくれますか?
Naveen Katakam 2017

2

で動作する「files-input」ディレクティブの作業デモng-change1

ようにするには<input type=file>要素作業NG-変更指令を、それが必要とするカスタムディレクティブで動作NG-モデルディレクティブを。

<input type="file" files-input ng-model="fileList" 
       ng-change="onInputChange()" multiple />

デモ

angular.module("app",[])
.directive("filesInput", function() {
  return {
    require: "ngModel",
    link: function postLink(scope,elem,attrs,ngModel) {
      elem.on("change", function(e) {
        var files = elem[0].files;
        ngModel.$setViewValue(files);
      })
    }
  }
})

.controller("ctrl", function($scope) {
     $scope.onInputChange = function() {
         console.log("input change");
     };
})
<script src="//unpkg.com/angular/angular.js"></script>
  <body ng-app="app" ng-controller="ctrl">
    <h1>AngularJS Input `type=file` Demo</h1>
    
    <input type="file" files-input ng-model="fileList" 
           ng-change="onInputChange()" multiple />
    
    <h2>Files</h2>
    <div ng-repeat="file in fileList">
      {{file.name}}
    </div>
  </body>


すばらしいですが、ディレクティブは一度だけ呼び出されます!選択したファイルを変更すると、ディレクティブが2回呼び出されません。この動作について何か知っていますか?
ハグスブルグ

デモにはその問題はありません。読者が調べられるように、再現可能な例を使用して新しい質問を作成します。
georgeawg

はい、その問題があります。選択したファイルを何度も変更しても、コンソールログの「入力変更」が一度しか表示されません。
ハグスブルグ

テスト後、それはクロムで動作しますが、Firefoxの最新バージョンでは動作しません...ブラウザーの互換性がいまいましい!!!
ハグスブルグ

0

以下に基づく完全すぎるソリューションベース:

`onchange="angular.element(this).scope().UpLoadFile(this.files)"`

入力フィールドを非表示にして画像で置き換える簡単な方法、ここではソリューションの後、角度のハックも必要ですが、仕事をします[TriggerEventは期待どおりに機能しません]

ソリューション:

  • 入力フィールドをdisplay:noneに配置します[入力フィールドはDOMに存在しますが、表示されません]
  • 画像の直後に画像を配置する画像上でnb-click()を使用してメソッドをアクティブ化します

画像をクリックすると、入力フィールドでDOMアクション「クリック」をシミュレートします。エボラ!

 var tmpl = '<input type="file" id="{{name}}-filein"' + 
             'onchange="angular.element(this).scope().UpLoadFile(this.files)"' +
             ' multiple accept="{{mime}}/*" style="display:none" placeholder="{{placeholder}}">'+
             ' <img id="{{name}}-img" src="{{icon}}" ng-click="clicked()">' +
             '';
   // Image was clicked let's simulate an input (file) click
   scope.inputElem = elem.find('input'); // find input in directive
   scope.clicked = function () {
         console.log ('Image clicked');
         scope.inputElem[0].click(); // Warning Angular TriggerEvent does not work!!!
    };

1
angular.element(this).scope()は使用できません。これはデバッグ機能です!
Cesar

0

私はこのようにしました。

<!-- HTML -->
<button id="uploadFileButton" class="btn btn-info" ng-click="vm.upload()">    
<span  class="fa fa-paperclip"></span></button>
<input type="file" id="txtUploadFile" name="fileInput" style="display: none;" />
// self is the instance of $scope or this
self.upload = function () {
   var ctrl = angular.element("#txtUploadFile");
   ctrl.on('change', fileNameChanged);
   ctrl.click();
}

function fileNameChanged(e) {
    console.log(self.currentItem);
    alert("select file");
}

0

ファイル入力の変更をリッスンするもう1つの興味深い方法は、入力ファイルのng-model属性を監視することです。もちろん、FileModelはカスタムディレクティブです。

このような:

HTML-> <input type="file" file-model="change.fnEvidence">

JSコード->

$scope.$watch('change.fnEvidence', function() {
                    alert("has changed");
                });

それが誰かを助けることができることを願っています。


0

Angular要素(ディレクティブのルート要素など)はjQuery [Lite]オブジェクトです。つまり、次のようにイベントリスナーを登録できます。

link($scope, $el) {
    const fileInputSelector = '.my-file-input'

    function setFile() {
        // access file via $el.find(fileInputSelector).get(0).files[0]
    }

    $el.on('change', fileInputSelector, setFile)
}

これはjQueryイベントの委譲です。ここでは、リスナーはディレクティブのルート要素にアタッチされています。イベントがトリガーされると、登録された要素までバブルし、jQueryは、定義されたセレクターに一致する内部要素でイベントが発生したかどうかを判断します。ある場合、ハンドラーが起動します。

この方法の利点は次のとおりです。

  • ハンドラーは$要素にバインドされ、ディレクティブスコープが破棄されると自動的にクリーンアップされます。
  • テンプレートにコードがありません
  • あなたは、イベントハンドラを登録する際に、ターゲットデリゲート(入力)がまだレンダリングされていない場合でも動作します(たとえば、使用している場合などng-ifng-switch

http://api.jquery.com/on/


-1

以下のコードをonchangeに追加するだけで、変更が検出されます。Xクリックなどでファイルデータを削除する関数を記述できます。

document.getElementById(id).value = "";

これは悪い習慣であり、角度のようなものではありません。
セバスチャン
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.