How do I access the transclude function from a directive link function - angularjs

Short question
How do I get access to the transclude function within a directive link function using Angular 1.1.1?
What I'm trying to achieve
Here is the (broken) fiddle http://jsfiddle.net/michaeldausmann/7NXZs/
I am trying to write a 'wrapper' directive...
<wrapper-dynamic>
<h2>Wrap me Dynamically!</h2>
</wrapper-dynamic>
...the dynamic template will contain an ng-transclude...
var tmpl = "<div>Dynamic Wrapper version {{wrapperVersion}}</div><hr/><div ng-transclude></div>";
...and I am $compiling it in the link function....
var thing = $compile(tmpl)(scope)
element.append(thing);
This fails with an error...."undefined is not a function"
I think I need to pass a transclude function to the $compile...
$compile(tmpl, transcludefn)(scope)
but I'm not sure how to access the transclude function... it does not appear to be available in the link function parameters like it is in later versions of angular.
Michael

Before AngularJS 1.2 you had to define the compile function to get a reference to the transclude function:
app.directive('myDirective', function($compile) {
return {
transclude: true,
compile: function compile(tElement, tAttrs, transcludeFn) {
return function postLink(scope, element, attributes) {
console.log(transcludeFn);
};
}
};
});
Demo: http://plnkr.co/edit/cNfwlf1RgMiDTASiLNIJ?p=preview

Related

Angularjs - Pass optional parameter for Directive from HTML

I am trying to pass an optional parameter isvalid from my html to the directive. I have followed all the steps mentioned in documentation but it still looks like i am doing something wrong.. i am not able to read the value in my directive. can you let me know what am i doing wrong?
HTML
<customvideo isvalid="true"></div>
MY Directive
Update: I had simplified for questioning purpose and hence you were seeing $scope. I have updated the actual directive now
// Set the directive
angular
.module('custom.directives')
.directive('customvideo', customvideoDirective);
// Set the directive $injections
customvideoDirective.$inject = ['$Scope'];
function customvideoDirective($Scope)
{
return {
compile: compile,
restrict: 'A',
$Scope: {
isvalid: '=?'
}
};
function compile() {
console.log($Scope.isvalid); //this is undefined
}
}
})();
Change you $Scope property inside Directive Definition Object (DDO) should be changed scope. Also use link function instead of using compile, as compile function has access to only raw DOM, there will be no scope available in it. Even you can return and PreLink/PostLink from compile function, but in this case I believe using link would be appropriate.
angular.module('directives')
.directive('customvideo', function () {
return {
link: link,
restrict: 'A',
scope: {
isvalid: '=?'
}
}
);
function link(scope) {
console.log(scope.isvalid);
}
Found a way to work it out.. not sure if it is the best.. but hey.. it works :) Posting it here for those who come here for answers...
I was writing the syntax for compile wrongly... I changed it to
function compile(tElement, tAttrs, transclude)
{
// the tAttrs param has all the attributes associated with the element
console.log(tAttrs.isvalid);
}

Provide template with expressions as attribute on directive

I'm wanting to pass a "template" into a directive, by means of an attribute. Here's a trite example of what I'm trying to accomplish:
This HTML:
<greeter person-name="Jim" greeting-template="Hello {{name}}"></greeter>
Would produce output: Hello Jim.
I've tried with a directive like this:
function greeter($interpolate) {
var directive = {
link: link,
restrict: 'EA',
template: '<div>{{evaluatedTemplate}}</div>'
};
return directive;
function link(scope, element, attrs) {
scope.name = attrs.personName;
scope.evaluatedTemplate = $interpolate(attrs.greetingTemplate)(scope);
}
}
But that doesn't work, because {{name}} in the greeting-template attribute gets evaluated in the parent scope before it gets as far as the directive link function.
Ultimately, I would need the value of attrs.greetingTemplate to literally be a string of: 'Hello {{name}}'. I figure I could do it with some alternative syntax, like having the greeting-template attribute value as: "Hello [name]" and convert "[" to "{{" before interpolation. But that feels messy. I looked at transclusion too, but the way it evaluates the directive against the parent scope looks like it could cause issues when I have multiple greeter's.
Instead of using the link function, you could use the compile function, which runs before any linking to a scope occurs, and gets passed the template element (the original DOM element) as well as its uninterpolated attributes as arguments. I think that's what you're looking for here.
In the compile function, you could store the uninterpolated template string in a variable for later use in your post-link function (which is the same as the link function if you use link rather than compile), where you can then bind it to your scope.
So your directive would look like this, with a compile property rather than a link property:
function greeter($interpolate) {
var directive = {
compile: compile,
restrict: 'EA',
scope: true,
template: '<div>{{evaluatedTemplate}}</div>'
};
return directive;
function compile(tElement, tAttrs) {
// save the uninterpolated template for use in our post-link function
var greetingTemplateUninterpolated = tAttrs.greetingTemplate;
return {
pre: function (scope, element, attrs) {},
post: function (scope, element, attrs) {
scope.name = attrs.personName;
scope.evaluatedTemplate = $interpolate(greetingTemplateUninterpolated)(scope);
}
};
}
}
Here's a fiddle showing it working.
And here's a really good article explaining how compile and link work.

Angular composite (super) directive not allowing 2 way binding on component (child) directives

I have a need to create a composite directive that incorporates separate fully functional directives. One of my component directives adds an element to the dom and that element binds to a value in the component directive's controller. When the composite directive adds the component directive in the compile function, it seems to work but the piece that has the 2 way binding in the component directive does not appear to get compiled and just renders the {{ctrl.value}} string on the screen. I realize this is a bit convoluted so I have included a plunk to help clarify the issue.
app.directive('compositeDirective', function($compile){
return {
compile: compileFunction
}
function compileFunction(element, attrs){
attrs.$set("component-directive", "");
element.removeAttr("composite-directive");
element.after("<div>Component value when added in composite directive: {{compCtrl.myValue}}</div>");
return { post: function(scope, element){
$compile(element)(scope);
}};
}
});
app.directive('componentDirective', function(){
return {
controller: "componentController as compCtrl",
link: link
};
function link(scope, element){
element.after("<div>Component value: {{compCtrl.myValue}}</div>");
}
});
app.controller('componentController', function(){
var vm = this;
vm.myValue = "Hello";
});
http://plnkr.co/edit/alO83j9Efz62VTKDOVgc
I don't think any compilation will happen as a result of changes in the link function, unless you call $compile manually, i.e.,
app.directive('componentDirective', function($compile){
return {
controller: "componentController as compCtrl",
link: link
};
function link(scope, element){
var elm = $compile("<div>Component value: {{compCtrl.myValue}}</div>")(scope);
element.append(elm);
}
});
Updated plunk: http://plnkr.co/edit/pIixQujs1y6mPMKT4zxK
You can also use a compile function instead of link: http://plnkr.co/edit/fjZMd4FIQ97oHSvetOgU
Also, make sure to use .append() instead of .after().

Adding directives to an element using another directive

I am trying to create a directive that adds some html code but also adds additional attributes/directives.
Using the code below, an ng-class attribute is indeed added, but it seems angular does not recognize it as a directive anymore. It is there, but it has no effect.
How can I get it to work? Thanks.
The Angular code:
angular.module('myModule', [])
.directive('menuItem', function () {
return {
restrict: 'A',
template: '<div ng-if="!menuItem.isSimple" some-other-stuff>{{menuItem.name}}</div>'
+'<a ng-if="menuItem.isSimple" ng-href="{{menuItem.link}}">{{menuItem.name}}</a>',
scope: {
menuItem: '='
},
compile: function (element, attrs) {
element.attr('ng-class', '{active : menuItem.isActivated()}');
}
}
});
And the html:
<li menu-item="menuItem" ng-repeat="menuItem in getMenuItems()" />
EDIT:
The solution by #Abdul23 solves the problem, but another problem arises: when the template contains other directives (like ng-if) these are not executed. It seems the problem just moved.
Is it possible to also make the directives inside the template work?
Or perhaps insert the html using the compile function instead of the template parameter. Since I want a simple distinction based on some value menuItem.isSimple (and this value will not change), perhaps I can insert only the html specific to that case without using ng-if, but how?
You need to use $compile service to achieve this. See this answer.
For your case it should go like this.
angular.module('myModule', [])
.directive('menuItem', function ($compile) {
return {
restrict: 'A',
template: '<a ng-href="{{menuItem.link}}">{{menuItem.name}}</a>',
scope: {
menuItem: '='
},
compile: function (element, attrs) {
element.removeAttr('menu-item');
element.attr('ng-class', '{active : menuItem.isActivated()}');
var fn = $compile(element);
return function(scope){
fn(scope);
};
}
}
});

Resolving a directive inside a linking function

I'm trying to build a mechanism which will allow me to resolve directives within a linking function.
Example:
angular.module('directives', [])
.directive('myContainer', ['$compile', function ($compile) {
return {
restrict: "E",
replace: true,
link: function (scope, element, attrs) {
angular.forEach(scope.component.components, function(component){
var newScope = scope.$new()
newScope.component = component;
var elem = angular.element('<'+component.type+'>'+'</'+component.type+'>')
//Trying to compile directive to be resolved
var resolvedDirective = $compile(elem)(scope)
element.append(resolvedDirective)
})
}
});
Problem is, the "resolvedDirective" (defined by component => type) simply creates a tag containing the other directive name, which will be resolved later on.
My mechanism was simplified for the sake of the example (recursive...)
Hope I've made my question clear enough...
Thanks in advance!
Need more clarity in your Question . Please update with desired result of your directive.
You can follow this pattern. This answer has explained lot about how to add compile directives within directives
Angularjs directive add directives as attribute and bind them dynamically
In your case you want to append new directive , compile and bind it .
So try to add append your element in Compile phase and use $compile in postLink phase.

Resources