Binding Directive attribute to Controller not working - angularjs

I am currently stuck in a situation and the scenario is I want to bind the directive to my HTML but nothing works I want to bind directive scope with HTML
this is what I am doing:
this is my directive what I want is to bind the attribute file to my HTML so I can get the name of file and modified date
.directive("fileinput", [function() {
return {
scope: {
file: "=",
filepreview: "="
},
link: function(scope, element, attributes) {
element.bind("change", function(changeEvent) {
scope.file = changeEvent.target.files[0];
scope.filepreview='';
var reader = new FileReader();
reader.onload = function(loadEvent) {
scope.$apply(function() {
console.log(scope.file)//printing file value but not reflect in html
scope.filepreview = loadEvent.target.result;
var ctx = document.getElementById('myCanvas').getContext('2d');
ctx.clearRect(0, 0, 300, 130);
var img = new Image();
img.src = scope.filepreview ;
img.onload = function() {
ctx.drawImage(img, 0, 0);
}
});
}
reader.readAsDataURL(scope.fileinput);
});
}
}
}])
index.html
<div class='form-group required'>
<label>{{file.LastModified}}</label>
<input type="file" fileinput="file" filepreview="filepreview"/>
</div>
Nothing happens when I upload file but in directive, I am getting file
any help will be appreciated.

your directive scope has "file" object but you are using "fileInput" to get the file , Can you try below code
<input type="file" fileinput file="file" filepreview="filepreview"/>

Related

AngularJS simple image preview on file input

Can someone tell me why this code does not work?
this my preview area: <div class="preview"><img src="" alt=""></div>
and this my input:<input type="file" file-input> and follow my directive code:
var cmos = angular.module('cmos', ['simditor']);
// controller
cmos.controller('cmosCtrl', function( $scope ){}
// directive
cmos.directive("fileInput", function( $parse ){
return{
link: function($scope, element, attrs){
element.on('change', function(event){
var files = event.target.files;
var reader = new FileReader();
var img = document.querySelector(".preview > img");
reader.addEventListener("load", function(){
img.src = reader.result;
}, false);
if(files){
reader.readAsDataURL(files[0]);
}
// console.log(files[0]);
});
}
}
});
When I insert an image on input would like to take an image and show it to preview area.
you can use $scope like this..
<div class="preview"><img src="{{imageUrl}}" alt=""></div>
and in js.
var files = event.target.files;
var reader = new FileReader();
reader.addEventListener("load", function(){
$scope.imageUrl = reader.result;
}, false);
if(files){
reader.readAsDataURL(files[0]);
}

Angularjs: any directive to convert a file XLSX, XLS to Array [object] ou other best practice?

I would like to know the best way to import a Excel file in my HTML, with Angular and read it data. Any ideias?
I did this but from .CSV to array object, it worked perfect with a angular directive. Is there anything to Excel files?
This is the directive:
// CSV -> Angularjs Object
MyApp.directive('fileReader', function () {
return {
scope: {
fileReader: "="
},
link: function (scope, element) {
$(element).on('change', function (changeEvent) {
var files = changeEvent.target.files;
if (files.length) {
var r = new FileReader();
r.onload = function (e) {
var contents = e.target.result;
scope.$apply(function () {
scope.fileReader = contents;
});
};
r.readAsText(files[0]);
}
});
}
};
});
HTML:
<input type="file" file-reader="fileContent" />
ANGULAR:
$scope.fileContent;
This can help : https://github.com/brexis/angular-js-xlsx
But it didn't get my number, just letters or letters+numbers.

Image loading issue in custom directive

I want the image URL received from server side in my custom directive.
The directive is used to create a canvas.
Seems the directive is loaded and the image URL is undefined. As it takes the time to get the URL from the server side.
Or maybe how did I get $rootScope data in my directive link function.
Edit:
The following is the directive:
app.directive('logocanvasdirective',['$rootScope','$templateRequest','$compile', function($rootScope,$templateRequest,$compile) {
return {
template: "<canvas id='logo' width='500' height='500'/>",
scope: true,
link: function($scope, element, attrs) {
var canvas1 = document.getElementById('logo'),
context1 = canvas1.getContext('2d');
make_base1();
function make_base1()
{
base_image1 = new Image();
base_image1.src =scope.variable; //How do I use this?
base_image1.onload = function() {
context1.drawImage(base_image1, 0, 0);
}
}
}
};
}]);
I want the image.src = $scope.variable which is receive from server side in my controller.
How do I do that ?
You need to use $watch since you are getting the src from an asynchronous AJAX call:
app.directive('logocanvasdirective',['$rootScope','$templateRequest','$compile', function($rootScope,$templateRequest,$compile) {
return {
template: "<canvas id='logo' width='500' height='500'/>",
scope: {
imgSrc: '='
},
link: function($scope, element, attrs) {
var canvas1 = document.getElementById('logo'),
context1 = canvas1.getContext('2d');
make_base1();
function make_base1()
{
base_image1 = new Image();
base_image1.src = scope.imgSrc;
base_image1.onload = function() {
context1.drawImage(base_image1, 0, 0);
}
}
scope.$watch('imgSrc', function(newValue) {
if (newValue) {
make_base1();
}
});
}
};
}]);
And pass the $scope.variable to your directive:
<logocanvasdirective img-src="variable" />
Or
<div logocanvasdirective img-src="variable"></div>

<input> file is not updating/wroking properly in AngularJs

I am trying to write a directive for <input> type file. This directive will handle file change event and I am trying to return following object as a model.
{ fileName: element[0].files[0].name, fileAsBuffer: e.target.result }
My directive code
"use strict";
(function() {
angular
.module("fileUploadApp")
.directive("customFileChange", customFileChange);
customFileChange.$inject = ["$parse"];
function customFileChange($parse) {
return {
restrict: "A",
link: function(scope, element, attrs) {
var model = $parse(attrs.customFileChange);
var modelSetter = model.assign;
element.bind("change", function() {
scope.$apply(function() {
var reader = new FileReader();
reader.onload = function(e) {
var fileModel = {
fileName: element[0].files[0].name,
fileAsBuffer: e.target.result
};
modelSetter(scope, fileModel);
}
reader.onerror = function(e) {
console.log(e.target.error);
}
reader.readAsArrayBuffer(element[0].files[0]);
});
});
}
};
}
})();
My html
<input type="file" name="name" data-custom-file-change="vm.newDocument.attachment" />
<p>{{vm.newDocument.attachment.fileName}}</p>
Problem:
First time when I upload a file, It is not showing anything in
<p>{{vm.newDocument.attachment.fileName}}</p>
Second time if I upload another file, the It is showing the first
file name in <p>{{vm.newDocument.attachment.fileName}}</p>
JS Bin
You need to trigger the digest cycle from inside the callback function itself.
For example:
reader.onload = function (e) {
var fileModel = { fileName: element[0].files[0].name, fileAsBuffer: e.target.result };
scope.$apply(function () {
modelSetter(scope, fileModel);
});
};
Demo: https://jsbin.com/nicocosige/1/edit?html,js,console,output

ng-model for `<input type="file"/>` (with directive DEMO)

I tried to use ng-model on input tag with type file:
<input type="file" ng-model="vm.uploadme" />
But after selecting a file, in controller, $scope.vm.uploadme is still undefined.
How do I get the selected file in my controller?
I created a workaround with directive:
.directive("fileread", [function () {
return {
scope: {
fileread: "="
},
link: function (scope, element, attributes) {
element.bind("change", function (changeEvent) {
var reader = new FileReader();
reader.onload = function (loadEvent) {
scope.$apply(function () {
scope.fileread = loadEvent.target.result;
});
}
reader.readAsDataURL(changeEvent.target.files[0]);
});
}
}
}]);
And the input tag becomes:
<input type="file" fileread="vm.uploadme" />
Or if just the file definition is needed:
.directive("fileread", [function () {
return {
scope: {
fileread: "="
},
link: function (scope, element, attributes) {
element.bind("change", function (changeEvent) {
scope.$apply(function () {
scope.fileread = changeEvent.target.files[0];
// or all selected files:
// scope.fileread = changeEvent.target.files;
});
});
}
}
}]);
I use this directive:
angular.module('appFilereader', []).directive('appFilereader', function($q) {
var slice = Array.prototype.slice;
return {
restrict: 'A',
require: '?ngModel',
link: function(scope, element, attrs, ngModel) {
if (!ngModel) return;
ngModel.$render = function() {};
element.bind('change', function(e) {
var element = e.target;
$q.all(slice.call(element.files, 0).map(readFile))
.then(function(values) {
if (element.multiple) ngModel.$setViewValue(values);
else ngModel.$setViewValue(values.length ? values[0] : null);
});
function readFile(file) {
var deferred = $q.defer();
var reader = new FileReader();
reader.onload = function(e) {
deferred.resolve(e.target.result);
};
reader.onerror = function(e) {
deferred.reject(e);
};
reader.readAsDataURL(file);
return deferred.promise;
}
}); //change
} //link
}; //return
});
and invoke it like this:
<input type="file" ng-model="editItem._attachments_uri.image" accept="image/*" app-filereader />
The property (editItem.editItem._attachments_uri.image) will be populated with the contents of the file you select as a data-uri (!).
Please do note that this script will not upload anything. It will only populate your model with the contents of your file encoded ad a data-uri (base64).
Check out a working demo here:
http://plnkr.co/CMiHKv2BEidM9SShm9Vv
How to enable <input type="file"> to work with ng-model
Working Demo of Directive that Works with ng-model
The core ng-model directive does not work with <input type="file"> out of the box.
This custom directive enables ng-model and has the added benefit of enabling the ng-change, ng-required, and ng-form directives to work with <input type="file">.
angular.module("app",[]);
angular.module("app").directive("selectNgFiles", function() {
return {
require: "ngModel",
link: function postLink(scope,elem,attrs,ngModel) {
elem.on("change", function(e) {
var files = elem[0].files;
ngModel.$setViewValue(files);
})
}
}
});
<script src="//unpkg.com/angular/angular.js"></script>
<body ng-app="app">
<h1>AngularJS Input `type=file` Demo</h1>
<input type="file" select-ng-files ng-model="fileArray" multiple>
<code><table ng-show="fileArray.length">
<tr><td>Name</td><td>Date</td><td>Size</td><td>Type</td><tr>
<tr ng-repeat="file in fileArray">
<td>{{file.name}}</td>
<td>{{file.lastModified | date : 'MMMdd,yyyy'}}</td>
<td>{{file.size}}</td>
<td>{{file.type}}</td>
</tr>
</table></code>
</body>
This is an addendum to #endy-tjahjono's solution.
I ended up not being able to get the value of uploadme from the scope. Even though uploadme in the HTML was visibly updated by the directive, I could still not access its value by $scope.uploadme. I was able to set its value from the scope, though. Mysterious, right..?
As it turned out, a child scope was created by the directive, and the child scope had its own uploadme.
The solution was to use an object rather than a primitive to hold the value of uploadme.
In the controller I have:
$scope.uploadme = {};
$scope.uploadme.src = "";
and in the HTML:
<input type="file" fileread="uploadme.src"/>
<input type="text" ng-model="uploadme.src"/>
There are no changes to the directive.
Now, it all works like expected. I can grab the value of uploadme.src from my controller using $scope.uploadme.
I create a directive and registered on bower.
This lib will help you modeling input file, not only return file data but also file dataurl or base 64.
{
"lastModified": 1438583972000,
"lastModifiedDate": "2015-08-03T06:39:32.000Z",
"name": "gitignore_global.txt",
"size": 236,
"type": "text/plain",
"data": "data:text/plain;base64,DQojaWdub3JlIHRodW1ibmFpbHMgY3JlYXRlZCBieSB3aW5kb3dz…xoDQoqLmJhaw0KKi5jYWNoZQ0KKi5pbGsNCioubG9nDQoqLmRsbA0KKi5saWINCiouc2JyDQo="
}
https://github.com/mistralworks/ng-file-model/
This is a slightly modified version that lets you specify the name of the attribute in the scope, just as you would do with ng-model, usage:
<myUpload key="file"></myUpload>
Directive:
.directive('myUpload', function() {
return {
link: function postLink(scope, element, attrs) {
element.find("input").bind("change", function(changeEvent) {
var reader = new FileReader();
reader.onload = function(loadEvent) {
scope.$apply(function() {
scope[attrs.key] = loadEvent.target.result;
});
}
if (typeof(changeEvent.target.files[0]) === 'object') {
reader.readAsDataURL(changeEvent.target.files[0]);
};
});
},
controller: 'FileUploadCtrl',
template:
'<span class="btn btn-success fileinput-button">' +
'<i class="glyphicon glyphicon-plus"></i>' +
'<span>Replace Image</span>' +
'<input type="file" accept="image/*" name="files[]" multiple="">' +
'</span>',
restrict: 'E'
};
});
For multiple files input using lodash or underscore:
.directive("fileread", [function () {
return {
scope: {
fileread: "="
},
link: function (scope, element, attributes) {
element.bind("change", function (changeEvent) {
return _.map(changeEvent.target.files, function(file){
scope.fileread = [];
var reader = new FileReader();
reader.onload = function (loadEvent) {
scope.$apply(function () {
scope.fileread.push(loadEvent.target.result);
});
}
reader.readAsDataURL(file);
});
});
}
}
}]);
function filesModelDirective(){
return {
controller: function($parse, $element, $attrs, $scope){
var exp = $parse($attrs.filesModel);
$element.on('change', function(){
exp.assign($scope, this.files[0]);
$scope.$apply();
});
}
};
}
app.directive('filesModel', filesModelDirective);
I had to do same on multiple input, so i updated #Endy Tjahjono method.
It returns an array containing all readed files.
.directive("fileread", function () {
return {
scope: {
fileread: "="
},
link: function (scope, element, attributes) {
element.bind("change", function (changeEvent) {
var readers = [] ,
files = changeEvent.target.files ,
datas = [] ;
for ( var i = 0 ; i < files.length ; i++ ) {
readers[ i ] = new FileReader();
readers[ i ].onload = function (loadEvent) {
datas.push( loadEvent.target.result );
if ( datas.length === files.length ){
scope.$apply(function () {
scope.fileread = datas;
});
}
}
readers[ i ].readAsDataURL( files[i] );
}
});
}
}
});
I had to modify Endy's directive so that I can get Last Modified, lastModifiedDate, name, size, type, and data as well as be able to get an array of files. For those of you that needed these extra features, here you go.
UPDATE:
I found a bug where if you select the file(s) and then go to select again but cancel instead, the files are never deselected like it appears. So I updated my code to fix that.
.directive("fileread", function () {
return {
scope: {
fileread: "="
},
link: function (scope, element, attributes) {
element.bind("change", function (changeEvent) {
var readers = [] ,
files = changeEvent.target.files ,
datas = [] ;
if(!files.length){
scope.$apply(function () {
scope.fileread = [];
});
return;
}
for ( var i = 0 ; i < files.length ; i++ ) {
readers[ i ] = new FileReader();
readers[ i ].index = i;
readers[ i ].onload = function (loadEvent) {
var index = loadEvent.target.index;
datas.push({
lastModified: files[index].lastModified,
lastModifiedDate: files[index].lastModifiedDate,
name: files[index].name,
size: files[index].size,
type: files[index].type,
data: loadEvent.target.result
});
if ( datas.length === files.length ){
scope.$apply(function () {
scope.fileread = datas;
});
}
};
readers[ i ].readAsDataURL( files[i] );
}
});
}
}
});
If you want something a little more elegant/integrated, you can use a decorator to extend the input directive with support for type=file. The main caveat to keep in mind is that this method will not work in IE9 since IE9 didn't implement the File API. Using JavaScript to upload binary data regardless of type via XHR is simply not possible natively in IE9 or earlier (use of ActiveXObject to access the local filesystem doesn't count as using ActiveX is just asking for security troubles).
This exact method also requires AngularJS 1.4.x or later, but you may be able to adapt this to use $provide.decorator rather than angular.Module.decorator - I wrote this gist to demonstrate how to do it while conforming to John Papa's AngularJS style guide:
(function() {
'use strict';
/**
* #ngdoc input
* #name input[file]
*
* #description
* Adds very basic support for ngModel to `input[type=file]` fields.
*
* Requires AngularJS 1.4.x or later. Does not support Internet Explorer 9 - the browser's
* implementation of `HTMLInputElement` must have a `files` property for file inputs.
*
* #param {string} ngModel
* Assignable AngularJS expression to data-bind to. The data-bound object will be an instance
* of {#link https://developer.mozilla.org/en-US/docs/Web/API/FileList `FileList`}.
* #param {string=} name Property name of the form under which the control is published.
* #param {string=} ngChange
* AngularJS expression to be executed when input changes due to user interaction with the
* input element.
*/
angular
.module('yourModuleNameHere')
.decorator('inputDirective', myInputFileDecorator);
myInputFileDecorator.$inject = ['$delegate', '$browser', '$sniffer', '$filter', '$parse'];
function myInputFileDecorator($delegate, $browser, $sniffer, $filter, $parse) {
var inputDirective = $delegate[0],
preLink = inputDirective.link.pre;
inputDirective.link.pre = function (scope, element, attr, ctrl) {
if (ctrl[0]) {
if (angular.lowercase(attr.type) === 'file') {
fileInputType(
scope, element, attr, ctrl[0], $sniffer, $browser, $filter, $parse);
} else {
preLink.apply(this, arguments);
}
}
};
return $delegate;
}
function fileInputType(scope, element, attr, ctrl, $sniffer, $browser, $filter, $parse) {
element.on('change', function (ev) {
if (angular.isDefined(element[0].files)) {
ctrl.$setViewValue(element[0].files, ev && ev.type);
}
})
ctrl.$isEmpty = function (value) {
return !value || value.length === 0;
};
}
})();
Why wasn't this done in the first place? AngularJS support is intended to reach only as far back as IE9. If you disagree with this decision and think they should have just put this in anyway, then jump the wagon to Angular 2+ because better modern support is literally why Angular 2 exists.
The issue is (as was mentioned before) that without the file api
support doing this properly is unfeasible for the core given our
baseline being IE9 and polyfilling this stuff is out of the question
for core.
Additionally trying to handle this input in a way that is not
cross-browser compatible only makes it harder for 3rd party solutions,
which now have to fight/disable/workaround the core solution.
...
I'm going to close this just as we closed #1236. Angular 2 is being
build to support modern browsers and with that file support will
easily available.
Alternatively you could get the input and set the onchange function:
<input type="file" id="myFileInput" />
document.getElementById("myFileInput").onchange = function (event) {
console.log(event.target.files);
};
Try this,this is working for me in angular JS
let fileToUpload = `${documentLocation}/${documentType}.pdf`;
let absoluteFilePath = path.resolve(__dirname, fileToUpload);
console.log(`Uploading document ${absoluteFilePath}`);
element.all(by.css("input[type='file']")).sendKeys(absoluteFilePath);

Resources