Angular directive is showing in wrong place - angularjs

I'm very new at Angular and I'm doing an app where I use ngTable:
var app = angular.module('myApp', ["ngTable"]).controller('mainController', function($scope, NgTableParams) {
$scope.data = [
{
a : "3",
b : "test"
},
{
a : "3",
b : "test"
},
{
a : "3",
b : "test"
}
];
$scope.tableParams = new NgTableParams({}, { dataset: $scope.data});
});
app.directive('appInfo', function() {
return {
restrict: 'E',
scope: {
info: '=infoData'
},
template:
" <tr> " +
" <td filter=\"{ a: 'text'}\" sortable=\"'a'\">{{info.a}}</td> " +
" <td filter=\"{ b: 'text'}\" sortable=\"'b'\">{{info.b}}</td> " +
" </tr> "
};
});
And here is the html:
<div class="main" ng-app="myApp" ng-controller="mainController">
<table ng-table="tableParams" class="table" show-filter="true">
<thead>
<tr>
<th>A</th>
<th>B</th>
</tr>
</thead>
<tbody>
<app-info ng-repeat="info in data" info-data="info" />
</tbody>
</table>
</div>
The problem is that <app-info> tag is going to wrong place, inside div above the table instead of tbody. What am I missing?
Whats happening:
<div class="main" ...>
<app-info ... />
<app-info ... />
...
<table ...
</div>
What would be right:
<div class="main" ...>
<table ...>
<thead>...</thead>
<tbody>
<app-info ... />
<app-info ... />
...
</tbody>
...
</div>

It is a very common issue where browser have special rendering method for tables, and that don't fit well with angular. It will try to render <table>
by searching tr, td, thead, etc., and just kick out stuff that is not suppose to be here, like app-info. It will put unknown element on top of the table, and render the rest.
Angular table row directive not rendering inside table
Angular Directive table rows issue
One way to resolve it is to make your directive an attribute instead of an element:
restrict: 'A',
<tbody>
<tr app-info ng-repeat="info in data" info-data="info">
</tr>
</tbody>
That way the browser will not remove app-info, and angular will have time to render correctly.
You could theorically use replace : true in the directive config, but it is deprecated because there is known bugs:
replace ([DEPRECATED!], will be removed in next major release - i.e. v2.0)

I think the browser doesn't like having an app-info tag inside tbody. Before Angular.js can do its magic, browser tries to "fix" your invalid HTML by moving app-info outside the table.
You can try changing restrict: 'A' and use <tr app-info... instead.
However, it's usually not preferred to have table-related tags at the root of templates.

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

limit the character to 10 for filter text-field - ngTable

I have created an application using angularjs and ngTable with filer feature, the application is working fine also the filtering, but the filtering text-field should accept only 10 characters according to my requirement
Can anyone please tell me how to limit the character to 10 for that text-field, my code is as given below:
JSFiddle
script
$scope.tableParams = new ngTableParams({
page: 0,
count: 0
}, {
total: 0,
counts:[],
getData: function ($defer, params) {
// use build-in angular filter
var orderedData = params.filter() ? $filter('filter')(data, params.filter()) : data;
$defer.resolve(orderedData);
}
});
html
<div ng-app="main" ng-controller="DemoCtrl">
<table ng-table="tableParams" show-filter="true" class="table">
<tr ng-repeat="user in $data">
<td data-title="'Name'" filter="{ 'name': 'text' }">{{user.name}}</td>
<td data-title="'Age'">{{user.age}}</td>
</tr>
</table>
</div>
You can't do that easily with the directives provided by ng-table.
In order to achieve that, you must create your own input filterer, apply it on the ng-repeat and use a simple directive which will limit the caracteres of the input field.
Here is a working example, hope it will help you.
fiddle
HTML:
<div ng-app="main" ng-controller="DemoCtrl">
<table ng-table="tableParams" class="table">
<!-- first row with custom input filterer using the charLimit directive -->
<tr>
<td data-title="'Name'"><input char-limit="10" ng-model="textFilter"/></td>
<td data-title="'Age'"></td>
</tr>
<!-- declare the filter on your ng-repeat directive -->
<tr ng-repeat="user in $data | filter: { 'name': textFilter }">
<td>{{user.name}}</td>
<td>{{user.age}}</td>
</tr>
</table>
</div>
DIRECTIVE:
directive('charLimit', function(){
return {
scope: {
limit: '#charLimit',
model: '=ngModel'
},
require: 'ngModel',
restrict: 'A',
link: function($scope, $node) {
var limit = $scope.limit ? parseInt($scope.limit, 10) : 0;
$scope.$watch('model', function(val) {
if(limit>0 && !isNaN(limit))
$scope.model = $scope.model.slice(0,limit);
});
}
};
})
EDIT
An html5 attribute called maxlength can do it for you so you can avoid the directive and simply add this attribute to your input field

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.

AngularJS does not pass data to JQuery DataTable

I am using datatables from datatables.net and am running into some issues with the AngularJS ng-repeat and values being populated into the table. I have added a button that will pass a value into the table and this works great. However, when I try to add sorting and scroll bar to the table it stops working. I'm not sure what I'm doing wrong here.
html
<div ng-controller="TodoCtrl" id="TodoCtrl">
<table id="example" cellpadding="0" cellspacing="0" border="0" class="display table table-striped table-bordered table-condensed">
<thead>
<tr>
<th>Bus Id</th>
<th>X Cords</th>
<th>Y Cords</th>
<th>Event Type</th>
<th>Time Stamp</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="todo in todos"><td>{{todo.busId}}</td><td>{{todo.xCord}}</td><td>{{todo.yCord}}</td><td>{{todo.eventType}}</td><td>{{todo.timeStamp}}</td></tr>
</tbody>
</table>
<form class="form-horizontal">
<input type="text" ng-model="vbusId" ng-model-instant>
<button class="btn" ng-click="addTodo()"><i class="icon-plus"></i>Add</button>
</div>
jscript
function TodoCtrl($scope) {
$scope.todos = [];
$scope.addTodo = function (vbusId, vxCord, vyCord, vposTimeStamp, veventType) {
$scope.todos.push({busId:'vbusId', xCord:vxCord, yCord:vyCord, timeStamp:vposTimeStamp, eventType:veventType});
}
}
table script
$(document).ready(function() {
$('#example').dataTable( {
"sScrollY": "200px",
"bPaginate": false
} );
} );
If I comment out the table script the dynamic table works and gets populated with the passed data. If i uncomment the table code the table shows up with the sorting and scroll bar but it will not accept the values. Can someone tell what I am missing?
Thanks a bunch!
You need to ensure that the datatables is being called AFTER angular has digested the page. Something like this:
function TodoCtrl($scope, $timeout) {
$scope.todos = [];
$scope.addTodo = function (vbusId, vxCord, vyCord, vposTimeStamp, veventType) {
$scope.todos.push({busId:'vbusId', xCord:vxCord, yCord:vyCord, timeStamp:vposTimeStamp, eventType:veventType});
}
$timeout(function(){
$('#example').dataTable( {
"sScrollY": "200px",
"bPaginate": false
} );
}, 0, false);
}
However, flat mixing of angular and jquery in this way is a terrible idea. You really should be writting an angular directive to wrap the jquery plugin, or just not use jQuery at all.

Resources