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]);
});
}
};
}
Related
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;
}); }
}
I'm writing an AngularJS application and I'm searching for a way to unit test every single aspect.
In this particular case, I need to unit test a custom directive which I've written that represents a control.
The directive can be found here:
var officeButton = angular.module('OfficeButton', []);
officeButton.directive('officeButton', function() {
return {
restrict: 'E',
replace: false,
scope: {
isDefault: '#',
isDisabled: '#',
control: '=',
label: '#'
},
template: '<div class="button-wrapper" data-ng-click="onClick()">' +
'<a href="#" class="button normal-button">' +
'<span>{{label}}</span>' +
'</a>' +
'</div>',
controller: ['$scope', function($scope) {
var event = this;
var api = {
changeLabel: function(label) {
$scope.label = label;
},
enable: function() {
$scope.isDisabled = false;
},
disable: function() {
$scope.isDisabled = true;
},
setAsDefault: function() {
$scope.isDefault = true;
},
removeDefault: function() {
$scope.isDefault = false;
}
};
event.onClick = function() {
if (typeof $scope.control.onClick === 'function') { $scope.control.onClick(); }
};
$.extend($scope.control, api);
function Init() {
if ($scope.isDefault === 'true') { $scope.isDefault = true; }
else { $scope.isDefault = false; }
}
Init();
}],
link: function(scope, element, attributes, controller) {
scope.$watch('isDefault', function(value) {
if (value === 'true' || value) { $('a', element).addClass('button-default'); }
else { $('a', element).removeClass('button-default'); }
});
scope.onClick = function() { controller.onClick(); }
}
}
});
This directive can be called by using the following HTML snippet:
<office-button label="Office Web Controls" control="buttonController"></office-button>
Now, this directive exposes an API which functions such as changeLabel, enable, disable, ....
Now, those functions are not defined on the load of the application, meaning if at the bottom of my HTML I call the following code:
$scope.buttonController.changeLabel('Office Web Controls for Web Applications Directive Demo');
It will throw an error because the changeLabel() method is not defined.
In order to make it function, I need to wrap those calls in an angular.ready function, such as:
angular.element(document).ready(function () {
$scope.buttonController.changeLabel('Office Web Controls for Web Applications Directive Demo');
});
Here's a plunker for your information.
Now, I'm writing unit tests using Jasmine, and here's what I have for the moment:
describe('Office Web Controls for Web Applications - Button Testing.', function() {
// Provides all the required variables to perform Unit Testing against the 'button' directive.
var $scope, element;
var buttonController = {};
// Loads the directive 'OfficeButton' before every test is being executed.
beforeEach(module('OfficeButton'));
// Build the element so that it can be called.
beforeEach(inject(function($rootScope, $compile) {
// Sets the $scope variable so that it can be used in the future.
$scope = $rootScope;
$scope.control = buttonController;
element = angular.element('<office-button control="buttonController"></office-button>');
$compile(element)($scope);
$scope.$digest();
}));
it('Should expose an API with certain functions.', function() {
});
});
Now, in the it function, I would like to test if the $scope.control does expose the API as defined in the directive.
The problem is that the page needs to be ready before the API is available.
Any tought on how to change the code or how to unit test this correctly?
I've found the issue, it was just a wrong configuration on the unit test.
When using this code:
$scope.control = buttonController;
element = angular.element('<office-button control="buttonController"></office-button>');
I must change the element to:
$scope.control = buttonController;
element = angular.element('<office-button control="control"></office-button>');
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.
I got any application where I need to display file from urls I got in database. Now this file can be an image and it can be a pdf. So I need to set some binding dynamically. I looked on internet and object tag looked promising but it is not working in IE11. It is working fine in Chrome and Firefox. SO that is why I am asking here for help.
I have created a directive just to make sure If we have to do any dom manipulation. Here goes my directive code.
mainApp.directive("displayFile", function () {
return {
restrict: 'AE', // only activate on element attribute
scope: {
displayFile: "=",
fileType:"="
},
link: function (scope, elem, attrs) {
scope.filePath = "";
var element = angular.element(elem);
// observe the other value and re-validate on change
scope.$watch('displayFile', function (val) {
if (val !== "") {
scope.filePath = val;
scope.type="application/"+ fileType;
//element.attr("data", scope.filePath)
}
});
},
template: '<object data="{{filePath}}" type="{{type}}">'
}
});
My html for directive
<div data-display-pdf="fileUrl" file-type="type"></div>
Attaching an image also for IE and Chrome/FF output
Above image is a comparison between IE and FF
Final cut of directive which is working on IE11, Chrome and Firefox
use it like
<div data-display-file="fileObject"></div>
where fileObject is like
$scope.fileObject = {
fileUrl: "",
type: ""
}
mainApp.directive("displayFile", function () {
var updateElem = function (element) {
return function (displayFile) {
element.empty();
var objectElem = {}
if (displayFile && displayFile.type !== "") {
if (displayFile.type === "pdf") {
objectElem = angular.element(document.createElement("object"));
objectElem.attr("data", displayFile.fileUrl);
objectElem.attr("type", "application/pdf");
}
else {
objectElem = angular.element(document.createElement("img"));
objectElem.attr("src", displayFile.fileUrl);
}
}
element.append(objectElem);
};
};
return {
restrict: "EA",
scope: {
displayFile: "="
},
link: function (scope, element) {
scope.$watch("displayFile", updateElem (element));
}
};
});
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