How to read a file in AngularJS? - javascript

Is it possible to read files in AngularJS? I want to place the file into an HTML5 canvas to crop.
I was thinking of using a directive? This is the javascript code I want to put into my directive:
function readURL(input) {
if (input.files && input.files[0]) {
var reader = new FileReader();
reader.onload = function (e) {
$('#blah').attr('src', e.target.result);
}
reader.readAsDataURL(input.files[0]);
}
}

Yes, directives is a right way, but it looks little bit different:
.directive("ngFileSelect",function(){
return {
link: function($scope,el){
el.bind("change", function(e){
$scope.file = (e.srcElement || e.target).files[0];
$scope.getFile();
});
}
}
})
Working example: http://plnkr.co/edit/y5n16v?p=preview
Thanks to lalalalalmbda for this link.

I've taken Cherniv's code and folded all of it into a directive:
.directive('fileSelect', ['$window', function ($window) {
return {
restrict: 'A',
require: 'ngModel',
link: function (scope, el, attr, ctrl) {
var fileReader = new $window.FileReader();
fileReader.onload = function () {
ctrl.$setViewValue(fileReader.result);
if ('fileLoaded' in attr) {
scope.$eval(attr['fileLoaded']);
}
};
fileReader.onprogress = function (event) {
if ('fileProgress' in attr) {
scope.$eval(attr['fileProgress'],
{'$total': event.total, '$loaded': event.loaded});
}
};
fileReader.onerror = function () {
if ('fileError' in attr) {
scope.$eval(attr['fileError'],
{'$error': fileReader.error});
}
};
var fileType = attr['fileSelect'];
el.bind('change', function (e) {
var fileName = e.target.files[0];
if (fileType === '' || fileType === 'text') {
fileReader.readAsText(fileName);
} else if (fileType === 'data') {
fileReader.readAsDataURL(fileName);
}
});
}
};
}]);
You can then use the directive as follows:
<input type="file" ng-model="file.data"
file-select="data"
file-loaded="myLoaded()"
file-error="myError($error)"
file-progress="myProgress($total,$loaded)">
Where "file.data", "myLoaded", "myError", and "myProgress" are in the enclosing scope.

Angular File reader.
link: function(scope, element, attrs) {
element.on('change', function(e) {
var reader = new FileReader();
reader.onload = function(e) {
scope.$apply(function() {
scope.onReadFile({$content:e.target.result});
});
};
reader.readAsText((e.srcElement || e.target).files[0]);
});
}
Live example : Live Run On Plunker

Related

Directives are overwriting each other

I have two directives, one checks the size of the file, while the other one ensures that the user is uploading a valid file format. They are both assigned to a input=file element, and seperate they work, but together, the validFileSize directive seems to cause the selectNgValidationFiles to not work.
//view
<input type="file" name="uploadedDocs" id="uploadedDocs" data-ng-model="fileStore.file" select-ng-validation-files valid-file-size multiple>
//controller
function validFileSize($parse){
return {
require: 'ngModel',
restrict: 'A',
link: function(scope, el, attrs, ngModel) {
var model = $parse(attrs.ngModel);
var modelSetter = model.assign;
var maxSize = 2000; //2000 B
el.bind('change', function() {
scope.$apply(function() {
scope.fileStore.maxSizeError = false;
if (el[0].files.length > 1) {
modelSetter(scope, el[0].files);
} else {
modelSetter(scope, el[0].files[0]);
}
if(el[0].files.length > 0) {
var fileSize = el[0].files[0].size;
if (fileSize === 0) {
scope.fileStore.maxSizeError = true;
}
}
});
});
}
}
}
function selectNgValidationFiles() { //if files to be uploaded vary in future, add condition to check type or create new directive
return {
require: "ngModel",
link: function postLink(scope,elem,attrs,ngModel) {
var validFormats = ['pdf', 'PDF', 'doc', 'DOC', 'docx', 'DOCX', 'jpg', 'JPG', 'jpeg', 'JPEG','png', 'PNG', 'gif', 'GIF', 'pptx', 'PPTX', 'csv', 'CSV', 'xlsx', 'XLSX', 'zip', 'ZIP'];
elem.bind('change', function () {
validFile(false);
scope.$apply(function () {
ngModel.$render();
});
});
ngModel.$render = function () {
console.log('elem : ',elem)
ngModel.$setViewValue(elem[0].files[0]);
};
function validFile(bool) {
ngModel.$setValidity('pdfIncorrect', bool);
}
ngModel.$parsers.push(function(value) {
var ext = value.name.substr(value.name.lastIndexOf('.')+1);
if(ext=='') return;
if(validFormats.indexOf(ext) == -1){
console.log('not valid format')
return value;
}
validFile(true);
return value;
});
}
}
};
question
What is causing my directives to break one another? I assume one is overwriting the other somehow but I can't work out how to fix it.
Could you not merge them into one directive or one service function?
Check file format first, if ok then proceed with file size check.
It's probably because you are now calling the $scope apply function twice (one in each directive), which generally does not work well.

Passing data between directives and controller in Angular

I'm just working on a projet in Angular 1. The task is to do a DOM tree from json object and manipulate with it through native drag'n'drop DOM operations (with out jQuery and etc). I've been done parsing json-> dom function, loading json from server, some drag'n'drop event handlers. Now my question is how to update a json object (from this object I get a ul>li tree structure) when I done a DROP event.
Now, code
View
<div ng-controller="TreeController">
<ng-dom-tree
style="background-color: #000"
ng-model = "treeJson"
class="tree"
ng-draggable
ng-droppable>
</ng-dom-tree>
</div>
Controller
.controller('TreeController', TreeController);
TreeController.$inject = ['treeService', '$scope'];
function TreeController(treeService, $scope) {
$scope.treeJson = '';
treeService.getTree().success(function (data) {
$scope.treeJson = data;
});
}
Main directive
.directive('ngDomTree', ngDomTree)
ngDomTree.$inject = [];
function ngDomTree() {
var isEmpty = function (object) {
for (var key in object) {
return false;
}
return true;
};
function createTree(tree, list) { /*creating tree -> json to dom*/}
return {
restrict: 'E',
replace: true,
link: function (scope, elt, attrs) {
scope.$watch('treeJson', function (data) {
if (isEmpty(data))
return;
**CAN'T HANDLE DATA CHANGING HERE**
elt.append(document.createElement('ul'));
createTree(data, document.getElementsByTagName('ul')[0]);
});
}
}
}
Sup directive
.directive('ngDroppable', ngDroppable)
ngDroppable.$inject = [];
function ngDroppable() {
var parseTreeToJson = function(tree){/* dom to json */}
return {
restrict: 'A',
link: function (scope, elt, attrs) {
elt.on('mouseover', function (e) {
var droppableElt = e.target || event.target;
if (!droppableElt.classList.contains('tree__node') && !droppableElt.classList.contains('tree__branch'))
return;
droppableElt.addEventListener(
'dragover',
function (e) {
this.classList.add('navigator');
e.dataTransfer.dropEffect = 'move';
if (e.preventDefault)
e.preventDefault();
this.classList.add('over');
return false;
},
false
);
droppableElt.addEventListener(
'dragenter',
function (e) {
this.classList.add('over');
return false;
},
false
);
droppableElt.addEventListener(
'dragleave',
function (e) {
this.classList.remove('over');
this.classList.remove('navigator');
return false;
},
false
);
droppableElt.addEventListener(
'drop',
function (e) {
if (e.stopPropagation) e.stopPropagation();
this.classList.remove('over');
let item = document.getElementById(e.dataTransfer.getData('Text'));
this.appendChild(item);
item.id = '';
//updating here
scope.treeJson = parseTreeToJson(elt[0].children[0]);
return false;
},
false
);
});
}
}
}
So, In Sup directive when drop event created, and I'm reinit treeJson variable, after that I need in main directive reinitializing the tree and also in controller get new json structure from this variable, because $watch is used, but it isn't happened.
PLEASE HELP
THNKS FOR ATTENTION :)
P.S. Here it is in Plnkr.co
Since you are using native DOM, it bypasses angular's processors. You need to call scope.$digest() after changing angular's state to tell it that something changed.
droppableElt.addEventListener(
'drop',
function (e) {
if (e.stopPropagation) e.stopPropagation();
this.classList.remove('over');
let item = document.getElementById(e.dataTransfer.getData('Text'));
this.appendChild(item);
item.id = '';
scope.treeJson = parseTreeToDOM(elt[0].children[0]);
scope.$digest();
},
false
);

AngularJS Image Preview before Upload showing previous image selected

myApp.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(e) {
$scope.$apply(function(e) {
modelSetter($scope, element[0].files[0]);
});
$scope.setimage();
});
}
The above code is my directive.
In the function $scope.setimage()
$scope.setimage = function() {
var file = $scope.myFile;
var reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = function(e) {
$scope.ImageSrc = e.target.result;
}
}
Whenever i choose the file,
<img width= 250 height=350 ng-src="{{ImageSrc}}"/>
<input type=`enter code here`"file" file-model="myFile"/>
Immediately the preview is not appearing. When i choose the second time, the preview for the previously selected image file is appearing. Not sure what is wrong.
Instead of element.bind('change', ..., you can use $scope.$watch on myFile, like so:
$scope.$watch('$scope.myFile', function(e) {
$scope.$apply(function(e) {
modelSetter($scope, element[0].files[0]);
});
$scope.setimage();
});
Note that $scope.myFile needs to be defined for this to occur, so if it's not defined in your directive you'll need to add this to your controller instead.
I added $scope.$apply for the imagesrc assignment and the image updated immediately.
$scope.setimage = function() {
var file = $scope.myFile;
var reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = function(e) {
$scope.$apply(function(){
$scope.ImageSrc = e.target.result;
}); }
}

FileReader() is not defined in Angularjs Directive

I am working on file upload functionality on UI. Basically, on my UI page, I need to read file contents and display when the user selects local file from his/her machine. For this, I googled and I found this http://plnkr.co/edit/eeQbR65oE8iljm7ueGhX?p=preview example. It is working fine. but I used in my application, its not working and giving me error that"FileReader" is not defined.
Here is my example:
function onReadFile($parse) {
return {
restrict: 'A',
scope: false,
link: function(scope, element, attrs) {
var fn = $parse(attrs.onReadFile);
element.on('change', function(onChangeEvent) {
var reader = new FileReader();
reader.onload = function(onLoadEvent) {
scope.$apply(function() {
fn(scope, {$fileContent:onLoadEvent.target.result});
});
};
reader.readAsText((onChangeEvent.srcElement || onChangeEvent.target).files[0]);
});
}
};
}

img ng-src : TypeError: Converting circular structure to JSON

I have to get a picture in a modal just after its upload. So I'm opening a modal ($scope.openResizeModal()), and putting my picture in my scope.
uploads.client.controller.js
$scope.openResizeModal = function (flow) {
$scope.pictureToCrop = {};
$scope.pictureToCrop = flow.files[0];
}
Then I try to print in on my HTML view :
resize-modal.client.view.html
<img-cropped src="{{pictureToCrop}}" selected='selected(cords)'></img-cropped>
And AngularJS tells :
Error: [$interpolate:interr] Can't interpolate: {{pictureToCrop}}
TypeError: Converting circular structure to JSON
I even tried with a classic
<img src={{pictureToCrop}} />
But it's the same.
Here is my directive :
angular.module('uploads').directive('imgCropped', function () {
return {
restrict: 'E',
replace: true,
scope: { src: '#', selected: '&' },
link: function (scope, element, attr) {
var myImg;
var clear = function () {
if (myImg) {
myImg.next().remove();
myImg.remove();
myImg = undefined;
}
};
scope.$watch('src', function (nv) {
clear();
if (nv) {
element.after('<img />');
myImg = element.next();
myImg.attr('src', nv);
$(myImg).Jcrop({
trackDocument: true,
onSelect: function (x) {
scope.$apply(function () {
scope.selected({ cords: x });
});
},
aspectRatio: 1/1.4
}, function () {
// Use the API to get the real image size
var bounds = this.getBounds();
pwGlobal.boundx = bounds[0];
pwGlobal.boundy = bounds[1];
});
}
});
scope.$on('$destroy', clear);
}
};
});
Thanks all !
It was a flow internal problem :
var fileReader = new FileReader();
fileReader.readAsDataURL(flow.files[0].file);
fileReader.onload = function (event) {
$scope.pictureToCrop = event.target.result;
};
Solved my problem. The image is converted in base64 encode.

Categories