In directive template, inside ng-repeat scope not working - angularjs

I have a directive as follows,
angular.module("sample").directive("sampleDir", [
"$compile", function($compile) {
return {
restrict: "E",
replace: true,
scope: {
options: '=',
source: '='
},
templateUrl: "sample.html"
};
}
]);
sample.html
<div ng-repeat="row in source" class="{{$parent.options.Class}}">
<label value="{{row.label}}">{{row.label}}</label>
</div>
Here inside template "source" parameter i can use as such. However, "options" parameter need to be fetched from parent scope. If i am creating another div outside, then options parameter i can get as such.
I tried adding another div outside this ng-repeat div and tried to access "options" parameter is working fine.
<div class="{{options.Class}}">
<div ng-repeat="row in source">
<label value="{{row.label}}">{{row.label}}</label>
</div>
</div>
But why it is not working with ng-repeat div?
How can i remove $parent and use "options" parameter as such?
Please help,
Thanks.

It's a known issue. It's happening when you use replace: true and ngRepeat on the top element of the directive. So the ngRepeat is bound to the outer scope instead of the isolated scope.
You can resolve it by either not using replace or adding an element to the top of the directive.

I think the scope variable name "options" might be conflicting. Try changing the scope variable of the directive to something else.
For eg: Your scope definition can change to this
scope: {
sampleOptions: '=',
source: '='
},
in case you are stuck with any syntax related issue doing this, this article will be helpful

Related

Angular: template from inner html and add existing data

Trying to add li from template and add items from $scope.menuItems (controller)
Items don't display
http://plnkr.co/edit/Jo7Vml?p=preview
app.directive("menuDemo", function(){
return {
templateUrl: 'tree-renderer.html'
}
});
Here you go : http://plnkr.co/edit/8YV6cdCIPCjEOq3DtjAz?p=preview
First : added the following to the directive :
scope:{
menuItems:"=menuDemo"
},
This enable the two-way binding on the data you pass to the menu-demo attribute when using your directives, without this you can't pass data to your directive.
Second : remove the script tag in the tree-renderer.html and sub-tree-renderer.html. Script tag is only usefull when using ng-include along with it. You were just define a template part without using it like this.
Third adding a top level ng-repeat for the 1st level in tree-renderer.html and remove the one from index.html.
<div ng-repeat="menuItem in menuItems">
As you can see menuItems match with what i have defined in the scope:{} part.
Fourth : I added the binding of the function addItem and addSubItem so you can define your own way of adding items to the tree outisde of the directive.
EDIT : Another plnkr where i move out the <div ng-repeat="menuItem in menuItems"> http://plnkr.co/edit/MZ3nsY6WTG3EiNKhXjAv?p=preview.
As you can see i put the menu-demo call in a <span> tag within the <li> tag. This is because they both use isoled scope and if i want to use data from the ng-repeat loop the easiest way is to move the other directive in a child DOM element.
Note that i also change the name of the binding element to menuItem (i remove the 's').
That is not a correct way to declare a directive try something like this:
angular.module('myModule')
.directive('menu', [function () {
return {
restrict: 'A',
scope: true,
templateUrl: 'tree-renderer.html',
link: function ($scope, $element, $attrs) {
//here some functions that do something
}
};
}]);
By the way, try improve your questions!

Angular - Directive is rendered first before variable evaluates?

I have this directive for showing all photos with a specific category:
<div ng-controller="CategoryController as c">
<photo-set category="{{ c.category_name }}"></photo-set>
</div>
Strangely, the template is rendered first before the variable sets in. So, it thought the attribute is empty. If I hard-code the attribute like <photo-set category="animal"> then it works fine.
There's no typo in the code, because when I inspect element, I can see the variable printed there.
.directive("photoSet", function() {
return {
restrict: "E",
templateUrl: "views/photo-set.html",
scope: {
category: "#category"
},
controller: "SetController", // the controller to render the photos
controllerAs: "s"
};
})
My Category Controller:
.controller("CategoryController", function($routeParams) {
this.category_name = $routeParams.category;
})
// the route is /c/:category
Edit: The JSFiddle. In the fiddle, It can't work using scope: { category: "#category" }, so I replace it with scope: true
it is very much dependent on your template code. Like, there is no 'link' or 'compile' function, so it is not clear how you are using the category_name inside your template. Try passing it as object using '=' instead '#' expression and use it through scope only. And for faster help try posting sample fiddle
EDIT:
Like i said above, your 'directive template code' should use the variables set in directive scope(in your case your directive tag is different and <ul> tag is different this <ul> tag should be part of your directive template):
<photo-set category="c.category_name">
<ul>
<li ng-repeat="photo in s.photos">{{ photo.src }}</li>
</ul>
</photo-set>
should be
<photo-set category="{{category_name}}"></photo-set>
and <ul> should be part of template
please checkout the working updated fiddle:
https://jsfiddle.net/Lp404d99/5/
more details about the execution flow
http://jasonmore.net/angular-js-directives-difference-controller-link/

Passing ng-repeat context to child directive

In parts of my application I have used a directive in this format:
<child-directive ng-repeat="item in vm.items"></child-directive>
This one has access to {{item}} from within child-directive without having to do anything.
Now I want to use the same directive along side other directives that all work with the same context data:
<div ng-repeat="item in vm.items">
<child-directive></child-directive>
<other-directive></other-directive>
</div>
The child directive does not need to alter the context data, it only needs the information from inside to display a widget.
I've tried using scope in the directive in this format:
angular
.module('myapp.dashboard')
.directive('childDirective', childDirective);
function childDirective() {
var directive = {
restrict: 'E',
templateUrl: 'client/components/child-directive.ng.html',
controller: 'ChildDirectiveController',
controllerAs: 'vm',
scope: {
item: '='
}
};
return directive;
}
and
<child-directive ng-attr-item="{{item}}"></child-directive>
Within the ng-repeat section. However that just throws an error.
I've also tried ng-bind with no luck.
Any suggestions?
You're using bi-directional scope binding rather than an interpolated property. You can read up on this more in the angular docs
Change your scope object to be:
scope: {
item: '#'
}
OR, change your template to:
<child-directive ng-attr-item="item"></child-directive>

How to get the attribute value in template html with angularjs

I wrote a directive but I don't know how to get the attribute of the directive in template html, those attributes are used in ngIf to determine which div elements will be displayed, pls take a look at below code snippet.
directive use:
<geo-country-selector type="WGC"></geo-country-selector>
template html:
<div ng-if="type.indexOf('W')>0">
...
</div>
<div ng-if="type.indexOf('G')>0">
...
</div>
I need to get the attribute type's value in the template html. I did lots of research on that, but no luck. Any ideas? Thanks
The directive can be like Anik said :
link : function(scope,element,attr){
...
},
scope: {
type: '#'
}
In your directive add a link function then add attrs value to scope then you can check condition in your template like that
try like this
link : function(scope,element,attr){
scope.type=attr.type
}
There is another way by creating isolate scope like this
scope: {
type: '#'
}

angular directive attributes isolate scope undefined outside ng-repeat

I have directive this directive:
myApp.directive('myDirective',['$http', function($http) {
return {
restrict: 'AEC',
replace: true,
scope: { attr1: '=' , attr2: '=' },
link: function(scope, element, attrs) {
...
}
}]);
If I put directive inner ng-repeat it works, so I have acces to value of attributes (eg. scope.attr1)
<div ng-repeat="item in items"
<my-directive attr1="item.value1" attr2="item.value2"></my-directive>
</div>
but if I put directive outside ng-repeat, so I have only my-directive:
{{mymodel.value1}} {{mymodel.value2}} //{{}} print correct value
<my-directive attr1="mymodel.value1" attr2="mymodel.value2"></my-directive> //this fail
I can't access to attributes, so If I access eg. scope.attr1 I'm getting undefined value.
the "item" object is only defined in the div with the ngRepeat so when you are trying to access specific items from the list "items" outside of the div with ngRepeat you have to use items[index].value1 syntax. Try:
<my-directive attr1="items[index].value1" attr2="items[index].value2"></my-directive>
If the values are simple types, you should use '#' instead of the two-way model binding that '=' implies.
Also how are you reading your values. Do you read them through scope.attr1 or attrs['attr1'] - since the attr collection only holds the value in the attributes, whereas the scope actually lnks to the objects
There should be nothing wrong with your code. Maybe you could spot what you're doing differently:
Working Example Here

Resources