I have the following page
<body ng-controller="myController">
<table>
<tbody>
<tr ng-repeat="r in rows">
<div show-row></div>
</tr>
</tbody>
</table>
</body>
with the following Angular module
ng.module('myModule', [ ])
.controller('myController', ['$scope', function ($scope) {
$scope.rows = [a non-empty array of values]
}]).directive('showRow', function () {
return {
replace: true,
template: '<td>Hello!</td>'
};
});
My problem is that Hello! is shown once before the table and not, as I would expect, once for every row of the table.
I guess I have to use the scope property for the directive, but how?
The problem is with div which cannot be a children to tr. Change the div to td.
<body ng-controller="myController">
<table>
<tbody>
<tr ng-repeat="r in rows">
<td show-row></td>
</tr>
</tbody>
</table>
</body>
I'm having a similar problem with the following layout:
<table>
<table-header data="headers" order="order"></table-header>
<tbody>
<tr ng-repeat="...">
...
</tr>
</tbody>
</table>
The tableHeader directive looks like this:
.directive('tableHeader', [function () {
return {
restrict: 'E',
replace: true,
scope: {
data: '=',
order: '='
},
template: '\
<thead><tr> \
<th ng-repeat="header in ::data" \
ng-click="orderBy(header)"> \
{{:: header.name }} \
</th> \
</tr></thead>',
link: function (scope, elem, attrs) {
scope.orderBy = function (header) {
if (scope.order.by === header.prop) {
scope.order.reverse = !scope.order.reverse;
} else {
scope.order.by = header.prop;
}
};
}
};
Weird enough, the thead element ends up out of the table, instead of inside.
Edit: seems to be documented in https://github.com/angular/angular.js/issues/7295.
Related
This is the problem: I want to sort a table by clicking on a table header. To avoid repeating code every , I have made a directive. The table headers show up nicely but I cannot find a way to make them sortable.
This is (part of) my HTML:
<div class="table-responsive" >
<table class="table table-striped" >
<thead>
<tr>
<th> </th>
<th><t-header name="vnaam" hoofd="voornaam" ></t-header></th>
<th><t-header name="anaam" hoofd="achternaam"></t-header></th>
</tr>
</thead>
<tbody>
<tr dir-paginate="cursist in homeCtrl.ckaart | orderBy: homeCtrl.sortKey:!homeCtrl.reverse" current-page="homeCtrl.currentPage">
<td><a ng-click="homeCtrl.gotoDetail(cursist.id)"><span ng-class="cursist.keuzemotivatie.length || cursist.leerdoelen.length ? 'infosign-true' : 'infosign-false'" class="fa fa-info-circle infosign"></span></a></td>
<td>{{cursist.vnaam}}</td>
<td>{{cursist.anaam}}</td>
</tr>
</tbody>
My directive:
.directive('tHeader', function(){
return {
restrict: 'EA',
scope: {
name: "#",
hoofd: "#"
},
templateUrl: 'templates/theader.html',
controllerAs: 'vm',
bindToController: true,
controller: function() {
var vm = this;
vm.sortKey = '-instrument';
// sort t.b.v. table sorting
vm.sort = function(keyname) {
// alert(keyname);
vm.sortKey = keyname;
vm.reverse = !vm.reverse;
console.log(vm.sortKey);
}
}
}
})
And the template belonging to the directive:
<div ng-click="vm.sort(vm.name)">{{vm.hoofd}}
<span ng-show="vm.sortKey === vm.name" ng-class="{ 'fa fa-chevron-up':vm.reverse, 'fa fa-chevron-down':!vm.reverse }"></span>
</div>
It seems to me that the 'vm.sortKey' parameter as well as the 'vm.reverse' parameter are not being passed into the HTML.
Anyone an idea? What mistake do I make?
I have the following table:
<table>
<thead>
<tr>
<th>Uno</th>
<th>Dos</th>
</tr>
</thead>
<!--directive template should be here-->
<double-rows></double-rows>
<!--/directive template should be here-->
</table>
The directive is defined as:
angular.module('app', []);
angular.module('app').directive('doubleRows', function () {
return {
templateUrl: 'template.html',
restrict: 'E',
scope: {
row: '='
}
}
});
As simple as it looks, it doesn't render properly. e.g:
<double-rows class="ng-isolate-scope">
Cell1
Cell2
</double-rows>
<table>
<thead>
<tr>
<th>Uno</th>
<th>Dos</th>
</tr>
</thead>
<!--directive template should be here-->
<!--/directive template should be here-->
</table>
You can see the full code in here: https://plnkr.co/edit/0Z65aK?p=preview
How to make it work?
This isn't exactly an angular problem but more to do with how the browser decides to interpret the HTML you wrote. Certain tags aren't allowed to be nested in other tags, this is particularly prevalent within lists (ul, or li) and tables (also applies to nested links and a few other things).
Instead of using restrict E if you use restrict A and use an element type that is allowed in the table then it works (I also used replace:true on your directive so it replaces the original element it's applied to instead of being a child of it)
https://plnkr.co/edit/fgRzZU?p=preview
angular.module('app').directive('doubleRows', function () {
return {
templateUrl: 'template.html',
restrict: 'A',
replace:true,
scope: {
row: '='
},
controller: function () {
console.log('hi');
}
}
});
The HTML
<table>
<thead>
<tr>
<th>Uno</th>
<th>Dos</th>
</tr>
</thead>
<!--directive template should be here-->
<tbody double-rows>
</tbody>
<!--/directive template should be here-->
</table>
I have a table in a template I want to populate with different data. My approach is using directives in Angular. I managed to make a template out of my table but I have no idea how to apply the value for the ng-repeat attribute from my html.
Here's a part of my index.html
<div id='unannounced' kc-item-table>
</div>
And here's a part of my template
<table class='table'>
<thead>
<tr>
<th>Name</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr ng-repeat='item in changableItemList'>
<td>{{item.name}}</td>
<td>{{item.description}}</td>
</tr>
</tbody>
</table>
Heres my directive
app.directive('kcItemTable', function() {
return {
restrict: 'E',
templateUrl: 'scripts/controllers/itemTableTemplate.html'
}
})
So in order to reuse the template I want to be able to change the
ng-repeat='item in itemList'
But I have no idea how to append it to right element.
Here is the simple explaination with your code./
Your html template -
<table class='table'>
<thead>
<tr>
<th>Name</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr ng-repeat='item in changableItemList'>
<td>{{item.name}}</td>
<td>{{item.description}}</td>
</tr>
</tbody>
</table>
Directive-With an isolate Scope
app.directive('kcItemTable', function() {
return {
restrict: 'E',
scope :{
itemList :'='
},
templateUrl: 'scripts/controllers/itemTableTemplate.html'
}
})
You can use directive with different list --
<div id='unannounced' kc-item-table item-list='ItemList1'>
</div>
<div id='unannounced' kc-item-table item-list='ItemList2'>
</div>
What you are trying to do is a very basic feature of AngularJS: data-binding to directives.
Check out the documentation about directives: https://docs.angularjs.org/guide/directive
Here is a very basic example forked from the above docs:
Main template:
<div my-customer name="naomi"></div>
<div my-customer name="boby"></div>
Directive:
.directive('myCustomer', function() {
return {
scope: {
name: "#"
},
template: 'Name: {{name}}'
};
});
http://plnkr.co/edit/r9tIzwxCFyEyAU3NX0G1?p=preview
To clarify, what you need in your case is a "scope" property on your directive. You will be able to pass the scope values through the DOM element attributes.
Thats easy just add this to your div where you add your attribute directive.
<div ng-controller="YourCustomController" id='unannounced' kc-item-table>
</div>
then in YourCustomController you would put a $scope property called.
$scope.changableItemList;
Or if you want multiple of these directives on the same page you can work with an isolated scope and do :
<div id='unannounced' kc-item-table customList='customList2'/>
<div id='unannounced' kc-item-table customList='customList1'/>
and in your directive do:
//you will need a controller above this which has $scope.customList declared
app.directive('kcItemTable', function() {
return {
restrict: 'E',
scope :{
customList :'=' //isolated scope with customList passed in
},
templateUrl: 'scripts/controllers/itemTableTemplate.html'
}
})
TL;DR version: I'm trying to write a directive to show various objects in a table, and add an edit / delete button at the end of the table rows.
The example entity will be ProjectType, so:
project-type.controller.js
// determines which attributes we want to show in the table
$scope.attr = [
'computerName',
'shortName',
'longName'
];
// puts the current projectType object to the form
$scope.edit = function(projectType) {
$scope.projectType = projectType;
$scope.computerName = projectType.computerName;
$scope.isEdit = true;
};
// shows a delete modal window
$scope.deleteConfirm = function (projectType) {
Modal.confirm.deleteModal('Project type', projectType.computerName, function () {
Message.createMessage('project-type.message.delete.success', { projectType: projectType.computerName }, { type: 'warning', timeout: 4000 });
$scope.remove(projectType);
})();
};
project-type.html
<!-- show table with directive -->
<entity-table object="projectTypes" entity="'project-type'" attr="attr"></entity-table>
<!-- show table without directive -->
<table class="table table-responsive">
<thead>
<tr>
<th>{{ 'project-type.create.label.computerName' | translate }}</th>
<th>{{ 'project-type.create.label.shortName' | translate }}</th>
<th>{{ 'project-type.create.label.longName' | translate }}</th>
<th>{{ 'common.operations' | translate }}</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="projectType in projectTypes">
<td><strong>{{ projectType.computerName }}</strong></td>
<td>{{ projectType.shortName }}</td>
<td>{{ projectType.longName }}</td>
<td>
<a ng-click="edit(projectType)" class="pencil"><span class="glyphicon glyphicon-pencil"></span></a>
<a ng-click="deleteConfirm(projectType)" class="trash"><span class="glyphicon glyphicon-trash"></span></a>
</td>
</tr>
</tbody>
</table>
entity-table.directive.js
angular.module('unioffice-centralApp')
.directive('entityTable', function () {
return {
restrict: 'E',
replace: true,
transclude : true,
templateUrl: '/components/directives/entity-table/entity-table.html',
scope: {
object: '=',
entity: '=',
attr: '='
},
link: function (scope, element, attrs, controllers, transclude) {
console.log(scope);
}
};
});
entity-table.html
<div>
<table class="table table-responsive">
<thead>
<tr>
<th ng-repeat="att in attr">{{ (entity + ".table.label." + att) | translate }}</th>
<th ng-repeat-end translate>common.operations</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="obj in object">
<td ng-repeat="att in attr">{{ obj[att] }}</td>
<td ng-repeat-end>
<a ng-click="edit(obj)" class="pencil"><span class="glyphicon glyphicon-pencil"></span></a>
<a ng-click="deleteConfirm(obj)" class="trash"><span class="glyphicon glyphicon-trash"></span></a>
</td>
</tr>
</tbody>
</table>
(there is a /div at the end of the entity-table.html, stackoverflow seems to kill it)
So the question is: how do I pass the edit() and deleteConfirm() functions to the directive to make them work?
In this picture you can see the two tables are the same, but the buttons in the first one obviously won't do anything at this point: (also I know the second one has bold first column, it's ok, that's not the point now)
Passing/binding the functions from the controller to the directive is done in the same manner as binding an object = or a string # with the difference being how it's referenced in the directive
Pass in the functions as attributes on the directive as shown below ..
<entity-table object="projectTypes" entity="'project-type'" on-delete="deleteConfirm" on-edit="edit" attr="attr"></entity-table>
In your directive do this ..
angular.module('unioffice-centralApp')
.directive('entityTable', function () {
return {
restrict: 'E',
replace: true,
transclude : true,
templateUrl: '/components/directives/entity-table/entity-table.html',
scope: {
object: '=',
entity: '=',
attr: '=',
onDelete: '&', // function referencing
onEdit: '&' // function referencing
},
link: function (scope, element, attrs, controllers, transclude) {
scope.deleteFn = function (obj) {
scope.onDelete()(obj); // this invokes the deleteConfirm function in the controller
}
scope.editFn = function (obj) {
scope.onEdit()(obj); // this invokes the deleteConfirm function in the controller
}
}
};
});
In your controller ...
$scope.edit = function(projectType) {
$scope.projectType = projectType;
$scope.computerName = projectType.computerName;
$scope.isEdit = true;
};
// shows a delete modal window
$scope.deleteConfirm = function (projectType) {
Modal.confirm.deleteModal('Project type', projectType.computerName, function () {
Message.createMessage('project-type.message.delete.success', { projectType: projectType.computerName }, { type: 'warning', timeout: 4000 });
$scope.remove(projectType);
})();
};
PLUNKR
I'm trying to use an AngularJS (1.2) directive to create row cells inside an HTML Table and I don't understand why Angular inserts the directive result as first child of 'body' instead of replacing the original directive element?
Here is the HTML markup:
<body ng-app="myApp" ng-controller="MainCtrl">
<table>
<thead>
<tr>
<th>col1</th>
<th>col2</th>
<th>col3</th>
<th>col4</th>
</tr>
</thead>
<tbody>
<my-directive></my-directive>
</tbody>
</table>
</body>
And the directive:
var app = angular.module('myApp', []);
app.controller('MainCtrl', function($scope) {
$scope.data = ['value1','value2','value3','value4'];
});
app.directive('myDirective', function () {
return {
restrict: "E",
replace: true,
scope:false,
link: function (scope, element) {
var html = angular.element('<tr></tr>');
angular.forEach(scope.data, function(value, index) {
html.append('<td>'+value+'</td>');
});
element.replaceWith(html);
}
};
});
Please use the Plunker link below to see the result:
http://plnkr.co/edit/zc00RIUHWNYW36lY5rgv?p=preview
It seems to work better if you dont restrict the directive to be an element:
app.directive('myDirective', function () {
return {
restrict: "A",
replace: true,
scope:false,
link: function (scope, element) {
var html = angular.element('<tr></tr>');
angular.forEach(scope.data, function(value, index) {
html.append('<td>'+value+'</td>');
});
element.replaceWith(html);
}
};
});
<table>
<thead>
<tr>
<th>col1</th>
<th>col2</th>
<th>col3</th>
<th>col4</th>
</tr>
</thead>
<tbody>
<tr my-directive></tr>
</tbody>
</table>