Here's some code http://jsfiddle.net/miuosh/n6yppypj/ with uploadfile directive. The problem is that I use this
<input type="file" file-model="myFile" />
<button ng-click="uploadFile()">upload me</button>
in ng-include="'views/uploadFileForm.html'".
In directive I add "myFile" to scope. It turns out that Angular create new scope with myFile. To add "myFile" to "rootScope" I need to use
modelSetter(scope.$parent.$parent.$parent[..],element[0].files[0])
which is inconvenient because I need to know how many parent scope I have.
I have faced similar problem dealing with file input with angular.
you can create a directive which will listen to file change and call its controller function with file. Here jsFiddle for it.
var app = angular.module('app', []);
app.controller('yourCtrl', function() {
$scope.image;
$scope.imageChanged= function (files) {
$scope.image = files;
};
});
app.directive('customOnChange', function() {
return {
restrict: 'A',
link: function (scope, element, attrs) {
var onChangeFunc = scope.$eval(attrs.customOnChange);
element.bind('change', function(event){
var files = event.target.files;
onChangeFunc(files);
});
element.bind('click', function(){
element.val('');
});
}
};
})
<input type="file" id="imgInput" custom-on-change="imageChanged"/>
I use a custom directive to get places from Google API. This directive works like a charm in a controller. But when I want to use it inside a modal, it doesn't work any more. It's a question of scope, but I can't figure out what's exactly happened. Any idea ?
My directive :
'use strict';
angular.module('app').directive('googleplace', function() {
return {
require: 'ngModel',
scope: {
ngModel: '=',
details: '=?'
},
link: function(scope, element, attrs, model) {
var options;
options = {
types: ['address'],
componentRestrictions: {}
};
scope.gPlace = new google.maps.places.Autocomplete(element[0], options);
google.maps.event.addListener(scope.gPlace, 'place_changed', function() {
scope.$apply(function() {
scope.details = scope.gPlace.getPlace();
if (scope.details.name) {
element.val(scope.details.name);
model.$setViewValue(scope.details.name);
element.bind('blur', function(value) {
if (value.currentTarget.value !== '') {
element.val(scope.details.name);
}
});
}
});
});
}
};
});
My modal controller :
modalInstance = $modal.open
templateUrl: "modal.html"
controller: ($scope, $modalInstance) ->
$scope.$watch 'placeDetails', ->
_.forEach $scope.placeDetails.address_components, (val, key) ->
$scope.myaddress = val.short_name + ' ' if val.types[0] is 'street_number'
return
And finally, my html :
<div class="modal-body">
<div>
<input type="text" placeholder="Start typing" ng-model="address" details="placeDetails" googleplace />
</div>
<div>
<input type="text" ng-model="myaddress">
</div>
</div>
I should have the ng-model="address" populated with the result of the call to Google Place API, and the ng-model="myaddress" populated by the $watch, but nothing happens.
Here is my plunkr http://plnkr.co/edit/iEAooKgfUUfxoBWm8mgw?p=preview
Click on "Open modal" causes the error : Cannot read property 'address_components' of undefined
working demo
According to how to create modal in angularjs
Extra things that i added :
1 : New Controller for modal
2 : Blur function that fires on property change Instead of $watch
I've got a directive which defines a input field of type="file", which I can print and is not empty, namely:
<form class="form-horizontal" role="form">
<input type="file" file-model="myFile"/>
{{myFile}} <-- this prints fine
<button type="submit" class="btn btn-success" ng-click="saveData()">Post</button>
</form>
which I can see if called in the view
app.js
.directive('fileModel', ['$parse', function ($parse) {
return {
restrict: 'A',
link: function(scope, element, attrs) {
var model = $parse(attrs.fileModel);
var modelSetter = model.assign;
element.bind('change', function(){
scope.$apply(function(){
modelSetter(scope, element[0].files[0]);
});
});
}
};
}]);
What I am trying to do now is access the field inside my controller:
.controller('Ctrl', function($scope, fileUpload) {
...
$scope.myFile; <-- initialise it
$scope.saveData = function() {
var file = $scope.myFile;
console.log(file); <-- prints out as undefined
}
.service('fileUpload', ['$http', function ($http) {
this.uploadFileToUrl = function(file, uploadUrl){
var fd = new FormData();
fd.append('file', file);
$http.post(uploadUrl, fd, {
transformRequest: angular.identity,
headers: {'Content-Type': undefined}
})
.success(function(){
})
.error(function(){
});
}
}]);
But file is undefined.
Any ideas why that would happen and how to access the value of the field?
If you want to bring in attribute values to your directive, I recommend doing it like so.
.directive('myDirective', ['$parse', function ($parse) {
return {
restrict: 'A',
scope: {
fileModel: '=fileModel'
}
link: function(scope, element, attrs) {
var model = scope.fileModel;
var modelSetter = model.assign;
element.bind('change', function(){
scope.$apply(function(){
modelSetter(scope, element[0].files[0]);
});
});
}
};
}]);
Note I changed your directive name since you already had an attribute with that name.
<input type="file" my-directive file-model="myFile"/>
I'm not sure what you are trying to do after you have the attribute value, but if you console.log(scope.fileModel) you can see what built in options are available. This is an example of isolated scope within directives.
Update with controller access
To access within your controller, you could emit the value from your directive:
scope.$emit('myFile', scope.fileModel);
and then listen for the event in your controller:
$scope.$on('myFile', function (event, myFile) {
$scope.myFile = myFile;
};
Update with working fiddle
http://jsfiddle.net/jonesmac82/376SS/26/
I am new to angular. I am trying to read the uploaded file path from HTML 'file' field whenever a 'change' happens on this field. If i use 'onChange' it works but when i use it angular way using 'ng-change' it doesn't work.
<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() is never calling. Firebug also doesn't show any error.
I made a small directive to listen for file input changes.
View 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();
});
}
};
});
No binding support for File Upload control
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>
instead of
<input type="file" style="display:none"
id="file" name='file' ng-Change="fileNameChanged()" />
can you try
<input type="file" style="display:none"
id="file" name='file' onchange="angular.element(this).scope().fileNameChanged()" />
Note: this requires the angular application to always be in debug mode. This will not work in production code if debug mode is disabled.
and in your function changes
instead of
$scope.fileNameChanged = function() {
alert("select file");
}
can you try
$scope.fileNameChanged = function() {
console.log("select file");
}
Below is one working example of file upload with drag drop file upload may be helpful
http://jsfiddle.net/danielzen/utp7j/
Angular File Upload Information
URL for AngularJS File Upload in ASP.Net
https://github.com/geersch/AngularJSFileUpload
AngularJs native multi-file upload with progress with NodeJS
http://jasonturim.wordpress.com/2013/09/12/angularjs-native-multi-file-upload-with-progress/
ngUpload - An AngularJS Service for uploading files using iframe
http://ngmodules.org/modules/ngUpload
This is a refinement of some of the other ones around, the data will end up in an ng-model, which is normally what you want.
Markup (just make an attribute data-file so the directive can find it)
<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();
});
}
};
});
The clean way is to write your own directive to bind to "change" event.
Just to let you know IE9 does not support FormData so you cannot really get the file object from the change event.
You can use ng-file-upload library which already supports IE with FileAPI polyfill and simplify the posting the file to the server. It uses a directive to achieve this.
<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);
});
}
}
}];
I've expanded on #Stuart Axon's idea to add two-way binding for the file input (i.e. allow resetting the input by resetting the model value back to null):
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("");
}
});
}
};
}]);
Demo
Similar to some of the other good answers here, I wrote a directive to solve this problem, but this implementation more closely mirrors the angular way of attaching events.
You can use the directive like this:
HTML
<input type="file" file-change="yourHandler($event, files)" />
As you can see, you can inject the files selected into your event handler, as you would inject an $event object into any ng event handler.
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);
}
};
}]);
This directive pass the selected files as well:
/**
*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);
}
});
});
}
};
});
The HTML, how to use it:
<input type="file" ng-model="file" on-file-change="onFilesSelected">
In my controller:
$scope.onFilesSelected = function(files) {
console.log("files - " + files);
};
I recommend to create a directive
<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});
});
});
}
};
}]);
this directive can be used many times, it uses its own scope and doesn't depend on parent scope. You can also give some params to handler function. Handler function will be called with scope object, that was active when you changed the input.
$apply updates your model each time the change event is called
The simplest Angular jqLite version.
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()">
Working Demo of "files-input" Directive that Works with ng-change1
To make an <input type=file> element work the ng-change directive, it needs a custom directive that works with the ng-model directive.
<input type="file" files-input ng-model="fileList"
ng-change="onInputChange()" multiple />
The 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>
Too complete solution base on:
`onchange="angular.element(this).scope().UpLoadFile(this.files)"`
A simple way to hide the input field and replace it with a image, here after a solution, that also require a hack on angular but that do the job [TriggerEvent does not work as expected]
The solution:
place the input-field in display:none [the input field exist in the DOM but is not visible]
place your image right after
On the image use nb-click() to activate a method
When the image is clicked simulate a DOM action 'click' on the input field. 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!!!
};
Another interesting way to listen to file input changes is with a watch over the ng-model attribute of the input file. Of course, FileModel is a custom directive.
Like this:
HTML -> <input type="file" file-model="change.fnEvidence">
JS Code ->
$scope.$watch('change.fnEvidence', function() {
alert("has changed");
});
Hope it can help someone.
I have done it like this;
<!-- 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");
}
Angular elements (such as the root element of a directive) are jQuery [Lite] objects. This means we can register the event listener like so:
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)
}
This is jQuery event delegation. Here, the listener is attached to the root element of the directive. When the event is triggered, it will bubble up to the registered element and jQuery will determine if the event originated on an inner element matching the defined selector. If it does, the handler will fire.
Benefits of this method are:
the handler is bound to the $element which will be automatically cleaned up when the directive scope is destroyed.
no code in the template
will work even if the target delegate (input) has not yet been rendered when you register the event handler (such as when using ng-if or ng-switch)
http://api.jquery.com/on/
You can simply add the below code in onchange and it will detect change. you can write a function on X click or something to remove file data..
document.getElementById(id).value = "";
In AngularJS, I'm using the approach described here to handle input type=file.
https://groups.google.com/forum/?fromgroups=#!topic/angular/-OpgmLjFR_U
http://jsfiddle.net/marcenuc/ADukg/89/
Markup:
<div ng-controller="MyCtrl">
<input type="file" onchange="angular.element(this).scope().setFile(this)">
{{theFile.name}}
</div>
Controller:
var myApp = angular.module('myApp', []);
myApp.controller('MyCtrl', function($scope) {
$scope.setFile = function(element) {
$scope.$apply(function($scope) {
$scope.theFile = element.files[0];
});
};
});
As mentioned it's a bit of a hack, but it mostly works for my purposes. What I need however is a way to clear the file input after the upload has finished - ie: from the controller.
I could completely hack it and use jQuery or something to find the input element and clear it, but was hoping for something a little more elegant.
Upon a successful upload, I clear up the input type file elements explicitly from my controller, like so:
angular.forEach(
angular.element("input[type='file']"),
function(inputElem) {
angular.element(inputElem).val(null);
});
The input[type='file'] selector requires jQuery, but everything else is plain Angular.
I would definitely use directive for this kind of task.
http://plnkr.co/edit/xLM9VX
app.directive('fileSelect', function() {
var template = '<input type="file" name="files"/>';
return function( scope, elem, attrs ) {
var selector = $( template );
elem.append(selector);
selector.bind('change', function( event ) {
scope.$apply(function() {
scope[ attrs.fileSelect ] = event.originalEvent.target.files;
});
});
scope.$watch(attrs.fileSelect, function(file) {
selector.val(file);
});
};
});
note: it is using jquery for element creation.
my solution without using $scope.
app.directive('fileChange',['UploadService',function (UploadService) {
var linker = function (element, attrs) {
element.bind('change', function (event) {
var files = event.target.files;
UploadService.upload({'name':attrs['name'],'file':files[0]});
element.val(null); // clear input
});
};
return {
restrict: 'A',
link: linker
};
}]);
It might help you!!
HTML code sample
<input type="file" id="fileMobile" file-model="myFile">
<button type="button" class="btn btn-danger" id="i-agree" ng-click="uploadFile()"> Upload </button>
AngularJs code sample
$scope.uploadFile = function () {
var file = $scope.myFile;
mobileService.uploadBulkFile(file).then(function (resp) {
if (resp !== undefined) {
$('#fileMobile').val('');
}
});
};
You can use ID to reset file field.
<div class="col-md-8">
<label for="files">Select File</label>
<input type="file" id="file_upload" class="form-control">
</div>
After uploading clear it.
var fileElement = angular.element('#file_upload');
angular.element(fileElement).val(null);
Above example working good for me. Will work for you too.
In my case, I broadcast events when a file upload succeeds.
So my directive watches for the broadcast, and clears the selection.
app.directive("fileInput", function( APP_EVENTS ){
return{
require: "ngModel",
link: function postLink( scope, elem, attrs, ngModel ){
elem.on("change", function( e ){
var files=elem[0].files;
ngModel.$setViewValue( files );
});
scope.$on( APP_EVENTS.FILE_UPLOAD_SUCCESS, function( event ){
elem.val( null );
});
}
}
});
It's used like so:
<input type="file" name="myFieldName" ng-model="myModel" file-input/>