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

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.

Related

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

read file directive with controller as

I need some help. I build the following directve to translate docx file into html string.
(function(){
'use strict';
angular
.module('app.core')
.directive('uploadFile', uploadFile);
function uploadFile($rootScope, $parse){
var directive = {
restrict: "A",
scope:{result : '='},
controller: 'refertazioneController',
controllerAs: "vm",
link: linkFunction,
};
function linkFunction(scope, element, attrs, controller){
document.getElementById("document")
.addEventListener("change", handleFileSelect, false);
function handleFileSelect(event) {
readFileInputEventAsArrayBuffer(event, function(arrayBuffer) {
mammoth.convertToHtml({arrayBuffer: arrayBuffer})
.then(displayResult)
.done();
});
}
function displayResult(result) {
scope.vm.result = resutl.value;
/* document.getElementById("output").innerHTML = result.value;
var messageHtml = result.messages.map(function(message) {
return '<li class="' + message.type + '">' + escapeHtml(message.message) + "</li>";
}).join("");
document.getElementById("messages").innerHTML = "<ul>" + messageHtml + "</ul>";*/
}
function readFileInputEventAsArrayBuffer(event, callback) {
var file = event.target.files[0];
var reader = new FileReader();
reader.onload = function(loadEvent) {
var arrayBuffer = loadEvent.target.result;
callback(arrayBuffer);
};
reader.readAsArrayBuffer(file);
}
function escapeHtml(value) {
return value
.replace(/&/g, '&')
.replace(/"/g, '"')
.replace(/</g, '<')
.replace(/>/g, '>');
}
};
return directive;
}
})();
the problem is that i'm not able to retrivie the translate string in the controller, defined as follows:
(function(){
'use strict';
angular
.module('app.core')
.controller('refertazioneController', refertazioneController);
function refertazioneController($stateParams, refertationService, $window, examinationService, dataService, $scope){
var vm = this;
vm.prova="refertazioneController";
vm.tinymceModel = '';
vm.sospeso=true;
vm.datiDaRefertare = $stateParams;
vm.paziente = dataService.getPatient(vm.datiDaRefertare.patientId);
examinationService.getPatientExamsDef(vm.datiDaRefertare.patientId).then(function(r){
vm.subjectExam = r.data[0].data;
})
console.log(vm.paziente);
vm.currentUser = sessionStorage;
vm.tinymceOptions = {
onChange: function(e) {
// put logic here for keypress and cut/paste changes
},
inline: false,
slector: 'textarea',
// toolbar: 'undo redo | styleselect | bold italic | link image | print save cancel',
height: 500,
plugins : 'advlist autolink link image lists charmap print preview template save paste',
skin: 'lightgray',
theme : 'modern',
language:'it',
statusbar: false,
templates:[ {title: 'Titolo1', description: 'Descrizione1', content: '<p style="text-align: center;">'+
'<strong>A.S.L. 02 LANCIANO-VASTO-CHIETI</strong>'+
'</p>'},
{title: 'Titolo2', description: 'Secondo referto', url: 'sections/refertazione/referto1.html'}
]
};
vm.html = {};
//vm.html.content = '<p>qui per esempio ci va il template che mi ridà il back end</p><h2>altra roba</h2>';
refertationService.openRefert(1,2);
refertationService.closeRefert(1,2);
refertationService.saveRefert(1,2);
/* vm.testoHtml = "";*/
}
})();
I thought that the line : scope.vm.result = result.value was able to bind the string to my controller and then that i was able to render it in the view as refertazione.result (refertazione is the name of my controller). But this not works, where I'm wrong?
A slightly better pattern that relies on events. You could pull this same pattern off with a scope variable that is two way.
Idea is you use an event to tell the controller data has changed.
function uploadFile($rootScope, $parse) {
var directive = {
restrict: "A",
scope: {},
link: linkFunction,
};
function linkFunction(scope, element, attrs) {
var fn = $parse(attrs.uploadFile);
element.on('change', function(onChangeEvent) {
var reader = new FileReader();
console.log(reader);
reader.onload = function(onLoadEvent) {
$rootScope.$broadcast('fileupdate', onLoadEvent.target.result);
};
reader.readAsText((onChangeEvent.srcElement || onChangeEvent.target).files[0]);
});
}
return directive;
}
Inside of your controller you would listen for the fileupdate event.
//inside your controller:
$scope.$on('fileupdate', showContent);
function showContent(event, $fileContent){
vm.content = $fileContent;
}

Angularjs, Range.insertNode, ngClick : After $compile Angular isn't aware of element - ngClick not working

ngClick is still not responding even after $compile. The new element is being applied to the DOM and is accessible via jQuery and JS. I assume that the issue is with the range.insertNode function. What am I missing here?
Here's my directive:
.directive('selectText', [
'$rootScope',
'$compile',
'$window',
function ($rootScope, $compile, $window) {
return {
restrict: 'A',
scope: {
hlid: "=",
tu: "="
},
link: function (scope, element, attrs) {
element.on('mouseup', function () {
//console.log("Attrs: "+JSON.stringify(attrs));
if ($window.getSelection().toString()) {
var text = $window.getSelection().toString();
if(text == '') {
console.log("No selection");
return;
}
var selection = $window.getSelection();
var range = selection.getRangeAt(0);
var selectionContents = range.extractContents();
var clk = "edSel('hl_"+scope.hlid+"','"+attrs.id+"');";
// var span = $compile(angular.element('<hlight id="hl_'+scope.hlid+'" class="cr-pr noselect clickable" title="Text Selection" ng-click="'+clk+'">'+text+'</hlight>'))(scope);
var span = angular.element($compile('<hlight id="hl_'+scope.hlid+'" class="cr-pr noselect clickable" title="Text Selection" ng-click="'+clk+'">'+text+'</hlight>')(scope));
console.log(span);
range.insertNode(span[0]);
scope.tu.target = element.html();
//selection.removeAllRanges();
var arr = {};
arr.action = 'add';
arr.tuid = attrs.id;
arr.hlid = 'hl_'+scope.hlid;
arr.content = element.html();
scope.$emit('hlChange', arr);
scope.hlid++;
console.log(element.html());
var modal = UIkit.modal("#hl_modal");
modal.show();
}
});
scope.edSel = function(id,tuid) {
console.log('ID: '+id+" - tuID: "+tuid);
}
}
};
}])
Thanks for any help

Angular Directive scope undefined

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>'
});

angularjs + typeahead input field initial vaule

I have this directive, which is basicly wrapper for typeahead plugin from bootstrap. Everything is working like a charm. But now I have to set initial vaule in typeahead's input field. The value is passed as a string in attrs.init. But I don't know how to insert it into text field.
angular.module('rcApp')
.directive('rcAutocomplete', ['$injector', function ($injector) {
return {
scope: {
model: '#',
search: '#',
key: '#',
show: '#',
init: '#',
ngModel: '='
},
template: '<input type="text">',
replace: true,
restrict: 'E',
require: 'ngModel',
link: function (scope, element, attrs) {
// inject model service
var service = $injector.get(attrs.model);
// define search function
var searchFunction = attrs.search;
// holds picked object id
scope.id = 0;
// holds objects that matched query, mapped by "show" value
scope.map = {};
element.on('focusout ac.itempicked', function () {
scope.$apply(function () {
scope.ngModel = scope.id;
});
});
// launch typehead plugin
element.typeahead(
{
source: function (query, process) {
// clear cache
scope.id = 0;
scope.map = {};
service[searchFunction](query).then(function (result) {
var dataValues = [];
var fieldsToShow = scope.show.split('|');
$.each(result.data, function (index, dataItem) {
// generate key-show string
var valueHash = '';
for (var i = 0; i < fieldsToShow.length; i++) {
valueHash += dataItem[fieldsToShow[i]] + ' ';
}
valueHash = $.trim(valueHash);
// map results
scope.map[valueHash] = dataItem;
// prepare return strings
dataValues.push(valueHash);
});
// return content
process(dataValues);
});
},
updater: function (item) {
if (typeof scope.key === 'undefined') {
scope.id = scope.map[item];
}
else {
scope.id = scope.map[item][scope.key];
}
element.trigger('ac.itempicked');
return item;
}
}
);
}
};
}]);
** UPDATE **
Solution, that worked for me is adding code like this to link function:
// init value
if (typeof attrs.init !== 'undefined') {
window.setTimeout(function () {
element.val(attrs.init);
scope.$apply();
}, 10);
}
But still I don't quite understand why "element.val(attrs.init);" don't updated the view, and calling scope.$apply() did, but throw an "$digest already in progress" error. Wrapping it in window.setTimeout helped, but this also is a hack for me....
It's got to be a way to make it cleaner/simpler...

Resources