├── .gitignore
├── LICENSE
├── README.md
└── src
├── aura
├── DataTableCellCmp
│ ├── DataTableCellCmp.cmp
│ ├── DataTableCellCmp.cmp-meta.xml
│ ├── DataTableCellCmpController.js
│ └── DataTableCellCmpHelper.js
├── DataTableCmp
│ ├── DataTableCmp.cmp
│ ├── DataTableCmp.cmp-meta.xml
│ ├── DataTableCmpController.js
│ ├── DataTableCmpHelper.js
│ └── DataTableCmpRenderer.js
├── DataTableColumnCmp
│ ├── DataTableColumnCmp.cmp
│ ├── DataTableColumnCmp.cmp-meta.xml
│ └── DataTableColumnCmpController.js
├── DataTableDemoApp
│ ├── DataTableDemoApp.app
│ ├── DataTableDemoApp.app-meta.xml
│ ├── DataTableDemoApp.css
│ ├── DataTableDemoAppController.js
│ └── DataTableDemoAppHelper.js
├── DataTablePageChangeEvent
│ ├── DataTablePageChangeEvent.evt
│ └── DataTablePageChangeEvent.evt-meta.xml
└── DataTableSortChangeEvent
│ ├── DataTableSortChangeEvent.evt
│ └── DataTableSortChangeEvent.evt-meta.xml
├── classes
├── DataTableDemoAppController.cls
└── DataTableDemoAppController.cls-meta.xml
└── package.xml
/.gitignore:
--------------------------------------------------------------------------------
1 | config/
2 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | BSD 3-Clause License
2 |
3 | Copyright (c) 2017, Doug Ayers
4 | All rights reserved.
5 |
6 | Redistribution and use in source and binary forms, with or without
7 | modification, are permitted provided that the following conditions are met:
8 |
9 | * Redistributions of source code must retain the above copyright notice, this
10 | list of conditions and the following disclaimer.
11 |
12 | * Redistributions in binary form must reproduce the above copyright notice,
13 | this list of conditions and the following disclaimer in the documentation
14 | and/or other materials provided with the distribution.
15 |
16 | * Neither the name of the copyright holder nor the names of its
17 | contributors may be used to endorse or promote products derived from
18 | this software without specific prior written permission.
19 |
20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Lightning Data Tables Component
2 |
3 | Lightning Component that renders SLDS data table that supports sorting and infinite scrolling with no third-party JS libraries.
4 |
5 |
6 |
8 |
9 |
--------------------------------------------------------------------------------
/src/aura/DataTableCellCmp/DataTableCellCmp.cmp:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
8 |
9 |
12 |
13 |
16 |
17 |
20 |
21 |
25 |
26 |
27 |
28 |
32 |
33 |
37 |
38 |
42 |
43 |
47 |
48 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 | {!v.value}
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
--------------------------------------------------------------------------------
/src/aura/DataTableCellCmp/DataTableCellCmp.cmp-meta.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 38.0
4 | A Lightning Component Bundle
5 |
6 |
--------------------------------------------------------------------------------
/src/aura/DataTableCellCmp/DataTableCellCmpController.js:
--------------------------------------------------------------------------------
1 | ({
2 | doInit : function( component, event, helper ) {
3 |
4 | var row = component.get( 'v.row' );
5 | var column = component.get( 'v.column' );
6 |
7 | // the column's name might be a single property on the object
8 | // like 'Subject' or it might be a compound reference
9 | // like 'Who.Name' so we split the string into its parts
10 | // and try to traverse the object graph through the properties.
11 | //
12 | // if the row does not have the full property graph
13 | // then null is returned, otherwise the value at the end of the rainbow.
14 | component.set( 'v.value', helper.parseFieldValue( component, row, column.get( 'v.name' ) ) );
15 |
16 | // set css class from column definition
17 | component.set( 'v.class', column.get( 'v.valueClass' ) );
18 |
19 | // determine ui theme
20 | var uiTheme = helper.getUITheme();
21 | component.set( 'v.uiTheme', uiTheme );
22 |
23 | // determine type of links to use
24 | var linkToRecord = column.get( 'v.linkToRecord' );
25 | var linkToURL = column.get( 'v.linkToURL' );
26 |
27 | // if linking to a record we first check if the expression evaluates to a field on the row object
28 | // that holds the value to link to, otherwise will use the value as-is for linking.
29 | // since this is intended to be an sobject record id, for classic theme only then we ensure
30 | // there's a leading '/'.
31 | if ( !$A.util.isUndefinedOrNull( linkToRecord ) ) {
32 |
33 | var parsedLinkToRecord = helper.parseFieldValue( component, row, linkToRecord );
34 |
35 | if ( !$A.util.isUndefinedOrNull( parsedLinkToRecord ) ) {
36 | component.set( 'v.linkToRecord', ( uiTheme === 'Classic' ? '/' : '' ) + parsedLinkToRecord );
37 | } else {
38 | component.set( 'v.linkToRecord', ( uiTheme === 'Classic' ? '/' : '' ) + linkToRecord );
39 | }
40 |
41 | if ( uiTheme === 'Classic' ) {
42 | component.set( 'v.classicLink', component.get( 'v.linkToRecord' ) );
43 | }
44 |
45 | }
46 |
47 | // if linking to a record we first check if the expression evaluates to a field on the row object
48 | // that holds the value to link to, otherwise will use the value as-is for linking
49 | if ( !$A.util.isUndefinedOrNull( linkToURL ) ) {
50 |
51 | var parsedLinkToURL = helper.parseFieldValue( component, row, linkToURL );
52 |
53 | if ( !$A.util.isUndefinedOrNull( parsedLinkToURL ) ) {
54 | component.set( 'v.linkToURL', parsedLinkToURL );
55 | } else {
56 | component.set( 'v.linkToURL', linkToURL );
57 | }
58 |
59 | if ( uiTheme === 'Classic' ) {
60 | component.set( 'v.classicLink', component.get( 'v.linkToURL' ) );
61 | }
62 |
63 | }
64 |
65 | },
66 |
67 | /**
68 | * For Salesforce1 and Lightning themes, action handler
69 | * for navigating to record or arbitrary URL.
70 | */
71 | handleOnClick : function( component, event, helper ) {
72 |
73 | var linkToRecord = component.get( 'v.linkToRecord' );
74 | var linkToURL = component.get( 'v.linkToURL' );
75 |
76 | if ( !$A.util.isUndefinedOrNull( linkToRecord ) ) {
77 | helper.navigateToRecord( linkToRecord );
78 | } else if ( !$A.util.isUndefinedOrNull( linkToURL ) ) {
79 | helper.navigateToURL( linkToURL );
80 | } else {
81 | console.warn( 'Unexpected click event. No value for v.linkToRecord or v.linkToURL' );
82 | console.log( event );
83 | }
84 |
85 | }
86 | })
--------------------------------------------------------------------------------
/src/aura/DataTableCellCmp/DataTableCellCmpHelper.js:
--------------------------------------------------------------------------------
1 | ({
2 | /**
3 | * Retrieves the field value from the JSON object
4 | * using dot notation to traverse the object graph.
5 | * If any part of the field path is not accessible then returns null.
6 | *
7 | * @param obj
8 | * The JSON object (e.g. { 'Account' : { 'Name' : 'Burlington' } } )
9 | * @param fieldPath
10 | * Uses dot notation to represent the JSON field value to retrieve (e.g. 'Account.Name' )
11 | */
12 | parseFieldValue : function( component, obj, fieldPath ) {
13 |
14 | var fields = fieldPath.split( '.' );
15 |
16 | var value = null;
17 |
18 | if ( obj.hasOwnProperty( fields[0] ) ) {
19 |
20 | value = obj[fields[0]];
21 |
22 | if ( fields.length > 1 ) {
23 |
24 | for ( var i = 1; i < fields.length; i++ ) {
25 | if ( value != null && value.hasOwnProperty( fields[i] ) ) {
26 | value = value[fields[i]];
27 | } else {
28 | value = null;
29 | break;
30 | }
31 | }
32 |
33 | }
34 |
35 | }
36 |
37 | return value;
38 | },
39 |
40 | // ----------------------------------------------------------------------------
41 |
42 | /**
43 | * Quick and dirty check to determine if code is running in
44 | * - Classic
45 | * - Salesforce1
46 | * - Lightning
47 | * based on the existence of certain features.
48 | */
49 | getUITheme : function() {
50 |
51 | var theme = null;
52 |
53 | var event = $A.get( 'e.force:navigateToSObject' );
54 |
55 | if ( event ) {
56 |
57 | theme = 'Lightning';
58 |
59 | } else if ( ( typeof sforce !== 'undefined' ) && ( typeof sforce.one !== 'undefined' ) ) {
60 |
61 | theme = 'Salesforce1';
62 |
63 | } else {
64 |
65 | theme = 'Classic'
66 |
67 | }
68 |
69 | return theme;
70 | },
71 |
72 | navigateToRecord : function( recordId ) {
73 |
74 | console.log( 'navigating to record: ' + recordId );
75 |
76 | var event = $A.get( 'e.force:navigateToSObject' );
77 |
78 | if ( event ) {
79 |
80 | event.setParams({
81 | 'recordId' : recordId
82 | }).fire();
83 |
84 | } else if ( ( typeof sforce !== 'undefined' ) && ( typeof sforce.one !== 'undefined' ) ) {
85 |
86 | sforce.one.navigateToSObject( recordId );
87 |
88 | } else {
89 |
90 | window.location.href = '/' + recordId;
91 |
92 | }
93 |
94 | },
95 |
96 | navigateToURL : function( url ) {
97 |
98 | console.log( 'navigating to url: ' + url );
99 |
100 | var event = $A.get( 'e.force:navigateToURL' );
101 |
102 | if ( event ) {
103 |
104 | event.setParams({
105 | 'url' : url
106 | }).fire();
107 |
108 | } else if ( ( typeof sforce !== 'undefined' ) && ( typeof sforce.one !== 'undefined' ) ) {
109 |
110 | sforce.one.navigateToURL( url );
111 |
112 | } else {
113 |
114 | window.location.href = url;
115 |
116 | }
117 |
118 | }
119 | })
--------------------------------------------------------------------------------
/src/aura/DataTableCmp/DataTableCmp.cmp:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
9 |
10 |
13 |
14 |
15 |
16 |
20 |
21 |
25 |
26 |
29 |
30 |
33 |
34 |
35 |
36 |
40 |
41 |
45 |
46 |
50 |
51 |
52 |
53 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
83 | |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
--------------------------------------------------------------------------------
/src/aura/DataTableCmp/DataTableCmp.cmp-meta.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 38.0
4 | A Lightning Component Bundle
5 |
6 |
--------------------------------------------------------------------------------
/src/aura/DataTableCmp/DataTableCmpController.js:
--------------------------------------------------------------------------------
1 | ({
2 | doInit : function( component, event, helper ) {
3 |
4 | // since these components are set via attribute and
5 | // not known at compile time then is not
6 | // correctly registering our event listener as anticipated.
7 | //
8 | // therefore, we dynamically add our event listener to all
9 | // added columns upon initialization.
10 | // https://developer.salesforce.com/docs/atlas.en-us.lightning.meta/lightning/js_cb_dynamic_handler.htm
11 |
12 | var columns = component.get( 'v.columns' );
13 | for ( var i = 0; i < columns.length; i++ ) {
14 | columns[i].addHandler( 'sortChangeEvent', component, 'c.handleSortChangeEvent' );
15 | }
16 |
17 | // default sort column if not specified yet
18 | if ( columns.length > 0 ) {
19 |
20 | var sortColumnName = component.get( 'v.sortColumnName' );
21 | var sortDirection = component.get( 'v.sortDirection' );
22 |
23 | if ( $A.util.isUndefinedOrNull( sortColumnName ) ) {
24 | sortColumnName = columns[0].get( 'v.name' );
25 | }
26 |
27 | if ( $A.util.isUndefinedOrNull( sortDirection ) ) {
28 | sortDirection = 'asc';
29 | }
30 |
31 | helper.syncColumnStates( component, sortColumnName, sortDirection );
32 |
33 | }
34 |
35 | // notify listeners to react and display initial page of data
36 | // this will send v.page and v.pageSize initial attribute values
37 | helper.firePageChangeEvent( component, component.get( 'v.pageNumber' ), component.get( 'v.pageSize' ) );
38 |
39 | },
40 |
41 | /**
42 | * Toggles the sort state of all the columns to reflect the
43 | * currently sorted column captured by the sort change event.
44 | */
45 | handleSortChangeEvent : function( component, event, helper ) {
46 |
47 | // column requested to sort data by
48 | var sortColumnName = event.getParam( 'columnName' );
49 | var sortDirection = event.getParam( 'sortDirection' );
50 |
51 | helper.syncColumnStates( component, sortColumnName, sortDirection );
52 |
53 | }
54 | })
--------------------------------------------------------------------------------
/src/aura/DataTableCmp/DataTableCmpHelper.js:
--------------------------------------------------------------------------------
1 | ({
2 | /**
3 | * Notify that there's a request for specific page of data.
4 | *
5 | * It is the responsibility of the developer to listen for the
6 | * page change event and update the table's rows accordingly.
7 | */
8 | firePageChangeEvent : function( component, pageNumber, pageSize ) {
9 |
10 | component.getEvent( 'pageChangeEvent' ).setParams({
11 | 'pageNumber' : pageNumber,
12 | 'pageSize' : pageSize
13 | }).fire();
14 |
15 | },
16 |
17 | /**
18 | * Designed to be called by the renderer during infinite scrolling events.
19 | * Increments the page number then fires page change event.
20 | */
21 | getNextPage : function( component ) {
22 |
23 | var pageNumber = component.get( 'v.pageNumber' );
24 | var pageSize = component.get( 'v.pageSize' );
25 |
26 | pageNumber++;
27 |
28 | component.set( 'v.pageNumber', pageNumber );
29 | component.set( 'v.pageSize', pageSize );
30 |
31 | this.firePageChangeEvent( component, pageNumber, pageSize );
32 |
33 | },
34 |
35 | /**
36 | * Designed to be called whenever a sort change event is fired.
37 | * The component iterates through all the columns attribute and
38 | * updates their isSorted status so that the table column headers
39 | * reflect the UI of the user's sorting intent.
40 | *
41 | * It is the responsibility of the developer to also listen for the
42 | * sort change event and update the table's rows accordingly.
43 | */
44 | syncColumnStates : function( component, sortColumnName, sortDirection ) {
45 |
46 | // for all columns update their attributes
47 | // to indicate if it is the sorted column or not
48 | var columns = component.get( 'v.columns' );
49 | for ( var i = 0; i < columns.length; i++ ) {
50 |
51 | var column = columns[i];
52 | var columnName = column.get( 'v.name' );
53 |
54 | if ( sortColumnName === columnName ) {
55 |
56 | column.set( 'v.isSorted', true );
57 | column.set( 'v.sortDirection', sortDirection );
58 |
59 | component.set( 'v.sortColumnName', sortColumnName );
60 | component.set( 'v.sortDirection', sortDirection );
61 |
62 | } else {
63 |
64 | column.set( 'v.isSorted', false );
65 |
66 | }
67 |
68 | }
69 |
70 | }
71 | })
--------------------------------------------------------------------------------
/src/aura/DataTableCmp/DataTableCmpRenderer.js:
--------------------------------------------------------------------------------
1 | ({
2 | afterRender : function( component, helper ) {
3 |
4 | this.superAfterRender();
5 |
6 | // this is done in renderer because we don't get
7 | // access to the window element in the helper js.
8 |
9 | // per John Resig, we should not take action on every scroll event
10 | // as that has poor performance but rather we should take action periodically.
11 | // http://ejohn.org/blog/learning-from-twitter/
12 |
13 | var didScroll = false;
14 |
15 | window.onscroll = function() {
16 | didScroll = true;
17 | };
18 |
19 | // periodically attach the scroll event listener
20 | // so that we aren't taking action for all events
21 | var scrollCheckIntervalId = setInterval( $A.getCallback( function() {
22 |
23 | // since this function is called asynchronously outside the component's lifecycle
24 | // we need to check if the component still exists before trying to do anything else
25 | if ( didScroll && component.isValid() ) {
26 |
27 | didScroll = false;
28 |
29 | // adapted from stackoverflow to detect when user has scrolled sufficiently to end of document
30 | // http://stackoverflow.com/questions/4841585/alternatives-to-jquery-endless-scrolling
31 | if ( window['scrollY'] >= document.body['scrollHeight'] - window['outerHeight'] - 100 ) {
32 | helper.getNextPage( component );
33 | }
34 |
35 | }
36 |
37 | }), 1000 );
38 |
39 | component.set( 'v.scrollCheckIntervalId', scrollCheckIntervalId );
40 |
41 | },
42 |
43 | unrender : function( component, helper ) {
44 |
45 | this.superUnrender();
46 |
47 | var scrollCheckIntervalId = component.get( 'v.scrollCheckIntervalId' );
48 |
49 | if ( !$A.util.isUndefinedOrNull( scrollCheckIntervalId ) ) {
50 | window.clearInterval( scrollCheckIntervalId );
51 | }
52 |
53 | }
54 | })
--------------------------------------------------------------------------------
/src/aura/DataTableColumnCmp/DataTableColumnCmp.cmp:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
9 |
10 |
14 |
15 |
19 |
20 |
24 |
25 |
29 |
30 |
34 |
35 |
39 |
40 |
43 |
44 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
63 |
64 |
65 |
66 |
67 |
68 | Sort
69 | {!v.label}
70 |
71 |
75 |
76 |
80 |
81 |
82 |
83 |
84 |
85 | {!v.label}
86 |
87 |
88 |
89 |
90 |
91 | |
92 |
93 |
--------------------------------------------------------------------------------
/src/aura/DataTableColumnCmp/DataTableColumnCmp.cmp-meta.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 38.0
4 | A Lightning Component Bundle
5 |
6 |
--------------------------------------------------------------------------------
/src/aura/DataTableColumnCmp/DataTableColumnCmpController.js:
--------------------------------------------------------------------------------
1 | ({
2 | fireSortChangeEvent : function( component, event, helper ) {
3 |
4 | var sortable = component.get( 'v.sortable' );
5 |
6 | if ( sortable == true ) {
7 |
8 | var sortDirection = component.get( 'v.sortDirection' );
9 |
10 | if ( sortDirection === 'asc' ) {
11 | sortDirection = 'desc';
12 | } else {
13 | sortDirection = 'asc';
14 | }
15 |
16 | component.getEvent( 'sortChangeEvent' ).setParams({
17 | 'columnLabel' : component.get( 'v.label' ),
18 | 'columnName' : component.get( 'v.name' ),
19 | 'sortDirection' : sortDirection
20 | }).fire();
21 |
22 | }
23 |
24 | }
25 | })
--------------------------------------------------------------------------------
/src/aura/DataTableDemoApp/DataTableDemoApp.app:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
25 |
26 |
30 |
31 |
35 |
36 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/src/aura/DataTableDemoApp/DataTableDemoApp.app-meta.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 38.0
4 | A Lightning Component Bundle
5 |
6 |
--------------------------------------------------------------------------------
/src/aura/DataTableDemoApp/DataTableDemoApp.css:
--------------------------------------------------------------------------------
1 | /* https://github.com/salesforce-ux/design-system/issues/357 */
2 | .THIS.slds-spinner_container {
3 | position: fixed;
4 | }
--------------------------------------------------------------------------------
/src/aura/DataTableDemoApp/DataTableDemoAppController.js:
--------------------------------------------------------------------------------
1 | ({
2 | doInit: function( component, event, helper ) {
3 |
4 | },
5 |
6 | handlePageChangeEvent : function( component, event, helper ) {
7 |
8 | var tableCmp = component.find( 'dataTable' );
9 |
10 | console.log( 'handling page change event in app container' );
11 | console.log( 'columnName=' + tableCmp.get( 'v.sortColumnName' ) );
12 | console.log( 'sortDirection=' + tableCmp.get( 'v.sortDirection' ) );
13 | console.log( 'page=' + event.getParam( 'pageNumber' ) );
14 | console.log( 'pageSize=' + event.getParam( 'pageSize' ) );
15 |
16 | helper.callAction(
17 | component,
18 | 'c.getContacts',
19 | {
20 | 'page' : event.getParam( 'pageNumber' ),
21 | 'pageSize' : event.getParam( 'pageSize' ),
22 | 'sortCol' : tableCmp.get( 'v.sortColumnName' ),
23 | 'sortDir' : tableCmp.get( 'v.sortDirection' )
24 | },
25 | function( data ) {
26 |
27 | var tableCmp = component.find( 'dataTable' );
28 |
29 | var rows = tableCmp.get( 'v.rows' );
30 |
31 | tableCmp.set( 'v.rows', rows.concat( data ) );
32 |
33 | }
34 | );
35 |
36 | },
37 |
38 | handleSortChangeEvent : function( component, event, helper ) {
39 |
40 | var tableCmp = component.find( 'dataTable' );
41 |
42 | tableCmp.set( 'v.pageNumber', 1 );
43 |
44 | console.log( 'handling sort event in app container' );
45 | console.log( 'columnName=' + event.getParam( 'columnName' ) );
46 | console.log( 'sortDirection=' + event.getParam( 'sortDirection' ) );
47 | console.log( 'pageNumber=' + tableCmp.get( 'v.pageNumber' ) );
48 | console.log( 'pageSize=' + tableCmp.get( 'v.pageSize' ) );
49 |
50 | helper.callAction(
51 | component,
52 | 'c.getContacts',
53 | {
54 | 'page' : tableCmp.get( 'v.pageNumber' ),
55 | 'pageSize' : tableCmp.get( 'v.pageSize' ),
56 | 'sortCol' : event.getParam( 'columnName' ),
57 | 'sortDir' : event.getParam( 'sortDirection' )
58 | },
59 | function( data ) {
60 |
61 | var tableCmp = component.find( 'dataTable' );
62 |
63 | tableCmp.set( 'v.rows', data );
64 |
65 | }
66 | );
67 |
68 | }
69 | })
--------------------------------------------------------------------------------
/src/aura/DataTableDemoApp/DataTableDemoAppHelper.js:
--------------------------------------------------------------------------------
1 | ({
2 | showSpinner : function( component ) {
3 |
4 | $A.util.removeClass( component.find( 'spinner' ), 'slds-hide' );
5 |
6 | },
7 |
8 | hideSpinner : function( component ) {
9 |
10 | $A.util.addClass( component.find( 'spinner' ), 'slds-hide' );
11 |
12 | },
13 |
14 | navigateToRecord : function( recordId ) {
15 |
16 | console.log( 'navigating to record: ' + recordId );
17 |
18 | var event = $A.get( 'e.force:navigateToSObject' );
19 |
20 | if ( event ) {
21 |
22 | event.setParams({
23 | 'recordId' : recordId
24 | }).fire();
25 |
26 | } else if ( ( typeof sforce !== 'undefined' ) && ( typeof sforce.one !== 'undefined' ) ) {
27 |
28 | sforce.one.navigateToSObject( recordId );
29 |
30 | } else {
31 |
32 | window.location.href = '/' + recordId;
33 |
34 | }
35 |
36 | },
37 |
38 | /**
39 | * actionName = the apex controller method to call (e.g. 'c.myMethod' )
40 | * params = JSON object specifying action parameters (e.g. { 'x' : 42 } )
41 | * successCallback = function to call when action completes (e.g. function( response ) { ... } )
42 | * failureCallback = function to call when action fails (e.g. function( response ) { ... } )
43 | */
44 | callAction : function( component, actionName, params, successCallback, failureCallback ) {
45 |
46 | this.showSpinner( component );
47 |
48 | var action = component.get( actionName );
49 |
50 | if ( params ) {
51 | action.setParams( params );
52 | }
53 |
54 | action.setCallback( this, function( response ) {
55 |
56 | this.hideSpinner( component );
57 |
58 | if ( component.isValid() && response.getState() === 'SUCCESS' ) {
59 |
60 | if ( successCallback ) {
61 | successCallback( response.getReturnValue() );
62 | }
63 |
64 | } else {
65 |
66 | console.error( 'Error calling action "' + actionName + '" with state: ' + response.getState() );
67 |
68 | if ( failureCallback ) {
69 | failureCallback( response.getError(), response.getState() );
70 | } else {
71 | this.logActionErrors( component, response.getError() );
72 | }
73 |
74 | }
75 | });
76 |
77 | $A.enqueueAction( action );
78 |
79 | },
80 |
81 | logActionErrors : function( component, errors ) {
82 | if ( errors ) {
83 | for ( var index in errors ) {
84 | console.error( 'Error: ' + errors[index].message );
85 | }
86 | } else {
87 | console.error( 'Unknown error' );
88 | }
89 | }
90 | })
--------------------------------------------------------------------------------
/src/aura/DataTablePageChangeEvent/DataTablePageChangeEvent.evt:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/src/aura/DataTablePageChangeEvent/DataTablePageChangeEvent.evt-meta.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 38.0
4 | A Lightning Component Bundle
5 |
6 |
--------------------------------------------------------------------------------
/src/aura/DataTableSortChangeEvent/DataTableSortChangeEvent.evt:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 |
10 |
11 |
14 |
15 |
--------------------------------------------------------------------------------
/src/aura/DataTableSortChangeEvent/DataTableSortChangeEvent.evt-meta.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 38.0
4 | A Lightning Component Bundle
5 |
6 |
--------------------------------------------------------------------------------
/src/classes/DataTableDemoAppController.cls:
--------------------------------------------------------------------------------
1 | public with sharing class DataTableDemoAppController {
2 |
3 | @AuraEnabled
4 | public static List getContacts( Decimal page, Decimal pageSize, String sortCol, String sortDir ) {
5 |
6 | System.debug('querying contacts');
7 | System.debug('page=' + page);
8 | System.debug('pageSize=' + pageSize);
9 | System.debug('sortCol=' + sortCol);
10 | System.debug('sortDir=' + sortDir);
11 |
12 | Integer skipRecords = Integer.valueOf( ( page - 1 ) * pageSize );
13 | Integer maxRecords = Integer.valueOf( pageSize );
14 |
15 | String query =
16 | ' SELECT ' +
17 | ' id, firstName, lastName, accountId, account.name ' +
18 | ' FROM ' +
19 | ' Contact '
20 | ;
21 |
22 | if ( String.isNotBlank( sortCol ) ) {
23 | query += ' ORDER BY ' + String.escapeSingleQuotes( sortCol ) + ' ' + String.escapeSingleQuotes( sortDir );
24 | }
25 |
26 | query += ' LIMIT :maxRecords ';
27 |
28 | query += ' OFFSET :skipRecords ';
29 |
30 | List contacts = (List) Database.query( query );
31 |
32 | //List contacts = [
33 | // SELECT
34 | // id, firstName, lastName, accountId, account.name
35 | // FROM
36 | // Contact
37 | // ORDER BY
38 | // firstName
39 | // LIMIT
40 | // :maxRecords
41 | // OFFSET
42 | // :skipRecords
43 | //];
44 |
45 | System.debug('queried contacts');
46 | System.debug( contacts );
47 |
48 | return contacts;
49 | }
50 |
51 | }
--------------------------------------------------------------------------------
/src/classes/DataTableDemoAppController.cls-meta.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 38.0
4 | Active
5 |
6 |
--------------------------------------------------------------------------------
/src/package.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | DataTableDemoAppController
5 | ApexClass
6 |
7 |
8 | DataTableCellCmp
9 | DataTableCmp
10 | DataTableColumnCmp
11 | DataTableDemoApp
12 | DataTablePageChangeEvent
13 | DataTableSortChangeEvent
14 | AuraDefinitionBundle
15 |
16 | 38.0
17 |
18 |
--------------------------------------------------------------------------------