Выбор файла с помощью Angular JS
Я хотел бы забрать файл с помощью AngularJS:
HTML:
<div ng-controller="TopMenuCtrl">
<button class="btn" ng-click="isCollapsed = !isCollapsed">Toggle collapse</button>
<input type="file" ng-model="filepick" ng-change="pickimg()" multiple />
<output id="list"></output>
</div>
JavaScript:
angular.module('plunker', ['ui.bootstrap']);
function TopMenuCtrl($scope) {
$scope.pickimg = function() {
alert('a');
};
}
Как связать действие входного файла onchange
с функцией AngularJS pickimg
?
И как я могу манипулировать файлами, загруженными после?
Ответы
Ответ 1
Angular пока не поддерживает ng-change для ввода [type = file], поэтому вам нужно выполнить рулон onchange.
Сначала в HTML определите Javascript для обмена следующим образом:
<input ng-model="photo"
onchange="angular.element(this).scope().file_changed(this)"
type="file" accept="image/*" />
И затем в коде контроллера Angular определите функцию:
$scope.file_changed = function(element) {
$scope.$apply(function(scope) {
var photofile = element.files[0];
var reader = new FileReader();
reader.onload = function(e) {
// handle onload
};
reader.readAsDataURL(photofile);
});
};
Ответ 2
Я использовал выше метод, пытаясь загрузить изображение предварительного просмотра, когда был выбран новый файл, однако он не работал, когда я пробовал его следующим образом:
$scope.file_changed = function(element, $scope) {
$scope.$apply(function(scope) {
var photofile = element.files[0];
var reader = new FileReader();
reader.onload = function(e) {
$scope.prev_img = e.target.result;
};
reader.readAsDataURL(photofile);
});
});
Я углубился в него и обнаружил, что $scope. $apply должен быть внутри reader.onLoad, иначе изменяя переменные $scope, не работает, поэтому я сделал следующее, и это сработало:
$scope.file_changed = function(element) {
var photofile = element.files[0];
var reader = new FileReader();
reader.onload = function(e) {
$scope.$apply(function() {
$scope.prev_img = e.target.result;
});
};
reader.readAsDataURL(photofile);
};
Ответ 3
Решение Teemu не будет работать для IE9.
Я собрал простую директиву angular с Flash polyfill для браузеров, не поддерживающих форму HTML5 FormData, вы также можете прослушать событие для отслеживания загрузки.
https://github.com/danialfarid/ng-file-upload
Демо: http://angular-file-upload.appspot.com/
<script src="angular.min.js"></script>
<script src="ng-file-upload.js"></script>
<div ng-controller="MyCtrl">
<input type="text" ng-model="additionalData">
<div ngf-select ng-model="files" >
</div>
контроллер:
Upload.upload({
url: 'my/upload/url',
data: additionalData,
file: files
}).then(success, error, progress);
Ответ 4
Ниже приведен мой подход с директивой.
Директива
angular
.module('yourModule')
.directive('fileChange', function() {
return {
restrict: 'A',
scope: {
handler: '&'
},
link: function (scope, element) {
element.on('change', function (event) {
scope.$apply(function(){
scope.handler({files: event.target.files});
});
});
}
};
});
HTML
<input type="file" file-change handler="fileSelect(files)">
контроллер
fileSelect = function (files) {
var file = files[0];
//you will get the file object here
}
Ответ 5
Вот легкая директива, которую я написал для решения этой проблемы, которая отражает способ angular привязки событий.
Вы можете использовать следующую директиву:
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 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);
}
};
}]);
Ответ 6
Используя Madura answer выше, здесь полный поток для чтения локального файла JSON:
Создать директиву:
angular
.module('app.services')
.directive('fileChange', function() {
return {
restrict: 'A',
scope: {
handler: '&'
},
link: function (scope, element) {
element.on('change', function (event) {
scope.$apply(function(){
scope.handler({files: event.target.files});
});
});
}
};
});
HTML:
<input type="file" file-change handler="fileSelect(files)">
JavaScript:
$scope.fileSelect = function(files) {
var file = files[0];
var reader = new FileReader();
reader.onload = function(e) {
console.log("on load", e.target.result);
}
reader.readAsText(file);
}
Ответ 7
Я сделал директиву. Вот fiddle.
Приложение работает для выбора csvs и отображения их в виде html-таблиц.
С директивой on-file-change вы сможете определить логику чтения и разбора файлов (с помощью служб, может быть) в самих контроллерах, что обеспечит большую гибкость. Как раз для примечания, функция ac.onFileChange
, переданная атрибуту on-file-change, станет обработчиком входной переменной события внутри директивы.
(function (angular, document) {
angular
.module("app.directives", [])
.directive("onFileChange", ["$parse", function ($parse) {
return {
restrict: "A",
link: function (scope, ele, attrs) {
// onFileChange is a reference to the same function which you would define
// in the controller. So that you can keep your logic in the controller.
var onFileChange = $parse(attrs.onFileChange.split(/\(/)[0])(scope)
ele.on("change", onFileChange)
ele.removeAttr("on-file-change")
}
}
}])
angular
.module("app.services", [])
.service("Parse", ["$q", function ($q) {
var Parse = this
Parse.csvAsGrid = function (file) {
return $q(function (resolve, reject) {
try {
Papa.parse(file, {
complete: function (results) {
resolve(results.data)
}
})
} catch (e) {
reject(e)
}
})
}
}])
angular
.module("app", ["app.directives", "app.services"])
.controller("appCtrl", ["$scope", "Parse", function ($scope, Parse) {
var ac = this
ac.fileName = ""
ac.onFileChange = function (event) {
if (!event.target.files.length) {
return
}
Parse.csvAsGrid(event.target.files[0]).then(outputAsTable)
}
ac.clearInput = function (event) {
var input = angular.element(event.target)
input.val("")
document.getElementById("output").innerHTML = ""
}
function outputAsTable(grid) {
var table = ['<table border="1">']
grid.map(function (row) {
table.push('<tr>')
row.map(function (cell) {
table.push('<td>' + cell.replace(/["']/g, "") + '</td>')
})
table.push('</tr>')
})
table.push('</table>')
document.getElementById("output").innerHTML = table.join("\n")
}
}])
})(angular, document)
table {
border-collapse: collapse;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.0.0/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.8/angular.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/PapaParse/4.1.2/papaparse.min.js"></script>
<div ng-app="app" ng-controller="appCtrl as ac">
<label>Select a comma delimited CSV file:-</label>
<input id="filePicker" type="file" on-file-change="ac.onFileChange(event)" ng-click="ac.clearInput($event)"/>{{ac.fileName}}
</div>
<div id="output"></div>