ng-repeat not binding when directive template is loaded via templateUrl - angularjs

I've been stuck on this for hours and hours - can anyone help?
I have a list of nested directives, which I'm iterating through ng-repeat. The templates for these directives are fairly chunky so I've modularised them into separate HTML files and loaded them via templateUrl, but this seems to be breaking the data binding.
I've replicated the problem here: http://plnkr.co/edit/72HUb0vhtpYWuRHnlq3b?p=preview
HTML:
<div project-ext ng-repeat="project in projects"></div>
project.html
{{project.name}} <button ng-click="projects.splice($index,1)">-</button><br>
<div schedule-ext ng-repeat="schedule in project.schedules"></div>
schedule.html
{{schedule.name}}<button ng-click="remove($index)">-</button>
JS:
app.directive('projectExt', function() {
return {
templateUrl: 'project.html'
};
});
app.directive('scheduleExt', function() {
return {
templateUrl: 'schedule.html',
link: function(scope) {
scope.remove = function(i) {
scope.$parent.project.schedules.splice(i,1)
};
}
};
});
Can anyone tell me why the remove buttons don't work in the second listing, when all I've done is change the directives construction from template to templateUrl?

This problem seems to be related to a bug reported at https://github.com/angular/angular.js/issues/2151
To workaround it, simply don't put ngRepeat and your directives which are using templateUrl on the same element; instead, place ngRepeat on an wrapper:
HTML:
<div ng-repeat="project in projects"><div project-ext></div></div>
project.html
{{project.name}} <button ng-click="projects.splice($index,1)">-</button><br>
<div ng-repeat="schedule in project.schedules"><div schedule-ext></div></div>
Plunk: http://plnkr.co/edit/BapWX0LpqkcLFegq1fhU

It is a known bug, please follow these links. You can use the other version you wrote as a workaround.
Transcluded element not being deleted on removal of list item when using ng-repeat
https://github.com/angular/angular.js/issues/2151
https://groups.google.com/forum/#!msg/angular/0CP0zpTnZMM/5OzBni7d9sgJ

Related

Angular directive from array element in ngRepeat

I am working on a project that will have any number of HTML cards, and the format and positioning of the cards is the same, but the content of them will differ.
My plan was to implement the content of each card as an angular directive, and then using ngRepeat, loop through my array of directives which can be in any order, displaying each directive in a card. It would be something like this:
inside my controller:
$scope.cards = [
'directice-one',
'directive-two',
//etc..
]
my directive:
.directive('directiveOne', function () {
return {
restrict: "C",
template: '<h1>One!!</h1>'
};
})
.directive('directiveTwo', function () {
return {
restrict: "C",
template: '<h1>Two!!</h1>'
};
})
//etc..
my html:
<div class="card" ng-repeat="item in cards">
<div class="{{item}}"></div>
</div>
But.. the directives don't render. So I just get a div with class="directive-one".
This might be a design flaw on my part, some sort of syntax error, or just a limitation of angular. I'm not sure.
I've also considered making a directive <card>, and then passing the templateUrl: into it, but that would cause me to lose my access to $scope and the javsacript capabilities that I would have if each card was it's own directive.
So, advise, code help, anything would be very helpful!
I choose directives only when I need to use them in HTML mark up. For example, assuming cards layout is same and it takes different information based on user preference.
HTML File
<my-card Name="First" Option="Myoptions"></Card>
<my-card Name="Second" Option="GenOptions"></Card>
Directive
angular.module("testapp").directive("MyCard", function() {
scope: {
name: '#',
Option: '#'
Controller: "myCardController",
templateURL: "~/myCard/myCardTemplate.html"
}
});
In Template you can implement the information passed from HTML page via the directive.
Hope this helps.
Do take note that the above approach is preferred when you are developing a framework sort of things. For example you develop a web framework and the header takes 5 parameters and these 5 parameters needs to be passed via mark up. Most important thing is that the framework/header is independent
In your controller, you need to require the directive modules. Then assign them to a scope variable which would be that array you have. Will update with code when I get to desktop, tried doing with phone kinda tuff.

Order AngularJS directives are created not as I understand from the documentation

I have an AngularJS app where I have to traverse some of the elements with the cursor keys. To do this I created a directive called selectable that adds some info to a list in a service when the directive is created.
It is important that this happens in the order that the selectable directives appear in the view.
From my research:
Directive compilation step by step and other
I thought that this would work because pre-link and controllers would be created in order. But this does not always happen.
Here is my HTML:
<selectable ng-repeat="suggestion in pCtrl.suggestions" value="suggestion">
{{suggestion}}
</selectable>
<div ng-repeat="cat in pCtrl.categories">
<selectable ng-repeat="item in cat" value="item">
{{item}}
</selectable>
</div>
<selectable ng-if="true" value="pCtrl.bottom">
<div>
Bot content
</div>
</selectable>
And here the directive:
app.directive('selectable', function() {
return {
restrict: 'E',
replace: true,
scope: {},
controller: 'SelectableCtrl',
controllerAs: 'selCtrl',
bindToController: {
value: '='
}
};
})
.controller('SelectableCtrl', [
function() {
var self = this;
console.log(this.value);
}
]);
What I see in the console log is that the bottom selectable with the ng-if is created just after the first ng-repeat and then the rest of the ng-repeats are created.
I made a plunker to demonstrate what happens. Please check the console log of the plunker.
Plunker: Directive creation order test
In angular JS Compilation order of nested directives based on priority
the deeper the element nested, the later it is compiled.
In your code
selectable for the Categories are nested in the div
div ng-repeat="cat in pCtrl.categories"
first the selectable directives which are present in the outer div tag gets compiled
and then remaining selectable directive which is present in inner div tag gets compiled.
Hence the console output showing the order as per its compilation.

AngularJS : directives which take a template through a configuration object, and show that template multiple times

I'm looking to create a custom directive that will take a template as a property of a configuration object, and show that template a given number of times surrounded by a header and footer. What's the best approach to create such a directive?
The directive would receive the configuration object as a scope option:
var app = angular.module('app');
app.directive('myDirective', function() {
return {
restrict: 'E',
scope: {
config: '=?'
}
...
}
}
This object (called config) is passed optionally to the directive using two way binding, as show in the code above. The configuration object can include a template and a number indicating the number of times the directive should show the template. Consider, for example, the following config object:
var config = {
times: 3,
template: '<div>my template</div>'
};
It would, when passed to the directive, cause the directive to show the template five times (using an ng-repeat.) The directive also shows a header and a footer above and below the template(s):
<div>the header</div>
<div>my template</div>
<div>my template</div>
<div>my template</div>
<div>the footer</div>
What's the best way to implement this directive? Note: When you reply, please provide a working example in a code playground such as Plunker, as I've run into problems with each possible implementation I've explored.
Update, the solutions I've explored include:
The use of the directive's link function to append the head, template with ng-repeat, and footer. This suffers from the problem of the template not being repeated, for some unknown reason, and the whole solutions seems like a hack.
The insertion of the template from the configuration object into middle of the template of the directive itself. This proves difficult because jqLite seems to have removed all notion of a CSS selector from its jQuery-based API, leading me to wonder if this solution is "the Angular way."
The use of the compile function to build out the template. This seems right to me, but I don't know if it will work.
You could indeed use ng-repeat but within your directive template rather than manually in the link (as that wouldn't be compiled, hence not repeated).
One question you didn't answer is, should this repeated template be compiled and linked by Angular, or is it going to be static HTML only?
.directive('myDirective', function () {
return {
restrict: 'E',
scope: {
config: '=?'
},
templateUrl: 'myTemplate',
link: function(scope) {
scope.array = new Array(config.times);
}
}
}
With myTemplate being:
<header>...</header>
<div ng-repeat="item in array" ng-bind-html="config.template"></div>
<footer>...</footer>
I'd think to use ng-transclude in this case, because the header & footer wrapper will be provided by the directive the inner content should change on basis of condition.
Markup
<my-directive>
<div ng-repeat="item in ['1','2','3']" ng-bind-html="config.template| trustedhtml"><div>
</my-directive>
Directive
var app = angular.module('app');
app.directive('myDirective', function($sce) {
return {
restrict: 'E',
transclude: true,
template: '<div>the header</div>'+
'<ng-transclude></ng-transclude>'+
'<div>the footer</div>',
scope: {
config: '=?'
}
.....
}
}
Filter
app.filter('trustedhtml', function($sce){
return function(val){
return $sce.trustedHtml(val);
}
})

Multiple use of a template in the same page in AngularJS

I have some repetitive components on my AngularJS page such as billingAddress, shippingAddress and primaryAddress. I have created a separate template file for address components and expected to be able to use ng-include to include the template thrice on my page. I am unable to find documentation around passing models to templates. What I am looking for is something like
<div ng-include="address.tpl.html" ng-model="{address: primaryAddress}"></div>
<div ng-include="address.tpl.html" ng-model="{address: billingAddress}"></div>
<div ng-include="address.tpl.html" ng-model="{address: shippingAddress}"></div>
Is this even possible as of now?
his is for what directives are made.
angular.module('docsSimpleDirective', [])
.directive('myAddress', function() {
return {
scope: {
address : '='
},
templateUrl: 'address.tpl.html'
};
});
Then in your template simple use the $scope.address.
On declaring the directive you should use it like this.
<my-address address="primaryAddress"><my-address>
<my-address address="billingAddress"><my-address>
<my-address address="shippingAddress"><my-address>

Bind ngInclude to different models

Is it possible to specify model for ngInclude so that any changes done inside the included template are reflected on the specified model. For instance:
I have a model such as :
$scope.member = {
name: "Member1",
children:[
{
name:"Child1"
},
{
name:"Child2"
}
]
};
and the template:
<script type="text/ng-template" id="name.html">
<input type="text" ng-model="member.name"/>
</script>
Now is it possible to pass ngInclude either "member" or any child and get their respective name properties modified? I tried passing ng-model to the ng-template but it doesn't work. I tried to dynamically load the scope with the intended model but if the model gets delete, I am left with an orphan template. Following is the jsfiddle code:
http://jsfiddle.net/vaibhavgupta007/p7E5K/
I wish to reuse the template rather than duplicating the same template for different models. I have refered to this question:
How to specify model to a ngInclude directive in AngularJS?
But here models are not getting deleted.
Edit
I have not grasped the concepts of creating custom directives till now. But will creating a new directive in conjuction with ng-include help?
The answer of your last question is: yes. In a directive, you define also a template and a scope. The content of the scope is completely in your hands.
See here: http://jsfiddle.net/vgWQG/1/
Usage:
Member: <member model="member"></member>
<ul>
<li ng-repeat="child in member.children">
Child {{$index}}: <member model="child"></member>
</li>
</ul>
The directive:
app.directive('member', function(){
return {
template : '<input type="text" ng-model="member.name"/>',
replace : true,
restrict: 'E',
scope : {
'member' : '=model'
},
link: function(scope, element, attr) {}
};
});
I've moved the template in an inline variant because I could not getting the template cache getting to work in jsfiddle. In a real world, a templateUrl: 'name.html' should be fine.
This is what you want?

Resources