Rendering html in Angular with Directive - javascript

I am storing html data in my database from the output of a WYSIWYG editor. The html also stores the html for some directives. One of them being the ui-bootstrap tooltip:
<span tooltip-placement="left" tooltip="On the Left!">Tooltip text here</span>
I am able to get any other html elements to work by using the binding:
<div ng-bind-html-unsafe="html.content"></div>
But the html with the directive's reference doesn't interact with the actual directive.
How can I get the directive to work?
Do I have to compile the html or something like that?

Yes, since the markup you are trying to render contains directives, you need to compile it so they are processed by Angular.
You could create a directive that does that for you:
app.directive('htmlRender', function($compile) {
return {
restrict: 'E',
scope: { html: '#' },
link: function(scope, element) {
scope.$watch('html', function(value) {
if (!value) return;
var markup = $compile(value)(scope);
element.append(markup);
});
}
};
});
Plunker here.

Do I have to compile the html or something like that?
Yes You need to complile the HTML as Angular will only consider as below as simple html
<div ng-bind-html-unsafe="html.content"></div>
Check below is documentation for $compile
$complie documenation

Related

Angular.JS - Is it possible to have multiple views in a directive?

I'm trying to find out if you can have more than one html file in a directive and then depending on what you pass through the html tag it shows a different HTML layout.
This is something that is required for a accordion that I am building. We want to be able to re-use the directive but sometimes the html will be of a different layout.
So far I believe this can be done by the following:
<accordion data-attr="layout('feedback')></accordion>
Then in the Js check what has been passed and point the accodion to a different view.
The bit I'm unsure about is pointing the accordion directive to a different html file.
You could simply use the ng-include directive in your template:
module.directive('accordion', function() {
return {
scope: { type: '&' },
template: '<div ng-include="type()"></div>'
};
});
Yes, in your directive you can set your template to be a function that accepts the element and attributes.
.directive('accordion', function() {
return {
templateUrl: function(elem, attr){
return 'layout-' + attr.type + '.html';
}
};
});
And use it like:
<accordion type="some-file"></accordion>

How to set HTML lang attribute dynamically

We have a project contains a lot of HTML files. Unfortunately, we have recognized we should have used lang tag for our HTML files. We use AngularJS and Java.
Is there any way to set all pages HTML lang?
You can use a simple directive:
myApp.directive('html', [function() {
return {
restrict: 'E',
link : function(scope, element, attrs) {
attrs.$set("lang", "en"); // Set the "lang" value dynamically here
}
};
}]);
Just make sure the app is initialized at html tag i.e. <html ng-app> and you don't have to do this in your every page.

How to deal with css and custom directive in angulajs

Consider a custom directive in angularjs:
.directive('myDir', function() {
return {
restrict: 'E',
template: "..."
}
})
As far as I see the tag <my-dir></myDir> has no default style associated (or, at least, it is not a block tag). Now I want to style it and place in the right point of my page. I have 2 alternatives:
1) Use this template:
.directive('myDir', function() {
return {
restrict: 'E',
template: "<div class="layout">...</div>",
}
})
And css:
.layout {
/* bla bla bla */
}
But this introduce a new unnecessary level in the DOM three, since if I wrote something like <my-dir class="layout"></my-dir> with the proper css attached it would have worked anyway BUT I'll have to remember to add the same css class every time I use <my-dir> inside my code (and this is not DRY).
2) This led me to add style inside post-link function:
.directive('myDir', function() {
return {
restrict: 'E',
template: "...",
link: function(scope, element, attrs) {
element.addClass('layout');
}
}
})
Which strategy is better? Are there pros or cons I can't see?
UPDATE:
Using replace: true in directive definition is not an option, since it has been deprecated and when using bootstrap things like <my-dir class="visible-xs"></my-dir> may be useful.
You could solve this problem by having replace: true option in your directive, which will basically replace your directive DOM with the template which you have given from your directive.
Directive
.directive('myDir', function() {
return {
restrict: 'E',
template: "<div class="layout">...</div>",
replace: true //added replace true property
}
})
Resultant HTML
<div class="layout">..Content..</div>
Like above I shown, you could get rid off the unnecessary HTML tag by simply using replace option of directive.
Demo Plunkr
Note
As per Angular Docs replace option has been deprecated in Angular2,
but in your case you can avoid that.
But if you really wanted to go avoid the use of replace option of directive then I'd suggest you to use your directive as an attribute instead of going for an element but then you need to take <div class="layout"> outside and then your could put your directive over that like <div class="layout" my-dir>. But if you again think over it in Angular2 perspective, you are changing your component structure to old angular way using attribute/class

ng-bind-html with UI Bootstrap directives

I don't think this is directly an issue but I don't know how to do this. I'm trying to dynamically load content that uses UI Bootstrap directives but when the content is loaded UI Bootsrap components don't work. Being more specific, tooltips don't work. Here is the important code:
<div ng-bind-html="trustSnippet(f.field.contentAfter)"></div>
The javascript
$scope.trustSnippet = function(snippet) {
return $sce.trustAsHtml(snippet);
};
The HTML I'm trying to inject is:
<i class="fa fa-exclamation-circle" tooltip-placement="right" tooltip="On the Right!"></i>
Any clues?
TY
This is because ng-bind-html doesn't compile the inserted elements, and so the UI Bootstrap directives - or any other directive or expression, for that matter, would not work.
If you are getting the HTML from a particular location, you could simply use ng-include.
For static location:
<div ng-include="'path/to/html'"></div>
Or, if the location is dynamic and stored in a scope-exposed variable: $scope.path = "path/to/html";:
<div ng-include="path"></div>
Otherwise, if the HTML itself with Angular expressions/directives is dynamically generated or imported (a rare case, which should make you re-examine your design to make sure that you are not offending any best practices), you would need to compile it using $compile service, and it is better done using a directive:
app.directive("ngBindHtmlCompile", function($compile, $sce){
return {
restrict: "A",
link: function(scope, element, attrs){
scope.$watch($sce.parseAsHtml(attrs.ngBindHtmlCompile), function(html){
var el = angular.element("<div>").html(html);
element.empty();
element.append(el.children());
$compile(element.contents())(scope);
})
}
};
});
Don't forget to add "ngSanitize" as a dependency. The usage is:
<div ng-bind-html-compile="html"></div>
I was facing the same problem. The following method worked for me.
In HTML,
<div ng-bind-html="f.field.contentAfter | unsafe"></div>
In Javascript,
app.filter('unsafe', function($sce) {
return function(val) {
return $sce.trustAsHtml(val);
};
});

How can I use a directive wrapper for ng-grid with the grid options determined by a directive attribute?

I am attempting to create a re-usable directive wrapper for ng-grid where I can apply the location of the ng-grid options through the use of an attribute.
Here is the skeleton of the code which gets very close to what I want:
angular.module('myApp').directive('grid', function() {
return {
restrict: 'E',
template: '<div class="gridStyle" ng-grid="someObject.gridOptions"></div>',
link: function(scope, element, attributes) {
// no code here necessary, to use the hard-coded ng-grid options
}
}
}]);
What I would like to do is supply the variable for ng-grid by using an attribute, similar to this:
<grid dataLocation="someObject.gridOptions"></grid>
I've tried using the compile and the link options with multiple methods, including reading the attributes, and then using the element.html() method to update the html to set the ng-grid attribute to "someObject.gridOptions", as well as using {{someScopeVariable}}, and setting scope.someScopeVariable to "someObject.gridOptions" in the linking function. I have verified using the chrome's html inspector that the div's attribute looks correct, but I have not been able to get the item to show up on my page.
I suspect I'm running into issues since the ng-grid is already a compiled directive. Is there any way I can pull this off? I've tried a large number of compile and linking methods with no success yet.
To access the original template's attributes, the template option of directive should be defined as a function as follows:
angular.module('myApp').directive('grid', function() {
return {
restrict: 'E',
template: function(element, attrs){
return '<div ng-grid="' + attrs.dataLocation + '"></div>';
}
};
});
Your HTML stays the same:
<grid dataLocation="someObject.gridOptions"></grid>

Categories

Resources