AngularJs: จะตรวจสอบการเปลี่ยนแปลงในฟิลด์อินพุตไฟล์ได้อย่างไร?


281

ฉันใหม่สำหรับเชิงมุม ฉันพยายามอ่านเส้นทางของไฟล์ที่อัปโหลดจากฟิลด์ 'ไฟล์' ของ 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

1
ดูเพิ่มเติมที่stackoverflow.com/questions/16631702/file-pick-with-angular-js
tliokos

คำตอบ:


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/

ข้อมูลการอัพโหลดไฟล์เชิงมุม

URL สำหรับการอัปโหลดไฟล์ AngularJS ใน ASP.Net

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

การอัปโหลดหลายไฟล์แบบดั้งเดิมของ AngularJs พร้อมดำเนินการกับ NodeJS

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

ngUpload - บริการ AngularJS สำหรับการอัปโหลดไฟล์โดยใช้ iframe

http://ngmodules.org/modules/ngUpload


2
onchange = "angular.element (นี่) .scope (). fileNameChaged (นี้)" $ scope.fileNameChaged = ฟังก์ชั่น (องค์ประกอบ) {console.log ('files:', element.files); } คุณสามารถเปลี่ยนสถานที่สองแห่งและตรวจสอบได้หรือไม่? ขึ้นอยู่กับเบราว์เซอร์คุณจะได้รับไฟล์เต็มเส้นทางฉันได้อัพเดทซอด้านล่างเป็น url อัพโหลดไฟล์และตรวจสอบคอนโซลjsfiddle.net/utp7j/260
JQuery Guru

13
เมธอดนี้หลีกเลี่ยงการประมวลผล AngularJS เริ่มต้นดังนั้น $ scope ไม่ได้เรียก $ digest () (การผูกไม่ได้รับการอัพเดต) โทร 'angular.element (นี่) .scope (). $ digest ()' หลังจากนั้นหรือเรียกวิธีขอบเขตที่ใช้ $ Apply
Ramon de Klein

113
นี่คือคำตอบที่น่ากลัว การเรียก angular.element (blah) .scope () เป็นคุณสมบัติการดีบักที่คุณควรใช้สำหรับการดีบักเท่านั้น เชิงมุมไม่ได้ใช้คุณสมบัติ. scope มีเพียงเพื่อช่วยนักพัฒนาแก้ปัญหา เมื่อคุณสร้างแอปและปรับใช้กับการผลิตคุณควรปิดข้อมูลการดีบัก นี่เป็นการเพิ่มความเร็วให้กับทุกอย่างของคุณ !!! ดังนั้นการเขียนโค้ดที่ขึ้นอยู่กับการเรียก element.scope () เป็นความคิดที่ไม่ดี อย่าทำอย่างนี้ คำตอบเกี่ยวกับการสร้างคำสั่งที่กำหนดเองเป็นวิธีที่เหมาะสมในการทำเช่นนี้ @JQueryGuru คุณควรอัปเดตคำตอบของคุณ เนื่องจากมีการโหวตมากที่สุดผู้คนจะทำตามคำแนะนำที่ไม่ดีนี้
น้ำค้างแข็ง

7
วิธีนี้จะทำให้แอปหยุดทำงานเมื่อข้อมูลการดีบักถูกปิดใช้งาน ปิดการใช้งานสำหรับการผลิตที่เป็นคำแนะนำอย่างเป็นทางการdocs.angularjs.org/guide/production โปรดดูblog. buttram.io/angularjs/2014/12/22/ …
wiherek

4
BAD SOLUTION ... ... อ่านความคิดเห็นอื่น ๆ เกี่ยวกับ 'การปิดการดีบัก' ... ... ... และดู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

12
นี่มี "บั๊ก" ที่โชคร้ายอย่างหนึ่ง - คอนโทรลเลอร์ไม่ได้ถูกผูกมัดว่า "นี่" ใน customOnChange แต่อินพุตไฟล์! มันโยนฉันออกไปเป็นเวลานานและฉันไม่พบข้อผิดพลาดจริง ฉันยังไม่แน่ใจว่าจะเรียกมันอย่างไรด้วยการตั้งค่าอย่างถูกต้อง "นี้"
Karel Bílek

4
ซอไม่ทำงานสำหรับฉันทั้งบน Chrome หรือ Firefox ณ วันนี้
seguso

2
คำตอบนี้มีข้อบกพร่องตามที่ระบุไว้โดย @ KarelBílek ฉันไม่สามารถหลีกเลี่ยงได้และตัดสินใจใช้แองกูลาร์ - ไฟล์ - อัปโหลด
Gunar Gessner

2
นี่เป็นวิธีที่จะทำมันได้ผลเหมือนเป็นเครื่องราง นอกจากนี้ทำไมคุณถึงใช้คุณสมบัติการแก้ไขข้อบกพร่องในการผลิต
Justin Kruse

42

นี่คือการปรับแต่งของคนอื่น ๆ รอบ ๆ ข้อมูลจะสิ้นสุดในรูปแบบ ng ซึ่งเป็นปกติสิ่งที่คุณต้องการ

มาร์กอัป (เพียงสร้างไฟล์ข้อมูลแอททริบิวต์เพื่อหาคำสั่ง)

<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

1
@ row1 คุณจะต้องทำการสมัครด้วยตนเองหากคุณมีสิ่งอื่น ๆ ที่ต้องรู้ในระหว่างการดำเนินการนั้น
Scott Sword

27

วิธีที่สะอาดคือการเขียนคำสั่งของคุณเองเพื่อผูกกับเหตุการณ์ "เปลี่ยน" เพียงเพื่อให้คุณรู้ว่า IE9 ไม่รองรับ FormData ดังนั้นคุณจึงไม่สามารถรับวัตถุไฟล์จากเหตุการณ์การเปลี่ยนแปลงได้

คุณสามารถใช้ได้ ไลบรารีng-file-uploadซึ่งรองรับ IE กับ FileAPI polyfill แล้วและทำให้การโพสต์ไฟล์ไปยังเซิร์ฟเวอร์ทำได้ง่ายขึ้น มันใช้คำสั่งเพื่อให้บรรลุนี้

<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

26

ฉันได้ขยายแนวคิดของ @Stuart Axon เพื่อเพิ่มการเชื่อมโยงสองทางสำหรับอินพุตไฟล์ (เช่นอนุญาตให้รีเซ็ตอินพุตโดยรีเซ็ตค่ารุ่นกลับเป็นโมฆะ):

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 หมายเหตุหนึ่งใช้data-ng-click="vm.theFile=''"งานได้ดีจริง ๆไม่จำเป็นต้องเขียนลงในวิธีการควบคุม
Sergey

20

คล้ายกับคำตอบที่ดีอื่น ๆ ที่นี่ฉันเขียนคำสั่งเพื่อแก้ไขปัญหานี้ แต่การใช้งานนี้ยิ่งสะท้อนวิธีเชิงมุมของการแนบเหตุการณ์มากขึ้น

คุณสามารถใช้คำสั่งเช่นนี้:

HTML

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

อย่างที่คุณเห็นคุณสามารถฉีดไฟล์ที่เลือกลงในตัวจัดการเหตุการณ์ของคุณได้เช่นเดียวกับที่คุณฉีด $ วัตถุออบเจ็กต์ลงในตัวจัดการเหตุการณ์ ng

จาวาสคริ

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

ฉันจะส่งพารามิเตอร์เพิ่มเติมไปยัง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 มีลักษณะอย่างไรในตัวควบคุมของคุณ
ingaham

หากไฟล์ถูกเปิดและเมื่อเราลองอัพโหลดในไฟล์ในเบราว์เซอร์ Microsoft Edge ไม่มีอะไรเกิดขึ้น คุณช่วยได้ไหม
Naveen Katakam

ใช่มันทำงานได้ดีกับเบราว์เซอร์อื่น ๆ ฉันกำลังเผชิญปัญหาใน microsoft edge @ingaham
Naveen Katakam

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});
                    });
                });
            }

        };
    }]);

คำสั่งนี้สามารถใช้ได้หลายครั้งโดยใช้ขอบเขตของตัวเองและไม่ขึ้นอยู่กับขอบเขตหลัก คุณยังสามารถมอบพารามิเตอร์ให้กับฟังก์ชันจัดการได้ ฟังก์ชันตัวจัดการจะถูกเรียกด้วยวัตถุขอบเขตที่ใช้งานอยู่เมื่อคุณเปลี่ยนอินพุต $ ใช้อัปเดตโมเดลของคุณในแต่ละครั้งที่มีการเรียกเหตุการณ์การเปลี่ยนแปลง


4

รุ่น jqLite เชิงมุมที่ง่ายที่สุด

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

2

สาธิตการทำงานของคำสั่ง "ไฟล์ - อินพุต" ที่ใช้งานได้กับng-change1

ในการทำให้<input type=file>องค์ประกอบทำงานตามคำสั่งng-changeมันต้องการคำสั่งที่กำหนดเองที่ทำงานกับคำสั่งng-model

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

DEMO

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>


ยอดเยี่ยม แต่สั่งเรียกเพียงครั้งเดียว! หากฉันเปลี่ยนไฟล์ที่เลือกคำสั่งจะไม่ถูกเรียกเป็นครั้งที่สอง! คุณมีความคิดเกี่ยวกับพฤติกรรมนี้หรือไม่?
hugsbrugs

DEMO ไม่มีปัญหานั้น สร้างคำถามใหม่ด้วยตัวอย่างที่ทำซ้ำได้เพื่อให้ผู้อ่านตรวจสอบ
georgeawg

ใช่มันมีปัญหานั้นบันทึกคอนโซล "การเปลี่ยนแปลงการป้อนข้อมูล" จะแสดงเพียงครั้งเดียวแม้ว่าคุณจะเปลี่ยนไฟล์ที่เลือกหลายครั้ง!
hugsbrugs

หลังจากการทดสอบมันใช้งานได้กับโครเมียม แต่ไม่ได้ใช้กับ Firefox รุ่นล่าสุด ... ใช้งานร่วมกับเบราว์เซอร์ได้
hugsbrugs

0

โซลูชันที่สมบูรณ์เกินไปโดยใช้:

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

วิธีง่ายๆในการซ่อนฟิลด์อินพุตและแทนที่ด้วยรูปภาพที่นี่หลังจากการแก้ปัญหาที่ยังต้องแฮ็คในเชิงมุม แต่ที่ทำงาน [TriggerEvent ไม่ทำงานตามที่คาดไว้]

การแก้ไขปัญหา:

  • วางอินพุตฟิลด์ในจอแสดงผล: ไม่มี [ฟิลด์อินพุตมีอยู่ใน 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 (นี่) .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

อีกวิธีที่น่าสนใจในการฟังการเปลี่ยนแปลงอินพุตไฟล์คือการเฝ้าดูแอ็ตทริบิวต์ ng-model ของอินพุตไฟล์ FileModel นอกหลักสูตรเป็นคำสั่งที่กำหนดเอง

แบบนี้:

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

รหัส JS ->

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

หวังว่ามันจะช่วยใครบางคน


0

องค์ประกอบเชิงมุม (เช่นองค์ประกอบรากของคำสั่ง) เป็นวัตถุ 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-ifหรือng-switch)

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


-1

คุณสามารถเพิ่มรหัสด้านล่างใน onchange และมันจะตรวจจับการเปลี่ยนแปลง คุณสามารถเขียนฟังก์ชั่นบน X click หรือบางสิ่งเพื่อลบข้อมูลไฟล์ ..

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

นี่คือการปฏิบัติที่ไม่ดีและไม่เป็นเชิงมุมเหมือน
เซบาสเตียน
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.