I have a JSON file that contains the page content I wish to load. In it, I have a link that looks like this:
<a data-ng-click='foo()'>Bar</a>
When I load the page content into the page as HTML:
<p class="body" ng-bind-html="jsonText | raw"></p>
using this filter:
app.filter('raw', function ($sce) {
return function (input) {
return $sce.trustAsHtml(input);
}
});
the link does not trigger foo() call on click. Is what I'm trying to do impossible or am I doing something wrong?
Using filter for this is not a good idea, because you need to $compile the loaded HTML and for that you need $scope. So, you either need to $compile it manually and put the result in $scope yourself, or create a directive instead of a filter:
.directive('dynamicHtml', ['$compile', function ($compile) {
return {
link: function ($scope, $element, $attrs) {
$scope.$watch($attrs.dynamicHtml, function (html) {
$element.empty().append($compile(html)($scope));
});
}
};
}]);
and use it instead of ngBindHtml:
<p dynamic-html="jsonText"></p>
Just be aware that by compiling the HTML yourself, you're completely bypassing contextual escaping, so you should only do it with your own, secure content.
The problem is that your adding a plain text into DOM. Your filter will just ad an piece of html text which includes the ng-click directive which as far as the browser is concerned, it is just an attribute it cannot understand.
You need to compile the template using the $compile service before injecting it into the dom
Related
I have an input element and, on DOM ready, I attach to this a jQuery plugin:
<input id="classic-input" type="text">
<script type="text/javascript">
$(document).ready(function () {
$("#classic-input").tokenInput("http://jquery-tokeninput-demo.herokuapp.com/");
});
</script>
This works pretty well and since I need to use this in various part of my application I've created template.html and inserted in it the previous snippet of code, than I created this:
(function() {
'use strict';
angular
.module('app', [])
.directive('myDir', myDir);
function myDir() {
return {
templateUrl : "template.html"
};
}
})();
When I insert this directive in the page the form is shown, but nothing happen when I interact with it. I'm pretty sure that ready handler is the root of all evil. Am I wrong? If I'm right how can edit this directive?
Here is a plunk with examples.
Your template is rather small, besides you could skip script part - just put your code in directive, like this:
function myDir() {
return {
link: function (scope, element) {
$(element).tokenInput("http://jquery-tokeninput-demo.herokuapp.com/");
}
};
}
And apply it to input tag like this:
<input type="text" my-dir>
This way you don't have id hard-coded in your template, which will become a problem in case there multiple such directives on page.
See updated demo.
I'm looking to have a function run every time an angular directive updates. In my case, I have an array of modal configurations that get used on a modal markup template.
Every time the template is used to generate a modal due to a change in the model, I want to run a positionModal() method.
scope.$watch in the link function doesn't seem to notice when I change the model, and I cant think of any other way of doing this. I tried a post-link function thinking that the compile function would get called when the directive was applied, but that doesn't seem to work either. Here is my example controller:
MyApp.controller("ModalController", function () {
//Define scope vars
$scope.modals = [];
$scope.$on("modalTrigger", function (event, settings) {
$scope.modals.push(settings);
});
});
Note: I've simplified the controller here- know that it DOES work.
Here is the template code:
<div class="modalParent" ng-controller="ModalController">
<div id="{{modal.id}}" class="modal" ng-class="modal.type" ng-repeat="modal in modals">
<div class="content">
<h2 ng-show="modal.title">{{modal.title}}</h2>
<p>{{modal.message}}</p>
<button>{{modal.button}}</button>
</div>
</div>
</div>
The directive is currently like this:
MyApp.directive("modalParent", function () {
var positionModals = function (element) {
element.find(".modal .content").each(function () {
//position logic here
});
};
return {
restrict: "C",
compile: function (tElement) {
positionModals(tElement);
}
};
});
Note: Also simplified for the purposes here.
The positionModals() call works when the first modal gets pushed to the array. After that, it stops working.
I've tried using the linking function as well, same result. scope.$watch(modals, function(){...}) does not work.
Can somebody help me figure out what I'm doing wrong?
Figured it out!
I was applying the directive to the parent, ".modalParent".
The ng-repeated element in this case is the modal itself ".modal".
You would want the directive to run on elements that get updates as the model changes, because then the linking function will get called each time the element is instantiated, rather than sitting and watching the parent and trying to update from there.
Hope this helps somebody.
Instead of calling like this, my approach is to write this in the services and inject that services in the controller wherever you want to get that function or the data to be notified as,
Services.js
as.service("xyzservice",function(factoryname){
//here the code goes...
})
Now inject in Controller,
ac.controller("controllername",function(xyzservice){
})
ac.controller("controllername",function(servicename){
})
ac.controller("controllername",function(xyzservice){
})
Here we have injected it in the two controller, we can get it.
So long story short, I've tried a billion ways to put a parameter into a string as text, with no solution to be had. I decided that instead I might use a Document.Write to cheat and print a global variable as HTML... However, I discovered that an HTML partial in AngularJS, won't let me use and tags on the partial page.
Example:
$routeProvider
.when('/score', {templateUrl: 'partials/score.html'})
Then, on score.html, I can't put in this statement. It simply does not execute:
<script type="text/javascript">
document.write("This is a test");
//document.write("This is a test"+$rootScope.variable);
</script>
I've also tried window.document.write, even ngApp.document.write. It just doesn't seem to write at all. On network view it looks like the JS isn't running at all. Why? How?
EDIT: I know about data binding. It doesn't work inside an attribute string... I don't know why. This doesn't not work:
<div addthis-toolbox class="addthis_toolbox addthis_default_style addthis_32x32_style social-width" style="width: 150 px">
<a class="addthis_button_twitter" addthis:title="My score is {{score}}"></a>
</div>
EDIT2: Actually, I think that there might be a directive overriding the div class. Here is the angular directive in question:
myApp.directive('addthisToolbox', function() {
return {
restrict: 'A',
transclude: true,
replace: true,
template: '<div ng-transclude></div>',
link: function ($scope, element, attrs) {
// Dynamically init for performance reason
// Safe for multiple calls, only first call will be processed (loaded css/images, popup injected)
// http://support.addthis.com/customer/portal/articles/381263-addthis-client-api#configuration-url
// http://support.addthis.com/customer/portal/articles/381221-optimizing-addthis-performance
addthis.init();
// Ajax load (bind events)
// http://support.addthis.com/customer/portal/articles/381263-addthis-client-api#rendering-js-toolbox
// http://support.addthis.com/customer/portal/questions/548551-help-on-call-back-using-ajax-i-lose-share-buttons
addthis.toolbox($(element).get());
}
}
});
How does one pass data bindings into an attribute if it's got a directive affecting it?
I am developing an app using Angularjs and adding HTML using $sce.trustAsHtml() in my page. I want to call a function in above dynamically added content. My html and script as below.
HTML
<div ng-app="ngBindHtmlExample">
<div ng-controller="ngBindHtmlCtrl">
<p ng-bind-html="myHTML"></p>
</div>
</div>
Javascript
angular.module('ngBindHtmlExample', ['ngSanitize'])
.controller('ngBindHtmlCtrl', ['$scope','$sce', function ngBindHtmlCtrl($scope, $sce) {
$scope.myHTML =$sce.trustAsHtml(
'I am an <code>HTML</code>string with links! and other <em>stuff</em>');
$scope.removeExp = function (){
console.log('dfdfgdfgdfg');
}
}]);
jsfiddle
Click Here to see
It's a bit tricky because ng-bind-html will simply insert plain old html and not bother compiling it (so any directives in the html will not be processed by angular.
The trick is finding a way to compile whenever the template changes. For example, you could create a directive that does this. It would look something like:
.directive('compileTemplate', function($compile, $parse){
return {
link: function(scope, element, attr){
var parsed = $parse(attr.ngBindHtml);
function getStringValue() { return (parsed(scope) || '').toString(); }
//Recompile if the template changes
scope.$watch(getStringValue, function() {
$compile(element, null, -9999)(scope); //The -9999 makes it skip directives so that we do not recompile ourselves
});
}
}
});
You can then use it like this:
<p ng-bind-html="myHTML" compile-template></p>
See the working example here:
http://jsfiddle.net/3J25M/2/
I'm trying to use a jQuery plugin (Plupload) with AngularJS. I have created a directive that will be my file upload "widget". The directive looks like this (The code in the link function is a very simplified version of the example on the Plupload site):
.directive('myFileUpload', function () {
return {
restrict: 'E',
replace: true,
link: function(scope, element, attrs) {
scope.uploaderProperties = {
runtimes : 'html5,flash,html4',
url : 'media/upload',
max_file_size : '10mb',
container: 'fileUploadContainer',
drop_element: 'fileUploadDropArea'
};
scope.uploader = new plupload.Uploader(scope.uploaderProperties);
scope.uploader.init();
scope.uploader.bind('FilesAdded', function(up, files) {
scope.$apply();
});
},
templateUrl: 'upload.html'
};
});
My upload.html file looks like this:
<div id="{{uploaderProperties.container}}">
<div id="{{uploaderProperties.drop_element}}" style="border: 1px black dashed;">Drop files here<br><br><br></div>
Files to upload:<br>
<div ng-repeat="currFile in uploader.files">{{currFile.name}} ({{currFile.size}}) </div>
<br><br>
<!-- for debugging -->
{{uploader.files}}
<br><br>
</div>
When I include the directive on my page with a <my-file-upload> element, all the data bindings happen correctly. The problem is, when scope.uploader.init(); runs, the ids haven't been inserted into the DOM yet, and so Plupload complains and breaks since it can't select those elements. If I just hard-code the fileUploadContainer and fileUploadDropArea ids in the template, it works just fine. However, I'd really like to define those ids in only one place.
So, is there any way I can run the init() on the uploader after the template is linked in? I thought about using $timeout to delay when it runs, but that seems like a pretty big hack to me. Is there a more correct way of accomplishing this?
UPDATE
I wrapped the init() in a $timeout like this
$timeout(function() {
scope.uploader.init();
}, 2000);
just to make sure it would behave the way I was thinking, and sure enough, with this delay the plugin gets set up correctly and works. However, I do not want to have to rely on the timing of the $timeout. If there was just some way I could call scope.uploader.init(); after the template is linked in, everything should work fine. Anyone know of a good way of doing this?
Your problem is actually the other way around - The link function happens after the template is put in. So in this case, scope.uploaderProperties isn't set when the template happens.
It looks like your template is too fancy for the templateUrl option, really. You could try manually setting your ids with jQuery, or setting everything up in the compile function.
http://docs.angularjs.org/guide/directive