Angular Directive scope undefined - angularjs

I'm using a directive to parse a xls file and pass the data to a button via scope. The problem is that within the link function I'm binding to the element change event and calling a function that parses the xls file, but scope is undefined within the handleFile function, so I can't pass the data on to the button. What's the correct way to get the data to the button?
angular.module('fileReaderModule')
.directive('xlsReader', function(){
return {
scope: {
search: "&"
},
link: function(scope, e, attr) {
e.bind('change', handleFile);
},
template: '<input type="file" ng-model="xlsFile"><button ng-click="search({stuff: scope.stuff})">Search</button>'
}
function handleFile(scope, e) {
var files = e.target.files;
var i,f;
for (i = 0, f = files[i]; i != files.length; ++i) {
var reader = new FileReader();
var name = f.name;
reader.onload = function(e) {
var data = e.target.result;
var workbook = XLSX.read(data, {type: 'binary'});
scope.stuff = workbook.Strings; // scope not available here
/* DO SOMETHING WITH workbook HERE */
var result = {};
workbook.SheetNames.forEach(function(sheetName) {
var roa = XLSX.utils.sheet_to_row_object_array(workbook.Sheets[sheetName]);
if(roa.length > 0){
result[sheetName] = roa;
}
});
};
reader.readAsBinaryString(f);
}
}
})

Inside the templates you don't need to use scope.
Replace this:
template: '<input type="file" ng-model="xlsFile"><button ng-click="search({stuff: scope.stuff})">Search</button>'
with this:
template: '<input type="file" ng-model="xlsFile"><button ng-click="search({stuff: stuff})">Search</button>'
You should also declare your function like this:
angular.module('fileReaderModule')
.directive('xlsReader', function($timeout){
return {
scope: {
search: "&"
},
link: function(scope, e, attr) {
e.bind('change', scope.handleFile);
scope.handleFile(e) {
var files = e.target.files;
var i,f;
for (i = 0, f = files[i]; i != files.length; ++i) {
var reader = new FileReader();
var name = f.name;
reader.onload = function(e) {
//Async code, need $timeout call so angular runs a digest cycle and updates the bindings
$timeout(function(){
var data = e.target.result;
var workbook = XLSX.read(data, {type: 'binary'});
scope.stuff = workbook.Strings;
var result = {};
workbook.SheetNames.forEach(function(sheetName) {
var roa = XLSX.utils.sheet_to_row_object_array(workbook.Sheets[sheetName]);
if(roa.length > 0){
result[sheetName] = roa;
}
});
});
};
reader.readAsBinaryString(f);
}
}
},
template: '<input type="file" ng-model="xlsFile"><button ng-click="search({stuff: stuff})">Search</button>'
});

Related

Some problems in the deployment of the dropzone in AngularJS 1.4.9

When I try to erase the image in the dropzone there are traces of it, which prevent me from uploading it again, unless I upload and erase another image before. I'm using the 1.4.9 version of AngularJS
//controller
app.controller('AppController',function($http){
var ctrl = this;
ctrl.data = { upload:[] } // <= upload data get pushed here
ctrl.upload= function(){
console.log(ctrl.data);
$http({
method : 'POST',
url : 'upload.php',
data : ctrl.data,
}).success(function(data){
alert(data);
});
}
ctrl.delete= function(){
ctrl.data = { upload:[] };
}
});
//and here the directive who controls the upload an the image base64 conversion
app.directive('dropZone',[
function(){
var config = {
template:'<label class="drop-zone">'+
'<input type="file" accept="jpg,png" />'+
'<div ng-transclude></div>'+ // <= transcluded stuff
'</label>',
transclude:true,
replace: true,
require: '?ngModel',
link: function(scope, element, attributes, ngModel){
var upload = element[0].querySelector('input');
upload.addEventListener('dragover', uploadDragOver, false);
upload.addEventListener('drop', uploadFileSelect, false);
upload.addEventListener('change', uploadFileSelect, false);
config.scope = scope;
config.model = ngModel;
}
}
return config;
// Helper functions
function uploadDragOver(e) { e.stopPropagation(); e.preventDefault(); e.dataTransfer.dropEffect = 'copy'; }
function uploadFileSelect(e) {
console.log(config.scope.app.data.upload);
if (config.scope.app.data.upload.length+1==1) {
/* console.log("Hola"+config.scope.app.data.upload.length); */
e.stopPropagation();
e.preventDefault();
var files = e.dataTransfer ? e.dataTransfer.files: e.target.files;
for (var i = 0, file; file = files[i]; ++i) {
/* console.log(file); */
var reader = new FileReader();
reader.onload = (function(file) {
return function(e) {
// Data handling (just a basic example):
// [object File] produces an empty object on the model
// why we copy the properties to an object containing
// the Filereader base64 data from e.target.result
var data={
data:e.target.result,
dataSize: e.target.result.length
};
for(var p in file){ data[p] = file[p] }
config.scope.$apply(function(){ config.model.$viewValue.push(data) })
}
})(file);
reader.readAsDataURL(file);
}
}else{console.log("Solo puedes subir una foto.")}
}
}
])
What i expect
When you delete the image in the dropzone, you can upload it again. That is the expected result.

FileReader() onload function not firing

I know this is a duplicate but none of the answers have helped so far..
I have this directive:
app.directive('fileReader', function () {
return {
scope: {
fileReader: "="
},
link: function (scope, element) {
$(element).on('change', function (changeEvent) {
var files = changeEvent.target.files;
if (files.length) {
var reader = new FileReader();
console.log('reader created');
reader.onload = function (e) {
console.log('onload hit');
};
}
});
}
};
})
And this DOM element linked to the directive:
<input type="file" id="file" file-reader="fileContent"/>
When I select a file for my input, my console prints out reader created, but never prints onload hit. I can not figure out how to properly fire off my onload function. What am I doing wrong here?
### Directive ###
app.directive('fileReader', ['$rootScope', function($rootScope) {
return {
scope: {
fileReader:"="
},
link: function(scope, element) {
$(element).on('change', function(changeEvent) {
var files = changeEvent.target.files;
if (files.length) {
$rootScope.uploadedFileName = files[0].name;
var r = new FileReader();
r.onload = function(e) {
var contents = e.target.result;
scope.$apply(function () {
scope.fileReader = contents;
$rootScope.uploadedFileName = files[0].name;
});
};
r.readAsText(files[0]);
}
});
}
};
}]);
### HTML ###
<input type="file" accept=".csv" id="myFile" file-reader="fileContent"/>

Updating model from directive in angularjs

I have a directive to drag and drop.
The drag and drop works well, but I have a problem with updating the model.
After I drop some text into textarea, the text is showing ok, but the model is not updating.
What am I missing here?
//markup
<textarea drop-on-me id="editor-texto" ng-trim="false" ng-model="mymodel"
name="templateSms.template">test.</textarea>
//directive
angular
.module('clinang')
.directive('dragMe', dragMe)
.directive('dropOnMe', dropOnMe);
dragMe.$inject = [];
function typeInTextarea(el, newText) {
var start = el.selectionStart
var end = el.selectionEnd
var text = el.value
var before = text.substring(0, start)
var after = text.substring(end, text.length)
el.value = (before + newText + after)
el.selectionStart = el.selectionEnd = start + newText.length
el.focus()
}
function dragMe() {
var DDO = {
restrict: 'A',
link: function(scope, element, attrs) {
element.prop('draggable', true);
element.on('dragstart', function(event) {
event.dataTransfer.setData('text', event.target.id)
});
}
};
return DDO;
}
dropOnMe.$inject = [];
function dropOnMe() {
var DDO = {
restrict: 'A',
link: function(scope, element, attrs) {
element.on('dragover', function(event) {
event.preventDefault();
});
element.on('drop', function(event) {
event.preventDefault();
var data = event.dataTransfer.getData("text");
var x=document.getElementById(data);
typeInTextarea(event.target,x.getAttribute('data-value'))
});
}
};
return DDO;
}
Update your textarea model inside typeInTextarea function and using $apply run digest cycle to update the model change across whole app. For that with your current structure of directives with only link functions you'll need to pass scope to the typeInTextarea function (as a parameter).
So your function will be:
function typeInTextarea(scope, el, newText) {
var start = el.selectionStart
var end = el.selectionEnd
var text = el.value
var before = text.substring(0, start)
var after = text.substring(end, text.length)
el.value = (before + newText + after);
scope.mymodel.textnote = el.value;
el.selectionStart = el.selectionEnd = start + newText.length;
el.focus();
}
and dropOnMe function will be:
function dropOnMe() {
var DDO = {
restrict: 'A',
link: function(scope, element, attrs) {
element.on('dragover', function(event) {
event.preventDefault();
});
element.on('drop', function(event) {
event.preventDefault();
var data = event.dataTransfer.getData("text");
var x=document.getElementById(data);
typeInTextarea(scope, event.target,x.getAttribute('data-value'))
scope.$apply();
});
}
};
return DDO;
}
Check out this example (I don't know which element you're dragging so e.g. I've considered span element & just used innerHTML for that ):
https://plnkr.co/edit/wGCNOfOhoopeZEM2WMd1?p=preview

Image upload, acquire image dimensions in directive

I have following angular-js directive to 'upload' local Images:
app.directive('ngFileModel', ['$parse', function ($parse) {
return {
restrict: 'A',
link: function (scope, element, attrs) {
var model = $parse(attrs.ngFileModel);
var isMultiple = attrs.multiple;
var modelSetter = model.assign;
element.bind('change', function () {
var values = [];
angular.forEach(element[0].files, function (item) {
var value = {
// File Name
name: item.name,
//File Size
size: item.size,
//File URL to view
url: (URL || webkitURL).createObjectURL(item),
// File Input Value
_file: item
};
values.push(value);
});
scope.$apply(function () {
if (isMultiple) {
modelSetter(scope, values);
} else {
modelSetter(scope, values[0]);
}
});
});
}
};
}]);
I now want to acquire the image-dimensions, but I don't know how.
I tried something like that (below the line values.push(value)):
var img = new Image();
img.src = value.url;
alert("Width: " + img.width);
but that didn't work. I also tried with img.onload, but I coudn't get a result either.
How can I acquire the image-dimensions?
Thanks!
Solved it by myself, image.onload was the right way to go:
app.directive('ngFileModel', ['$parse', function ($parse) {
return {
restrict: 'A',
link: function (scope, element, attrs) {
var model = $parse(attrs.ngFileModel);
var isMultiple = attrs.multiple;
var modelSetter = model.assign;
element.bind('change', function () {
var values = [];
angular.forEach(element[0].files, function (item) {
var img = new Image();
var imgheight = 0;
var imgwidth = 0;
var imgurl = (URL || webkitURL).createObjectURL(item);
img.src = imgurl;
img.onload = function() {
imgheight = img.height;
imgwidth = img.width;
//alert("Width: " + imgheight + " height: " + imgwidth);
var value = {
// File Name
name: item.name,
//File Size
size: item.size,
//File URL to view
url: imgurl,
// File Input Value
_file: item,
width: imgwidth,
height: imgheight,
mystyle: {}
};
scope.$apply(function () {
values.push(value);
});
};
});
scope.$apply(function () {
if (isMultiple) {
modelSetter(scope, values);
} else {
modelSetter(scope, values[0]);
}
});
});
}
};
}]);

AngularJS directive is not updating template if controller name is not attached

If I specify the controller property in the code below then my code will work. Otherwise the code is not updating the HTML template:
function ngVisualizer($http, $timeout, SharedFactory, ThreejsFactory) {
return {
restrict: 'A',
template: '<div id="canvas"></div>',
transclude: 'true',
priority: Number.MIN_SAFE_INTEGER, //Lowest priority
link: function (scope, elem, attr, ctrl) {
angular.element(document).ready(function () {
var log = [];
// play audio sound.
var audio = new Audio('/Audio/Om_Sound.mp3');
audio.loop = true;
//audio.play();
scope.$watch("fileData", function (newValue, oldValue) {
if (SharedFactory.getRefreshStatus()) {
scope.RemoveEntity();
scope.dateTime = 0;
debugger;
console.log(scope.fileData);
//ESTS service and service name created and added to scene.
var planetSize = scope.centralPlanetSize(scope.countOfMachines);
var centralPlanet = scope.drawCentralPlanet(planetSize, scope.getColor(scope.planetPressure, 100));
var centralPlanetText = scope.draw2DText(scope.sliceName, scope.destinationPoint, planetSize);
scope.sceneObject.add(centralPlanet);
scope.sceneObject.add(centralPlanetText);
//scope.nwScene.add(centralPlanet);
//scope.nwScene.add(centralPlanetText);
//scope.central = null;
angular.forEach(scope.fileData, function (value, key) {
try {
var sphere = new THREE.Mesh(scope.outerGeometry, scope.outerMaterial);
//line creation among services
var sourcePoint = new THREE.Vector3(scope.destinationPoint.x, scope.destinationPoint.y, scope.destinationPoint.z);
var destinationPoint = new THREE.Vector3(ThreejsFactory.getPlanetList()[key].x, ThreejsFactory.getPlanetList()[key].y, ThreejsFactory.getPlanetList()[key].z);
//var path = new THREE.LineCurve3(sourcePoint, destinationPoint);
var path = scope.getCurvePath(sourcePoint, destinationPoint);
//var hexColorValue = scope.getHexColorValue(value.MaxCount);
var tube = scope.drawPipe(path, scope.getColor(value.MaxCount, 65000));
//text for service name
var serviceTextGeometry = scope.draw2DText(value.ServiceName , ThreejsFactory.getPlanetList()[key], 40);
//scope.nwScene.add(serviceTextGeometry);
scope.sceneObject.add(serviceTextGeometry);
//sphere for service name
sphere.position.set(ThreejsFactory.getPlanetList()[key].x, ThreejsFactory.getPlanetList()[key].y, ThreejsFactory.getPlanetList()[key].z);
//scope.nwScene.add(tube);
scope.sceneObject.add(tube);
//scope.nwScene.add(sphere);
scope.sceneObject.add(sphere);
//from ests
var toChild = scope.drawTorusIn(tube.geometry.parameters.path.v2, scope.pipeWidth, scope.getDonutFatness(value.DataBytesIn), "red");
//scope.nwScene.add(toChild);
scope.sceneObject.add(toChild);
//to ests
var toParent = scope.drawTorusOut(tube.geometry.parameters.path.v1, scope.pipeWidth, scope.getDonutFatness(value.DataBytesOut), "blue");
//scope.nwScene.add(toParent);
scope.sceneObject.add(toParent);
scope.torusMovementPositive(tube, toParent, value.AvgSRTT);
scope.torusMovementNegative(tube, toChild, value.AvgSRTT);
//start
scope.addToServiceList(value, tube, sphere, serviceTextGeometry, toChild, toParent);
} catch (err) {
console.log(err);
}
}, log);
scope.nwScene.add(scope.sceneObject);
}
});
});
}
//,
//controller: "LandingPageController"
}
};
I am not able to figure out the root cause behind this issue. If the controller property is uncommented then it starts working, otherwise not.

Resources