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
Марсель Gwerder

Відповіді:


240

Немає підтримки для управління завантаженням файлів

https://github.com/angular/angular.js/isissue/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 (this) .scope (). fileNameChaged (this)" $ range.fileNameChaged = function (element) {console.log ('файли:', element.files); } Чи можете ви змінитись у двох місцях і перевірити? Залежно від браузера, ви отримаєте повний шлях до файлу, я оновив загадку нижче - це файл завантаження URL-адреси та перевірити консоль jsfiddle.net/utp7j/260
JQuery Guru

13
Цей спосіб обходить обробку AngularJS за замовчуванням, тому $ range. $ Digest () не викликається (прив'язки не оновлюються). Викличте 'angular.element (this) .scope (). $ Digest ()' після цього або зателефонуйте методу області, що використовує $ apply.
Рамон де Кляйн

113
Це жахлива відповідь. Виклик angular.element (blah) .scope () - це функція налагодження, яку слід використовувати лише для налагодження. Кутовий не використовує функцію .scope. Це лише допомогти розробникам налагодити. Коли ви збираєте додаток і розгортаєте його до виробництва, вам слід вимкнути інформацію про налагодження. Це прискорює ваші ВСІ !!! Отже, писати код, який залежить від викликів element.scope () - погана ідея. Не робіть цього. Відповідь про створення власної директиви - це правильний спосіб зробити це. @JQueryGuru слід оновити свою відповідь. Оскільки у нього найбільше голосів, люди дотримуватимуться цієї поганої поради.
морозний

7
Це рішення порушить додаток, коли інформація про налагодження буде відключена. Вимкнення цього для виробництва є офіційною рекомендацією docs.angularjs.org/guide/production . Дивіться також blog.thoughtram.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;
    };
});     

direktiva.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, а введення файлу! Це мене давно відкинуло, і я не зміг знайти фактичну помилку. Я ще не впевнений, як насправді викликати це, якщо правильно встановити "це".
Карел Білек

4
сьогодні ця скрипка не працює для мене, ні на Chrome, ні на Firefox.
сегузо

2
Ця відповідь є хибною, як заявив @ KarelBílek. Я не зміг її обійти і вирішив використовувати angular-file-upload.
Гунар Гесснер

2
Це абсолютно, як це зробити, спрацювало як шарм. Крім того, навіщо ви використовуєте функцію налагодження у виробництві?
Джастін Круз

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подія як і раніше, здається, без нього.
рядок1

1
@ row1 вам потрібно вручну запустити додаток лише у тому випадку, якщо у вас є інші кутові речі, про які потрібно знати про це під час цієї операції.
Скотт Меч

27

Чистий спосіб - це написати власну директиву, яка прив'язується до події "зміни". Просто для того, щоб ви знали, що IE9 не підтримує FormData, тому ви не можете дійсно отримати об’єкт файлу від події зміни.

Ви можете використовувати бібліотеку ng-file-upload, яка вже підтримує IE з поліфайлом FileAPI та спрощує розміщення файлу на сервері. Для цього використовується директива.

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

Цей приклад застарів. Отримайте нову документацію тут .
Гунар Гесснер

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=''"працює добре, не потрібно писати її методом контролера
Сергій

20

Як і деякі інші хороші відповіді тут, я написав директиву для вирішення цієї проблеми, але ця реалізація більш ретельно відображає кутовий спосіб приєднання подій.

Ви можете використовувати директиву так:

HTML

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

Як ви бачите, ви можете вставити вибрані файли в обробник подій, як ви б ввели об'єкт $ події в будь-який обробник подій.

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

боролися з цим пару днів, поки я не спробував вашу відповідь. Дякую!
Майкл Транчіда

Як я можу передати додатковий параметр до 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);
};

Як виглядає $ range.onFilesSelected у вашому контролері?
ingaham

Якщо файл відкрито і ми намагаємося завантажити його в браузер Microsoft Edge. Нічого не відбувається. Ви можете допомогти?
Naveen Katakam

Так, це добре працює з іншими браузерами. Я зіткнувся з проблемою в області мікрософт @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});
                    });
                });
            }

        };
    }]);

ця директива може використовуватися багато разів, вона використовує власну сферу дії і не залежить від батьківської області. Ви також можете надати кілька параметрів функції обробника. Функція обробника буде викликана об'єктом області, який був активним при зміні вводу. $ apply оновлює вашу модель щоразу, коли викликається подія зміни


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-change 1

Щоб змусити <input type=file>елемент працювати директивою ng-change , йому потрібна спеціальна директива, яка працює з ng-моделлю директивою .

<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-клавішу (), щоб активувати метод

Після натискання на зображення імітуйте дію DOM 'клік' у полі введення. Et voilà!

 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 (), це функція налагодження!
Сезар

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 або щось для видалення файлових даних ..

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

Це погана практика, а не кутова.
Себастьян
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.