I am new to AngularJS, and in learning stage, i have come across ngTransclude, when i start reading the document http://docs.angularjs.org/api/ng/directive/ngTransclude, i could able to get , what that really does,
Can any of you guys, please tell me what actually ngTransclude does, And what is the change when it is included, when when it is not included in directive.
Please give me a clear understanding of ngTransclude, and how important is, and when we can use it.
I Just Want To See It Working:
http://plnkr.co/edit/liRDIEy9sWoSPSHJ1oln?p=preview
Explanation Of The Example
Think of "ngTransclude" as saying "keep whatever is inside of this element, inside of the element, even after I rewrite it". It is most used with directives. Take the following as an example, I defined a directive "emphasize-text" which really just is going to wrap whatever I am provided in an "h1" element. The following is that directive:
app.directive("emphasizeText", function () {
return {
restrict: 'E',
transclude: true,
template: '<h1 ng-transclude></h1>',
}
});
Now, to use this on a page, the following html will work quite well:
<!-- transclude will keep text of course -->
<emphasize-text>Example With Just Text</emphasize-text>
<!-- transclude also keep html tags intact -->
<emphasize-text><i>Example With Italics Tag</i></emphasize-text>
<!-- translcude will even keep angular variables intact! -->
<emphasize-text>
<div>This is a first div</div>
<div>This is a {{secondVariable}} div</div>
</emphasize-text>
If you take a look at this plunker (http://plnkr.co/edit/liRDIEy9sWoSPSHJ1oln?p=preview). Use a modern browser and "view source" on the output and you'll see what is happening. The tags are maintained exactly, angular just wraps them in "h1"! Perfectly what we wanted!
The key here is you can modify the semantics, behavior or really anything you want in html! Personally, I feel this is best suited for custom controls - not for something simple like I used it for. In fact, my example is probably a really bad one because future programmers would know what "h1" is much faster than they will know what "emphasize-text" will turn into. That said, you get the idea.
Happy customizing!
Related
I have a need to use the same directive within a directive, depending on a conditional param. However, when ever i try to do it, it seems to go to an endless loop. As i understand, it tries to pre-load the templates and that causes an endless recursion and at the end, it just throws me the following error:"RangeError: Maximum call stack size exceeded".
I have created an example in fiddle.. as you can see in the example, when the param's value is "1", it creates the error (even when the second level param is valued as "2" so it shouldn't have real recursion issues in the controller/app).
https://jsfiddle.net/qh9nh1gx/
"custom-directive"'s template:
<div>
<div ng-if='info==1'><div custom-directive info='2'></div></div>
<div ng-if='info==2'>DONE,single.</div>
</div>
Thanks
I have found 2 options to deal with the issue, the first one, is exactly what Jju described - creating a new "compiler" method (it can be grabbed from the url he sent).
The second option - always using an additional template for the "recursive" parts of the directive. For example, in my directive, i had a "ng-repeat" part that depending on the items value, it could request to display the directive again. while i used "ng-include" to have the other directive, it worked.
<div ng-repeat="item in items" ng-include="'inline-possibly-recursive-template"'></div>
in that template, you can call the directive again without any issues..
I hope that it will anyone else that will stumble into that issue.
You can look into https://stackoverflow.com/a/19065910/1680674 that describe a common approach to create directive that use himself inside
I have an angular app that shows a list of things
<div ng-repeat="thing in things" regular-thing>
implemented with an ng-repeat that shows a regularThing directive for each thing in my list.
It's actually a little bit trickier than that though:
<div ng-repeat-start="thing in things">
<div ng-if="$first || thing.isSpecial" special-thing></div>
<div ng-if="!$first && !thing.isSpecial" regular-thing></div>
</div>
<div ng-repeat-end></div>
I use a different directive for the first thing in my list, and also for any "special" things. For our purposes, a thing becomes "special" when the user clicks on it - so, as they're scrolling through the list, they can click on a thing to have it displayed in a different (more extensive) template.
The way I have it now feels wrong to me. For one thing, I really don't need to have two different directives - just different templates. The logic is identical, specialThing just has a bit more of it. For another thing, I'm toggling a property on the data (namely thing.isSpecial) for purely view-related reasons, which makes me die a little bit inside.
So my question: Don't I deserve to die a little bit inside for this? Isn't there a cleaner, more "angular" way to handle this (i.e. to toggle between the directive templates)?
First, no one deserves do die. So the answer to your question is "no".
But you do raise some interesting points.
First, it's ok to have "view model" information in your scope (or controller, depending on if you are using the ControllerAs syntax or not). However, you definitely don't want to add view model information to your data models. Here's how I might do it (using your click-toggles-something-special example).
<div ng-repeat-start="thing in things">
<div ng-if="$first || isSpecial" ng-include="/specialtemplate.html" ng-click="isSpecial = !isSpecial"></div>
<div ng-if="!$first && !isSpecial" ng-include="/regulartemplate.html" ng-click="isSpecial = !isSpecial"></div>
</div>
<div ng-repeat-end></div>
The key difference is I'm adding the isSpecial property to the scope, not to thing, and isSpecial will be specific to that particular ng-repeat item's scope.
Also, unless you're planning on doing DOM manipulation, you can replace them with ng-include + ng-controller in the template html.
Some people prefer that pattern (include + controller) instead of directives, and other prefer to go ahead and others prefer to go ahead and write directives because it's more "componenty" (I made that word up). I think either is a valid way to go.
I'm learning how to create directives because they seem very useful and I thought it would be a good use for a top navigation bar. I'm not sure if I'm misunderstanding how they should be used, missed something small along the way or something entirely different.
The templateUrl isn't loading and looking through other posts and the docs, I can't see what went wrong.
Here is the directive
.directive('stNavDir', function() {
return {
restrict: 'E',
transclude: true,
templateUrl: 'partials/TopNav.html',
scope: {
siteName: "=",
},
link: function(scope, element, attrbiutes) {
element.addClass('topBar');
}
}
Using it in index.html
<body>
<st-NavDir site-name={{siteName}}>{{title}}</st-NavDir>
TopNav.html
<div>
<button>Menu</button>
</br>
<div >
This will hold the navigation with a menu button, title of current location in app, and maybe other things
</div>
</div>
So it only shows the value of {{title}} and, looking in the console, there are no errors and it doesn't seem to even load TopNav.html.
If I'm using it completely wrong or there's a better way, I'm all ears as well. But this seemed like a good place to try out using a directive. I can load it fine using ng-include but I wanted to try this way and see if it would be more effective.
I'm also having trouble getting the style to take but that may be caused by this initial problem.
Change this line
<st-NavDir site-name={{siteName}}>{{title}}</st-NavDir>
to
<st-nav-dir site-name={{siteName}}>{{title}}</st-nav-dir>
Camel-case should be converted to snake-case.
st-nav-dir
in html may help.
stNavDir is the corresponding directive definition name.
Here is an interesting article:
Naming a directive
I've been trying to use the CSS content property to make somewhat of a "template" for an element of a specific class.
I've tried multiple things. . .
Many places I have seen told me to convert everything to hexadecimal, so I did, until I saw that using hex wrote the litteral characters into the element, instead of evaluating the characters as HTML.
I then tried just litterally entering the characters into the content, and I got the exact same result (this makes it appear as if there is no purpose for the hex, yet thats hard to belive with how many people say there is. . . ).
Is there any way that I can place HTML content into an element using the CSS content attribute?
I've made a JS-Fiddle for this:
And, of course, Stack wants my source:
HTML:
<button id="normal" >Show with normal output</button>
<button id="hex" >Show with Hexadecimal output</button>
<div id="class_changer" ></div>
JS:
function changeClass(evt)
{
class_changer.className = evt.srcElement.id;
}
var class_changer = document.getElementById('class_changer');
var normal = document.getElementById('normal').addEventListener('click', changeClass, true);
var hex = document.getElementById('hex').addEventListener('click', changeClass, true);
And the un-godly long CSS:
.normal::before {
content: '<img alt="Facebook" src="http://cache.addthis.com/icons/v1/thumbs/32x32/facebook.png" />';
}
.hex::before {
content: '\0027\003c\0061\0020\0068\0072\0065\0066\003D\0022\0068\0074\0074\0070\003A\002F\002F\0061\0070\0069\002E\0061\0064\0064\0074\0068\0069\0073\002E\0063\006F\006D\002F\006F\0065\0078\0063\0068\0061\006E\0067\0065\002F\0030\002E\0038\002F\0066\006F\0072\0077\0061\0072\0064\002F\0066\0061\0063\0065\0062\006F\006F\006B\002F\006F\0066\0066\0065\0072\003F\0070\0063\006F\003D\0074\0062\0078\0033\0032\006E\006A\002D\0031\002E\0030\0026\0061\006D\0070\003B\0075\0072\006C\003D\0068\0074\0074\0070\0025\0033\0041\0025\0032\0046\0025\0032\0046\0077\0077\0077\002E\0063\0069\006D\0074\0072\0061\006B\002E\0063\006F\006D\0026\0061\006D\0070\003B\0075\0073\0065\0072\006E\0061\006D\0065\003D\0063\0069\006D\0063\006F\0072\0022\0020\0074\0061\0072\0067\0065\0074\003D\0022\005F\0062\006C\0061\006E\006B\0022\003e\003c\0069\006D\0067\0020\0061\006C\0074\003D\0022\0046\0061\0063\0065\0062\006F\006F\006B\0022\0020\0073\0072\0063\003D\0022\0068\0074\0074\0070\003A\002F\002F\0063\0061\0063\0068\0065\002E\0061\0064\0064\0074\0068\0069\0073\002E\0063\006F\006D\002F\0069\0063\006F\006E\0073\002F\0076\0031\002F\0074\0068\0075\006D\0062\0073\002F\0033\0032\0078\0033\0032\002F\0066\0061\0063\0065\0062\006F\006F\006B\002E\0070\006E\0067\0022\0020\002F\003e\003c\002F\0061\003e';
}
Check it out at JS-Fiddle and see what you can do! Let me know! Thanks everybody!
UPDATE: SOLVED (ish...)
Yes, wierd question sometimes accept wierd answers (like iterating over the DOM...) but if you have a better solution, I'm all ears.
As it turns out, the accepted answers means of evaluating a "CSS template" may be the best means of performing "templating" without the use of third-party libraries or the new <template> tag (that I'm still not sure of) even though it makes my skin crawl (if anyone has a better solution, please post it). Either way, I've updated my JSFiddle, so check it out!
Although, I guess the best answer would be purely making a template as a string in JavaScript, that is, if we are going to be evaluating it later on and pre-pending it to an element. Yea, that would make more sense...
No, this is not possible with plain CSS. However, if you really want to save these templates in CSS, you could iterate over all elements and use
window.getComputedStyle(element, ':before').content
to fetch the content and then prepend/append it to the element. To parse the HTML, you could either use jQuery.parseHTML, new DOMParser().parseFromString or a dummy DOM element. Alternatively, you could also use .innerHTML directly, but I wouldn't recommend that..
I'm working on a directive for AngularJS that builds a taggable element and utilizes TagsInput
Here's a working fiddle: http://jsfiddle.net/mgLss/
Not sure why but when I add that directive to my application IT works fine, but anything else below it running angular fails and I get this error message:
Error: node is undefined
compositeLinkFn#http://ajax.googleapis.com/ajax/libs/angularjs/1.0.2/angular.js:3837
compositeLinkFn#http://ajax.googleapis.com/ajax/libs/angularjs/1.0.2/angular.js:3837
nodeLinkFn#http://ajax.googleapis.com/ajax/libs/angularjs/1.0.2/angular.js:4216
compositeLinkFn#http://ajax.googleapis.com/ajax/libs/angularjs/1.0.2/angular.js:3834
compositeLinkFn#http://ajax.googleapis.com/ajax/libs/angularjs/1.0.2/angular.js:3837
compositeLinkFn#http://ajax.googleapis.com/ajax/libs/angularjs/1.0.2/angular.js:3837
compositeLinkFn#http://ajax.googleapis.com/ajax/libs/angularjs/1.0.2/angular.js:3837
nodeLinkFn#http://ajax.googleapis.com/ajax/libs/angularjs/1.0.2/angular.js:4216
compositeLinkFn#http://ajax.googleapis.com/ajax/libs/angularjs/1.0.2/angular.js:3834
compositeLinkFn#http://ajax.googleapis.com/ajax/libs/angularjs/1.0.2/angular.js:3837
compile/<#http://ajax.googleapis.com/ajax/libs/angularjs/1.0.2/angular.js:3746
bootstrap/</<#http://ajax.googleapis.com/ajax/libs/angularjs/1.0.2/angular.js:932
Scope.$eval#http://ajax.googleapis.com/ajax/libs/angularjs/1.0.2/angular.js:7808
Scope.$apply#http://ajax.googleapis.com/ajax/libs/angularjs/1.0.2/angular.js:7888
bootstrap/<#http://ajax.googleapis.com/ajax/libs/angularjs/1.0.2/angular.js:930
invoke#http://ajax.googleapis.com/ajax/libs/angularjs/1.0.2/angular.js:2788
bootstrap#http://ajax.googleapis.com/ajax/libs/angularjs/1.0.2/angular.js:929
angularInit#http://ajax.googleapis.com/ajax/libs/angularjs/1.0.2/angular.js:904
#http://ajax.googleapis.com/ajax/libs/angularjs/1.0.2/angular.js:14397
f.Callbacks/o#http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js:2
f.Callbacks/p.fireWith#http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js:2
.ready#http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js:2
B#http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js:2
I've spent the last hour on IRC but cant get any acknowledgment of my question so here's hoping Stack will come to the rescue as it has so many times before.
This is something related to the plugin you are using, it manipulates the dom in a way angular does not like it, I didn't to go into the source code the point you to the root of the issue, to be honest. But here is a way (an ugly one) to fix it.
<div ng:controller="TestCtrl">
{{ hello }}
<div><taggable default="changed"></taggable></div>
</div>
Just wrap that directive within another DOM element, making sure the plugin is isolated.
http://jsfiddle.net/mgLss/33/
Building on #fastreload's answer, a slightly less ugly solution, which does not require changing your HTML:
// as per #fastreload, wrap input in a div to prevent Angular from getting confused
template: "<div><input type=\"text\"></div>",
link: function(scope, elm, attrs) {
elm.children().tagsInput({ // note children()
You should also include Angular last in your fiddle (under Manage Resources) (and in your code in general), then elm is automatically a wrapped jQuery element, rather than a wrapped Angular element, and hence we don't need to use $(elm) in the link function.