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 также не показывает никаких ошибок.



Ответы:


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/

Нативная многофайловая загрузка AngularJ с прогрессом с 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)" $ scope.fileNameChaged = function (element) {console.log ('files:', element.files); } Можете ли вы поменять в двух местах и ​​проверить? в зависимости от браузера вы получите полный путь к файлу, который я обновил. Ниже приведена ссылка для
— JQuery Guru

13
Этот метод обходит обработку AngularJS по умолчанию, поэтому $ scope. $ Digest () не вызывается (привязки не обновляются). Затем вызовите angular.element (this) .scope (). $ Digest () или вызовите метод области действия, который использует $ apply.
— Рамон де Кляйн

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

12
У этого есть одна очень печальная «ошибка» - контроллер не привязан как «this» в 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событие до сих пор, кажется, без него.
— row1

1
@ row1, вам нужно только вручную запустить заявку, если у вас есть другие угловатые вещи, которые нужно знать об этом во время этой операции.
— Скотт Меч

27

Чистый способ - написать собственную директиву для привязки к событию «change». Просто чтобы вы знали, что IE9 не поддерживает FormData, поэтому вы не можете получить объект file из события change.

Вы можете использовать библиотеку 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)" />

Как видите, вы можете вставить файлы, выбранные в ваш обработчик событий, так же, как вы вставили бы объект $ 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);
      }
    };
  }]);

боролся с этим пару дней, пока не попробовал ваш ответ. Спасибо!
— Майкл Транчида

Как я могу передать дополнительный параметр в 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 в вашем контроллере?
— Ингаам

Если файл открыт и когда мы пытаемся загрузить файл в браузере Microsoft Edge. Ничего не происходит. Вы можете помочь?
— Навин Катакам

Да, он отлично работает с другими браузерами. Я сталкиваюсь с проблемой в Microsoft Edge @ingaham
— Навин Катакам

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 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. Ничего не происходит. Вы можете помочь?
— Навин Катакам

2

Рабочая демонстрация директивы «file-input», которая работает с ng-change1

Чтобы заставить <input type=file>элемент работать с директивой ng-change , ему нужна пользовательская директива, которая работает с директивой ng-model .

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


Замечательно, но директива вызывается только один раз! Если я изменяю выбранный файл, то директива не вызывается во второй раз! Есть ли у вас какие-либо идеи об этом поведении?
— hugsbrugs

ДЕМО не имеет этой проблемы. Создайте новый вопрос с воспроизводимым примером для читателей.
— georgeawg

да, есть такая проблема: консольный журнал «изменение ввода» отображается только один раз, даже если вы много раз меняли выбранные файлы!
— hugsbrugs

после тестирования он работает на хроме, но не на последней версии Firefox ... чертовски совместимость браузера !!!
— hugsbrugs

0

Слишком полное решение основано на:

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

Простой способ скрыть поле ввода и заменить его изображением, здесь после решения, которое также требует взлома angular, но выполняет свою работу [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 (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 click или что-то, чтобы удалить данные файла ..

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

Это плохая практика, а не угловатая.
— Себастьян
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.