How to dynamically nest directives in AngularJS - javascript

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>

Related

In a named transclude slot, how do I transclude *only* the element's contents?

I've got a basic directive that I want to use transclusion. The relevant options are set up as follows:
restrict: "E",
replace: true,
transclude: {
'toolbar': 'toolbarSlot'
},
scope: {
pageTitle: "#"
},
templateUrl: "path/to/my/template.html"
My template is:
<header class="page-header">
<h1 class="page-title heading">{{ pageTitle }}</h1>
<div class="page-header-toolbar toolbar" ng-transclude="toolbar">
<!-- "toolbar" transcluded content should go here -->
</div>
</header>
And finally, when I use the directive, I'm using it this way:
<my-custom-page-header-directive page-title="Title">
<toolbar-slot>
<button>Button</button>
<button>Another button</button>
</toolbar-slot>
</my-custom-page-header-directive>
The problem is, in the DOM it ends up as something like this, with an extraneous toolbar-slot element mixed into the transcluded content:
<header class="page-header">
<h1 class="page-title heading">Title</h1>
<div class="page-header-toolbar toolbar">
<toolbar-slot>
<button>Button</button>
<button>Another button</button>
</toolbar-slot>
</div>
</header>
Is there a way (using ngTransclude) to only transclude the contents of the slot element?
Looks like the answer is no, there isn't a way to do that as of right now.
An open issue on the Angular.js repository shows that there are misgivings about implementing the ability to use ngTransclude that way:
I'm not a fan of adding a replace functionality to the slot transclusion. It would introduced the same problems we have with normal "replace" and transclude: element.
But, apparently, you "can actually do this manually if your really need it".

Angular Directive placed on ng-repeat not removed from ng-repeat

I have an AngularJS directive meant to interact with notifications pushed from a server. The problem I am having is that currently the method I am using is to have an ng-repeat which keeps track of all the notifications during the session, and then displaying them as they are added to the array. What I want to happen, is that an alert comes, it's added to the DOM, it stays for a couple seconds, and removes itself from the DOM. At this point the issue is that the element will hide, but it will not remove itself from the DOM. Being that the element is absolutely positioned, after the fadeout is prevents me from accessing item that it is in front of. I assumed that element.remove() and the element.$destroy() would do the trick, but it seems like the element is not being from the ng-repeat or possibly that the $scope of the directive is not being deleted. Any help would be greatly appreciated.
angular.module('bmuApp').directive('messageNoti', function($timeout){
return {
scope: {
alert: '=messageNoti'
},
replace: true,
restrict: 'EA',
templateUrl: 'partials/authenticated/homepage/alerts.html',
link: function(scope, element, attrs) {
$timeout(function(){
element.addClass('fadeOut');
$timeout(function(){
element.remove();
}, 500);
}, 5000);
element.on('$destroy', function () {
scope.$destroy();
});
}
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<!------------>
<!-- Alerts -->
<!------------>
<div class="alert-container" ng-cloak>
<a class="alert animated fadeInUp" ng-repeat="alert in alerts" ng-href="messages/{{ alert.user.thread_id }}" message-noti="alert">
</a>
</div>
<!-----Template for Alert------>
<div class="row">
<span class="close">
<i class="glyphicon glyphicon-remove"></i>
</span>
<div class="col-xs-12 text-left">
<span class="profile-image" style="background-image:url('https://www.blackmarketu.com/{{ ::alert.user['profile-picture'] }}');"></span>
<h5> {{ ::alert.user["user-firstname"] }} {{ ::alert.user["user-lastname"] }} </h5>
<p>{{ ::alert.message }}</p>
</div>
</div>
You don't need to interact with the DOM directly to remove it. Just splice (remove) that array member from the array.
So your logic is a bit wrong, because you have an array of alerts and you are not removing them, you just want to remove the DOM element.
If you don't want to change your code too much and add a parent directive to handle this, you can pass both "alert" (current array member) and "alerts" (entire array of alerts) to your directive and then in your timeout callback instead of removing the DOM element, you splice that array member from the array.
Remove replace: true. Since the ng-repeat directive creates inherited scopes and your directive creates isolate scope, they won't play well together on the same element.
From the Docs:
replace ([DEPRECATED!], will be removed in next major release - i.e. v2.0)
specify what the template should replace. Defaults to false.
true - the template will replace the directive's element.
false - the template will replace the contents of the directive's element.
-- AngularJS Comprehensive Directive API
From GitHub:
Caitp-- It's deprecated because there are known, very silly problems with replace: true, a number of which can't really be fixed in a reasonable fashion. If you're careful and avoid these problems, then more power to you, but for the benefit of new users, it's easier to just tell them "this will give you a headache, don't do it".
-- AngularJS Issue #7636

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>.

Creating directive to part of template which is shown after a click

I've got a template with two kind of article-container: Viewer and Editor:
<article ng-if="!editor" ng-controller="article">
<div>Some content</div>
</article>
<article ng-if="editor" ng-controller="article">
<div mySharedScope></div>
</article>
While clicking the button the user can switch between those two container:
<button ng-click="editor = !editor" ng-bind="editor ? 'Abort' : 'Edit'"></button>
Now I want to create a directive on the second container. So this is what I did:
app.directive('mySharedScope', function () {
return {
template: 'New content'
};
});
But something is missing, as this doesn't work.
I want to use a directive to do some DOM mainpulation link: function ($scope, element, attrs) { }
Two things, first is that the directive mySharedScope will transform in it's directive definition from camel case
<div mySharedScope></div>
to a dashes like so
<div my-shared-scope></div>
After you switch that out, you'll need to make sure you're translcuding content nested inside of your first directive (article), and placing the ng-transclude directive inside of its template.
see docs for this on angular website
as a basic implementation of this, i've created a fiddle with the two of your two directives that appropriately switch when a button is triggered. The content is transcluded here, so feel free to cherry pick what you need from it.
https://jsfiddle.net/wvty8rpc/2/
A directive named 'mySharedScope' translates to attribute 'my-shared-scope':
<article ng-if="editor" ng-controller="article">
<div my-shared-scope></div>
</article>

Template inside of a directive

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.

Categories

Resources