ng-bind-html strips elements attributes - angularjs

I'm trying to interpolate a string that contains some markup in a template.
In the controller:
$scope.message = "Hello moto <a ui-sref='home.test'>click</a>";
Template:
<div ng-bind-html="message.text"></div>
which renders as:
<div ng-bind-html="message.text" <div="" class="ng-binding">Hello moto <a>click</a></div>
Trying to use the following filter does not help either; the text is simpy escaped for either of the commented choices:
angular.module('test-filters', ['ngSanitize'])
.filter('safe', function($sce) {
return function(val) {
return $sce.trustAsHtml(val);
//return $sce.trustAsUrl(val);
//return $sce.trustAsResourceUrl(val);
};
});
How can I interpolate my string without escaping it nor stripping attributes?
Edit: Plunker http://plnkr.co/edit/H4O16KgS0mWtpGRvW1Es?p=preview (updated with sylwester's version that has reference to ngSanitize

Let have a look here http://jsbin.com/faxopipe/1/edit it is sorted now.
It didn't work because there was another directive inside a tag 'ui-sref',
so you have to use $sce service.
in your js please add method:
$scope.to_trusted = function(html_code) {
return $sce.trustAsHtml(html_code);
and in view :
<p ng-bind-html="to_trusted(message)"></p>

In scenario where you are using ui.router path you must need to use $compile in combination with $sce for your dynamic html so that ui-sref work properly. If you don't do that you'll just see a Link which actually do not work.
e.g <span> Hello moto <a ui-sref='home.test'> Link </a> </span>
//You must need to add boundary conditions, this is just for demonstration
$scope.to_trusted = function(someHTML) {
var compiledVal = $compile(someHTML)($scope);
var compiledHTML = compiledVal[0].outerHTML;
return $sce.trustAsHtml(compiledHTML);
}
And you use like this,
<p ng-bind-html="to_trusted(message)"></p>
Note that your message has to be a valid HTML starting from "<" so if you pass a non HTML to $compile you'll get jqlite error. I used <span> to handle your case.

You missed reference to angular-sanitize.js and you have inject it as well to angular.app
var app = angular.module('plunker', ['ngSanitize']);
the simplest option in to bind html is ng-bind-html :
<li>link ng-html-bind <div ng-bind-html="message"></div></li>
please see Plunkr

Related

Angular: ng-bind-html filters out ng-click?

I have some html data that I'm loading in from a json file.
I am displaying this html data by using ngSanitize in my app and using ng-bind-html.
Now I would like to convert any links in the json blob from the standard
link
to:
<a ng-click="GotoLink('some_link','_system')">link</a>.
So I'm doing some regExp on the json file to convert the links, but for some reason however ng-bind-html is filtering out the ng-click in it's output, and I can't figure out why. Is it supposed to do this, and if so is it possible to disable this behavior?
Check out this jsFiddle for a demonstration:
http://jsfiddle.net/7k8xJ/1/
Any ideas?
Ok, so the issue is that it isn't compiling the html you include (angular isn't parsing it to find directives and whatnot). Can't think of a way to make it to compile from within the controller, but you could create a directive that includes the content, and compiles it.
So you would change
<p ng-bind-html="name"></p>
to
<p compile="name"></p>
And then for the js:
var myApp = angular.module('myApp', ['ngSanitize']);
angular.module('myApp')
.directive('compile', ['$compile', function ($compile) {
return function(scope, element, attrs) {
scope.$watch(
function(scope) {
return scope.$eval(attrs.compile);
},
function(value) {
element.html(value);
$compile(element.contents())(scope);
}
)};
}]).controller('MyCtrl', function($scope) {
var str = 'hello http://www.cnn.com';
var urlRegEx = /((([A-Za-z]{3,9}:(?:\/\/)?)(?:[\-;:&=\+\$,\w]+#)?[A-Za-z0-9\.\-]+|(?:www\.|[\-;:&=\+\$,\w]+#)[A-Za-z0-9\.\-]+)((?:\/[\+~%\/\.\w\-]*)?\??(?:[\-\+=&;%#\.\w]*)#?(?:[\.\!\/\\\w]*))?)/g;
result = str.replace(urlRegEx, "<a ng-click=\"GotoLink('$1',\'_system\')\">$1</a>");
$scope.GotoLink = function() { alert(); }
$scope.name = result;
});
Angular 1.2.12: http://jsfiddle.net/7k8xJ/4/
Angular 1.4.3: http://jsfiddle.net/5g6z58yy/ (same code as before, but some people were saying it doesn't work on 1.4.*)
I still faced some issue with the compile, as that was not fulfilling my requirement. So, there is this, a really nice and easy hack to work around this problem.
We replace the ng-click with onClick as onClick works. Then we write a javascript function and call that on onClick event.
In the onClick function, we find the scope of the anchor tag and call that required function explicitly.
Below is how its done :)
Earlier,
<a id="myAnchor" ng-click="myControllerFunction()" href="something">
Now,
<a id="myAnchor" onClick="tempFunction()" href="something">
at the bottom or somewhere,
<script>
function tempFunction() {
var scope = angular.element(document.getElementById('myAnchor')).scope();
scope.$apply(function() {
scope.myControllerFunction();
});
}
</script>
This should work now. Hope that helps someone :)
For more info, see here.
Explicitly Trusting HTML With $sce
When you want Angular to render model data as HTML with no questions asked, the $sce service is what you’ll need. $sce is the Strict Contextual Escaping service – a fancy name for a service that can wrap an HTML string with an object that tells the rest of Angular the HTML is trusted to render anywhere.
In the following version of the controller, the code asks for the $sce service and uses the service to transform the array of links into an array of trusted HTML objects using $sce.trustAsHtml.
app.controller('XYZController', function ($scope, $sce) {
$sce.trustAsHtml("<table><tr><td><a onclick='DeleteTaskType();' href='#workplan'>Delete</a></td></tr></table>");

Rendering dynamic HTML(angularjs content) content after ajax call in AngularJS

I am new to Angular getting stuck after making ajax call. How do I render/compile the html content once you inject in DOM so that I can still use the AngularJs functions.
Due to the way my backend is set up I have to get content via ajax ($http). And I am making the app without jQuery. I tried $compile and $apply but didn't work. What am I missing here.
I have the code set up at http://jsfiddle.net/rexonms/RB7FQ/3/ . I want the second div content to have the same properties as the first div.
HTML
<div ng-controller="MyCtrl" class="section">
<input ng-model="contentA">
<div>
And the input is: {{contentA}}
</div>
</div>
<div ng-controller="MyAjax" class="section">
<div id="dumpAjax">
{{ajaxData}}
</div>
<button ng-click=getajax()> Get Ajax</button>
</div>
SCRIPT
var myApp = angular.module('myApp',[]);
//myApp.directive('myDirective', function() {});
//myApp.factory('myService', function() {});
function MyCtrl($scope) {
}
function MyAjax($scope){
var data = '<input ng-model="contentB">{{contentB}}';
$scope.getajax = function(){
$scope.ajaxData = data;
}
}
Thanks in advance.
ng-bind-html-unsafe is not available 1.2 and later verison of angular...
so you should use ng-bind-html which creates a binding that will innerHTML the result of evaluating the expression into the current element in a secure way.
using $scope variable in your string make it unsafe, so you should use $sce.trustAsHtml but this time variables in your string cannot be bind because they will be not compiled...
basically you should compile your string in order to bind your variables. Here comes custom directives you can create a directive which can replace with ng-html-bind...
Writing a custom directive which extends ng-bind-html with some extra functions can be a solution...
here is my PLUNKER
and here is your updated JSFIDDLE with my solution...
Instead of {{ajaxData}}, you should use something like:
<div ng-bind-html-unsafe="ajaxData"></div>
However, you'd still need to set the proper model to bind the contentB and get it working.

How can I generate on the fly directives in AngularJS?

I want to be able to take an array of strings, and then create directives based upon those strings. Either element or attribute will work fine but can't seem to get it working.
<div ng-repeat="module in modules.directives">
<div {{module.directive}}></div>
</div>
<div ng-repeat="module in modules.directives">
<{{module.directive}}></{{module.directive}}>
</div>
<div ng-repeat="module in modules.directives">
<{{module.directive}} />
</div>
Can't get any of these to work. Any ideas?
You could define a directive that would proxy another directive like so
<div proxy="'ng-if'" proxy-value="'foo'"></div>
<div ng-init="n='ng-if'; v='foo';" proxy="n" proxy-value="v"></div>
that would both be equivalent to
<div ng-if="foo"></div>
the proxy directive definition would be
app.directive('proxy', function ($parse, $injector) {
return function (scope, element, attrs) {
var nameGetter = $parse(attrs.proxy);
var name = nameGetter(scope);
var value = undefined;
if (attrs.proxyValue) {
var valueGetter = $parse(attrs.proxyValue);
value = valueGetter(scope);
}
var directive = $injector.get(name + 'Directive')[0];
if (value !== undefined) {
attrs[name] = value;
}
return directive.compile(element, attrs, null)(scope, element, attrs);
};
});
This is actually kind of a fun directive to write once in a life. :-) but it lacks a lot of the native directive features (for instance template, templateUrl, controller, etc). All those features are available in the original Angular source code in a private function called applyDirectivesToNode, so it is easy to copy/paste some parts, but ugly... I have written a demo matching your usecase here.
Another solution, if you don't mind your proxied directive does not share the same element as the proxy directive's one, would be to $compile a dynamic template at runtime that you would include. Here is a demo.
ng-include can help you. The approach would be to define a template for each of the directives. Something like this
<script type="text/ng-template" class="template" id="test-module">
<test-module></test-module>
</script>
Then in ng-repeat do
<div ng-repeat="module in modules.directives">
<ng-include src="module.directive">
</div
If the template id matches with module.directive that directive would get rendered.

Angular directive does not work if we change name

I am seeing a strange behaviour when using angular directive.
In the code below
HTML
<body ng-app="loadTweetsModule">
<div tweets> Load </div>
<div loadTweets> loadTweets </div>
</body>
Javascript
var loadTweetsModule = angular.module("loadTweetsModule",[]);
loadTweetsModule.directive('tweets',function(){
return {
link : function(scope,element){
element.bind("mouseenter", function(){
console.log("tweets");
});
}
};
});
loadTweetsModule.directive('loadTweets',function(){
return {
link : function(scope,element){
element.bind("mouseenter", function(){
console.log("loadTweets");
});
}
};
});
JSFiddle Link
The two directive loadTweets and tweets are the same except for the name. The directive tweets works as expected but loadTweets does not work. I am not able to find out the reason for this behaviour. Can somebody explain this?
if you write in js loadTweets you should write in html load-tweets
from http://docs.angularjs.org/guide/directive
Directives have camel cased names such as ngBind. The directive can be
invoked by translating the camel case name into snake case with these
special characters :, -, or _. Optionally the directive can be
prefixed with x-, or data- to make it HTML validator compliant. Here
is a list of some of the possible directive names: ng:bind, ng-bind,
ng_bind, x-ng-bind and data-ng-bind.

How to generate url encoded anchor links with AngularJS?

<a href="#/search?query={{address}}" ng-repeat="address in addresses">
{{address}}
</a>
generates links that are not url encoded if I understand correctly. What is the proper way to encode #/search?query={{address}}?
Example address is 1260 6th Ave, New York, NY.
You can use the native encodeURIComponent in javascript.
Also, you can make it into a string filter to utilize it.
Here is the example of making escape filter.
js:
var app = angular.module('app', []);
app.filter('escape', function() {
return window.encodeURIComponent;
});
html:
<a ng-href="#/search?query={{address | escape}}">
(updated: adapting to Karlies' answer which uses ng-href instead of plain href)
#Tosh's solution will return #/search?query=undefined if address is undefined in
<a ng-href="#/search?query={{address | escape}}">
If you prefer to get an empty string instead for your query you have to extend the solution to
var app = angular.module('app', []);
app.filter('escape', function() {
return function(input) {
if(input) {
return window.encodeURIComponent(input);
}
return "";
}
});
This will return #/search?query= if address is undefined.
You could use the encodeUri filter: https://github.com/rubenv/angular-encode-uri
Add angular-encode-uri to your project:
bower install --save angular-encode-uri
Add it to your HTML file:
<script src="bower_components/angular-encode-uri/dist/angular-encode-uri.min.js"></script>
Reference it as a dependency for your app module:
angular.module('myApp', ['rt.encodeuri']);
Use it in your views:
<a href="#/search?query={{address|encodeUri}}">
Tosh's answer has the filter idea exactly right. I recommend do it just like that. However, if you do this, you should use "ng-href" rather than "href", since following the "href" before the binding resolves can result in a bad link.
filter:
'use strict';
angular.module('myapp.filters.urlEncode', [])
/*
* Filter for encoding strings for use in URL query strings
*/
.filter('urlEncode', [function() {
return window.encodeURIComponent;
}]);
view:
<a ng-href="#/search?query={{ address | urlEncode }}" ng-repeat="address in addresses">
{{address}}
</a>
this is a working code example:
app.filter('urlencode', function() {
return function(input) {
return window.encodeURIComponent(input);
}
});
And in the template:
<img ng-src="/image.php?url={{item.img_url|urlencode}}"

Resources