Sélection de fichier avec JS angulaire
Je voudrais récupérer un fichier avec 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');
};
}
Comment puis-je lier l'action du fichier d'entrée onchange
sur la fonction AngularJS pickimg
?
Et comment puis-je manipuler les fichiers téléchargés après?
7 réponses
Angular ne prend pas encore en charge ng-changepour input[type=file] vous devez donc lancer vous-même l'implémentation onchange.
Tout d'abord, dans le HTML, définissez Javascript pour onchange comme suit:
<input ng-model="photo"
onchange="angular.element(this).scope().file_changed(this)"
type="file" accept="image/*" />
Puis dans votre code de contrôleur angulaire, définissez la fonction:
$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);
});
};
J'ai utilisé la méthode ci-dessus en essayant de charger une image d'aperçu lorsque le nouveau fichier est sélectionné, mais cela n'a pas fonctionné quand je l'ai essayé comme ça:
$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);
});
});
J'ai creusé plus dedans et j'ai trouvé que le $scope.$s'appliquent doivent être à l'intérieur du lecteur.onLoad sinon changer une variable $ scope ne fonctionnera pas, donc j'ai fait ce qui suit et cela a fonctionné:
$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);
};
La solution Teemu ne fonctionnera pas pour IE9.
J'ai mis en place une directive angulaire simple avec flash polyfill pour les navigateurs ne supportant pas HTML5 FormData, vous pouvez également écouter upload progress event.
Https://github.com/danialfarid/ng-file-upload Démo: 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>
Contrôleur:
Upload.upload({
url: 'my/upload/url',
data: additionalData,
file: files
}).then(success, error, progress);
Voici mon approche avec une directive.
Directive
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)">
Contrôleur
fileSelect = function (files) {
var file = files[0];
//you will get the file object here
}
Voici une directive légère que j'ai écrite pour résoudre ce problème, qui reflète la manière angulaire d'attacher des événements.
Vous pouvez utiliser la directive comme ceci:
HTML
<input type="file" file-change="yourHandler($event, files)" />
Comme vous pouvez le voir, vous pouvez injecter les fichiers sélectionnés dans votre gestionnaire d'événement que vous donneriez à un $objet d'événement dans toute ng gestionnaire d'événement.
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);
}
};
}]);
En utilisant la réponse de Madura d'en haut, voici le flux complet pour lire un fichier JSON local:
Créer la directive:
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);
}
J'ai fait une directive. Voici le violon .
L'application fonctionne pour sélectionner des CSV et les montrer sous forme de tables html.
Avec la directive on-file-change, vous seriez en mesure de définir la lecture et l'analyse des fichiers (avec des services, peut-être) dans les contrôleurs eux-mêmes, ce qui fournira plus de flexibilité. Juste pour la note, la fonction ac.onFileChange
passée à l'attribut on-file-change deviendra le gestionnaire de l'événement de changement d'entrée à l'intérieur directive.
(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>