$sce.trustAsHtml vs. ng-bind-html - angularjs

Why can't I do this:
<div>{{data | htmlfilterexample}}</div>
When, inside the filter, I am returning:
return $sce.trustAsHtml(input);
Using <div ng-bind-html="data | htmlfilterexample"></div> works regardless if the filter returns input or $sce.trustAsHtml(input).
I was under the impression that $sce makes HTML trustworth and ng-bind-html isn't needed for output returned by that method.
Thanks.

$sce.trustAsHtml() produces a string that is safe to use with ng-bind-html. Were you to not use that function on the string then ng-bind-html would yield the error: [$sce:unsafe] Attempting to use an unsafe value in a safe context. So $sce doesn't get rid of the need for ng-bind-html rather it makes the string it processes safe to use with it.
The specific issue you're running into lies in the difference between ng-bind and ng-bind-html
Using {{}} is the equivalent of ng-bind. So, looking at the ng-bind source code (ng-bind-* source code) we see that it uses this:
element.text(value == undefined ? '' : value);
while ng-bind-html, amongst other things, does the following:
var parsed = $parse(attr.ngBindHtml);
element.html($sce.getTrustedHtml(parsed(scope)) || '');
The key takeaway being that the ng-bind use of .text (http://api.jquery.com/text/) results in the text representation of the string being displayed (ignoring whether it's HTML trustworthy).
While the ng-bind-html use of .html (http://api.jquery.com/html/) results in the html interpreted version (if declared safe by getTrustedHtml())

Related

Why can't I use interpolation with a directive?

I have searched and found a few articles that address "How does one assign a scoped variable to the value of an html attribute tag". See:
https://docs.angularjs.org/guide/interpolation
How to assign angularjs variable value to html element attribute, such as input elememnt's name attribute
In my controller I have defined the following:
$scope.reportFields = "{'summary.imageID':'Image ID' }";
in my html document I have defined the following:
A TEST EXAMPLE
<button ng-json-eport-excel title="CSV" class="bt btn-md btn-success" separator="," data="mdcData" report-fields="{{reportFields}}" filename="'mySearch'">
when I bring the page into the browser I find that the page reports a parse syntax error on defining {{reportFields}} for my button element.
Error: [$parse:syntax] Syntax error: Token '{' invlaid key at column 2 of the expression [{{reportFields}}] starting at [reportFields}}].
if I forgo using a variable and hardcode the value as in:
<button ng-json-eport-excel title="CSV" class="bt btn-md btn-success" separator="," data="mdcData" report-fields="{'summary.imageID':'Image ID' }" filename="'mySearch'">
it compiles and works correctly. If I add the 'ng-if' it compiles correctly as seen below:
<button ng-json-eport-excel title="CSV" class="bt btn-md btn-success" separator="," data="mdcData" report-fields="{{reportFields}}" ng-if="reportFields.length > 0" filename="'mySearch'">
Furthermore when I examine the generated HTML via the inspector I can see that for the 'ahref' tag that angularjs's interpolation has translated 'reportFields' to '"{'summary.imageID':'Image ID' }"' for the ahref tag but it hasn't translate the value for the report-fields tag specified on the button. I have also tried:
ng-if="1==1" filename="'mySearch'">
for my expression which should always evaluate to true. My questions (and I am new to angularjs and I need help understanding) are:
Why do I get a syntax parse error for using the double curly angles with the second HTML element (button) but not the first (href)??
Why does the parse error go away with the 'ng-if' statement
Why doesn't the interpolation happen in the case for
report-fields="{{reportFields}}"
but occurs just fine for:
A TEST EXAMPLE
Thanks for your help in advance.
Pete
What you probably want is:
<a ng-href="reportFields">A TEST EXAMPLE</a>
Although this doesn't make any sense either given the way you've defined reportFields. Notice the ng-href and lack of interpolation. As it is, you are assigning the reportFields expression to the regular HTML attribute href which is not interpreted by Angular.
Angular treats your HTML like a template rather than trying to render it like a browser would. It takes that template and turns it into browser-readable DOM HTML. This helps explain the behavior you're asking about.
Answers to your questions:
Interpolation (curly braces) is actually a directive with special syntax which the parser has to evaluate along with any other directives such as ng-show, ng-repeat, custom directives, etc. There is no guarantee that an interpolation directive will be processed before another directive that tries to bind to it. And in fact, those other directives such as report-fields will get the raw interpolation markup and not the intended data. This is what throws the error. See the last paragraph in the interpolation documentation
Why mixing interpolation and expressions is bad practice
Since href is not actually a directive-- it's an attribute-- Angular doesn't attempt to treat it as a directive or bind it to the interpolation, so no error is thrown. It just outputs the expected result to the DOM: href="parsed value".
ng-if has no special assistance for this issue except it does get parsed before your report-fields directive and then cancels further parsing on the element if false. If you don't get an error in that situation, it's likely only because the if condition evaluated to false and the report-fields directive was never parsed. (Can't see all your code to confirm.)
The interpolation doesn't happen in the error case because the error is thrown before it has a chance to. Again, this is because it gets processed after other directive linking. (I'm not sure why they made that design choice though I imagine there is a good reason.)

How to render special characters like ™ in HTML with Angular JS

I have some special characters to show in various screens of my application so i wanted to have some way where i can handle special characters like "special char - æ ™ &amp" in controller/service instead of HTML.
I know i can use
ng-bind-html
to show special characters for the example string above. However i need to show same string in multiple screens so it would make more sense for me to do it in JS. Any alternative or equivalent JS side snippet for ng-bind-html?
Note: If you have come across these kind of strings you might be knowing that they can be rendered directly with HTML but if you are using Angular JS with
{{some scope value}}
then it doesn't format special characters on it's own.
You can use $sce like so:
function myCtrl($scope,$sce){
$scope.html = $sce.trustAsHtml('HTML_CODE;');
}
And then in your HTML you use ng-bind-html to bind the content to an element.
<span ng-bind-html="html"></span>

Angular render markup that is nested

I get JSON like this
{
"lots of":"keys"
"description" : {
"key":"Some sample key",
"value":"This is the markup™"
}
}
from server and I ultimately iterate the description objects and populate table rows with two columns: one for the key and one for the value.
I have tried putting on my <td> tag ng-bind-html as well as injecting $sce into my controller and using trustAsHtml but so far the string always displays as it is in the JSON. Not every value will be HTML but I can easily detect based on the key if HTML is a possibility. It seemed when I put in the directive on the td it did not display anything if no HTML was present. I am interested in using something that can allow HTML in the value but not require it so I can display either
HTML fragment
<tr ng-repeat="(key, val) in record.description">
<td>{{key}}:</td>
<td>{{val}}</td>
</tr>
I created a quick fiddle here:
https://jsfiddle.net/frishi/mvw97s3q/6/
I used angular-sanitize, which I am not sure you mentioned injecting in your module dependency list. Either way, the example works simply by using ng-bind-html
Relevant docs page: https://docs.angularjs.org/api/ng/directive/ngBindHtml
It works by using the directive ng-bind-html on the element you want to display the HTML string in. You use it like so:
<p ng-bind-html="data.firstName"></p>
assuming that data.firstName = "<strong>FeeFee</strong>" or something like that.
I would also like to add that Angular does not allow this natively because of legitimate security concerns. That and the fact that allowing arbitrary HTML to be rendered might not always produce desirable results. Your page layout could quite possibly break because of some HTML you allowed to be passed through.
Angular was designed with security in mind, and will prevent you from displaying HTML from raw strings whenever possible - to prevent various injection attacks.
Here is workarround for your problem: AngularJS: Insert HTML from a string. Generally you should use ng-bind-html insted of ng-bind (this is used by curly braces).

AngularJS: How to bind-html-unsafe to an attribute?

What is the correct way to make an unsafe bind to an attribute? In my case, I get undesired HTML entities in the title-attribute:
Link
(Angular 1.1.5)
" is how " is escaped in HTML (and XML) attributes, there's no way around it. It will display correctly on the page, however. Attribute contents aren't parsed as HTML so there's no need for an equivalent to bind-html-unsafe.

Is it possible to use multiple lines in Angular attributes?

Is is possible to include a newline in an Angular JS expression in an attribute tag? Something like this:
<p ng-repeat="
foo in foos
| filter:{attr: 'something really long'}
| orderBy:bar">
{{foo}}
</p>
EDIT: To clarify, the above doesn't work. I was wondering if there is some other syntax that allows for breaking this kind of expressions into multiple lines.
angular.js parser would be able to handle it, but there's a quick regex check before handing it to the parser (see http://docs.angularjs.org/error/ngRepeat:iexp):
Be aware, the ngRepeat directive parses the expression using a regex
before sending collection and optionally id to the AngularJS parser.
This error comes from the regex parsing.
I filed a bug to loosen this restriction: https://github.com/angular/angular.js/issues/5537, you can hand-patch it in the meantime, it's just 1 character: m (/regex/m).
It is possible for an expressions to span multiple lines. But ng-repeat throws an error if you try to span the expression on multiple lines.
Take a look at this plunker:
Add a new line in the ng-repeat expression and open the browser console to see the error message.
http://plnkr.co/edit/E1O8Iy3VzL3kzj72BDUL?p=preview
Yes, it is possible to use multiline attributes with ANY HTML element, including AngularJS directives.

Resources