Template inside of a directive - javascript

I have a strange situation where I need to put a template inside of a template in my directive. The reason for this is that AngularJS will not read ng-repeat code inside of attributes.
In an ideal world, this is what my code would look like:
<div ng-repeat="phone in phones">
<button popover="<div ng-repeat='friend in phone.friends'>{{friend.name}}</div>"></button>
</div>
Unfortunately this does not work because of the quotes around the popover attribute. This has led me down a pretty deep rabbit hole where I'm trying to put a template inside of a template like in this plunker:
http://plnkr.co/edit/ZA1uA1UOlU3cCH2mbE0X?p=preview
HTML:
<div my-popover></div>
Javascript:
angularApp.directive('myPopover', function( $compile) {
var getTemplate = function()
{
var scope = {title: "other title"};
var template = "<div> test template. title: {{title}}</div> ";
return $compile(template)(scope);
}
return {
restrict: 'A',
template: '<button class="btn btn-default" popover="{{content}}" popover-title="title">template</button>',
link: function(scope) {
scope.content = getTemplate();
}
};
})
Unfortunately this does not work because AngularJs complains about a circular reference. Please help! (this has been taking me all day)

I'm not sure I understand exactly what you are trying to achieve, but from the look of it you might want check out the transclude option for directives.
From the docs:
use transclude: true when you want to create a directive that wraps
arbitrary content.
If you use transclude, you can store the popover content inside the button, and "forward" that content to where you want it using the ng-transclude directive.
Your code would then look something like this:
<button>
<div ng-repeat='friend in phone.friends'>{{friend.name}}</div>
</button>
You can see some examples in action in the guide to directives.

Related

angularjs click event, after appending content [duplicate]

I am working with angularjs 1.2.0-rc.3. I'd like to include html code into a template dynamically. For that I use in the controller :
html = "<div>hello</div>";
$scope.unicTabContent = $sce.trustAsHtml(html);
In the template I got :
<div id="unicTab" ng-bind-html="unicTabContent"></div>
It works fine for regular html code. But when I try to put angular template it is not interpreted, it is just included in the page. For example I'd like to include :
<div ng-controller="formCtrl">
<div ng-repeat="item in content" ng-init="init()">
</div>
</div>
Thanks a lot
This solution doesn't use hardcoded templates, and you can compile Angular expressions embedded within an API response.
Step 1.
Install this directive: https://github.com/incuna/angular-bind-html-compile
Step 2. Include the directive in the module.
angular.module("app", ["angular-bind-html-compile"])
Step 3. Use the directive in the template:
<div bind-html-compile="letterTemplate.content"></div>
Result:
Controller Object
$scope.letter = { user: { name: "John"}}
JSON Response
{ "letterTemplate":[
{ content: "<span>Dear {{letter.user.name}},</span>" }
]}
HTML Output =
<div bind-html-compile="letterTemplate.content">
<span>Dear John,</span>
</div>
For reference sake, here's the relevant directive:
(function () {
'use strict';
var module = angular.module('angular-bind-html-compile', []);
module.directive('bindHtmlCompile', ['$compile', function ($compile) {
return {
restrict: 'A',
link: function (scope, element, attrs) {
scope.$watch(function () {
return scope.$eval(attrs.bindHtmlCompile);
}, function (value) {
element.html(value);
$compile(element.contents())(scope);
});
}
};
}]);
}());
This is what I've made, no idea if it's the angular wayTM, but it works and is super simple;
.directive('dynamic', function($compile) {
return {
restrict: 'A',
replace: true,
link: function (scope, element, attrs) {
scope.$watch(attrs.dynamic, function(html) {
$compile(element.html(html).contents())(scope);
});
}
};
});
So;
<div id="unicTab" dynamic="unicTabContent"></div>
Edit: I found the angular way, and it's super simple.
$templateCache.put('unicTabContent', $sce.trustAsHtml(html));
<div id="unicTab" ng-include="'unicTabContent'"></div>
Don't need to make your own directives or anything.
But it's a bind-once sort of deal, it wont see changes made to your html like the custom directive does.
As Vinod Louis says in his comment, the best way to do that was to use templates. I had to define a template outside of the regular code, for example I added that code inside of my index.html :
<script type="text/ng-template" id="unic_tab_template.html">
<div ng-switch on="page">
<div ng-switch-when="home"><p>{{home}}</p></div>
<div ng-switch-when="form">
<div ng-controller="formCtrl">
<div ng-repeat="item in content">{{item.name}}:{{item.value}}</div>
</div>
</div>
<div ng-switch-default>an error accured</div>
</div>
</script>
This template is conditional, so depending on the value of $scope.page, it switches between the 3 templates (the third being an error handler). To use it I had :
<div id="unicTab" ng-controller="unicTabCtrl">
<div ng-include="'unic_tab_template.html'"></div>
</div>
That way my page changes depending on the $scope inside of my unicTabCtrl controller.
To conclude the idea of inserting angularsjs template seams to be difficult to realize ($compile seams to be the solution, but I wasn't able to make it work). But instead you may use conditional templating.
I was trying to do the same thing and came across this module.
http://ngmodules.org/modules/ng-html-compile
I just included it and then I was able to use "ng-html-compile" instead of "ng-bind-html"
My solution to similar problem in my current app without using template (not elegant, but working):
directive('ngBindHtmlCompile', ['$compile', function ($compile) {
return {
restrict: 'A',
compile: function compile(tElement, tAttributes, transcludeFn) {
return function postLink(scope, element, attributes) {
scope.$watch(function() {
return scope.$eval(attributes.ngBindHtml);
}, function(newValue, oldValue) {
$compile(element.children())(scope);
});
};
}
};
}]);
It requires ngBindHtml on the same element and compiles the element content after it changes with ngBindHtml.
<div id="unicTab" ng-bind-html="unicTabContent" ng-bind-html-compile></div>
ng-html-compile looks similar but at first glance it won't be recalculated when the template content is changing. But I haven't tried it.
One way is use a directive for purpose of inserting custom templates that include angular expresssions
<div id="unicTab" unic-tab-content></div>
app.directive("unicTabContent",function(){
return {
restrict:"A",
template:'{{unicTabContent}}'
}
})
The code below is much simpler using Angular's built-in $interpolate and $sce objects. First inject the $interpolate and $sce Angular objects into your directive as you do anything custom you need in your directive.
amqApp.directive('myDir', ['$interpolate', '$sce', function ($interpolate,$sce ) {...}
Then create all your scoped variables found in your imported html expressions...
$scope.custom = 'Hello World';
Next use $interpolate to process your custom HTML and its expressions...then make sure you use the $sce object to trust it as HTML before binding...
var html = $interpolate('<b>{{custom}}</b>')($scope);
$scope.data = $sce.trustAsHtml(html);
Finally, in your view, just make sure use an element with the "ng-bind" or "ng-bind-html" on it in your view display. I found the $sce piece wont display the HTML as HTML (sees it as text) if you don't bind it in your html template like this...
<span ng-bind-html="data"></span>
You should see in bold...
Hello World
I used this trick to import in text/HTML with custom angular {{expressions}} from a web.config.
A lot simplified solution of #clement-roblot based on built-in templates.
Controller:
app.controller('TestCtrl', [
'$scope',
'$templateCache',
function ($scope, $templateCache) {
$templateCache.put('test.html', '2 + 2 = {{ 2 + 2 }}');
}
]);
View:
<div ng-include="'test.html'"></div>

Transclude example not working for me

I've been taking a look for this tutorial, and now I'm trying to follow it. But somehow, when I reach the following JSBin and paste it all on my test folder, it just won't work:
http://teropa.info/blog/2015/06/09/transclusion.html
You can see at the right side the card showing up perfectly. Well, when I copy paste this code, the content doesn't get rendered inside the "content" div of the template, which means that transclusion isn't working at all.
What may be happening? The code is perfectly pasted, both HTML, CSS and JS. Even tried with my local version of Angular (last one).
But the content keeps being hidden! Any help with this? I really wanna learn how the transclusion works.
Consider I have created a directive called myDirective as an element
<div ng-controller="myCtrl">
<my-directive>
<button>some button</button>
and a link
</my-directive>
</div>
myDirective has a template which is using transclude
myApp.directive('myDirective', function(){
return{
restrict: 'E',
transclude: true,
template: '<div class="something" ng-transclude> my directive goes here...</div>'
}
});
It will render the DOM as
<div class="something">
my directive goes here...
<button>some button</button>
and a link
</div>.

$http.get on HoverOver to populate PopOver text

I'm trying to implement a feature using Angular and Boostrap where the user can get a popOver on an item in the list, and have it perform an angular factory $http.get function to retrieve data and populate the popover text.
I'm not sure this is the best approach, but I have a ng-repeat like so:
<ul>
<li ng-repeat="product in products">
<model-popover ng-attr-id="{{product.Id}}"></model-popover>
</li>
</ul>
And my best guess is to use an angular directive, taking in the id number as a scope attribute,and performing a factory call from the directive. I've read up on the controller/link functions within the directive, but not sure the proper implementation
app.directive('modelPopover', ['Factory', function (Factory) {
return {
restrict: 'E',
replace: true,
scope: { id: "=" },
controller: function($scope){
var prod = Factory.getProductDetail(id);
},
template: '<a popover-placement="bottom" popover="{{prod}}">{{prod}}</a> '
};
}]);
I know the directive is incorrect, but i'm hoping there's enough information to help me out. Thanks in advance!
You do not need special directive for this, due to value-binding u can just change scope variable and popover will change also.
So u simply:
<button popover="{{var}}" popover-trigger="mouseenter" class="btn btn-default" ng-mouseover="changeVar()">Mouseenter</button>
And in changeVar you can change $scope.var any way you want.
Here is example plunk ($http call emulated using $timeout):
http://plnkr.co/edit/gnm1BtnHzNLnvO62Ar2i?p=preview
This var prod = Factory.getProductDetail(id);
has to be changed to $scope.prod = Factory.getProductDetail(id) if you want to use the mustaches

How to dynamically nest directives in AngularJS

New to Angular and need some assistance.
I have a block of HTML content that will be coming from a database that will contain a group of widgets. These are simple widgets that will essentially render out various elements, but for the purposes of this question we'll assume they're all basic HTML inside.
Those widgets are included in an unpredictable way, so my first thought was to use directives to render the HTML. So, we'd have something like:
<div widget data="This is the content."></div>
So I've got a directive that will place the value of data into the div. Easy enough!
Now, how would I go about nesting those widgets? So, how would I get something like:
<div widget data="Welcome! ">
<div widget data="This is some inside content."></div>
</div>
to render out:
Welcome! This is some inside content.
... because the issue I'm noticing is that if I place anything inside the directive HTML, it essentially gets ignored since it gets replaced with its own result (thus only echoing out Welcome!).
I realize I may be going the wrong direction on this in the first place, so any insight would be greatly appreciated. Thanks so much!
This is where you need to use the transclusion feature of the directive combined with ng-transclude directive.
Directive that marks the insertion point for the transcluded DOM of the nearest parent directive that uses transclusion.
Any existing content of the element that this directive is placed on will be removed before the transcluded content is inserted.
A very basic version of transclusion of content based on your example might look something like this:
.directive('widget', function() {
return {
transclude: true,//Set transclusion
template: '{{text}} <section ng-transclude></section>', <!-- set where you need to present the transcluded content -->
scope: {
text: "#"
}
}
});
Demo
angular.module('app', []).directive('widget', function() {
return {
transclude: true,
template: '{{text}} <section ng-transclude></section>',
scope: {
text: "#"
}
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app">
<span widget data-text="Welcome! ">
<div widget data-text="This is some inside content.">
<span widget data-text="This is some inside inside content."></span>
</div>
</span>
</div>

Mustache.js to Angular.js, triple bracers in Angular?

I have the following in Mustache.js:
<div>{{{icon.tmpl}}}</div>
icon.tmpl is a template on its own with the following content:
<div id="{{id}}" class="glyphicon glyphicon-send"></div>
In Mustache.js, thanks to the triple bracers, this works perfectly, both levels of templates gets compiled. Now I can't make this work in Angular.js. The second embedded template does not get compiled, but is instead surrounded by quotation marks "..."
How to make this work in Angular?
You could either use an ngInclude or create a directive. Here is an example of an icon directive that essentially just replaces any icon element with the div info you've specified.
http://plnkr.co/edit/NK5bOFvsgpMGeTkteMif?p=preview
html:
<icon></icon>
js:
app.directive('icon', function ( $compile, $timeout) {
return {
restrict: 'EA',
replace: true,
template: '<div id="{{id}}" class="glyphicon glyphicon-send"></div>'
}
})
The directive could just as easily be something like <div class="icon"> or <div icon> and you could apply the template to it.
An example of the ngInclude:
<ng-include src="'icon.html'"></ng-include>
Where icon.html just has your template info. Make sure that id is in the scope in both cases.

Categories

Resources