Howto set template variables in Angular UI Bootstrap? (accordion) - angularjs

I try to build a accordion with Angular UI Bootstrap (http://angular-ui.github.io/bootstrap/#/accordion). On How do I set model according to chosen accordion group. UI Bootstrap i found a working solution to use a template.
In my code i add the template with <script type="text/ng-template" id="template/accordion/accordion-group.html">
In this template a can use {{heading}} set by <accordion-group heading="{{group.title}}" content="{{group.content}}" ng-repeat="group in groups"></accordion-group>.
But how do i set other custom template variables?
I tried to set content="{{group.content}}" in the accordion tag too. Even if set, i don't know how to use it, tried {{content.group}} {{content}} and {% content %}.
Complete code on: http://plnkr.co/dSIVGg64vYSTAv5vFSof

See the edited plunker: http://plnkr.co/edit/8YCUemqILQy3knXqwomJ
You were trying to the nest a controller within the template of a directive. I might be mistaken, but that doesn't work or at least not in a very nice manner.
Instead of nesting controllers I would recommend converting CollapseDemoCtrl into a directive too. You can then pass the {{group.content}} or any other content to this directive.
EDIT: Example of how to pass data to directive scope
The HTML will be something like this:
<span ng-repeat="group in groups">
<div testDirective content="group.content" title="group.title"> </div>
</span>
The directive will look something like this:
angular.module('testModule', [])
.directive('testDirective', function(){
return {
restrict: 'A',
scope: {
content:'=content',
title: '=title'
},
template: '<h1>{{title}}<h1> <div>{{content}}</div>',
link: function(scope, element, attrs) {
}
}
});

Related

How to embed an html page using angular directive

I am creating an angular.js application.
I have written a html page and wants to put it under div using directive
<div data-(<directive-name)>
</div>
DxPDictionary.directive('import', [function () {
return {
restrict: 'A',
templateUrl: 'template/Import.html',
scope: false,
}
It's not working, is this approch is right or should use another way to achieve this
<body ng-controller="userCtrl">
<div class="container">
<div ng-include="'myUsersList.html'"></div>
<div ng-include="'myUsersForm.html'"></div>
</div>
</body>
use like this.
<div data-directive-name>
</div>
DxPDictionary.directive('dataDirectiveName', [function () {
return {
restrict: 'A',
templateUrl: 'template/Import.html',
scope: false,
}
your directive name dataDirectiveName in directive definition in camel case format and directive name data-directive-name on DOM should match.
You can use ng-include if you are not creating reusable components using directive and want use is as only html of the page.
There is already a directive for this purpose. You do not need to create your own.
https://docs.angularjs.org/api/ng/directive/ngBindHtml
Ashley's answer is good if you keep your html in a file. If you dynamically generate your html, you can use ng-bind-html directive.

Manually moving transcluded content

I have a directive foo whose template includes a list via ng-repeat:
<div>
<h5>I want to insert transcluded template into body of the li:</h5>
<ul>
<li ng-repeat='item in items'>
<!-- need item template here -->
</li>
</ul>
</div>
I want the template for each item to (optionally) to be specifiable at the point of usage of the directive:
<foo items='people'>
<h5>{{item.name}}</h5>
</foo>
<foo items='people'>
{{item.name}} is {{item.age}} years old.
</foo>
So I need the innerHTML of the directive (e.g. <h5>{{item.name}}</h5>) to be copied to the marked location in the directive template.
<ng-transclude> does this, but it gives the transcluded items the scope of the directive rather than the item. I also need to be able to optionally pull the item template from somewhere else. So I need to do the transclusion manually.
I have access to the transcluded content during link:, but at that point the list item in question is gone!
<div>
<h5>I need to insert transcluded template into body of the li:</h5>
<ul>
<!-- ngRepeat: item in items -->
</ul>
</div>
I think I need to do it during compile, but the transclusion function passed to the compile function is deprecated.
Found a way to do it with a second directive, but that seems unnecessary...
You can achieve that using $interpolate service and changing a bit your approach please see demo here http://plnkr.co/edit/bSb7fEWiMTdNVJYyiXD8?p=preview
set your dynamic template as attribute in directive.
<foo template="'<h1>{{item.name}}</h1>'" items="people"></foo>
and change your directive to :
app.directive('foo', function($interpolate) {
return {
scope: {
items: '=',
template:'='
},
restrict: 'E',
transclude: true,
templateUrl: 'foo-template.html',
link: function(scope, element, attributes, controller, transclude) {
//interpolate your template like below
scope.getValue= function(item) {
var exp = $interpolate(scope.template);
var result =exp({item:item})
return result;
}
}
}
});
and in your template use ng-bind-html
<li ng-repeat='item in items'>
<div ng-bind-html="getValue(item)"></div>
</li>
don't forget to add ngSanitize module to your app

Can an angular directive contain a controller?

Or perhaps a better question is, should a directive contain a controller?
For reasons of separation, my index.html is a simple file. Everything is rendered into it via templates. So my index.html is real simple:
<body ng-app="myapp"><mainmenu></mainmenu><div ng-view></div></body>
I don't really need a directive for mainmenu, but it allows me to put the menu in a separate template file. The main menu itself contains user info, login/logout, and a search box.
<div class="leftmenu" ng-show="isLogin()">
<ul class="menu">
<li>Part1</li>
<li>Part2</li>
<li>Part3</li>
</ul>
<div ng-controller="Search" class="Search><input type="text" ui-select2="s2opts" style="width:250px;" ng-model="search" data-placeholder="search"></input></div>
</div>
<div class="rightmenu">
<ul ng-show="isLogin()" class="menu">
<li>My Account</li>
<li>Logout</li>
</ul>
<ul ng-show="!isLogin()" class="menu">
<li>Login</li>
<li>Register</li>
</ul>
</div>
So there is the menu part, with its own controller, and the search, with its own, embedded between the two parts.
Of course, my mainmenu directive unit tests fail because SearchController isn't defined. But this leaves me wondering if I am going about this wrong. Should I even have it like this, a section of html with an explicit ng-controller defined inside it? Doesn't this create all sorts of weird dependencies?
How should I better structure this? A search directive that is included so I can unit test it separately? Something feels wrong here structurally...
UPDATE:
As requested, a basic fiddle: http://jsfiddle.net/nj4n44zx/1/
As specified by the Angular documentation, the best practice is to define a controller inside a directive only to expose an API to another directive. Otherwise the link function is sufficient.
See at the bottom of :
Angular directives
By experience using controllers inside a directives shadow what you are doing in your scope. It does not help to have a easy readable code.
I do prefer using the main controller where the directive is included. With a non isolated scope you have access to everything from the link function.
I usually deal with it like that:
app.directive('topMenu', function() {
return {
restrict: 'E', // or whatever You need
templateUrl: '/partials/topmenu', //url to Your template file
controller: function($scope) {
$scope.foo = "bar";
}
};
});
Then, in that template You don't have to add ng-controller.
sure your directive can contain a controller because you declare a directive like this
myApp.directive('mainMenu', function() {
return {
restrict: 'E',
replace: true,
scope: true,
templateUrl: 'menu.html',
controller:['$scope', function($scope) {
//define your controller here
}]
};
});

ng-repeat with ng-include not working

I am trying to use an ng-repeat that includes an ng-include. The problem is that the first element in the ng-repeat is just the ng-include template with none of the data from the ng-repeat filled in. Is there a way I can somehow bind the template from the ng-include so it works on the first ng-repeat?
<div ng-repeat="item in items">
<div ng-include src="'views/template.html'"></div>
</div>
For example, if my ng-repeat contains 10 items, then the first item that is rendered will just be the empty template. Items 2-10 WILL be rendered as they should be. What am I doing wrong?
First make sure that the data that is contained in the first index of items actually has the data that you want.
One possible solution to your problem would be to simply not show the first index of the ng-repeat:
<div ng-repeat="item in items" ng-show="!$first">
<div ng-include src="'views/template.html'"></div>
</div>
This may not actually tackle the root of your problem, but it may still get your application working a bit more like what you expect.
Another possible solution:
<div ng-repeat="item in items" ng-include="'views/template.html'"></div>
see example here:
http://plnkr.co/edit/Yvd73HiFS8dXvpvpEeFu?p=preview
One more possible fix just for good measure:
Use a component:
html:
<div ng-repeat="item in items">
<my-include></my-include>
</div>
js:
angular.module("app").directive("myInclude", function() {
return {
restrict: "E",
templateUrl: "/views/template.html"
}
})
I ran into the same problem, and finally figured out that the first element has not been fetched and compiled in time for the first ng-repeat iteration. Using $templateCache will fix the problem.
You can cache your template in a script tag:
<script type="text/ng-template" id="templateId.html">
<p>This is the content of the template</p>
</script>
Or in your app's run function:
angular.module("app").run(function($http, $templateCache) {
$http.get("/views/template.html", { cache: $templateCache });
});
You can also use $templateCache inside your directive, although it's a bit harder to setup. If your templates are dynamic, I would recommend creating a template cache service. This SO question has some good examples of template caching inside a directive and a service:
Using $http and $templateCache from within a directive doesn't return results
Using a directive worked for me: https://stackoverflow.com/a/24673257/188926
In your case:
1) define a directive:
angular.module('myApp')
.directive('mytemplate', function() {
return {
templateUrl: 'views/template.html'
};
});
2) use your new directive:
<mytemplate />
... or if you're concerned about HTML validation:
<div mytemplate></div>

Angular Directive not replacing Element

I'm trying to create a directive that will work against xml that i am injecting into the dom via a service.
Here is my a relatively reduced example, having removed the async service call for data and also the template: jsBin - transforming elements using directive
Everything works well with regard getting my post elements' header attribute into an h2 tag but it is leaving a element within my page which will fail for some browsers.
to clarify, this is what i get:
<post class="ng-isolate-scope ng-scope" heading="test heading">
<div class="ng-scope">
<h2 class="ng-binding">test heading</h2>
<div>test</div>
</div>
</post>
and this is what i would expect:
<div class="ng-scope">
<h2 class="ng-binding">test heading</h2>
<div>test</div>
</div>
I think Adam's answer is the better route, but alternatively and easier if you include jquery you can do this in your link function:
var e =$compile(template)(scope);
element.replaceWith(e);
You aren't using template correctly in your directive. Your link function should not applying your template as you are in the example code.
var linker = function(scope, element, attrs) {
var template = getTemplate();
element.html(template);
$compile(element.contents())(scope);
};
Instead of that, just do this:
return {
restrict: 'E',
replace: true,
scope: {
heading: '#'
},
template: '<div><h2>{{heading}}</h2><div>test</div></div>'
};
In your post directive. 'replace: true' will not impact anything if you are compiling and manipulating the DOM yourself.
At the same time, though, I have no idea what your compile directive is for and why you have a factory that returns an HTML string. All of that code looks like it could be reduced to a single directive. I can't really comment on what you're trying to do, but once you start using $compile all over the place, odds are you aren't doing things the 'Angular way'.

Resources