ng-click, ng-model not working in angularjs datatable - angularjs

I have a datatable with column filters made with AngularJS.
Here is the HTML:
<body ng-app="myApp" ng-controller="appController as Ctrl">
<table class="table table-bordered table-striped table-hover dataTable js-exportable" datatable="ng" dt-options="Ctrl.dtOptions" dt-columns="Ctrl.dtColumns">
<thead>
<tr>
<th></th>
<th>Name</th>
</tr>
</thead>
<tfoot>
<tr>
<th></th>
<th>Name</th>
</tr>
</tfoot>
<tbody>
<tr ng-repeat="user in userList">
<td>
<input type="checkbox" id="user-{{ $index }}" ng-model="Ctrl.checkboxValue[$index]" ng-click="Ctrl.checkValue(user.id)" ng-true-value="{{user.id}}" />
<label for="user-{{ $index }}"></label>
</td>
<td>
<a href="#">
{{ ::user.name }}
</a>
</td>
</tr>
</tbody>
</table>
Here's the script:
angular.module('myApp', ['ngAnimate', 'ngSanitize', 'datatables', 'datatables.columnfilter'])
.controller('appController', function($scope, $compile, DTOptionsBuilder, DTColumnBuilder){
$scope.userList = [
{
id: '1',
name: 'hello'
},
{
id: '2',
name: 'hi'
}
];
var vm = this;
vm.dtOptions = DTOptionsBuilder.newOptions()
.withPaginationType('full_numbers')
.withOption('createdRow', function (row, data, dataIndex) {
$compile(angular.element(row).contents())($scope);
})
.withColumnFilter({
aoColumns: [{
}, {
type: 'text',
bRegex: true,
bSmart: true
}]
});
vm.dtColumns = [
DTColumnBuilder.newColumn('').withTitle(''),
DTColumnBuilder.newColumn('name').withTitle('Name'),
];
vm.checkboxValue = [];
vm.checkValue = function(id){
console.log(id);
}
});
Issues:
id of the user does not get passed to checkValue function. Hence, the console.log is undefined.
Suppose if the checkbox of 1st user is checked, the value of checkboxValue array is [undefined: '1']. If checkbox of 2nd user is checked the value of checkboxValue array becomes [undefined: '2'].
Only one checkbox gets checked. Why is that?
Demo: https://plnkr.co/edit/A3PJfBuwtpUQFAIz8hW7?p=preview

You kill your code with redundancy. Look at this :
When using the angular way, you CANNOT use the dt-column directive.
Indeed, the module will render the datatable after the promise is
resolved. So for DataTables, it's like rendering a static table.
You are in fact using the "angular way" along with dt-columns. You could switch to use DTColumnDefBuilder but why define the columns when you already have a <thead> section? It would only make sense if you need to use sorting plugins etc on specific columns. And / or not is specifying the header in the markup.
Moreover, when you are rendering with angular it is not necessary to $compile anything, in fact is is very wrong, angular already does that. So
remove your dt-columns or replace it with a dt-column-defs literal
remove your $compile from the createdRow callback
Then it works. I would also remove the ng-true-value="{{user.id}}" attribute. You want an array representing the checkboxes state, why set the state to the user.id and not true or false?
vm.dtOptions = DTOptionsBuilder.newOptions()
.withPaginationType('full_numbers')
.withColumnFilter({
aoColumns: [{
}, {
type: 'text',
bRegex: true,
bSmart: true
}]
});
and
<input type="checkbox" id="user-{{ $index }}" ng-model="Ctrl.checkboxValue[user.id]" ng-click="Ctrl.checkValue(user.id)" />
Is really all you need.
forked plunkr -> https://plnkr.co/edit/Z82oHi0m9Uj37LcdUSEW?p=preview

Related

AngularJS recursive templates using tables

I am trying to work out how to do recursion using table tags with angularjs. I have the following data structure
$scope.report = [{
'Name': 'Moo',
'Value': 'MooV',
'Children': [{
'Name': 'Moo2',
'Value': 'Moo2V',
'Children': [{
'Name': 'Moo3',
'Value': 'Moo3V'
}]
}]
}];
The recursion can have no limit. I am looking to put in a simple table with the format
<table>
<tr>
<td>Moo</td>
<td>MooV</td>
</tr>
<tr>
<td>Moo2</td>
<td>Moo2V</td>
</tr>
<tr>
<td>Moo3</td>
<td>Moo3v</td>
</tr>
<table>
but I am unable to get it working just right. This will allow me to do certain things to the sections while looking flat to the user. Currently I have the code
<div ng-app="app" ng-controller='AppCtrl'>
<script type="text/ng-template" id="rloop">
<td>{{data.Name}}</td>
<td>{{data.Value}}</td>
<tr ng-repeat="data in data.Children" ng-include src="'rloop'"></tr>
</script>
<table class="table table-condensed">
<thead>
<tr>
<th>Name</th>
<th>Value</th>
</tr>
</thead>
<tbody>
<tr ng-repeat-start="data in report" ng-include="'rloop'"></tr>
<tr ng-repeat-end></tr>
</tbody>
</table>
</div>
but this is not going to work due to in repeating it creates a tr tag within a tr tag. I have tried various different methods of moving the repeat to tbody etc etc but I can't seems to get it working due to the restrictions of tables in HTML. Example, is not allowed within tr.
JSFiddle showing issue here: JSfiddle
As you say, this is not going to work within the table. Maybe it would be better to recursively convert the data to an array of Name/Value pairs, then use those in the normal way?
var app = angular.module('app', []);
function includeChildren([first, ...rest]) {
if (!first) return [];
const {Name, Value, Children = []} = first;
return [{Name, Value}, ...includeChildren(Children), ...includeChildren(rest)];
}
app.controller('AppCtrl', function ($scope) {
$scope.report = [{
'Name': 'Moo',
'Value': 'MooV',
'Children': [{
'Name': 'Moo2',
'Value': 'Moo2V',
'Children': [{
'Name': 'Moo3',
'Value': 'Moo3V'
}]
}]
}];
$scope.reportTable = includeChildren($scope.report);
// $scope.reportTable now contains:
// [{Name: 'Moo', Value: 'MooV'}, {Name: 'Moo2', Value: 'Moo2V'}, {Name: 'Moo3', Value: 'Moo3V'}]
});
Now you can use a more unsurprising template:
<div ng-app="app" ng-controller='AppCtrl'>
<table class="table table-condensed">
<thead>
<tr>
<th>Name</th>
<th>Value</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="data in reportTable">
<td>{{data.Name}}</td>
<td>{{data.Value}}</td>
</tr>
</tbody>
</table>
</div>
See it working here: https://jsfiddle.net/k6rf9g1p/1/

Angular-js ng-if issue in tables TD

I'm trying to hide a td, when getting a value = 0,
I typed
<table>
<tr ng-repeat="row in typeData"
<td ng-if="row.AMOUNTPAID > 0">#{{row.AMOUNTPAID}}</td>
</tr>
</table>
The td is not hiding, I tried ng-show it works but ng-show works on the css, and an empty space still showing in the table with the td border.
I read about updating angular-js will fix the problem, i have 1.4.6 it has the ngif directive but not working.
Any help please ?
Hard to see how your data object is defined, please find a possible solution beneath. I've made a sample, provided with some dummy data.
Also see the JSFiddle
The problem in your example is that the tr close tag is missing at :
<tr ng-repeat="row in typeData"
VIEW
<div ng-app="myApp">
<div ng-controller="testCtrl">
<table>
<tr ng-repeat="row in data">
<td>{{row.name}}</td>
<td ng-if="row.amountpaid > 0">{{row.amountpaid}}</td>
<td ng-if="row.amountpaid <= 0">NO PAYMENT YET</td>
</tr>
</table>
</div>
</div>
CONTROLLER
var myApp = angular.module('myApp', []);
// controller
myApp.controller("testCtrl", function($scope) {
// set data
$scope.data = [{
'name': 'bot1',
'amountpaid': '145'
}, {
'name': 'bot2',
'amountpaid': '10'
}, {
'name': 'bot3',
'amountpaid': '5'
}, {
'name': 'bot4',
'amountpaid': '0'
}, {
'name': 'bot5',
'amountpaid': '0'
}];
})

How to render a column with model binding using angular-datatables?

Is there a way to render a column with model binding in textbox using DTColumnBuilder?
Something like:
DTColumnBuilder.newColumn('ColumnName').withTitle('Column Name').renderWith(function (data) {
return '<input type="text" ng-model="ColumnName" />';
}),
No. You can render the table with (example) :
DTColumnBuilder.newColumn('firstName', 'First name')
.renderWith(function (data) {
return '<input type="text" ng-model="json.firstName" />'
}),
but the ng-model is never recognized because it is not angular itself that do the rendering. If you let angular do the rendering, i.e datatable="ng" and ng-repeat it works :
<table datatable="ng" dt-options="dtOptions" dt-columns="dtColumns">
<tr ng-repeat="item in json">
<td>{{ item.id }} </td>
<td><input ng-model="item.firstName"/></td>
<td>{{ item.lastName }} </td>
</tr>
</table>
demo -> http://plnkr.co/edit/f0ycjJvsACaumY13IVUZ?p=preview
notice that the JSON items is updated when you are editing in the input boxes.
Had the same problem, here is my solution:
Register callback for dtInstance
On "draw.dt" from DataTable $compile related html with angular
In other words:
HTML:
<table datatable=""
dt-options="vm.dtOptions"
dt-columns="vm.dtColumns"
dt-instance="vm.dtInstanceCallback"
class="table table-bordered table-condensed">
</table>
JS:
renderWith(function(data, type, full) {
return `<a class="ng-scope"><span ng-click='vm.remove("${data}")' class='fa fa-times-circle'></span></a>`
});
...
vm.dtInstanceCallback = (dtInstance) => {
vm.dtInstance = dtInstance;
dtInstance.DataTable.on('draw.dt', () => {
let elements = angular.element("#" + dtInstance.id + " .ng-scope");
angular.forEach(elements, (element) => {
$compile(element)($scope)
})
});
}
I minimized selection of elements, to optimize performance, maybe it's not needed. So far tested in Chrome & Safari, worked in both

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

Pagination controls not showing up in ng-table when fetching data from backend

I am fetching a list of data from the backend and displaying it using ng-table. The problem is that its not showing the pagination controls. Previously, when I used dummy data to show the ng-table, pagination was working totally fine. Could someone help me out here?
This is my HTML:
<table ng-table="tableParams" show-filter="true" class="table">
<thead>
<tr>
<th ng-repeat="column in columns" ng-show="column.visible"
class="text-center" ng-class="{
'sort-asc': tableParams.isSortBy(column.field, 'asc'),
'sort-desc': tableParams.isSortBy(column.field, 'desc'),
'sortable': !$first
}"
ng-click="tableParams.sorting(column.field, tableParams.isSortBy(column.field, 'asc') ? 'desc' : 'asc')">
<div>{{column.title}}</div>
</th>
</tr>
</thead>
<tr ng-repeat="user in data | filter:searchText">
<td width="30" style="text-align: left">
<input type="checkbox" ng-model="checkboxes.items[user.id]" />
</td>
<td data-title="'Email Id'" class="text-center" sortable="email" ng-show="columns[1].visible">
<span>{{user.email}}</span>
</td>
<td data-title="'User Karma'" class="text-center" sortable="userkarma" ng-show="columns[2].visible">
<span>{{user.userkarma}}</span>
</td>
<td data-title="'Date Joined'" class="text-center" sortable="datejoined" ng-show="columns[3].visible">
<span>{{user.datejoined}}</span>
</td>
<td data-title="'Unsubscribed'" class="text-center" sortable="status" ng-show="columns[4].visible">
<span>{{user.unsubscribed}}</span>
</td>
</tr>
</table>
Below is my js file:
for (var i = 0; i < UserList.getUsers()
.length; i++) {
$scope.data.push({
id: UserList.getUsers()[i]._id,
email: UserList.getUsers()[i].email,
userkarma: UserList.getUsers()[i].healthScore,
datejoined: moment(UserList.getUsers()[i].firstSessionAt)
.format("MMMM Do YYYY"),
unsubscribed: UserList.getUsers()[i].unsubscribed
})
};
$scope.columns = [{
title: '',
field: 'checkbox',
visible: true
},
{
title: 'Email',
field: 'email',
visible: true
}, {
title: 'User Karma',
field: 'userkarma',
visible: true
}, {
title: 'Date Joined',
field: 'datejoined',
visible: true
}, {
title: 'Unsubscribed',
field: 'unsubscribed',
visible: true
}
];
$scope.tableParams = new ngTableParams({
page: 1,
count: 10, // count per page
filter: {
name: 'M' // initial filter
},
sorting: {
name: 'asc'
}
}, {
total: $scope.data.length, // length of data
getData: function ($defer, params) {
// use build-in angular filter
var filteredData = params.filter() ?
$filter('filter')($scope.data, params
.filter()) :
data;
var orderedData = params.sorting() ?
$filter('orderBy')($scope.data,
params.orderBy()) :
$scope.data;
params.total(orderedData.length); // set total for recalc paginationemail
$defer.resolve(orderedData.slice((
params.page() -
1) * params.count(),
params.page() *
params.count()));
}
});
This happens because the usual $scope.tableParams.reload() function used to refresh the data after an asynchronous data load does not refresh the total item count in the table. It didn't happen before because this value is correctly set at the beginning when you were using dummy data.
You need to add a params.total(data.length); the getData function to manually refresh the value.
For me, it's because I was using coffeescript, which automatically returns the value of the last thing in your function. This causes problems because ng-table's getData() gets back a promise, and if it doesn't get one, it creates one itself, from $defer. By using coffeescript and not quite properly converting the example from the Configuring your table with ngTableParams wiki page, I was returning something that wasn't a promise.
In coffeescript, make sure your callback to getData() ends with either
$defer.promise
or
return
so that ng-table gets a promise, or knows to make one itself.
I found the answer. Actually there is an issue with ng-table when loading dynamic data. When the data is dynamically loaded, the getData function is not called. So this is what I did. I created a refreshTable function to call the setTable function which then calls the getData function and renders the table.
$scope.refreshTable = function () {
console.log('\n\n refreshing table')
$scope['tableParams'] = {
reload: function () {},
settings: function () {
return {}
}
};
$timeout(setTable, 100)
};
$scope.refreshTable();
function setTable(arguments) {
$scope.tableParams = new ngTableParams({
page: 1, // show first page
count: 10, // count per page
filter: {
name: '' // initial filter
},
sorting: {
name: 'asc'
}
}, {
filterSwitch: true,
total: $scope.users.length, // length of data
getData: function ($defer, params) {
console.log(
'\n\nngTable getData called now')
var orderedData = params.sorting() ?
$filter('orderBy')($scope.users,
params.orderBy()) :
data;
params.total(orderedData.length);
$defer.resolve(orderedData.slice((params.page() -
1) * params.count(), params.page() *
params.count()));
}
});
}
I had this problem and applied solution provided here but it not solved my problem.My usage was like below
<table ng-table="tableParams" class="table ng-table table-striped" show-filter="{{::showFilter}}">
<tr ng-show="showHeaders">
<th class="th-select" ng-repeat="column in columns">{{column.title}}</th>
</tr>
<tr ng-repeat="row in $data">
<td ng-repeat="column in columns" ng-show="column.visible" sortable="column.field" ng-click="save(row)">
{{row[column.field][column.subfield] || row[column.field]}}
<span compile="column.getValue()" ></span>
</td>
</tr>
</table>
and after one day effort I understand that the problem is using css class "ng-table" . I mentioned this to save your time so other than adding
params.total(data.length)
you should check your css too and the working code is :
<table ng-table="tableParams" class="table table-striped" show-filter="{{::showFilter}}">
<tr ng-show="showHeaders">
<th class="th-select" ng-repeat="column in columns">{{column.title}}</th>
</tr>
<tr ng-repeat="row in $data">
<td ng-repeat="column in columns" ng-show="column.visible" sortable="column.field" ng-click="save(row)">
{{row[column.field][column.subfield] || row[column.field]}}
<span compile="column.getValue()" ></span>
</td>
</tr>
</table>
I had the same experience with loadData + asynchronous load.
On my side the problem was that the data returned by the API was an array of 2 other arrays meanwhile I was using only 1 of the 2 for my table.
See below:
*.js
return $http(options).then(function (resp) {
params.total(resp.data.users.length)
return resp.data
}
*.html
<tr ng-repeat="row in usrCtrl.servicesTable.data.users" ng-if="row.Services.hasOwnProperty(usrCtrl.selectedService)">
By returning "resp.data.users" on javascript side and correctly looping on "usrCtrl.servicesTable.data" on html side then the pager appeared!
Loïc

Resources