Angular directive doesn't work on table - angularjs

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>

Related

Nesting a directive inside of a table

Is this possible? I want the ability to tidily swap out a table body depending on user input, so i just threw this little *test together to see if it would work, but it's loading all wonky, with the body preceding and exterior to the table itself in the DOM even though I nest it appropriately in my html. so my questions are thus:
1) what's this behavior all about? and
2) can i achieve what I want the way i'm going about it?
*simple fiddle
html:
<div ng-app="myApp" ng-controller="myController">
<table class="table">
<thead>
<tr>
<th>Month</th>
<th>Savings</th>
</tr>
</thead>
<my-directive></my-directive><!-- this should be the tbody -->
<tfoot>
<tr>
<td>Sum</td>
<td>$180</td>
</tr>
</tfoot>
</table>
</div>
js:
var app = angular.module("myApp", []);
app.directive("myDirective", function () {
return {
restrict: 'E',
template: '<tbody><tr><td>January</td><td>$100</td></tr><tr><td>February</td><td>$80</td></tr></tbody>',
};
});
You are currently rendering markup within a <my-directive></my-directive> element, which is messing up the table layout.
Instead, change your directive to an attribute-based directive and place it on the <tbody> element, replacing the content..
Template
</thead>
<tbody my-directive></tbody><!-- this should be the tbody -->
<tfoot>
Directive
return {
restrict: 'A',
template: '<tr><td>January</td><td>$100</td></tr><tr><td>February</td><td>$80</td></tr>'
};
See working fiddle.

How do i append my attribute to a element in my Angular directive

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'
}
})

Table row as directive displays out of table context in angularjs

I have a table and each row is to displayed using a directive and ng-repeat.
Unfortunately, the rows are rendered out of the table context.
Please run the fiddle here: http://jsfiddle.net/nu1tu9qL/4/
The directive:
.directive('myrow', [function () {
return {
restrict: 'E',
replace: true,
scope: {
rowdata: "="
},
template: '<tr><td>{{rowdata.a}}</td><td>{{rowdata.b}}</td></tr>',
};
}]);
The HTML:
<table>
<thead>
<th>A</th>
<th>B</th>
</thead>
<tbody>
<myrow ng-repeat="d in data" rowdata="d"></row>
</tbody>
</table>
Without going deep into Angular's source code to diagnose this issue, I'd say that this is likely an artifact of the fact that you are using an unexpected element within <tbody>. <tbody> accepts only <tr> (for the purpose of this question).
And so, change your myrow directive to be an attribute, and make the template only add <td>s:
<tbody>
<tr myrow rowdata="d" ng-repeat="d in data"></tr>
</tbody>
and the directive:
.directive("myrow", function(){
return {
scope: {
rowdata: "=?"
},
template: "<td>{{rowdata.a}}</td><td>{{rowdata.b}}</td>"
};
});
(you could even use myrow as the scope, rather than rowdata)
EDIT:
For the sake of verification of the assumption above, I reproduced the behavior with the following:
<table>
<tr><th>Column 1</th></tr>
<tbody>
<myrow></myrow>
</tbody>
</table>
and using jQuery (something similar to what Angular would have done):
$(function(){
$("myrow").replaceWith("<tr><td>A</td></tr>");
});
and the result is that the inserted row appearing above <table>, as is the case in the original question. In fact, <myrow> is moved outside of table (in Chrome and IE - not sure if this is browser-specific behavior) even before the replaceWith happens.

Angular directive showing in the wrong place

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.

Angular directive doesn't work properly inside an HTML table

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>

Resources