use ng-bind-html with URL - angularjs

Currently I have a div with ng-bind-html that I use to bind to HTML that I pass in
<div ng-bind-html="renderHtml(message.text)" />
Currently message.text is HTML. I would rather like to pass in a URL and have that HTML inserted in to the div.
How can I achieve that?

Personally, i would make a custom directive to do such a thing.
I would send the url as attribute paramater, make the directive fetch the html with $http.get(), and when its loaded, append it to the html.

You can add the source as:
<div ng-bind-html="URl | safeHtml"></div>
And need to add $sce for safe binding of the url with this directive:
app.filter('safeHtml', function ($sce) {
return function (val) {
return $sce.trustAsHtml(val);
};
});
Edit:
Rather than direct binding the url, embed it in iframe and use it for safe binding:
$scope.URL = <iframe ng-src="url"></iframe>

I would do it like this:
<input ng-model="myUrl"/>
<div id="myDiv" />
And then add a watch for myUrl, and make a request for the url each time it changes:
$scope.$watch('message.text', function(newValue) {
$.ajax({ url: 'newValue', success: function(data) {
$("#myDiv").html(data);
}});
}

Related

Is it possible to access JSP style tag values in an angularjs html view?

Is it possible to access JSP style tag values in an angularjs html view? For example, can I get the ‘principal.firstName’ value in the browser, from the tag below? Is there an Angularjs way to do this?
<sec:authentication property="principal.firstName"/>
… or, do I need to access this serverside, and return in JSON? If I do need to access it server side, could you point me to an example, everything I have found just shows how to get principal.getName().
I’m trying to convert our jsp pages to be html.
I was able to do essentially the same thing by creating a JSON object, the values of which were these JSP variables which would be filled in by the server. This JSON object can be a separate page that is accessed by Angular using an $http.get() function.
Like this:
{'mySettings': {
'userID': <%=user.ID%>,
'firstName': '<%=user.first_name%>',
'lastName': '<%=user.last_name%>'
}}
The server will process the <%=whatever%> directives and replace them with actual data. Then your AngularJS code will issue an http.get to pull in this JSON and use it on the client.
You can create directive, that will read your attribute from sec:authentication tag and pass it on variable.
Example on jsfiddle.
angular.module('ExampleApp', [])
.controller('ExampleController', function() {
this.firstName = 1;
})
.directive("myProperty", function() {
return {
scope: {
val: "=myProperty"
},
link: function(scope, elem, attr) {
scope.val = attr.property;
}
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="ExampleApp">
<div ng-controller="ExampleController as vm">
<sec:authentication my-property="vm.firstName" property="principal.firstName" />{{vm.firstName}}
<div my-property="vm.name" property="john doe"></div>{{vm.name}}
</div>
</div>

How do I insert a partial from a directive?

I have a custom directive like below. In it I'd like to insert html from a partial:
.directive('myDirective', function($parse){
return {
restrict: 'A',
scope: { foo: '=' },
link: function(scope, element, attrs){
//add children to element[0] using html from partials/content.html
//...
}
});
Google/Stack doesn't reveal much, is there a way to do this or am I not meant to use directives in this way?
There are 3 possible things that you can do
You could either make $templateRequest to the html page, which internally make an ajax to fetch html & then put that html inside the $templateCache.
Code
$templateRequest('mypath.html').then(function(res){
//inside you could have html content in res.data;
var html = res.data;
})
You could put that html content inside $templateCache service inside run block & then use it whenever required using $templateCache.get('templateName')
Code
app.run(function($templateCache){
$templateCache.put('mytemplate.html', '<div>My HTML</div>')
})
//inside directive do below thing
var html = $templateCache.get('mytemplate.html');
Place the html content inside script block which will have type="text/ng-template" which will again force this template to put inside the $templateCache service. and this template will make you available instantly.
Markup
<script type="text/ng-template">
<div>My Content<div>
</script>
//inside directive you need to access it from $templateCache.
var html = $templateCache.get('mytemplate.html');

AngularJS get external template and $compile

I have an external template called _include.tmpl.html it's contents are:
<script id="sBlock1" type="text/ng-template">
<li ng-repeat="user in users" data-customer-id="{{user.CustomerID}}" ng-class-odd="'alternate'" ng-click="GetOrdersForUser($event)">
<span class="name">{{user.ContactName}}</span><br>
<span class="title">{{user.ContactTitle}}</span><br>
<span class="phone">{{user.Phone}}</span><br>
<span class="country">{{user.Country}}</span>
</li>
</script>
I would like to load the external template, feed it my array of users and get the compiled result? I was trying the below with no luck?
$http.get('_include.tmpl.html', { cache: $templateCache })
.then(function (response) {
var test = $compile(response.data)($scope.users);
console.log(test);
});
The use case is - for infinite scroll. You scroll down, I fetch results from a db feed the results to the template, get the compiled html and append that to the DOM. Each time you scroll down you get more results and the results keep getting appended to the DOM element.
You seem to be trying to mimic what a custom directive can already do for you
HTML
<users></users>
JS
angular.module('myApp').directive('users', function(){
return {
templateUrl:'_include.tmpl.html'
};
});
This will make the server side ajax call for you and populate the data. It will also store the template in $templateCache so another call to server isn't needed
However you would need to remove the wrapping script tag which is part of what is currently causing you problems
It should be $compile(response.data)($scope);.
And considering _include.tmpl.html template has been loaded, and sBlock1 is in template cache, it has to be
$http.get('sBlock1', { cache: $templateCache })
.then(function (response) {
var test = $compile(response.data)($scope);
console.log(test);
});

ng-bind-html strips elements attributes

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

Angularjs two way data binding on ng-repeat with directive

What I am trying to achieve: trigger an angular js directive on a jquery ui event which in turn calls a method in the controller, which adds a value in an array which should appear in the view because of the two way data binding.
What does not work: the value is properly added in the array but the view is not updated
HTML
<li id="collection_{{$index+1}}" class="collection" ng-repeat="collection in column | filter:avoidUndefined" ng-controller="collectionController" ng-model="collection">
<h1 class="collection-title"> {{ collection.title }}</h1>
<ul class="bookmarks sortable droppable" droppable=".draggable" sortable=".bookmarks.sortable">
<li class="bookmark" ng-repeat="bookmark in collection.bookmarks">
<a class="bookmark-link bookmark-{{bookmark.id}}" href="{{bookmark.url}}">{{bookmark.title}}</a>
</li>
</ul>
</li>
DIRECTIVE
directives.directive('droppable',function(){
return {
link:function(scope,el,attrs){
el.droppable({
accept: attrs.droppable,
drop: function( event, ui ) {
var url = $(ui.helper).text();
scope.addBookmark(null, url, url);
$(this).removeClass('ui-state-highlight');
},
...
CONTROLLER
$scope.addBookmark = function (id, title, url){
if (id == null) {
...
}
var bookmark = {
'id': id,
'title': title,
'url': url,
'type': 'bookmark'
};
$scope.collection.bookmarks.push(bookmark);
};
$scope.collection.bookmarks is updated properly but the view stays the same. If I call the same addBookmark function directly with a normal button it works.
You forgot to wrap your drop-callback in $apply:
directives.directive('droppable',function(){
return {
link:function(scope,el,attrs){
el.droppable({
accept: attrs.droppable,
drop: function( event, ui ) {
scope.$apply(function(scope){
var url = $(ui.helper).text();
scope.addBookmark(null, url, url);
$(this).removeClass('ui-state-highlight');
});
},
...
My recommendation is to use $emit instead of calling a method of the controller directly in your directive.
Directives should be always independent components, if inside the directive there is a call to a method from a controller(outside the directive) this will create a dependency between my directive and the controller and of course this will force one not being able to exist without the other.
If I would have to apply a design principle to a directive it will be the S in SOLID, Single responsibility principle. Directives should be able to encapsulate and work independently.
I would probably try this on my directive: scope.$emit("UrlChanged", url);
Something like this:
directives.directive('droppable',function(){
return {
link:function(scope,el,attrs){
el.droppable({
accept: attrs.droppable,
drop: function( event, ui ) {
var url = $(ui.helper).text();
scope.$emit("UrlChanged", url);
$(this).removeClass('ui-state-highlight');
},
On my controller:
$scope.$on("UrlChanged", function(event, url){
... //your has changed.
});

Resources