Can't bind html with angularJS and tinymce - html

so the goal is to edit some text with tinymce, persist it and display it in a div using angularJS with the same html, style formating.
I'm using tinymce 3.5.8 with angularUI directive, I've managed to save the content of the wysiwyg in my database (mySQL, TEXT). I'm retrieving it through Spring as a String and send it back to the angularJS app.
I've tried putting a
<div ng-bind-html-unsafe="myModel">
where myModel is defined as
$scope.myModel = Projet.get(getting the json somewhere);
but tags are not interpreted as html, they just print like
<p><span style="color #ff9900;>Texte de test</span></p>.
I've also tried with ngSanitize and ng-bind-html.
html :
<div class="content-swipe-box">
<h3>Contexte</h3>
<div ng-bind-html-unsafe="projet.contexte"></div>
</div>
controller :
$scope.projet = ProjetService.getProject($routeParams.projectId);
Database entry (TEXT)
<p><span style="color: #ff9900;">aaaaa</span></p>
<p>&nbsp;</p>
directive (that's the angularui directive where I've added options):
...
link: function (scope, elm, attrs, ngModel) {
var expression, options, tinyInstance;
// generate an ID if not present
if (!attrs.id) {
attrs.$set('id', 'uiTinymce' + generatedIds++);
}
options = {
skin:"bootstrap",
theme_advanced_disable:"styleselect, anchor",
plugins : "advlist, fullscreen, preview",
theme_advanced_buttons1:"bold, italic, underline, justifyleft, justifycenter, justifyright, justifyfull, formatselect, fontselect, fontsizeselect, forecolor",
theme_advanced_buttons2:"bullist, numlist, outdent, indent, undo, redo, link, unlink, image, cleanup, code, blockquote, hr,removeformat,visualaid,separator,charmap, preview, fullscreen ",
theme_advanced_resizing: true,
theme_advanced_resize_horizontal : false,
force_br_newlines : true,
force_p_newlines : false,
Thank you for your help !

It sounds like you have saved the html to your database as escaped html. If this is what has happened then you will have to unescape it first, you can do that using the technique described in this answer
function htmlDecode(input){
var e = document.createElement('div');
e.innerHTML = input;
return e.childNodes.length === 0 ? "" : e.childNodes[0].nodeValue;
}
htmlDecode("<img src='myimage.jpg'>");
// returns "<img src='myimage.jpg'>"

Related

HTML.TextAreaFor - removing html tags for display only

In an MVC application I have to use #HTML.TextAreaFor to display some text from a database, the trouble is sometimes that text may have HTML tags within it and I can't see a way to remove those for display only.
Is it possible to do this in the view (maybe with CSS?) without having to strip the tags in the controller first?
EDIT
The data coming from the controller contains html tags which I do not want to remove, I just don't want to display them
Normally I would use #HTML.Raw but it has to work in a #HTML.TextAreaFor control.
If you want to decode Html returned from the Controller you can use the following JavaScript method:
This method decodes "Chris&apos; corner" to "Chris' corner".
var decodeEntities = (function () {
// this prevents any overhead from creating the object each time
var element = document.createElement('div');
function decodeHTMLEntities(str) {
if (str && typeof str === 'string') {
// strip script/html tags
str = str.replace(/<script[^>]*>([\S\s]*?)<\/script>/gmi, '');
str = str.replace(/<\/?\w(?:[^"'>]|"[^"]*"|'[^']*')*>/gmi, '');
element.innerHTML = str;
str = element.textContent;
element.textContent = '';
}
return str;
}
return decodeHTMLEntities;
})();
You can do this by using a razor code in your view.
#Html.Raw(HttpUtility.HtmlDecode(Model.Content))
if I set Model.Content to this string "<strong>This is me</strong><button>click</button>", the code above will render it like HTML code and will have a strong text next to a button as an output like the image below:
There's some nice rich text editors libraries like CK Editor, Quill, or TinyMCE that can display HTML while still maintaining the editor capabilities of being a text editor. All of these libraries have capabilities of being read-only as well if that's necessary.
Example from Quill -
Sorted this by changing TextAreaFor toTextBoxFor and setting a formatted value.
#Html.TextBoxFor(x => Model.MyItem, new { #class = "form-control", #required = "true", Value = Regex.Replace(Model.MyItem, "<.*?>", String.Empty) })

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);
}
});
}
}
});

Bold specific words doesn't display b tags angular js

I am creating an Angular JS app and i get a text from the server that i need to display in my site.
The text i got from server :
"My name is <b> John </b>" with b tags.
Unfortunately, when I display it every one notify the strong text tags.
{{text}}
I would like that users will see only "John" in bold.
Any ideas?
PLUNKER DEMO
You can use ng-bind-html as #CAT suggested, but if you don't want to go as much as use $sce.trustAsHtml() explicitly you can use this directive:
.directive('htmlSafe', function($parse, $sce) {
return {
link: function(scope, elem, attr) {
var html = attr.ngBindHtml;
if(angular.isDefined(html)) {
var getter = $parse(html);
var setter = getter.assign;
setter(scope, $sce.trustAsHtml(getter(scope)));
}
}
};
});
and then you can use it like this in your html:
<div ng-bind-html="html" html-safe></div>
That's normal behavior, it's happening because of Angular's sanitize. You can't just print out html tags. You need to use ng-bind-html.

jQuery mobile: json processing before mobile enhancements

I'm starting to develop a mobile app with jQuery Mobile. The idea is to build HTML static pages, and before showing them, call to the server to obtain the i18n text for the input labels and buttons. I mark the HTML elements that are susceptible to change the inner text with an special attribute: "data-i18n":
For a label:
<label data-i18n="login.username" for="loginPaciente.username">login.username</label>
For a button:
<button data-i18n="login.submit" type="submit" data-theme="a">login.submit</button>
I call to the server using JSON:
$('#pageLogin').live('pagebeforeshow',function(event, ui){
var action = "/MyServerApp/namespace1/mobile_Action_Login_configPage.action";
$.getJSON(action, function(data) {
var resources = data.i18n_resources;
var id, text;
var $scope = $('#pageLogin');
for (i=0; i<resources.length; i++){
id = resources[i].id;
text = resources[i].text;
$scope.find('[data-i18n="' + id + '"]').html(text);
}
});
});
This works perfectly with the labels, because JQM doesn't modify these HTML elements. The problem comes with the button, because JQM hides the button I've defined, and creates a new span to render the button. When I read the JSON result, I can find and change the button I've defined, but not the new span that JQM has created, so the text that appears on screen is the old one: "login.submit".
Is there any way to execute the JSON call before JQM changes the HTML code?
P.D.: The reason for not building the whole HTML page dynamically (including i18n texts) is that in the future, I want to encapsulate the web app with PhoneGap or a similar shell, and I want to distribute the HTML pages, CSS and scripts inside the application, and minimize the data traffic with the server.
Thanks in advance:
Carlos.
EDIT: invoking $scope.trigger('create') after changing the text doesn't solve the problem.
Finally I've found the solution to my problems by myself, catching the event "pagebeforecreate".
I invoke this function on each page I need to internationalize, passing the server action I need to call and the page id:
function utils_loadConfigPage(action, pageid){
$(document).bind("pagebeforecreate", function(){
var $page = $('#' + pageid);
var _action = action;
var paramCallback = "jsoncallback=?";
var concat = "?";
if (_action.indexOf("?")!=-1){
concat = "&";
}
_action += concat + paramCallback;
$.ajaxSetup({"async": false});
$.getJSON(_action, function(data){
utilis_doConfigPage(data, $page);
});
$.ajaxSetup({"async": true});
});
}
Note that I force to use synchronous calls to the server, in order to avoid the page mobile enhancing before the i18n texts were ready.
This is the function that is invoked in the json callback:
function utils_doConfigPage(data, $scope){
utils_seti18nTexts(data, $scope);
utils_setPlaceholders($scope);
}
This function finds all i18n elements and override their inner html with the translated texts:
function utils_seti18nTexts(data, $scope){
var resources = data.i18n_resources;
var id, text;
for (i=0; i<resources.length; i++){
id = resources[i].id;
text = resources[i].text;
$scope.find('[data-i18n="' + id + '"]').html(text);
}
}
This function overrides the placeholder texts for the inputs:
function utils_setPlaceholders($scope){
$scope.find('div[data-role="fieldcontain"].ui-hide-label').each(function(){
var textLabel = $(this).find('label').html();
$(this).find('.placeholder').attr('placeholder', textLabel);
});
}
And finally, this is the jsp that produces the i18n resources. I use Struts2, so the jsp is not invoked directly. I invoke an action and the jsp is only the view. The i18n resources are obtained using Struts2 capabilities:
<%# page contentType="application/json; charset=UTF-8" pageEncoding="UTF-8" %>
<%# taglib prefix="s" uri="/struts-tags"%>
<s:property value="jsoncallback" />({
"i18n_resources":
[
{
"id" : "MOBILE_APP_NAME",
"text" : "<s:text name="APP_NAME" />"
}
,{
"id" : "TITLE_LOGIN",
"text" : "<s:text name="TITLE_LOGIN" />"
}
,{
"id" : "LOGIN_USERNAME",
"text" : "<s:text name="LOGIN_USERNAME" />"
}
,{
"id" : "LOGIN_PASSWORD",
"text" : "<s:text name="LOGIN_PASSWORD" />"
}
,{
"id" : "BUTTON_OK",
"text" : "<s:text name="BUTTON_OK" />"
}
,{
"id" : "MOBILE_APP_FOOTER",
"text" : "<s:text name="MOBILE_APP_FOOTER" />"
}
]
})
I don't know if this is the best way to internationalize a JQM application. Any suggestion will be apreciated.
You can just change the text of the <span> element with the ui-btn-text class:
$scope.find('[data-i18n="' + id + '"]').find('.ui-btn-text').html(text);
Or if you aren't sure if the element will have been initialized by jQuery Mobile you can check for the existence of the ui-btn-class first:
var $btn_text = $scope.find('[data-i18n="' + id + '"]').find('.ui-btn-text');
if ($btn_text.length > 0) {
$btn_text.html(text);
} else {
$scope.find('[data-i18n="' + id + '"]').html(text);
}