This is a demo using knockout.js to create a table for listing records of any object custom, standard or combinations of data from multiple objects. The table is sortable and searchable using a typeahead search field. It also includes a dropdown to limit the number of records visible as well as pagination.
This is what it looks like in its most basic styling:
This is the link to the working demo
This is the component that holds the knockout code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 |
<apex:component controller="AccountListController"> <script> var j$ = jQuery.noConflict(); j$.fn.dataTable.ext.order['dom-checkbox'] = function (settings,col){ return this.api().column(col,{order:'index'}).nodes().map(function (td, i){ return $('input', td).prop('checked') ? '1' : '0'; }); }; ko.bindingHandlers.dataTablesForEach = { page: 0, init: function(element,valueAccessor,allBindingsAccessor,viewModel,bindingContext){ var binding = ko.utils.unwrapObservable(valueAccessor()); ko.unwrap(binding.data); if (binding.options.paging) { binding.data.subscribe(function(changes) { var table = j$(element).closest('table').DataTable(); ko.bindingHandlers.dataTablesForEach.page = table.page(); table.destroy(); }, null, 'arrayChange'); } var nodes = Array.prototype.slice.call(element.childNodes, 0); ko.utils.arrayForEach(nodes, function(node) { if (node && node.nodeType !== 1) {node.parentNode.removeChild(node);} }); return ko.bindingHandlers.foreach.init(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext); }, update: function(element,valueAccessor,allBindings,viewModel,bindingContext){ var binding = ko.utils.unwrapObservable(valueAccessor()), key = 'DataTablesForEach_Initialized'; ko.unwrap(binding.data); var table; if (!binding.options.paging) { table = j$(element).closest('table').DataTable(); table.destroy(); } ko.bindingHandlers.foreach.update(element,valueAccessor,allBindings, viewModel,bindingContext); table = j$(element).closest('table').DataTable(binding.options); if (binding.options.paging) { if (table.page.info().pages - ko.bindingHandlers.dataTablesForEach.page === 0){ table.page(--ko.bindingHandlers.dataTablesForEach.page).draw(false); } else {able.page(ko.bindingHandlers.dataTablesForEach.page).draw(false);} } if (!ko.utils.domData.get(element, key) && (binding.data || binding.length)){ ko.utils.domData.set(element, key, true);} return {controlsDescendantBindings: true}; } }; function AccountListVM(){ var self = this; self.accountItemList = ko.observableArray(); function accountItem(item) { var self = this; self.name = ko.observable(item.name); self.number = ko.observable(parseFloat(item.number)); self.industry = ko.observable(item.industry); self.revenue = ko.observable(item.revenue); self.state = ko.observable(item.state); self.city = ko.observable(item.city); self.zip = ko.observable(item.zip); } Visualforce.remoting.Manager.invokeAction('{!$RemoteAction.AccountListController.getAccountList}', function(result,event){ if(event.status == true && result != null){ //var name,number,industry,revenue,state,city,zip; for(var i = 0 ;i < result.length; i++){ var name = result[i].Name; var number = result[i].AccountNumber; var industry = result[i].Industry; var revenue = result[i].AnnualRevenue; var state = result[i].BillingState; var city = result[i].BillingCity; var zip = result[i].BillingPostalCode; self.accountItemList.push(new accountItem({ name:name,number:number,industry:industry, revenue:revenue,state:state,city:city,zip:zip })); } } },{escape:false}); } var AccountList = new AccountListVM; ko.applyBindings(AccountList); </script> </apex:component> |
This is the controller to retrieve the records using a remote action:
1 2 3 4 5 6 7 8 |
public with sharing class AccountListController { @RemoteAction public static object getAccountList(){ List<Account> accountList = new List<Account>(); accountList = [SELECT id,Name,AccountNumber,Industry,AnnualRevenue,BillingState,BillingCity,BillingPostalCode FROM Account]; return accountList; } } |
The visualforce page:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
<apex:page standardStylesheets="false" cache="true" sidebar="false" showHeader="false" Controller="AccountListController" docType="html-5.0"> <head> <apex:includescript value="https://code.jquery.com/jquery-3.2.1.min.js" /> <apex:includescript value="https://code.jquery.com/ui/1.12.1/jquery-ui.min.js"/> <apex:includescript value="//cdn.datatables.net/1.10.16/js/jquery.dataTables.min.js" /> <apex:includeScript value="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js"/> <apex:includeScript value="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-debug.js"/> <apex:includeScript value="https://cdn.jsdelivr.net/npm/knockout-datatable@0.7.1/knockout-datatable.min.js"/> <apex:stylesheet value="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"/> <apex:stylesheet value="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.8.13/themes/base/jquery-ui.css"/> <apex:stylesheet value="//cdn.datatables.net/1.10.4/css/jquery.dataTables.css" /> </head> <div class="Wrapper" style="margin:50px;"> <div class="col-md-12" style="text-align:center;"><h1>Sortable and searchable table with pagination example.</h1></div> <div class="col-md-12"> <div class="row"> <table class="table table-striped table-bordered dataTable" id="acountListTable"> <thead> <tr> <th>Name</th> <th>Revenue</th> <th>Industry</th> <th>City</th> <th>State</th> <th>Zip</th> </tr> </thead> <tbody data-bind="dataTablesForEach : { data: accountItemList, options: { deferRender: true, paging: true, responsive: true, bRetrieve: true } }"> <tr> <td data-bind="text:name"/> <td data-bind="text:revenue"/> <td data-bind="text:industry"/> <td data-bind="text:city"/> <td data-bind="text:state"/> <td data-bind="text:zip"/> </tr> </tbody> </table> </div> </div> </div> <c:AccountListKO /> </apex:page> |