AFAIK this is not documented, but I found in angular source a locals attribute in a directive example:
angular.module('transclude', [])
.directive('pane', function(){
return {
restrict: 'E',
transclude: true,
scope: 'isolate',
locals: { title:'bind' },
template: '<div style="border: 1px solid black;">' +
'<div style="background-color: gray">{{title}}</div>' +
'<div ng-transclude></div>' +
'</div>'
};
});
What does it do? How can I use it?
EDIT
to be more precise:
How can I access locals from directive's controller or linking function?
How can I dynamicly change locals from directive's controller or linking function?
Can I use locals in every directive, or does it have to be a directive with a transclude=true ?
The example code is on the ngTransclude page, inside the script.js tab.
I believe this is just the older syntax (which still seems to work). The newer syntax would replace
scope: 'isolate',
locals: { title:'bind' },
with
scope: { title: '#' },
I just want to close this question.
So the answer is like #ArunPJohny said
#param {Object=} locals (Optional object).
If preset then any argument names are read from this object first, before the $injector is consulted
Related
Update #2:
Sometimes using template: '<div id="{{vm.divId}}"></div>' works fine and the variable is input into the template and other times it doesn't work and instead of using the variable the ID is set to id="{{vm.divId}}". I still haven't figured out what is making it work sometimes and not others
Original Post
I have a simple directive that needs an element in the template to have the ID set dynamically. I figured this would be simple but no matter what I do the variable doesn't interpolate.
I have tried setting the ID like this, template: '<div id="{{vm.divId}}"></div>' like this template: '<div ng-attr-id="{{vm.divId}}"></div> and a number of variations of the two but none seem to work.
Here is the directive:
.directive('gMap', function() {
return {
restrict: 'E',
scope: {
'options': '='
},
transclude: true,
controllerAs: 'vm',
bindToController: true,
controller: function(mapService) {
console.log(this.options.mapId); // This logs the specified ID
this.divId = this.options.mapId;
},
template: '<div id="{{vm.divId}}"></div><div ng-transclude></div>'
};
})
Update:
I cut and paste my code into plunkr and it works fine there so it must be something else outside this directive but I have no idea where to start
I set up a directive as follows:
.directive('ogTakeATour', function() {
return {
restrict: 'E',
replace: true,
templateUrl: '../scripts/directives/TakeATourTemplate.html',
scope: {
content: '#',
uid: '#'
},
link: function(scope) {
angular.element(scope.uid).css("top","250px");
}
};
});
Directive template looks like this:
<div id="{{uid}}" class="tourContainer">
{{content}}
</div>
And this is how I call my directive:
<og-take-a-tour content="Content goes here" uid="menuTour"></og-take-a-tour>
However for some reasons this does not apply the css to the applicable div.
angular.element(scope.uid).css("top","250px");
Why is this? Could it be that the directive does not know what the id of my element is at the time the link function is running? How would I get around this if that is the case?
Angular's jQlite does not support search by id or CSS selector. So change your code like this:
angular.element(document.querySelector('#' + scope.uid)).css("top", "250px");
I am creating an ASP.net MVC application and in that I am displaying ng-grid with a column which has link as cell content.
Here's how I achieved it.
Controller (ng-grid):
columnDefs: [
{ field: 'FreightRules', visible: true,
cellTemplate:
'<div class="rulesDirective" freight-rules="{{row.entity.FreightItemGuid}}">
</div>',
enableCellEdit: false, enableCellSelection: false }]
Directive:
directives.directive('rulesDirective', function () {
return {
restrict: 'C',
replace: true,
transclude: true,
scope: { freightRules: '#freightRules' },
template: '<div ng-switch on="freightRules | areRulesAvailable">' +
'<div ng-switch-when = true><a ng-click="getRulesTest(\'{{freightRules}}\')" href="#addFreightRuleModal">Rules</a></div>' +
'<div ng-switch-when = false>No Rules</div>' +
'<div ng-switch-default class="grid">Error</div>' +
'</div>'
}});
Filter:
filters.filter('areRulesAvailable', function () {
return function (value) {
if (value != '00000000-0000-0000-0000-000000000000') {
result= true;
}
else {
result = false;
}
return result;
};
});
It works fine but when I click on the Rules link I am not able to display the dialog.
I tried to open the dialog by clicking on a button separately and it worked. Need help!
Since your directive has an isolated scope (see Isolating the Scope of a Directive), which I can tell because its declaration contains scope: {...}, the directive will limit the items on the scope to only what your directive defines.
It is possible for directives to inherit items from a parent scope, or even use the same scope as the parent, but it won't in this case.
When you have an isolated scope, the only way to define items on that scope are to place them there in a controller, link function, or define them as a scope option within your directive. Now, if getRulesTest() is specific only to your directive (not reusable outside of it, not defined on a parent controller, etc.), then just define it in your link function (or directive's controller if it has one, whatever makes sense):
directives.directive('rulesDirective', function () {
return {
restrict: 'C',
replace: true,
transclude: true,
scope: { freightRules: '#freightRules' },
link: function($scope) { // looky here
$scope.getRulesTest = function() {
console.log('check me out');
};
},
template: '<div ng-switch on="freightRules | areRulesAvailable">' +
'<div ng-switch-when = true><a ng-click="getRulesTest(freightRules)" href="#addFreightRuleModal">Rules</a></div>' +
'<div ng-switch-when = false>No Rules</div>' +
'<div ng-switch-default class="grid">Error</div>' +
'</div>'
}});
Since you never define it within the directive's scope anywhere in your example, I presume you are declaring it on a parent controller and you are thinking it should just call out of the directive's scope and find it on the parent scope. As I said above, this won't work. You can use a scope option that binds to a function using the & syntax. This lets your view declaratively assign a function from the parent scope for your directive to use when it needs to call out.
directives.directive('rulesDirective', function() {
return {
restrict: 'C',
replace: true,
transclude: true,
scope: {
freightRules: '=',
getRulesTest: '&rulesTest' // <- this guy. the text on the left is what you want the item named on the scope locally. the text on the right, after the & is what the attribute on the element should be called. if you want them to be the same on both sides, simply enter '&'
},
template: '<div ng-switch on="freightRules | areRulesAvailable">' +
'<div ng-switch-when="true"><a ng-click="getRulesTest({rules: freightRules})" href="">Rules</a></div>' +
'<div ng-switch-when="false">No Rules</div>' +
'<div ng-switch-default class="grid">Error</div>' +
'</div>'
};
})
We added the scope option to your scope declaration. Now your parent controller would add a function on the scope to be bound to:
.controller('MyCtrl', function($scope) {
$scope.doSomethingWithRules = function(rules) {
console.log('sup?');
};
});
And the view would look like:
<div class="rules-directive" rules-test="doSomethingWithRules(rules)"></div>
The other thing that might be problematic is whatever you're doing with this line in the template:
ng-click="getRulesTest(\'{{freightRules}}\')"
Are you trying to pass that function the evaluated string result of {{freightRules}} (which doesn't seem very useful), or do you want to pass it in the actual freightRules object (which seems more likely)? If so, just change it to:
ng-click="getRulesTest({rules: freightRules})"
Edit
Here is a plunk with a working example.
I did catch a small error in my original answer on how to call function scope options. There are a couple of ways to call functions that you have bound, depending on whether or not you have parameters and how you want them matched. This part is a little confusing. If you're not following, just stick to option #2.
1. You want to just pass the name of the function it should call, with parameters determined ordinally.
Say you want them to simply state (note that we only supply the name of the rules, no parenthesis or parameter names):
<div class="rules-directive" ... rules-test="doSomethingWithRules">
Then your directive should invoke the function like this (note that we call a bare function, which generates an invoker that can be used to actually call the function, and then we call it with the actual parameters):
ng-click="getRulesTest()(rules)"
2. You want the invoker to pass in named arguments so you can change the order of or omit parameters.
This is best illustrated if you have multiple parameters. Say the directive supplies two parameters, paramA and paramB. You invoke the function in your directive using an object containing parameter names and values.
ng-click="getRulesTest({paramA: valuesForA, paramB: valuesForB })"
Then when using the directive, your markup looks like this:
<div class="rules-directive" ... rules-test="doSomethingWithRules(paramA, paramB)">
The reason this is preferable is in the situation where you want to reorder or omit a parameter in the function being invoked. Let's say doSomethingWithRules only needs paramB. Now I can do:
<div class="rules-directive" ... rules-test="doSomethingWithRules(paramB)">
I couldn't have done that with Option #1 above, because it always invokes with all of the parameters ordinally. So, Option #2 is preferable because it gives consumers of your directive more flexibility and saves them the hassle of having to remember what order the parameters are supplied to your function.
I have seen a number of questions on StackOverflow discussing ng-transclude, but none explaining in layman's terms what it is.
The description in the documentation is as follows:
Directive that marks the insertion point for the transcluded DOM of the nearest parent directive that uses transclusion.
This is fairly confusing. Would someone be able to explain in simple terms what ng-transclude is intended to do and where it might be used?
Transclude is a setting to tell angular to capture everything that is put inside the directive in the markup and use it somewhere(Where actually the ng-transclude is at) in the directive's template. Read more about this under Creating a Directive that Wraps Other Elements section on documentation of directives.
If you write a custom directive you use ng-transclude in the directive template to mark the point where you want to insert the contents of the element
angular.module('app', [])
.directive('hero', function () {
return {
restrict: 'E',
transclude: true,
scope: { name:'#' },
template: '<div>' +
'<div>{{name}}</div><br>' +
'<div ng-transclude></div>' +
'</div>'
};
});
If you put this in your markup
<hero name="superman">Stuff inside the custom directive</hero>
It would show up like:
Superman
Stuff inside the custom directive
Full example :
Index.html
<body ng-app="myApp">
<div class="AAA">
<hero name="superman">Stuff inside the custom directive</hero>
</div>
</body>
jscript.js
angular.module('myApp', []).directive('hero', function () {
return {
restrict: 'E',
transclude: true,
scope: { name:'#' },
template: '<div>' +
'<div>{{name}}</div><br>' +
'<div ng-transclude></div>' +
'</div>'
};
});
Output markup
Visualize :
For those who come from React world, this is like React's {props.children}.
it's a kind of yield, everything from the element.html() gets rendered there but the directive attributes still visible in the certain scope.
I'm totally confused on transclude/replace and directives. I thought I understood but now I'm lost on how to get this particular test case working. From everything I've in the docs, SO, and blogs, it seems like my code below should work. First, some code:
The markup:
<my-directive><h1>My Title</h1></my-directive>
The end result I want:
<my-directive><div class="awesome"><h1>My Title</h1></div></my-directive>
The directive:
myApp.directive('myDirective', function() {
return {
restrict: 'E',
transclude: true,
template: '<div class="awesome"></div>'
}
});
Instead of what I want, I get:
<my-directive><div class="awesome"></div></my-directive>
What am I doing wrong?
To place the transcluded content into the div, add the ng-transclude directive to it...
template: '<div class="awesome" ng-transclude></div>'
Demo - Fiddle
You are missing the "ng-transclude" in the directive template:
<div class="awesome" ng-transculde></div>