Working on a directive to highlight <code> tags that are being outputted by a directive to render <markdown> tags.
The problem is that the <code> directive is never hit after the <markdown> directive runs. However the <code> directive runs on code tags that are not outputted from the <markdown> directive.
The Markdown directive
angular.module('App').directive "markdown", ->
converter = new Showdown.converter()
scope: true
restrict: 'E'
link: (scope, element, attrs) ->
if attrs.markdown
scope.$watch attrs.markdown, (newVal) ->
html = converter.makeHtml(newVal)
element.html(html)
else
redraw = ->
html = converter.makeHtml(element.text())
element.html(html)
#### expecting the code directive to be trigger after this.
scope.$on "$includeContentLoaded", redraw
redraw()
Any thoughts?
AngularJS doesn't know to compile anything from your markdown. There are two ways to get that stuff to compile. One way is using transclusion, but I don't think that will work for your usecase. In your case, you will need to compile the changes from your markdown. To do this, you use Angular's $compile service.
first, inject the $compile service into your directive. Then after setting element.html(html), try this: $compile(element.contents())(scope);
Related
I have this code:
app.directive('foo', function($compile) {
return {
restrict: 'E',
scope: {},
template: '<span>{{bar}}</span>',
compile: function(element, attrs) {
element.attr('title', '{{bar}}');
return function(scope, element, attrs) {
scope.bar = 'hello';
$compile(element)(scope);
}
}
}
});
Plunkr:
http://plnkr.co/edit/nFTgvYqoiFAthmjoizWS?p=preview
If I remove the $compile bit in the link function then the title attribute remains with the expression text ({{bar}}) and not the value ('hello');
Anyone can explain why?
I thought (from what I read in the docs) that this is what the compile phase is for - manipulating the template and preparing it for the link with scope and data binding. Why do I need to manually call $compile again? Isn't the template already compiled?
Maybe the phase names should be changed from compile, preLink, and postLink to postCompile, preLink, and postLink. The postCompile phase is availble to manipulate DOM before linking to a scope, at this point the linking function has been created but no scopes have been created. DOM can be added that requires no compilation. If additional elements are added that include directives or require interpolation, those additional elements need to be compiled and linked in order for the directives and interpolation to work.
To manupulate the template before compile, furnish a function to the template property: template: function(tElement, tAttrs) {}. For more information, see AngularJS Comprehensive Directive API Reference -- Template.
can you share a reference to "DOM can be added that requires no compilation, etc." or explain how did you found out about this?
Some sources of information:
AngularJS Developers Guide -- HTML Compiler
AngularJS Developers Guide -- Creating a Directive that Manipulates the DOM
My directive "addOnce" is required to check if it has an "restrict" attribute and if yes, then attach certain other directives to the input box which is one of it's sub-children.
I have been able to layout a framework to get this done. However getting stuck at how I can dynamically attach other directives to the input box.
Plnkr here: http://plnkr.co/edit/jYiTtTQ0uPuH40zcMcer
app.directive('addOnce', ['$timeout', function($timeout){
return {
restrict: 'E',
link: function(scope, el, attrs){
if(attrs.restrict){
var input = el.find('input');
$timeout(function(){
input.val('testing');
}, 50)
// now attach directive restrict-symbols to the input box
// as a result the html would look like
//<input type="text" class="aClass" ng-model="aModel" restrict-symbols>
}
}
};
}]);
Any clues will be appreciated.
The necessary part for nested directives would be:
input.attr('restrict-symbols', '');
$compiled = $compile(input)(scope);
Take a look at the modified plunkr for the full example: http://plnkr.co/edit/HCTGj1ivp48cUXBXpn1Q?p=preview
What I did was simply to add the needed attribute to the input field, afterwards compile the element, so the nested directive gets executed. The code of the second directive is not very sophisticated but enough to demonstrate that it works
I'm writing an angular.js directive, which would conditionally hide the element.
So it would look like this:
link: function(scope, elem, attrs) {
...
elem.hide()
}
I found a lot of examples that were doing exactly that, but somehow my elem attribute is an array not an element, so it does not have a hide() method.
What am I missing?
Thanks!
Most people are loading jQuery before they load angular, which extends its jqLite to the full jQuery.
The hide method doesn't seem to be part of jqLite API (https://docs.angularjs.org/api/ng/function/angular.element), hence such method is not exposed.
That doesn't mean you need jQuery, but that it is not the correct way to handle your problem. There are already the ng-show and ng-if directives to conditionnaly hide an element based on the controller, couldn't you use them?
In your html, add <div ng-show="isDisplayed">, and in your linking function scope.isDisplayed = false
Does anyone know a way to have a directive that can transclude the body of the directive selectively?
For example, if I have the following div and directive of myWrapper.
<div data-my-wrapper="foo">
<h1>Hello World</h1>
</div>
I'd like to have code in my directive that is like this pseudo code.
if (foo.locked) {
// user a static template saying that the item is locked.
}
else {
// Tranclude the body content
}
Is there a way to direct to the directive what content should be transcluded? Or is there a recommended way to have the equivalent of a if/then/else that could allow me to swap out the content instead of the body of the element that the directive sits in?
You can gain access to transcluded content inside the link function of a directive by using making use of the fifth argument, which happens to be a transclude linking function. Remember also to set the transclude property of the directive.
function link(scope, iElement, iAttrs, controller, transcludeFn) { ... }
Here's a crude demonstration of the transclude linking function at work: JSFiddle.
Glad that helped.
NOTE:
Please be aware that if you're using a version of AngularJS prior to 1.2.x that the transclude linking function is instead available as the third argument to the compile property of a directive. This has since been deprecated as pointed out by the official documentation.
I need a directive ngTree to get the controller of a parent ngTree element. Note that require: "^ngTree" would return the controller of current directive.
I know I could do a ngTreeHelper and put a div with it, between any two ngTree divs and require: "^ngTreeHelper" would work, but it's ugly. I wonder if there is something else I could do.
There's not a great built in way to do this. However it is done in angular's form directive.
Swiping the code from there, this is what you need
var ngTreeController = $element.parent().controller('ngTree');
This is in the form directive's controller, but you should be able to do this from a link function too.