How to compile DOM added by third party libraries in angularjs? - angularjs

I am using sidr( http://www.berriart.com/sidr/ ) with angular for my navigation bar. What sidr does is it copies my nav-bar's markup and puts it just before the body wrapped in its custom div.
Now I have defined a directive on my nav-bar which looks some what like this <ul filter>....</ul>. Since sidr is replicating my navigation bar markup I have 2 ul's with filter directive. But my directive's link function is called only once because angular dose not know about another occurrence of that directive.
My markup looks like this
<ul class="side-nav nested-nav" filter>
<li class="has-sub-menu" ng-repeat="filter in filters">
<ul class="sub-menu" options>
<li ng-repeat="option in filter.options">
</li>
</ul>
</li>
</ul>
and I my directive
angular.module('test')
.directive('filter', function() {
return {
link: function($scope, iElement, iAttrs) {
console.log('hello world!'):
}
}
})
So how do I tell angular to compile the second <ul filter>....</ul> which was added by sidr and when ?

I haven't used this jQuery plugin yet. But looking at the doc, it has this callback feature:
$('#callback-menu').sidr({
name: 'sidr-callback',
source: function(name) {
return '<h1>' + name + ' menu</h1><p>Yes! You can use a callback too ;)</p>';
}
});
So I guess you can put in your own html template into the source callback and $compile it.

Related

How to generate dynamic templateUrl as well as template content in Angular.js directive

I want to generate a tree layout by recussion in the template in my AngularJs application.
The template content is generated by bellow codes.
function genTemplate(moduleName) {
return `<script type="text/ng-template" id="${moduleName}-zgTree">
<span class="{{node.fa5Classes}}"></span> {{node.title}}
<ul class="nav list-group margin-bottom-0" uib-collapse="node.isCollapsed">
<li class="list-group-item list-group-item-action"
ng-repeat="node in node.children"
ng-init="render(node)"
ng-class="{'list-group-item-success': node.isActive}"
ng-click="onClick(node)"
ng-include="'${moduleName}-zgTree'">
</li>
</ul>
</script>`;
}
In the directive definition I use:
this.templateUrl = genTemplate(moduleName);
These result:
Error: [$templateRequest:tpload] Failed to load template:
The point is that I need to use ng-include for the recussion, and I need to provide the template-id to ng-include, so I cannot set this.template instead;
Most talks about the dynamic-templateUrl (such as this) focus on generating the URL dynamically/conditionally, but none about the template content; so they do not help much.

AngularJS control over precise DOM rendered by directive

I want to "directivize" search results. Each result will be rendered as a li within a ul HTML tag.
This means that I want to avoid the default AngularJS behavior of rendering to the DOM the containing directive element.
<ul>
<my-result>
<li></li>
</my-result>
<my-result>
<li></li>
</my-result>
</ul>
I have tried using replace: true, but this results in a TypeError:
definition.match is not a function
Can someone point me in the right direction here? Perhaps I should use an attribute directive, but then I am unsure how to bind the isolate scope items?
My directive looks like this:
var template = require('text!./template.html');
return function() {
return {
restrict: 'E',
scope: {
select: '&',
result: '=',
},
template: template,
};
};
I am using an ng-repeat to render the results:
<ul>
<my-result
ng-repeat="result in results"
result="result"
select="select({result:result})"
>
</my-result>
</ul>
I am using Angular 1.4.
The replace option is deprecated. (since 1.3 I think?)
It would look better if your directive template included the <ul> element. In which case you wouldn't have the problem of seing the directive element between <ul> and <li>.
If you have no other option, as you said you can still use the directive as an attribute of the <li> like so:
<ul>
<li my-result
ng-repeat="result in results"
result="result"
select="select({result:result})"
>
</li>
</ul>
result and select should still be available to your directive's scope.

Angularjs compile a directive inside ng-repeat with isolated scope

I have a directive in the form of a dropdown, pretty simple. The user can click a button to add as many as they need to in a ul, make their selections, and save it off. This is all inside of several ng-repeats.
I'm having trouble mastering the scope. As I expected, this works:
<div ng-repeat="group in groups" question-group="group" class="question-group">
<div ng-repeat="question in questions">
<ul>
<li ng-repeat="case in question.cases"></li>
<li><new-case group='group'></new-case></li>
</ul>
</div>
</div>
When I say "works", I mean that group is properly scoped (the data of the entire group is necessary for the resulting input).
When I switch it to "click to add":
<div ng-repeat="group in groups" question-group="group" class="question-group">
<div ng-repeat="question in questions">
<ul>
<li ng-repeat="case in question.cases"></li>
<li>add case</li>
</ul>
</div>
</div>
group is undefined in the scope. Here is my createNewCase function:
function createNewCase($event) {
var thisLi = angular.element($event.target).closest('li');
var listItem = $compile('<li><new-case group=\'group\'></new-case></li>');
var html = listItem($scope);
thisLi.before(html);
}
$scope.createNewCase = createNewCase;
And the newCase directive:
angular.module('groups.directives.newCaseDirective', [])
.directive('newCase', ['$window', function() {
return {
restrict: 'EA',
scope: { group: '=' },
templateUrl: 'groups/views/newcase.tpl.html'
};
}]);
I've been reading for days and I've tried a few other derivatives but I'm ultimately just not getting it. Help is greatly appreciated.
Thanks!
The issue is that group is created by ng-repeat and is only available in child scopes of ng-repeat.
Each repeated element is in it's own child scope. So your directive version works but your other one doesn't because the controller doesn't see those child scopes.
You would have to pass group as argument of the function if you want to access it in controller
<a href="#" ng-click="createNewCase($event, group)">

Hide/show an element in Angularjs using custom directive, ng-show and $scope

When a link is clicked in the app navigation a dropdown with ui-view content shows below each respective link.
The HTML:
<div class="sc-dash-header">
<ul>
<li>
<a class="navbar-brand" show-nav-popup href="">download</a>
<div id="nav-download-progress" class="dash-hdr-popup" ng-show="showPopup">
<div ui-view="hdr-download-progress"></div>
</div>
</li>
<li>
<a class="navbar-brand" show-nav-popup href="">add</a>
<div id="nav-add" class="dash-hdr-popup" ng-show="showPopup">
<div ui-view="hdr-add-content"></div>
</div>
</li>
<li>
<a class="navbar-brand" show-nav-popup href="">enter pin</a>
<div id="nav-unlock" class="dash-hdr-popup" ng-show="showPopup">
<div ui-view="hdr-unlock"></div>
</div>
</li>
</ul>
</div>
I've included an ng-show attribute to open the dropdown when $scope.showPopup is set to true.
To achieve this I've created a custom directive with an on click called show-nav-popup.
The JS:
.directive('showNavPopup', function () {
return {
restrict: 'A',
// scope: {},
link: function(scope, el, attrs) {
el.on('click', function(){
scope.$apply(function () {
scope.showPopup = true;
});
console.log(scope);
});
}
};
});
The above works, but the dropdown opens on each element.
Question: I need to isolate the scope, so on each click, only the respective dropdown appears. I uncomment the line // scope: {} - but this doesn't work..
Angularjs n00b here - any help would be much appreciated
Having an isolate scope in this situation wouldn't fix the problem. There are a ton of ways to achieve what you want though. One of which is to assign each show-popup-nav an id, turn $scope.showPopup into an array, and keep an individual true/false for each id. Then for each ng-show, you look at the index corresponding to each id for the true/false value.
I coded it up on that guy's Plunker, working as you expect: http://plnkr.co/edit/CSikLIiuPNT9dfsfZfLk
EDIT: I should say, you COULD use an isolate scope to fix this, but that would require a lot of changes to your DOM, as the ng-show directive is a sibling to your show-popup-nav, and not a child.
When you create the isolate scope, the scope applies to the element that your directive is applied to, and it's child elements. In this case that's just the anchor tag:
<a class="navbar-brand" show-nav-popup href="">download</a>
You are using an ng-show on a tag that is a sibling to the anchor tag:
<div id="nav-download-progress" class="dash-hdr-popup" ng-show="showPopup">
The sibling is not part of the isolate scope, and so it never notices that the value of showPopup has changed.
The ng-show would work if it were applied to a DOM element that was a child of the anchor tag.
EDIT
One way to make this work would be to wrap your two siblings in a parent tag, and use the directive on the parent:
<div show-nav-popup>
Download
<div ng-show="showPopup"></div>
</div>
Then you'd need to modify your directive's code to find the anchor tag and apply the click handler.
You might instead try a completely different approach as suggest in the other answer by #Bill Bergquist

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

Resources