How Do I Get HTML To Bind Properly To An Angular App - html

I have an angular app which submits data via json to a PHP application. This application returns to the angular app a response object which has a renderedTable property, and a quote property.
The renderedTable property is batch of HTML text with angular {{expression}}'s within it. Embedded in this table is also <input> tags that bind to ng-models in the other mentioned property quote.
Quote is nothing more than a data class, with various properties within it. I am trying to use ng-bind-html in order to get the html code to display (and it does), however, I have 2 issues to work out.
The first issue is that I cannot get the html to properly render, if I use $interpolate I get the values from the quote object, but the <input> tag is filtered out (which is necessary), and it's not clear to me that the defined bind points would be dynamic still based on the fact that $interpolate returns a string.
I have tried creating my own unsafe directive, but that has resulted in the same output as with ng-bind-html. How do I get my ajax returned HTML to populate and dynamically update based on the also returned object, AND while keeping all input fields in the page, and bound properly using ng-model?
My code (excerpts):
...ajax call this is the meat of the function
if (response.success)
{
$scope.crmquote = response.data.crmquote;
$scope.quoteTable = response.data.crmtable; //this is where I initally applied $interpolate but realized it wasnt what I was looking for
console.log($scope.quoteTable);
}
//this is the function I have setup to try to get angular to "trust" the html and not strip the <input> tags but it doesn tseem to work
$scope.to_trusted = function(html_code) {
return $sce.trustAsHtml(html_code);
}
//this is the directive I attempted to use to achieve the desired result
.directive('bindUnsafeHtml', ['$compile', function ($compile) {
return function(scope, element, attrs) {
scope.$watch(
function(scope) {
return scope.$eval(attrs.bindUnsafeHtml);
},
function(value) {
element.html(value);
$compile(element.contents())(scope);
}
);
};
}]);
And these are the HTML code snippets I have tried:
<div ng-bind-html='to_trusted(quoteTable)' ng-show="editIF == false">
<div bind-unsafe-html='quoteTable' ng-show="editIF == false">
This is the contents of quoteTable:
<table class="table table-bordered table-striped"><thead><tr><th>Description</th><th>Qty</th><th>Rate</th><th>Total</th></tr></thead><tr><td>Monthly Rent for 16'</td><td>1</td><td>$ <input type='text' ng-model='crmquote.rr16'/></td><td>$ {{crmquote.rr16 * crmquote.rr16units | number:2}}</td></tr><tr><td>Initial Delivery</td><td>1</td><td><a href='#' editable-text='crmquote.initialdelivery'>$ {{crmquote.initialdelivery}}</span></td><td>$ {{crmquote.initialdelivery * crmquote.units | number:2}}</td></tr><tr><td>Final Pickup</td><td>1</td><td><span id='editrate'>$ {{crmquote.finalunit}}</span></td><td>$ {{crmquote.finalunit * crmquote.units | number:2}}</td></tr><tr><td colspan=3 style='text-align:right'><b>Sub-Total</b></td><td id='subtotal'>$ {{crmquote.total | number:2}}</td></tr><tr><td colspan=3 style='text-align:right'><b>Taxes (8.50%)</b></td><td id='taxes'>$ {{crmquote.tax | number:2}}</td></tr><tr><td colspan=3 style='text-align:right'><b>Total Due On Delivery</b></td><td id='total'>$ {{crmquote.grandtotal | number:2}}</td></tr></table><br><table class="table table-bordered table-striped" style="margin-bottom:0;"><tr><td colspan=2><b>Estimated Future Costs</b></td></tr><tr><td>Additional Monthly Rent for 16' </td><td>$ {{crmquote.rr16 | number:2}} each</td></tr></table>
Any help here is greatly appreciated!

From the Docs:
var ngBindHtmlDirective = ['$sce', function($sce) {
return function(scope, element, attr) {
scope.$watch($sce.parseAsHtml(attr.ngBindHtml), function(value) {
element.html(value || '');
});
};
}];
-- https://docs.angularjs.org/api/ng/service/$sce#how-does-it-work-

Related

Angular DomSanitizer adding extra tags to html sent as strings

I was trying to sanitize my values before displaying on anywhere UI like saved inputs, tables, etc.
when the value <div><div> is passed to the sanitize() function it returns <div><div></div></div>
return this.domSanitizer.sanitize(SecurityContext.HTML, value);
That's one of the expected sanitization tasks from Angular DOMSanitizer.
It calls endElement
private endElement(current: Element) {
const tagName = current.nodeName.toLowerCase();
if (VALID_ELEMENTS.hasOwnProperty(tagName) && !VOID_ELEMENTS.hasOwnProperty(tagName)) {
this.buf.push('</');
this.buf.push(tagName);
this.buf.push('>');
}
}
During its sanitizeChildren routine

How can I display dynamic HTML having Vue variables inside?

I am trying to make the page content dynamic. I am using ck-editor in which i added html content and used the same vue variables inside it which i declared in the vue file where i want to show ck-editor data. I found a similar post vuejs - “editing” html inside variable
which works fine if i write the html inside a variable. But in my case, i am saving data in database. It is saving properly with html tags, without converting the tags. When i get data using axios it returns it in form of string. And i used vue variable to display that html.
Here is my code for better understanding:
<div v-html="htmlText"></div>
new Vue({
el: '#app',
created() {
this.getSalesContent();
},
data: {
salesContent: '',
pageName: 'Sales',
salesNumber: '987-586-4511'
},
computed: {
htmlText() {
return `${this.salesContent}`;
//return this.salesContent;
}
},
methods: {
getSalesContent(){
axios.get('api/Sales').then(({ data }) => { // getting data from DB
this.salesContent = data.sales; //data.sales have this.pageName and this.salesNumber variables
});
}
}
});
Here is the example of data saved in db:
<p style="font-weight:bold"><span style="color:red">{{pageName}}</span>,</p>
<p style="font-weight:bold"><span style="color:red">${this.pageName} ${this.pageName}</span></p>
<p style="font-weight:bold">Contact Sales at ${this.salesNumber} {{salesNumber}}</span></p>
I used variables in all possible ways. But on the page they are printing in it the same way i saved it. Here is the output:
screenshot
Can anyone help me make it working.
Thanks in Advance.
According to the docs this does not seem possible:
https://v2.vuejs.org/v2/guide/syntax.html#Raw-HTML
Particularly:
The contents of the span will be replaced with the value of the
rawHtml property, interpreted as plain HTML - data bindings are
ignored.
You could as suggested in that answer just use a computed based on what you get from the server.
IMHO since the salesContent is fetched from db, it's a plain String. Thus nor vuejs or vanilla javascript will replace the inline variables with their values. (It may be possible by using eval, but it's totally out of question...) You should manually do that with String replace function. Like the following:
<p style="font-weight:bold"><span style="color:red">{{pageName}}</span>,</p>
<p style="font-weight:bold">Contact Sales at {{salesNumber}}</span></p>
methods: {
getSalesContent(){
axios.get('api/Sales').then(({ data }) => { // getting data from DB
let salesContent = data.sales; //data.sales have this.pageName and this.salesNumber variables
salesContent = salesContent.replace(/{{pageName}}/g, this.pageName)
salesContent = salesContent.replace(/{{salesNumber}}/g, this.salesNumber)
this.salesContent = salesContent
});
}
}

How to compile/add HTML inside md-tooltip

I am trying to add HTML inside an md-tooltip but haven't had any luck, even with ng-bind-html.
Without using ng-bind-html, the tooltip outputs:
Some html<br>
<strong>card</strong>.
With it, my HTML outputs as a string:
Some html<br><strong>card</strong>
In my controller, I use this custom filter to compile HTML used within an ng-repeat:
app.filter('unsafe', function($sce) { return $sce.trustAsHtml; });
This filter successfully works with other elements aside from tooltips.
The tooltip is written as:
<md-tooltip md-delay="1000" md-direction="bottom" class="tooltip-sort-display">
<span ng-bind-html="categoryItem.ToolTip | unsafe">
</md-tooltip>
Please note, when I don't use a json variable and instead add static text to the tooltip, HTML has no trouble rendering
<md-tooltip md-delay="1000" md-direction="bottom" class="tooltip-sort-display">
<strong>Tool</strong><br><em>tip</em>
</md-tooltip>
Any ideas on how I can make this work? I would put together an example, but my Angular skills aren't that advanced. I mainly do the front-end development off my colleagues' work.
In your case, your problem is that you are using HTML special chars. If not, your code will works fine. Anyways if you cannot avoid receive special chars, you can add the decode in your filter:
JSFIDDLE DEMO
.filter('unsafeSpecial', function($sce) {
return function(value) {
var txt = document.createElement("textarea");
txt.innerHTML = value;
return $sce.trustAsHtml(txt.value);
}
})
And the you can use like this way:
HTML
<md-tooltip>
<span ng-bind-html="msg | unsafeSpecial"></span>
</md-tooltip>
CONTROLLER
.controller('mainCtrl', function($scope) {
$scope.msg = 'Some html<br><strong>card</strong>';
})
For more info about decode html topic, check this question if you want: HTML Entity Decode

Include directive before html render

I tried wrapping my head around the following problem:
I have a html string which I render using ng-bind-html
I managed to change a given placeholder (in the html string) with a directive (or more). For example I have: [placeholder]test[/placeholder] and replaced with <my-directive></my-directive> for a certain functionality.
This approach is needed to make some content dynamic.
When rendering the html string I notice that the directive is missing, I understand, but is there a way to render it and make the directive functionally?
P.S:
Tried rendering it as a normal string but the html is escaped
Tried using $sce.trustAsHtml()
I cannot apply $compile(element.contents())(scope); since the directive is not triggered
I have managed to do achieve this by doing the following:
Add the directive in the html:
<my-directive update-data-trigger="someObject.content" data="someObject"></<my-directive>
The directive:
app.directive("myDirective", function ($compile) {
return {
replace:true,
restrict: "E",
scope: {
//Data holds the html in the content attribute
data: '=',
updateDataTrigger: '='
},
link: function ($scope, element) {
//Add a watcher to refresh data because the loaded data passed is async
$scope.$watch('updateDataTrigger', function(){
//Check if data passed has been loaded with our desired object
if($scope.updateDataTrigger != null) {
//Do some content manipulation here
//Append directives to the content as well
//render as html
element.html(data.content);
$compile(element.contents())($scope);
}
});
}
}
});

Using controller-scoped data in a directive's jqlite-generated html

This question is similiar to them one asked in Mike's post Using ng-model within a directive.
I am writing a page which is small spreadsheet that displays calculated output based on user input fields. Using a directive, I'm making custom tags like this:
<wbcalc item="var1" title="Variable 1" type="input"></wbcalc>
<wbcalc item="var2" title="Variable 2" type="input"></wbcalc>
<wbcalc item="calc" title="Calculation" type="calc"></wbcalc>
The 'item' field references scoped data in my controller:
$scope.var1 = '5'; // pre-entered input
$scope.var2 = '10'; // pre-entered input
$scope.calc = function() {
return parseInt($scope.var1) + parseInt($scope.var2);
};
And the 'type' field is used in the directive's logic to know whether to treat the item as a string or a function.
Here's a fiddle for this: http://jsfiddle.net/gregsandell/PTkms/3/ I can get the output elements to work with the astonishing line of code:
html.append(angular.element("<span>")
.html(scope.$eval(attrs.item + "()"))
);
...and I'm using this to get my inputs connected to my scoped controller data (I got this from Mike's post:
var input = angular.element("<input>").attr("ng-model", attrs.item);
$compile(input)(scope);
html.append(input);
...while it does put the values in the fields, they aren't bound to the calculation, as you can see by changing inputs in my fiddle.
Is there a better and/or more intuitive way to link my controller-scoped data to the jqlite-generated html in my directive?
Take a look at this, I think you can simplify the process a fair bit.
http://jsfiddle.net/PTkms/4/
angular.module('calculator', []).directive('wbcalc', function($compile) {
return {
restrict: 'E',
template: '<div><div class="span2">{{title}}</div><input ng-model="item"></div>',
scope: {
title: '#',
item: '='
},
link: function(scope, element, attrs) {
// Don't need to do this.
}
}
});
function calcCtrl($scope) {
$scope.var1 = '5';
$scope.var2 = '10';
$scope.calc = function() {
// Yes, this is a very simple calculation which could
// have been handled in the html with {{0 + var1 + var2}}.
// But in the real app the calculations will be more
// complicated formulae that don't belong in the html.
return parseInt($scope.var1) + parseInt($scope.var2);
};
}
I know you said you like jQuery - but to make best use of Angular you need to think in an Angular way - use bindings, don't manipulate the DOM directly etc.
For this example, it would be helpful to read up on the isolated scope bindings used - '#' and '=', see:
http://docs.angularjs.org/guide/directive