$sce.trustAsResourceUrl Not Working With Twitter URL on Iframe - angularjs

I am trying to use $sce.trustAsResourceUrl to sanitize a Twitter URL that I am generating on the controller, however the button that is produced ignores the URL, and uses the current page URL instead.
If I try printing what is in the controller to screen, that works fine.
<iframe id="twitter-widget-0" scrolling="no" frameborder="0" allowtransparency="true" class="twitter-share-button twitter-share-button-rendered twitter-tweet-button" title="Twitter Tweet Button" ng-src="{{ctrl.twitterUniqueURL}}" style="position: static; visibility: visible; width: 60px; height: 20px;"></iframe>
.directive('referral', function ($rootScope, $sce, $interval, regex) {
return {
restrict: 'E',
replace: true,
templateUrl: $sce.trustAsResourceUrl($rootScope.environment.resources + 'website/html/manage/my-stuff/referral.html'),
scope: true,
link: function (scope) {
scope.ctrl = {
init: function() {
scope.ctrl.getReferralCode();
},
form: {
email: ''
},
twitterUniqueURL: null,
getReferralCode: function() {
var check = $interval(function() {
if ($rootScope.ctrl && $rootScope.ctrl.data &&
$rootScope.ctrl.data.customer) {
scope.ctrl.referralCode =
$rootScope.ctrl.data.customer.referralCodeForCustomer;
scope.ctrl.twitterUniqueURL = $sce.trustAsResourceUrl("https://platform.twitter.com/widgets/tweet_button.b813cd3227574096a07e094b73331535.en.html#dnt=false&id=twitter-widget-0&lang=en&original_referer=https%3A%2F%2Fexample.co.uk%2Faccount%2F%3Freferral%26Code%3D" + scope.ctrl.referralCode + "&size=m&text=Receive%20%C2%A310%20when%20signing%20up%20for%20example.%20Live%20a%20Bigger%20Life%2C%20One%20Box%20at%20a%20Time.&time=1528123666174&type=share&url=https%3A%2F%2Fexample.co.uk%2F%3FReferral%3D" + scope.ctrl.referralCode + "&via=exampleUK");
}
}, 300)
},
};
scope.ctrl.init();
}
};
})
Any ideas?
Apologies if I haven't explained this very well - only started using AngularJS two weeks ago.
Thanks
James

Related

Angular how to correctly destroy directive

I have a 'regionMap' directive that includes methods for rendering and destroying the map. The map is rendered inside of a modal and upon clicking the modal close button the 'regionMap' destroy method is called, which should remove the element and scope from the page. However, when returning to the modal page, that includes the 'region-map' element, the previous 'region-map' element is not removed, resulting in multiple maps being displayed. What is the correct way to remove the regionMap directive from the page when the modal is closed?
// directive
(function(){
'use strict';
angular.module('homeModule')
.directive('regionMap', regionMap);
function regionMap() {
var directive = {
restrict: 'E',
template: '',
replace: true,
link: link,
scope: {
regionItem: '=',
accessor: '='
}
}
return directive;
function link(scope, el, attrs, controller) {
if (scope.accessor) {
scope.accessor.renderMap = function(selectedRegion) {
var paper = Raphael(el[0], 665, 245);
paper.setViewBox(0, 0, 1100, 350, false);
paper.setStart();
for (var country in worldmap.shapes) {
paper.path(worldmap.shapes[country]).attr({
"font-size": 12,
"font-weight": "bold",
title: worldmap.names[country],
stroke: "none",
fill: '#EBE9E9',
"stroke-opacity": 1
}).data({'regionId': country});
}
paper.forEach(function(el) {
if (el.data('regionId') != selectedRegion.name) {
el.stop().attr({fill: '#ebe9e9'});
} else {
el.stop().attr({fill: '#06767e'});
}
});
}
scope.accessor.destroyMap = function() {
scope.$destroy();
el.remove();
}
}
}
}
})();
// controller template:
<region-map accessor="modalvm.accessor" region-item="modalvm.sregion"></region-map>
// controller:
vm.accessor = {};
...
function showMap() {
$rootScope.$on('$includeContentLoaded', function(event) {
if (vm.accessor.renderMap) {
vm.accessor.renderMap(vm.sregion);
}
});
function closeMap() {
if (vm.accessor.destroyMap) {
vm.accessor.destroyMap();
}
$modalInstance.dismiss('cancel');
}
The issue is related to loading a template with a directive inside of it. Fixed it by adding a var to check if the map has previously been rendered:
vm.accessor.mapRendered = false;
$rootScope.$on('$includeContentLoaded', function(event) {
if (vm.accessor.renderMap && !vm.accessor.mapRendered) {
vm.accessor.renderMap(vm.selectedRegions);
vm.accessor.mapRendered = true;
}
});

How to bind events in angular directive

I am new to angular and having problem to bind an click event to my directive.
I have an ul-list with a links in each li. When I click a link I want to do a service call that adds or removes the clicked items ID and refresh the list.
When refreshed, each list items will show if an id is "marked" or not.
Can anyone help me?
html view:
<a href="#" class="showbooklist" qtip="12568">
<img src="image.png">
</a>
Directive:
listControllers.directive('qtip', ['boklistorservice', function (boklistorservice) {
return {
restrict: 'A',
controller: ["$scope", "$attrs","$element", "boklistorservice", function ($scope, $attrs,$element, boklistorservice) {
boklistorservice.getdata().then(function (data) { //serice call to gett data
$scope.booklist = data;
$element.qtip({ //use the jquery.tip2.js tooltip plugin
content: {
text: getcurrentbooklist($attrs.qtip, data.barnenskrypin.booklistor)
},
position: {
my: 'bottom center',
at: 'top center'
},
hide: {
fixed: true,
delay: 300
}
});
})
}]
};
}]);
//function returns a string. An ul to show in the toolbox
var getcurrentbooklist = function (bookid, arr) {
var rettext = "<ul>";
$.each(arr, function (item, val) {
item;
var inlist = false;
$.each(val.bookitems, function (i, v) {
if (v.bookid == bookid) {
inlist = true;
return false;
} else {
inlist = false;
}
});
if (inlist) {
rettext += "<li><a (NEED A CLICK EVENT HERE and pass bookid) > " + val.booklistnamn + "-- MARK </a></li>";
} else {
rettext += "<li><a (NEED A CLICK EVENT HERE and pass bookid) >" + val.booklistnamn + "--</a></li>";
}
});
rettext += "</ul>";
return rettext;
};
Use the link function of a directive (code untested):
listControllers.directive('qtip', ['boklistorservice', function (boklistorservice) {
return {
restrict: 'A',
link: function ($scope, element, attrs) {
element.bind('click', function () {
//Do your work here
});
},
controller: ["$scope", "$attrs","$element", "boklistorservice", function ($scope, $attrs,$element, boklistorservice) {
boklistorservice.getdata().then(function (data) { //serice call to gett data
$scope.booklist = data;
$element.qtip({ //use the jquery.tip2.js tooltip plugin
content: {
text: getcurrentbooklist($attrs.qtip, data.barnenskrypin.booklistor)
},
position: {
my: 'bottom center',
at: 'top center'
},
hide: {
fixed: true,
delay: 300
}
});
})
}]
};
}]);
jquery.tip2 adds the html to body so you need to find the content in body and add the click event
$element.qtip();
angular.element(document.body).find('[qtip container id or class or element] a').bind('click', function () {
//Do your work here
});

Heatmap.js directive for Angularjs

is there a angularJS directive for heatmap.js?
Can't find anything and can't get it to work
Thanks
= Edit =
I get this error whether I used my code or the one below (both work). My problem was actually the version of the heatmap.js that that I was using from the bower. When I download the min.js used in the fiddle it all works fine.
TypeError: Cannot read property 'style' of null
at Object.heatmap.resize (http://localhost:56080/app/bower_components/heatmap.js/src/heatmap.js:363:74)
at Object.heatmap.init (http://localhost:56080/app/bower_components/heatmap.js/src/heatmap.js:386:20)
at Object.heatmap (http://localhost:56080/app/bower_components/heatmap.js/src/heatmap.js:331:14)
at Object.heatmapFactory.create (http://localhost:56080/app/bower_components/heatmap.js/src/heatmap.js:627:24)
at link (http://localhost:56080/app/js/directives/MainDirective.js:9:36)
Simple wrapper directive for heatmap.js
HTML
<div ng-app="myapp">
<div ng-controller="MyCtrl1">
<heat-map data="passed_data"></heat-map>
</div>
</div>
JS
var myApp = angular.module('myapp',[]);
myApp
.controller('MyCtrl1', function ($scope) {
// now generate some random data
var points = [];
var max = 0;
var width = 840;
var height = 400;
var len = 200;
while (len--) {
var val = Math.floor(Math.random()*100);
max = Math.max(max, val);
var point = {
x: Math.floor(Math.random()*width),
y: Math.floor(Math.random()*height),
value: val
};
points.push(point);
}
// heatmap data format
$scope.passed_data = {
max: max,
data: points
};
})
.directive('heatMap', function(){
return {
restrict: 'E',
scope: {
data: '='
},
template: '<div container></div>',
link: function(scope, ele, attr){
scope.heatmapInstance = h337.create({
container: ele.find('div')[0]
});
scope.heatmapInstance.setData(scope.data);
}
};
});
CSS
heat-map {
width: 840px;
height: 400px;
display: block;
}
heat-map div {
height: 100%;
}
JsFiddle - http://jsfiddle.net/jigardafda/utjjatuo/2/
heatmap.js example reference link
http://www.patrick-wied.at/static/heatmapjs/example-minimal-config.html
jad-panda's answer (https://stackoverflow.com/a/30193896/3437606) is really helpfull.
But if you don't want to make the size of the heatmap hardcoded in css and apply them dynamicaly with ng-style, you have to make the following minor changes.
HTML
<div ng-style="heatMapStyle">
<heat-map data="passed_data"></heat-map>
</div>
Controller
just add the style object to the $scope like
$scope.heatMapStyle = {
"height": 100+ "px",
"width": 150+ "px"
};
The rest of the controler is the same as in jad-panda's answer.
Directive
.directive('heatMap', ['$timeout', function ($timeout) {
return {
restrict: 'E',
scope: {
data: '='
},
template: '<div container></div>',
link: function (scope, ele, attr) {
function init() {
scope.heatmapInstance = h337.create({
container: ele.find('div')[0]
});
scope.heatmapInstance.setData(scope.data);
}
//to ensure that the wrapping style is already applied
$timeout(init,0);
}
};
}])
The $timout is essential to ensure that the heatmap is initialized in the next digestcycle of AngularJs when the ng-styleis already applied.
CSS
And last the new CSS:
heat-map {
position: relative;
width: 100%;
height: 100%;
}
heat-map div {
height: 100%;
}
Just found an oficial wrapper for heatmap.js, hosted in the same github repository.
It can be downloaded from: https://github.com/pa7/heatmap.js/blob/master/plugins/angular-heatmap/angular-heatmap.js
And it's explained here:
https://www.patrick-wied.at/static/heatmapjs/plugin-angular-heatmap.html

Plupload + AngularJS UI modal doesn't work in IE

I've already seen a lot of articles about plupload and modal dialogs in old versions of IE, but any of them had a solution for my problem. I'm using AngularJS UI to open modals which contain the container div of plupload, and I need to do this work in this way.
I've tried all the solutions: uploader.refresh(), I've used require.js to load the plupload script when the dialog was already opened, but I still haven't found one that works.
Here's the function of the controller that calls the modal dialog:
$scope.EnviarNovoAnexoClick = function () {
var modalInstance = $modal.open({
templateUrl: '/Dialog/EnviarAnexo',
controller: 'EnviarAnexoDialogController',
resolve: {
documentoId: function () {
return $scope.documentoId;
}
}
});
modalInstance.result.then(function (anexo) {
$scope.documento.anexos.push(anexo);
}, function () {//dismiss callback
});
}
Here's the function that calls the uploader:
require(["/Scripts/plupload.full.js"], function (util) {
$scope.anexoUploader = new plupload.Uploader({
runtimes: 'gears,html5,flash,silverlight,browserplus,html4',
browse_button: 'anexoBtUpload',
container: 'anexoUploadDiv',
unique_names: true,
multi_selection: false,
max_file_size: '150mb',
chunk_size: '64kb',
url: '/Documento/Upload',
flash_swf_url: '/Scripts/plupload.flash.swf',
silverlight_xap_url: '/Scripts/plupload.silverlight.xap',
resize: { width: 320, height: 240, quality: 90 },
filters: [
{ title: "PDFs ", extensions: "pdf" },
{ title: "Imagens", extensions: "jpg,gif,png" },
{ title: "Zips", extensions: "zip" },
{ title: "Todos", extensions: "*" }
],
init: {
FilesAdded: function (up, files) {
if ($scope.uploadDocumento == null) {
$scope.showOrigemAnexo = false;
$scope.novoAnexo.upload = {};
$scope.InicializaUpload($scope.novoAnexo.upload);
$scope.uploadDocumento = $scope.novoAnexo.upload;
}
var fileName = $scope.anexoUploader.files[$scope.anexoUploader.files.length - 1].name;
$scope.uploadDocumento.nome = fileName;
$scope.novoAnexo.descricao = dotlessName(fileName);
$scope.$apply();
up.refresh(); // Reposition Flash/Silverlight
up.start();
},
UploadProgress: function (up, file) {
$scope.uploadDocumento.size = file.size;
$scope.uploadDocumento.percentage = file.percent;
$scope.$apply();
},
FileUploaded: function (up, file, response) {
$scope.uploadDocumento.id = file.id;
$scope.uploadDocumento.size = file.size;
$scope.$apply();
}
}
});
$scope.anexoUploader.init();
});
The file dialog is opening in Chrome, IE10 and Firefox, but I need that it works on IE9 and 8.
Thanks (:
This has something to do with caching and dynamically loaded script tag.
Solution that worked for me:
Add this directive:
.directive('cachedTemplate', ['$templateCache', function ($templateCache) {
"use strict";
return {
restrict: 'A',
terminal: true,
compile: function (element, attr) {
if (attr.type === 'text/ng-template') {
var templateUrl = attr.cachedTemplate,
text = element.html();
$templateCache.put(templateUrl, text);
}
}
};
}])
Declare your modal content in
<div data-cached-template="myInnerModalContent.html" type="text/ng-template">
<!-- content -->
</div>
You may need this style as well:
*[type="text/ng-template"]{
display: none;
}
In controller:
$scope.open = function() {
var modalInstance = $modal.open({
templateUrl: 'ModalContent.html',
controller: modalInstanceCtrl
});
};
Reference: http://blog.tomaszbialecki.info/ie8-angularjs-script-cache-problem/

Load Angular Directive Template Async

I want to be able to load the directive's template from a promise. e.g.
template: templateRepo.get('myTemplate')
templateRepo.get returns a promise, that when resolved has the content of the template in a string.
Any ideas?
You could load your html inside your directive apply it to your element and compile.
.directive('myDirective', function ($compile) {
return {
restrict: 'A',
link: function (scope, element, attrs) {
//Some arbitrary promise.
fetchHtml()
.then(function(result){
element.html(result);
$compile(element.contents())(scope);
}, function(error){
});
}
}
});
This is really interesting question with several answers of different complexity. As others have already suggested, you can put loading image inside directive and when template is loaded it'll be replaced.
Seeing as you want more generic loading indicator solution that should be suitable for other things, I propose to:
Create generic service to control indicator with.
Manually load template inside link function, show indicator on request send and hide on response.
Here's very simplified example you can start with:
<button ng-click="more()">more</button>
<div test="item" ng-repeat="item in items"></div>
.throbber {
position: absolute;
top: calc(50% - 16px);
left: calc(50% - 16px);
}
angular
.module("app", [])
.run(function ($rootScope) {
$rootScope.items = ["One", "Two"];
$rootScope.more = function () {
$rootScope.items.push(Math.random());
};
})
.factory("throbber", function () {
var visible = false;
var throbber = document.createElement("img");
throbber.src = "http://upload.wikimedia.org/wikipedia/en/2/29/Throbber-Loadinfo-292929-ffffff.gif";
throbber.classList.add("throbber");
function show () {
document.body.appendChild(throbber);
}
function hide () {
document.body.removeChild(throbber);
}
return {
show: show,
hide: hide
};
})
.directive("test", function ($templateCache, $timeout, $compile, $q, throbber) {
var template = "<div>{{text}}</div>";
var templateUrl = "templateUrl";
return {
link: function (scope, el, attr) {
var tmpl = $templateCache.get(templateUrl);
if (!tmpl) {
throbber.show();
tmpl = $timeout(function () {
return template;
}, 1000);
}
$q.when(tmpl).then(function (value) {
$templateCache.put(templateUrl, value);
el.html(value);
$compile(el.contents())(scope);
throbber.hide();
});
},
scope: {
text: "=test"
}
};
});
JSBin example.
In live code you'll have to replace $timeout with $http.get(templateUrl), I've used the former to illustrate async loading.
How template loading works in my example:
Check if there's our template in $templateCache.
If no, fetch it from URL and show indicator.
Manually put template inside element and [$compile][2] it.
Hide indicator.
If you wonder what $templateCache is, read the docs. AngularJS uses it with templateUrl by default, so I did the same.
Template loading can probably be moved to decorator, but I lack relevant experience here. This would separate concerns even further, since directives don't need to know about indicator, and get rid of boilerplate code.
I've also added ng-repeat and run stuff to demonstrate that template doesn't trigger indicator if it was already loaded.
What I would do is to add an ng-include in my directive to selectively load what I need
Check this demo from angular page. It may help:
http://docs.angularjs.org/api/ng.directive:ngInclude
````
/**
* async load template
* eg :
* <div class="ui-header">
* {{data.name}}
* <ng-transclude></ng-transclude>
* </div>
*/
Spa.Service.factory("RequireTpl", [
'$q',
'$templateCache',
'DataRequest',
'TplConfig',
function(
$q,
$templateCache,
DataRequest,
TplConfig
) {
function getTemplate(tplName) {
var name = TplConfig[tplName];
var tpl = "";
if(!name) {
return $q.reject(tpl);
} else {
tpl = $templateCache.get(name) || "";
}
if(!!tpl) {
return $q.resolve(tpl);
}
//加载还未获得的模板
return new $q(function(resolve, reject) {
DataRequest.get({
url : "/template/",
action : "components",
responseType : "text",
components : name
}).success(function(tpl) {
$templateCache.put(name, tpl);
resolve(tpl);
}).error(function() {
reject(null);
});
});
}
return getTemplate;
}]);
/**
* usage:
* <component template="table" data="info">
* <span>{{info.name}}{{name}}</span>
* </component>
*/
Spa.Directive.directive("component", [
"$compile",
"RequireTpl",
function(
$compile,
RequireTpl
) {
var directive = {
restrict : 'E',
scope : {
data : '='
},
transclude : true,
link: function ($scope, element, attrs, $controller, $transclude) {
var linkFn = $compile(element.contents());
element.empty();
var tpl = attrs.template || "";
RequireTpl(tpl)
.then(function(rs) {
var tplElem = angular.element(rs);
element.replaceWith(tplElem);
$transclude(function(clone, transcludedScope) {
if(clone.length) {
tplElem.find("ng-transclude").replaceWith(clone);
linkFn($scope);
} else {
transcludedScope.$destroy()
}
$compile(tplElem.contents())($scope);
}, null, "");
})
.catch(function() {
element.remove();
console.log("%c component tpl isn't exist : " + tpl, "color:red")
});
}
};
return directive;
}]);
````

Resources