Cannot reinitialise DataTable - AngularJS with datatable - angularjs

When I combine AngularJS (1.6.x) with jQuery data-tables, I get alert error:
Cannot reinitialise DataTable
First I use AngularJS to bind and fill data-tables,
then I try to add Individual column searching (text inputs) feature.
Individual column searching (select inputs)
AngularJS initialize data-tables, but does not give me a handle.
Here is my code:
var app1=angular.module('formvalid', ['ui.bootstrap','ui.utils']);
app1.controller('validationCtrl',function($scope){
angular.element(document).ready(function () {
// Setup - add a text input to each footer cell
$('#example tfoot th').each( function () {
var title = $(this).text();
$(this).html( '<input type="text" placeholder="Search '+title+'" />' );
} );
console.log(' document ready function, add search by column feature ');
var table = $('#example').DataTable();
// Apply the search
table.columns().every( function () {
var that = this;
$( 'input', this.footer() ).on( 'keyup change', function () {
if ( that.search() !== this.value ) {
that
.search( this.value )
.draw();
}
} );
} );
});// document ready
$scope.data=[[
"Tiger Nixon",
"System Architect",
"Edinburgh",
"5421",
"2011\/04\/25",
"$320,800"
]];
$scope.dataTableOpt = {
//custom datatable options
// or load data through ajax call also
// "data": $scope.data00, // this is not real binding, the real binding is ui-jq="dataTable" ui-options="dataTableOpt", fill $scope.data
"aLengthMenu": [[10, 50, 100,-1], [10, 50, 100,'All']],
};
});

demo on codepen.io
demo on jsFiddle
earlier angularjs initialize datatable( must add "retrieve": true, otherwise, will get above error retrieve existing table handle) , but don't get a table handle,
later here, $('#id').DataTable(); will 1) if existing, will retrieve table handle.
2) if not exsiting, will create a new table.
so the solution is
$scope.dataTableOpt = {
//custom datatable options
// or load data through ajax call also
// "data": $scope.data00, // this is not real binding, the real binding is ui-jq="dataTable" ui-options="dataTableOpt", fill $scope.data
"retrieve": true, // angularjs at begining initialize datatable, but don't get a handle to the table, later you want to add search column, you need to get the table handle.
"aLengthMenu": [[10, 50, 100,-1], [10, 50, 100,'All']],
};

codepen : ui-grid (angularjs 1.x) demo
jsFiddle : ui-grid (angularjs 1.x) demo
var app = angular.module('app', ['ngAnimate', 'ngTouch', 'ui.grid', 'ui.grid.exporter', 'ui.grid.selection']);
app.controller('MainCtrl', ['$scope', '$http', '$interval', '$q','uiGridConstants', function ($scope, $http, $interval, $q, uiGridConstants) {
I build those demo, is best solution for angularjs 1.x datatable.
angularjs 1.x + jquery(datatables) isn't best solution.
ui-grid is pure angularjs 1.x, is best solution so far.

Related

AngularJS and DataTables refreshing data

My project outputs results to a DataTable from an AngularJS controller function, but I'm running into some strangeness when I try to modify my search params. The first rendering of the table works as expected. But when I select different options and run the search again, extra rows appear in the table, but the info section shows the previous search's row count, and changing the number of rows shown via the length menu causes the new rows to disappear. Here's my table declaration, using attributes to wire up DataTables:
<table ui-jq="dataTable" ui-options="dataTableOptions" id="search-results" class="display nowrap datatable cell-borders" style="width: 100%;">
And this is my AngularJS controller code:
$scope.dataTableOptions = {
dom: "lfBrtip",
lengthMenu: [[25, 50, -1], [25, 50, "All"]],
language: {
emptyTable: 'No items matched your search criteria'
},
buttons: [
{
text: 'Export',
className: 'button button:hover',
extend: 'csv'
}
]
};
$scope.getItemInfo = function (model) {
$http({
method: 'POST',
url: $scope.getUrl('/My/ServerSide/Url'),
data: { model: $scope.model }
}).then(function successCallBack(response) {
$scope.model.SearchResults = response.data;
}, function errorCallback(response) {
alert("There was an error gathering the entity information. Please try again");
});
};
I'm not sure why submitting new queries with different params doesn't simply update the data in the DataTables table. Any suggestions?
I ended up using a bit of an ugly hack to get this to work. Even DataTables author wasn't sure how to get around the issue of using AngularJS with DataTables, so I had to force a reinitialization every time the form posted. I persisted the search params to localStorage, and called location.reload(). Then when the page loads and the AngularJS init() function runs, I pick up the search params and call the search function from inside an Angular document ready function, like this:
$scope.init = function () {
$scope.ValidationErrors = [];
$scope.model = {};
$scope.model.SearchResults = [];
$scope.model.ItemNumber = localStorage.getItem("itemNumber");
$scope.model.StartDate = localStorage.getItem("startDate");
$scope.model.EndDate = localStorage.getItem("endDate");
angular.element(document).ready(function () {
if ($scope.model.ItemNumber) {
$scope.getItemRecords();
}
});
localStorage.clear();
};
And then of course I clear the localStorage after the query. Not terribly elegant, but it'll have to do for now.

ng-table going to first page when data changes

I'm working on a meteor app that uses angular-meteor and ng-table. The dataset of the ng-table is from a collection. You can add rows to the table dynamically via a modal form. The dataset is reactive and changes the view once there's a change in the collection. I'm watching for status changes of the item (QUEUED, PROCESSING, CREATED).
Thing is, if the table is on page 2 and the status changes (any change in the dataset), the table is redrawn and goes back to page 2. Is there a way to make it stay on the current page even with dataset changes?
Here's a sample of what I have so far:
(function () {
'use strict';
function myController($scope, $reactive, ngTableParams, ngTableEventsChannel, myService) {
$reactive(this).attach($scope);
this.subscribe('myCollection');
this.currentPage = 1;
this.helpers({
tableParams: () => {
return new ngTableParams({
page: this.currentPage
}, {
//this is just a simple collection call
dataset: myService.getItems().fetch()
});
}
});
$scope.$watch('vm.tableParams', (params) => {
//not triggered :(
ngTableEventsChannel.onDatasetChanged(() => {
console.log('data changed');
tableParams.page($scope.currentPage);
}, $scope, tableParams);
ngTableEventsChannel.onPagesChanged(() => {
$scope.currentPage = tableParams.page();
}, $scope, tableParams);
});
}
angular.module('MyApp').controller('MyController', myController);
})();
The onDatasetChanged event isn't being triggered. I was going to change the page there but I'm not sure if that's a good idea anyway.

ng-hide & ng-show in leaflet legend

I tried to create a leaflet legend using the 'ng-show' and 'ng-hide' attributes.
Unfortunately, the legend is not created on site load but on map load.
The attributes don't seem to work if they are added with javascript directly.
This code:
onAdd: function() {
var controlDiv = L.DomUtil.create('div', 'air-quality-legend');
controlDiv.setAttribute('ng-hide', 'true');
controlDiv.className = "airQualityIndex";
L.DomEvent
.addListener(controlDiv, 'click', L.DomEvent.stopPropagation)
.addListener(controlDiv, 'click', L.DomEvent.preventDefault);
var table = document.createElement('table');
var tr = document.createElement('table');
var td = document.createElement('table');
td.innerHTML = "test";
tr.appendChild(td);
table.appendChild(tr);
controlDiv.appendChild(table);
return controlDiv;
}
Produces that output.
As described there is a table when there should not.
Is there any way to add 'ng-hide' or 'ng-show' via javascript on runtime?
Thank you for your help in advance.
You'll need to compile the DOM of your custom control. To do that, you'll need to inject $compile into your controller, then after having added the control to your map use the getContainer method on your control instance and run $compile on it and attach it to the scope:
Control:
L.Control.Custom = L.Control.extend({
onAdd: function () {
var container = L.DomUtil.create('div', 'leaflet-control-custom')
header = L.DomUtil.create('h1', 'leaflet-control-custom-header', container);
header.textContent = 'NG-Hide test';
header.setAttribute('ng-hide', 'hide');
return container;
}
});
Controller:
angular.module('app').controller('controller', [
'$scope', 'leaflet', '$compile',
function ($scope, leaflet, $compile) {
$scope.hide = false;
leaflet.map.then(function (map) {
var control = new L.Control.Custom().addTo(map);
$compile(control.getContainer())($scope);
});
}
]);
Here's a working example on Plunker: http://plnkr.co/edit/xzRwTp9OZ8Zp8v7ktt2c?p=preview

Angular-DataTables custom filter

I am trying to add a custom filter to angular-DataTables with server side processing, which works perfectly with sorting and built in search of datatables.
I was following example Angular-DataTables, to build the server side processing and setup the DataTable, in searching around i have found some info but haven't been able to make it work.
What i am trying to get is to redraw the table with filtered data once the checkbox [Player] has been triggered.
Does anyone know a solution for this or has a working example for this?
have found this example Custom Table Filter, but it seems it doesn't work either.
HTML:
<div ng-app="showcase"><div ng-controller="ServerSideProcessingCtrl">
<label><input type="checkbox" id="customFilter" value="player"> Player</label>
<table datatable="" dt-options="dtOptions" dt-columns="dtColumns" class="row-border hover"></table>
JS part:
'use strict';
angular.module('showcase', ['datatables'])
//.controller('ServerSideProcessingCtrl', ServerSideProcessingCtrl);
.controller('ServerSideProcessingCtrl',["$scope", "DTOptionsBuilder", "DTColumnBuilder", function($scope, DTOptionsBuilder, DTColumnBuilder) {
//function ServerSideProcessingCtrl(DTOptionsBuilder, DTColumnBuilder) {
console.log($scope);
$scope.dtOptions = DTOptionsBuilder.newOptions()
.withOption('ajax', {
// Either you specify the AjaxDataProp here
// dataSrc: 'data',
url: 'getTableData.php',
type: 'POST'
})
// or here
.withDataProp('data')
.withOption('serverSide', true)
.withPaginationType('full_numbers');
$scope.dtColumns = [
DTColumnBuilder.newColumn('id').withTitle('ID'),
DTColumnBuilder.newColumn('name').withTitle('First name'),
DTColumnBuilder.newColumn('position').withTitle('Position'),
DTColumnBuilder.newColumn('type').withTitle('Type')
];
$scope.$on('event:dataTableLoaded', function(event, loadedDT) {
console.log(event);
console.log(loadedDT);
$('#customFilter').on('change', function() {
loadedDT.DataTable.draw();
} );
});
}]);
JSON on load:
{"draw":"1","recordsTotal":8,"recordsFiltered":8,"data":[{"id":"1","name":"Raul","position":"front","type":"player"},{"id":"2","name":"Crespo","position":"front","type":"player"},{"id":"3","name":"Nesta","position":"back","type":"player"},{"id":"4","name":"Costacurta","position":"back","type":"player"},{"id":"5","name":"Doc Brown","position":"staff","type":"medic"},{"id":"6","name":"Jose","position":"staff","type":"manager"},{"id":"7","name":"Ferguson","position":"staff","type":"manager"},{"id":"8","name":"Zinedine","position":"staff","type":"director"}]}
After searching and browsing, combined few examples and came up with this.
HTML :
<label><input type="checkbox" id="customFilter" value="player" ng-click="reload()" > Player</label>
JS:
'use strict';
angular.module('showcase', ['datatables'])
//.controller('ServerSideProcessingCtrl', ServerSideProcessingCtrl);
.controller('ServerSideProcessingCtrl',["$scope", "DTOptionsBuilder", "DTColumnBuilder","DTInstances", function ($scope, DTOptionsBuilder, DTColumnBuilder, DTInstances) {
//function ServerSideProcessingCtrl(DTOptionsBuilder, DTColumnBuilder) {
console.log($scope);
$scope.dtOptions = DTOptionsBuilder.newOptions()
.withOption('ajax', {
// Either you specify the AjaxDataProp here
// dataSrc: 'data',
url: 'getTableData.php',
type: 'POST',
// CUSTOM FILTERS
data: function (data) {
data.customFilter = $('#customFilter').is(':checked');
}
})
// or here
.withDataProp('data')
.withOption('serverSide', true)
.withPaginationType('full_numbers');
$scope.dtColumns = [
DTColumnBuilder.newColumn('id').withTitle('ID'),
DTColumnBuilder.newColumn('name').withTitle('First name'),
DTColumnBuilder.newColumn('position').withTitle('Position'),
DTColumnBuilder.newColumn('type').withTitle('Type')
];
DTInstances.getLast().then(function (dtInstance) {
$scope.dtInstance = dtInstance;
});
$scope.reload = function(event, loadedDT) {
$scope.dtInstance.reloadData();
};
}]);
and on the backend just go through the $_POST and check for custom filter, hopefully this will help someone
You can use withFnServerData with fromSource functions instead of
withOption:
This API allows you to override the default function to retrieve the data (which is $.getJSON according to DataTables documentation) to something more suitable for you application.
It's mainly used for Datatables v1.9.4. See DataTable documentation.
$scope.dtOptions = DTOptionsBuilder.fromSource('data.json')
.withFnServerData(serverData);
function serverData (sSource, aoData, fnCallback, oSettings) {
oSettings.jqXHR = $.ajax({
'dataType': 'json',
'type': 'POST',
'url': sSource,
'data': aoData,
'success': fnCallback
});
:)
Ok sorry its not a full blown example. This only works with angular and datatables, if you do a filter on the ng-repeat eg | aFilter:this The this transfers the scope. The filtering applied can now be quite complex. Within the ng-controller <div> you can have an html partial containing drop downs or input texts, all having an ng-model value.
When these change they kick off the filter routineaFilter an angular.filter('aFilter'.... js routine. The records are piped through the afilter routine allowing the ones wanted to be pushed onto an array and this is what is returned with the return. It doesn't work with breeze, yet. Be aware it is unlikely to be server side. To deal with server side maybe an SQL call in the service....another day.
eg in the ng-table id="test" :
<tr ng-repeat="edRec in aSetOfJSonRecords | aFilter:this | orderBy:'summat'">
{{edRec.enCode}} etc
</tr>
in the aFilter, the fltEnCode represents the ng-model values, the test variable allows freedom from nulls causing issues upon comparison, good idea to test for undefined first:
app.filter('aFilter', [function () {
return function (items, $scope) {
var countItems = 0;
var filtered = [];
var isOK = 0;
angular.forEach(items, function (item) {
isOK = 1;
// some conditions
if ($scope.fltEnCode !== "") {
if (item.enCode === null) { test = ""; } else { test = item.enCode; }
if (test.indexOf($scope.fltEnCode) < 0) isOK = 0;
}
// end of conditions
if (isOK > 0) {
filtered.push(item);
countItems++;
}
});
// alert(countItems);
return filtered;
};
}]);
Hope its of some use. I've avoided boolean variables as they have given grief before. Odd occasions have needed an ng-change in the html items pointing to an angular function resetting the data by calling the getTheItemsForTest() in the controller. This redraws the list. Having
$scope.dtOptions = {
stateSave: false, .......
in your controller, keeps the sorting columns correct.
$(document).ready(function() {
var table = $('#test').DataTable();
table.draw();
};
might also be useful if its recalcitrant. I need to know how to make it work for breeze??? Enjoy..
here is what I really missed after I searched alot
bower install datatables-light-columnfilter

Best way to retrieve selected item from Kendo treeview within Angular JS framework

I'm integrating the Kendo UI treeview widget into my Angular based app (using asp.net mvc4 framework).
I'm seeking advice on the best way to retrieve the tree's selected item, as I'm not sure if the way I'm doing it - e.sender._current.text(); - is best practice.
In my html div below you'll notice k-on-change="vm.onTreeSelect(kendoEvent)" , and the associated js event in function onTreeSelect(e) .
I find the current documentation a little weak at http://kendo-labs.github.io/angular-kendo/#/TreeView , so what I'm looking for is two things at the moment:
1) The best way to get the currently-selected item from the treeview.
2) How do I know when I've reached the bottom leaf of my tree.
Thank you ahead of time of your advice, and please find some code snippets below...
My html snippet is :
<div class="widget-content text-left text-info">
Selected: {{vm.selected}}
<span id="treeview" kendo-tree-view="tree"
k-options="vm.treeOptions"
k-data-source="vm.hierarchy"
k-on-change="vm.onTreeSelect(kendoEvent)">
</span>
</div>
and a snippet from my javascript code is :
(function () {
'use strict';
var controllerId = 'dashboard';
angular.module('app').controller(controllerId, ['common', 'datacontext', dashboard]);
vm.hierarchy = [];
vm.onTreeSelect = onTreeSelect;
vm.treeOptions = {
checkboxes: {
checkChildren: true
}
};
vm.selected = null;
activate();
function activate() {
var promises = [getHierarchy(), getCountries()];
common.activateController(promises, controllerId)
.then(function () { log('Activated Dashboard View'); });
}
function dashboard(common, datacontext) {
function onTreeSelect(e) {
vm.selected = e.sender._current.text();
}
})();
the best way to get the data item would be:
var dataItem = e.sender.dataItem(e.sender.select());
This will give you the item with all data attached. Make sure you call it on change event. this would work because e.sender is actually the kendo-tree-view in angular and the other methods are standard. also pass the kendoEvent as event argument.

Resources