@@ -3,8 +3,6 @@ import VdtExportData from "./ExportData/ExportData.vue"
33import VdtPagination from "./Pagination/Pagination.vue"
44import VdtPerPage from "./PerPage/PerPage.vue"
55import VdtSearchFilter from "./SearchFilter/SearchFilter.vue"
6- import VdtSortingIcon from "./SortableColumn/SortingIcon.vue"
7- import VdtSortingIndex from "./SortableColumn/SortingIndex.vue"
86import VdtTable from "./Table/Table.vue"
97
108import {
@@ -46,7 +44,7 @@ export default defineComponent({
4644 } ,
4745 data : {
4846 type : Array ,
49- required : true ,
47+ required : false ,
5048 } ,
5149 defaultColumn : {
5250 type : Object ,
@@ -61,6 +59,14 @@ export default defineComponent({
6159 type : String ,
6260 default : "download" ,
6361 } ,
62+ fetchUrl : {
63+ type : String ,
64+ required : false ,
65+ } ,
66+ fetchCallback : {
67+ type : Function ,
68+ required : false ,
69+ } ,
6470 footerComponent : {
6571 type : [ Object , String ] ,
6672 default : null
@@ -109,16 +115,12 @@ export default defineComponent({
109115 }
110116 } ,
111117 sortingIndexComponent : {
112- type : Object ,
113- default : function ( ) {
114- return VdtSortingIndex
115- }
118+ type : [ Object , String ] ,
119+ default : "vdt-sorting-index"
116120 } ,
117121 sortingIconComponent : {
118- type : Object ,
119- default : function ( ) {
120- return VdtSortingIcon
121- }
122+ type : [ Object , String ] ,
123+ default : "vdt-sorting-icon"
122124 } ,
123125 tableClass : {
124126 type : String ,
@@ -136,6 +138,8 @@ export default defineComponent({
136138
137139 data : ( ) => {
138140 return reactive ( {
141+ dataFetched : [ ] as Column [ ] ,
142+ dataFetchedLinks : [ ] as any [ ] ,
139143 currentPage : 1 ,
140144 currentPerPage : 10 ,
141145 parsedColumns : [ ] as Column [ ] ,
@@ -153,11 +157,16 @@ export default defineComponent({
153157 paginationSearchText : "" ,
154158 paginationSearchButtonText : "" ,
155159 search : "" ,
156- searchText : ""
160+ searchText : "" ,
161+ totalRecords : 0 ,
157162 } )
158163 } ,
159164
160165 computed : {
166+ actualData ( ) {
167+ return ( this . data != null ) ? this . data : this . dataFetched
168+ } ,
169+
161170 /**
162171 * Get the total number of columns
163172 */
@@ -201,7 +210,7 @@ export default defineComponent({
201210
202211 // assign key to track row
203212 const key = this . vKey ;
204- const data = this . data . map ( ( value : any , index ) => {
213+ const data = this . actualData . map ( ( value : any , index ) => {
205214 if ( key !== "" && value [ key ] ) {
206215 index = value [ key ] ;
207216 }
@@ -240,7 +249,10 @@ export default defineComponent({
240249 * Indicates if there are no rows to shown
241250 */
242251 isEmpty ( ) {
243- return this . dataDisplayed . length === 0
252+ if ( ! this . data )
253+ return this . dataFetched . length === 0
254+ else
255+ return this . dataDisplayed . length === 0
244256 } ,
245257
246258 //
@@ -273,13 +285,18 @@ export default defineComponent({
273285 * Get the number of records
274286 */
275287 totalEntries ( ) {
276- return this . data . length
288+ if ( this . data == null )
289+ return this . totalRecords
290+ else
291+ return this . actualData . length
277292 } ,
278293
279294 /**
280295 * Get the number of records filtered
281296 */
282297 filteredEntries ( ) {
298+ if ( this . data == null )
299+ return this . totalRecords
283300 return this . dataFiltered . length
284301 } ,
285302
@@ -435,11 +452,15 @@ export default defineComponent({
435452 * The props for the Table component
436453 */
437454 propsTable ( ) {
455+ const dataNotNull = this . data != null
456+ const data = ( dataNotNull ) ? this . data : this . dataFetched
457+ const dataDisplayed = ( dataNotNull ) ? this . dataDisplayed : this . dataFetched
458+ const dataFiltered = ( dataNotNull ) ? this . dataFiltered : this . dataFetched
438459 return {
439460 columns : this . parsedColumns ,
440- data : this . data ,
441- dataDisplayed : this . dataDisplayed ,
442- dataFiltered : this . dataFiltered ,
461+ data : data ,
462+ dataDisplayed : dataDisplayed ,
463+ dataFiltered : dataFiltered ,
443464 emptyTableText : this . emptyTableText ,
444465 footerComponent : this . footerComponent ,
445466 isEmpty : this . isEmpty ,
@@ -496,9 +517,50 @@ export default defineComponent({
496517
497518 mounted ( ) {
498519 this . setDefaults ( )
520+ this . updateData ( )
499521 } ,
500522
501523 methods : {
524+ /**
525+ * Update data, fetching it if needed.
526+ * If all data was previously fetched, it is stored in state variables,
527+ * therefore nothing is done in that case.
528+ */
529+ async updateData ( ) {
530+ if ( this . data === null || this . data === undefined )
531+ this . fetchData ( )
532+ } ,
533+
534+ async fetchData ( url = "" ) {
535+ if ( this . fetchUrl == null || this . fetchCallback == null )
536+ throw Error ( "Fetch parameters are null" ) ;
537+
538+ // empty URL but we have the URL stored
539+ if ( url === "" && this . dataFetchedLinks . length > 1 ) {
540+ url = this . dataFetchedLinks [ this . currentPage ] . url
541+ + this . getSearchQuery ( ) + this . getSortQuery ( ) ;
542+ }
543+
544+ // initial URL
545+ if ( url === "" ) {
546+ url = this . fetchUrl
547+ }
548+
549+ this . fetchCallback ( url ) . then ( ( responseData : any ) => {
550+ // Laravel API.
551+ // If response is from ResourceCollection,
552+ // then the metadata is in a nested object called meta.
553+ // Otherwise, the metadata is directly in the JSON response.
554+ const { data } = responseData
555+ const meta = responseData . meta ?? responseData ;
556+ this . dataFetched = data
557+ this . dataFetchedLinks = meta . links
558+ this . currentPage = meta . current_page
559+ this . currentPerPage = meta . per_page
560+ this . totalRecords = meta . total
561+ } )
562+ } ,
563+
502564 /**
503565 * Propagate upwards an event from user's custom component
504566 */
@@ -594,8 +656,8 @@ export default defineComponent({
594656
595657 // column is being sorted in ascending mode
596658 // so, mark it as sorted in descending mode
597- if ( column . sortingMode === "asc" ) {
598- column . sortingMode = "desc"
659+ if ( column . sortingMode === SORTING_MODE . ASC ) {
660+ column . sortingMode = SORTING_MODE . DESC
599661 this . columnsBeingSorted . splice (
600662 column . sortingIndex - 1 ,
601663 1 ,
@@ -631,9 +693,11 @@ export default defineComponent({
631693 * Set the current page being displayed
632694 */
633695 setPage ( value : any ) {
634- if ( this . isValidPage ( value ) ) {
635- this . currentPage = value
696+ if ( ! this . isValidPage ( value ) ) {
697+ return
636698 }
699+ this . currentPage = value
700+ this . updateData ( )
637701 } ,
638702
639703 /**
@@ -684,6 +748,46 @@ export default defineComponent({
684748 const value = getEventTargetValue ( ) || ""
685749 this . search = value . trim ( )
686750 this . currentPage = 1
751+ this . updateData ( ) ;
752+ } ,
753+
754+ /**
755+ * Get search query URI for fetching data.
756+ *
757+ * @returns string
758+ */
759+ getSearchQuery ( ) {
760+ const encodedSearch = encodeURIComponent ( this . search ) ;
761+ let searchQueryUri = ""
762+ this . searchableColumns . forEach ( ( col : Column ) => {
763+ if ( col . key ) {
764+ searchQueryUri += `&filter[${ col . key } ]=${ encodedSearch } `
765+ }
766+ } )
767+ return searchQueryUri
768+ } ,
769+
770+ /**
771+ * Return the sort query URI for fetching data.
772+ *
773+ * @returns string
774+ */
775+ getSortQuery ( ) {
776+ let { columnsBeingSorted } = this
777+
778+ // nothing being sorted
779+ if ( columnsBeingSorted . length == 0 )
780+ return ""
781+
782+ let searchQueryUri = "&sort="
783+ const descPrefix = "-"
784+ const sep = ","
785+ columnsBeingSorted . forEach ( ( col : Column ) => {
786+ if ( col . sortingMode == SORTING_MODE . DESC )
787+ searchQueryUri += descPrefix
788+ searchQueryUri += col . key + sep
789+ } )
790+ return searchQueryUri
687791 }
688792 } ,
689793
@@ -698,6 +802,11 @@ export default defineComponent({
698802 deep : true ,
699803 immediate : true
700804 } ,
805+ columnsBeingSorted : {
806+ handler : "updateData" ,
807+ deep : false ,
808+ immediate : false ,
809+ } ,
701810 text : {
702811 handler : "parseTextProps" ,
703812 deep : true ,
0 commit comments