diff --git a/docs/content-services/content-node-selector-panel.component.md b/docs/content-services/content-node-selector-panel.component.md index 166f326b5b..0a25c29e17 100644 --- a/docs/content-services/content-node-selector-panel.component.md +++ b/docs/content-services/content-node-selector-panel.component.md @@ -39,6 +39,7 @@ Opens a Content [`Node`](https://github.com/Alfresco/alfresco-js-api/blob/develo | isSelectionValid | `ValidationFunction` | defaultValidation | Function used to decide if the selected node has permission to be selected. Default value is a function that always returns true. | | pageSize | `number` | | Number of items shown per page in the list. | | rowFilter | `RowFilter` | null | Custom row filter function. See the [Document List component](document-list.component.md#custom-row-filter) for more information. | +| where | `string` | `null` | Custom where filter function. See the [Document List component](document-list.component.md) for more information. | ### Events diff --git a/docs/content-services/content-node-selector.component.md b/docs/content-services/content-node-selector.component.md index 5f613c884e..0af44ac83c 100644 --- a/docs/content-services/content-node-selector.component.md +++ b/docs/content-services/content-node-selector.component.md @@ -37,6 +37,7 @@ export interface ContentNodeSelectorComponentData { dropdownHideMyFiles?: boolean; dropdownSiteList?: SitePaging; rowFilter?: any; + where?: string; imageResolver?: any; isSelectionValid?: (entry: Node) => boolean; breadcrumbTransform?: (node) => any; @@ -55,6 +56,7 @@ The properties are described in the table below: | dropdownHideMyFiles | `boolean` | `false` | Hide the "My Files" option added to the site list by default. See the [Sites Dropdown component](sites-dropdown.component.md) for more information. | | dropdownSiteList | [`SitePaging`](https://github.com/Alfresco/alfresco-js-api/blob/master/src/alfresco-core-rest-api/docs/SitePaging.md) | `null` | Custom site for site dropdown same as siteList. See the [Sites Dropdown component](sites-dropdown.component.md) for more information. | | rowFilter | [`RowFilter`](../../lib/content-services/document-list/data/row-filter.model.ts) | `null` | Custom row filter function. See the [Document List component](document-list.component.md#custom-row-filter) for more information. | +| where | `string` | `null` | Custom where filter function. See the [Document List component](document-list.component.md) for more information. | | imageResolver | [`ImageResolver`](../../lib/content-services/document-list/data/image-resolver.model.ts) | `null` | Custom image resolver function. See the [Document List component](document-list.component.md#custom-row-filter) for more information. | | pageSize | `number` | | Number of items shown per page in the list. | | isSelectionValid | [`ValidationFunction`](../../lib/content-services/content-node-selector/content-node-selector-panel.component.ts) | `defaultValidation` | Function used to decide if the selected node has permission to be selected. Default value is a function that always returns true. | diff --git a/docs/content-services/document-list.component.md b/docs/content-services/document-list.component.md index 914768b98c..ae88786e79 100644 --- a/docs/content-services/document-list.component.md +++ b/docs/content-services/document-list.component.md @@ -66,6 +66,7 @@ Displays the documents from a repository. | emptyFolderImageUrl | `string` | | Custom image for empty folder. Default value: './assets/images/empty_doc_lib.svg' | | imageResolver | `any \| null` | null | Custom image resolver | | includeFields | `string[]` | | Include additional information about the node in the server request. For example: association, isLink, isLocked and others. | +| where | `string` | | filter the Node list using the where condition of the rest api for example (isFolder=true) see the rest api documentation for more information. | | loading | `boolean` | false | Toggles the loading state and animated spinners for the component. Used in combination with `navigate=false` to perform custom navigation and loading state indication. | | locationFormat | `string` | "/" | The default route for all the location-based columns (if declared). | | maxItems | `number` | | Default value is stored into user preference settings use it only if you are not using the pagination | diff --git a/lib/content-services/content-node-selector/content-node-dialog.service.ts b/lib/content-services/content-node-selector/content-node-dialog.service.ts index fddbfbbcf1..6529ed0a5b 100644 --- a/lib/content-services/content-node-selector/content-node-dialog.service.ts +++ b/lib/content-services/content-node-selector/content-node-dialog.service.ts @@ -143,7 +143,7 @@ export class ContentNodeDialogService { actionName: action, currentFolderId: contentEntry.parentId, imageResolver: this.imageResolver.bind(this), - rowFilter: this.rowFilter.bind(this, contentEntry.id), + where: '(isFolder=true)', isSelectionValid: this.isCopyMoveSelectionValid.bind(this), excludeSiteContent: excludeSiteContent || ContentNodeDialogService.nonDocumentSiteContent, select: select @@ -186,7 +186,7 @@ export class ContentNodeDialogService { currentFolderId: contentEntry.id, imageResolver: this.imageResolver.bind(this), isSelectionValid: this.hasPermissionOnNodeFolder.bind(this), - rowFilter: this.rowFilter.bind(this, contentEntry.id), + where: '(isFolder=true)', select: select }; @@ -232,16 +232,6 @@ export class ContentNodeDialogService { return null; } - private rowFilter(currentNodeId: string, row: ShareDataRow): boolean { - const node: Node = row.node.entry; - - if (node.id === currentNodeId || node.isFile) { - return false; - } else { - return true; - } - } - private isNodeFile(entry: Node): boolean { return entry.isFile; } diff --git a/lib/content-services/content-node-selector/content-node-selector-panel.component.html b/lib/content-services/content-node-selector/content-node-selector-panel.component.html index b9b95e6238..9a59658c19 100644 --- a/lib/content-services/content-node-selector/content-node-selector-panel.component.html +++ b/lib/content-services/content-node-selector/content-node-selector-panel.component.html @@ -56,7 +56,7 @@ [adf-highlight]="searchTerm" adf-highlight-selector=".adf-name-location-cell-name" [showHeader]="false" - [node]="nodes" + [node]="nodePaging" [maxItems]="pageSize" [rowFilter]="_rowFilter" [imageResolver]="imageResolver" @@ -65,6 +65,7 @@ [contextMenuActions]="false" [contentActions]="false" [allowDropFiles]="false" + [where]="where" (folderChange)="onFolderChange()" (ready)="onFolderLoaded()" (node-dblclick)="onNodeDoubleClick($event)" diff --git a/lib/content-services/content-node-selector/content-node-selector-panel.component.spec.ts b/lib/content-services/content-node-selector/content-node-selector-panel.component.spec.ts index 128183d372..48491a1396 100644 --- a/lib/content-services/content-node-selector/content-node-selector-panel.component.spec.ts +++ b/lib/content-services/content-node-selector/content-node-selector-panel.component.spec.ts @@ -517,7 +517,7 @@ describe('ContentNodeSelectorComponent', () => { it('should clear the search field, nodes and chosenNode when clicking on the X (clear) icon', () => { component.chosenNode = {}; - component.nodes = { + component.nodePaging = { list: { entries: [{ entry: component.chosenNode }] } @@ -528,7 +528,7 @@ describe('ContentNodeSelectorComponent', () => { component.clear(); expect(component.searchTerm).toBe(''); - expect(component.nodes).toEqual(null); + expect(component.nodePaging).toEqual(null); expect(component.chosenNode).toBeNull(); expect(component.showingSearchResults).toBeFalsy(); }); diff --git a/lib/content-services/content-node-selector/content-node-selector-panel.component.ts b/lib/content-services/content-node-selector/content-node-selector-panel.component.ts index 65b7bd3e65..f1d6c6c72d 100644 --- a/lib/content-services/content-node-selector/content-node-selector-panel.component.ts +++ b/lib/content-services/content-node-selector/content-node-selector-panel.component.ts @@ -19,7 +19,7 @@ import { Component, EventEmitter, Input, OnInit, Output, ViewChild, ViewEncapsul import { AlfrescoApiService, HighlightDirective, UserPreferencesService, PaginationModel } from '@alfresco/adf-core'; import { FormControl } from '@angular/forms'; import { Node, NodePaging, Pagination, SiteEntry, SitePaging } from '@alfresco/js-api'; -import { DocumentListComponent, PaginationStrategy } from '../document-list/components/document-list.component'; +import { DocumentListComponent } from '../document-list/components/document-list.component'; import { RowFilter } from '../document-list/data/row-filter.model'; import { ImageResolver } from '../document-list/data/image-resolver.model'; import { ContentNodeSelectorService } from './content-node-selector.service'; @@ -62,6 +62,13 @@ export class ContentNodeSelectorPanelComponent implements OnInit { _rowFilter: RowFilter = defaultValidation; + /** Custom where filter function. See the + * [Document List component](document-list.component.md) + * for more information. + */ + @Input() + where: string; + /** Custom row filter function. See the * [Document List component](document-list.component.md#custom-row-filter) * for more information. @@ -125,7 +132,7 @@ export class ContentNodeSelectorPanelComponent implements OnInit { @ViewChild(HighlightDirective) highlighter: HighlightDirective; - nodes: NodePaging | null = null; + nodePaging: NodePaging | null = null; siteId: null | string; searchTerm: string = ''; showingSearchResults: boolean = false; @@ -133,7 +140,6 @@ export class ContentNodeSelectorPanelComponent implements OnInit { inDialog: boolean = false; _chosenNode: Node = null; folderIdToShow: string | null = null; - paginationStrategy: PaginationStrategy = PaginationStrategy.Infinite; pagination: BehaviorSubject; skipCount: number = 0; @@ -255,7 +261,7 @@ export class ContentNodeSelectorPanelComponent implements OnInit { */ clearSearch() { this.searchTerm = ''; - this.nodes = null; + this.nodePaging = null; this.skipCount = 0; this.chosenNode = null; this.showingSearchResults = false; @@ -276,7 +282,7 @@ export class ContentNodeSelectorPanelComponent implements OnInit { * Load the first page of a new search result */ private startNewSearch(): void { - this.nodes = null; + this.nodePaging = null; this.skipCount = 0; this.chosenNode = null; this.folderIdToShow = null; @@ -313,14 +319,7 @@ export class ContentNodeSelectorPanelComponent implements OnInit { this.showingSearchResults = true; this.loadingSearchResults = false; - // DocumentList hack, since data displaying for preloaded nodes is a little bit messy there - if (!this.nodes) { - this.nodes = nodePaging; - } else { - this.documentList.data.loadPage(nodePaging, true); - } - - this.pagination.next(nodePaging.list.pagination); + this.nodePaging = nodePaging; } /** diff --git a/lib/content-services/content-node-selector/content-node-selector.component-data.interface.ts b/lib/content-services/content-node-selector/content-node-selector.component-data.interface.ts index fb7d05d652..0ef5cccfa5 100644 --- a/lib/content-services/content-node-selector/content-node-selector.component-data.interface.ts +++ b/lib/content-services/content-node-selector/content-node-selector.component-data.interface.ts @@ -25,6 +25,7 @@ export interface ContentNodeSelectorComponentData { dropdownHideMyFiles?: boolean; dropdownSiteList?: SitePaging; rowFilter?: any; + where?: string; imageResolver?: any; isSelectionValid?: (entry: Node) => boolean; breadcrumbTransform?: (node) => any; diff --git a/lib/content-services/content-node-selector/content-node-selector.component.html b/lib/content-services/content-node-selector/content-node-selector.component.html index 88a7d66e18..0cf1300106 100644 --- a/lib/content-services/content-node-selector/content-node-selector.component.html +++ b/lib/content-services/content-node-selector/content-node-selector.component.html @@ -13,6 +13,7 @@ [isSelectionValid]="data?.isSelectionValid" [breadcrumbTransform]="data?.breadcrumbTransform" [excludeSiteContent]="data?.excludeSiteContent" + [where]="data?.where" (select)="onSelect($event)"> @@ -30,4 +31,4 @@ (click)="onClick()" data-automation-id="content-node-selector-actions-choose">{{ buttonActionName | translate }} - \ No newline at end of file + diff --git a/lib/content-services/content-node-share/content-node-share.directive.ts b/lib/content-services/content-node-share/content-node-share.directive.ts index 18486d228b..6fddadc131 100644 --- a/lib/content-services/content-node-share/content-node-share.directive.ts +++ b/lib/content-services/content-node-share/content-node-share.directive.ts @@ -48,13 +48,13 @@ export class NodeSharedDirective implements OnChanges { constructor(private dialog: MatDialog, private zone: NgZone) {} - shareNode(node: NodeEntry) { - if (node && node.entry && node.entry.isFile) { + shareNode(nodeEntry: NodeEntry) { + if (nodeEntry && nodeEntry.entry && nodeEntry.entry.isFile) { this.dialog.open(ShareDialogComponent, { width: '600px', panelClass: 'adf-share-link-dialog', data: { - node: node, + node: nodeEntry, baseShareUrl: this.baseShareUrl } }); @@ -63,7 +63,7 @@ export class NodeSharedDirective implements OnChanges { ngOnChanges() { this.zone.onStable.subscribe(() => { - if (this.node) { + if (this.node && this.node.entry) { this.isFile = this.node.entry.isFile; this.isShared = this.node.entry.properties['qshare:sharedId']; } diff --git a/lib/content-services/document-list/components/document-list.component.spec.ts b/lib/content-services/document-list/components/document-list.component.spec.ts index bb827b6def..a92209f8b0 100644 --- a/lib/content-services/document-list/components/document-list.component.spec.ts +++ b/lib/content-services/document-list/components/document-list.component.spec.ts @@ -1269,6 +1269,24 @@ describe('DocumentList', () => { documentList.ngOnChanges({ currentFolderId: new SimpleChange(null, '-root-', false) }); expect(documentListService.getFolder).toHaveBeenCalledWith(null, { + where: undefined, + maxItems: 25, + skipCount: 0, + rootFolderId: 'fake-id' + }, ['test-include']); + }); + + it('should add where in the server request when present', () => { + fixture.detectChanges(); + documentList.currentFolderId = 'fake-id'; + documentList.includeFields = ['test-include']; + documentList.where = '(isFolder=true)'; + spyOn(documentListService, 'getFolder').and.callThrough(); + + documentList.ngOnChanges({ currentFolderId: new SimpleChange(null, '-root-', false) }); + + expect(documentListService.getFolder).toHaveBeenCalledWith(null, { + where: '(isFolder=true)', maxItems: 25, skipCount: 0, rootFolderId: 'fake-id' diff --git a/lib/content-services/document-list/components/document-list.component.ts b/lib/content-services/document-list/components/document-list.component.ts index cf08933d85..8d8e0f51cb 100644 --- a/lib/content-services/document-list/components/document-list.component.ts +++ b/lib/content-services/document-list/components/document-list.component.ts @@ -39,7 +39,8 @@ import { ThumbnailService, CustomLoadingContentTemplateDirective, CustomNoPermissionTemplateDirective, - CustomEmptyContentTemplateDirective + CustomEmptyContentTemplateDirective, + RequestPaginationModel } from '@alfresco/adf-core'; import { Node, NodeEntry, NodePaging } from '@alfresco/js-api'; @@ -88,6 +89,10 @@ export class DocumentListComponent implements OnInit, OnChanges, OnDestroy, Afte @Input() includeFields: string[]; + /** filter the Node list using the where condition of the rest api for example (isFolder=true) see the rest api documentation for more information. */ + @Input() + where: string; + /** Change the display mode of the table. Can be "list" or "gallery". */ @Input() display: string = DisplayMode.List; @@ -260,7 +265,9 @@ export class DocumentListComponent implements OnInit, OnChanges, OnDestroy, Afte // @deprecated 3.0.0 folderNode: Node; - private _pagination: BehaviorSubject; + private _pagination: PaginationModel; + private $pagination: BehaviorSubject; + private layoutPresets = {}; private subscriptions: Subscription[] = []; private rowMenuCache: { [key: string]: ContentActionModel[] } = {}; @@ -274,6 +281,13 @@ export class DocumentListComponent implements OnInit, OnChanges, OnDestroy, Afte private customResourcesService: CustomResourcesService, private contentService: ContentService, private thumbnailService: ThumbnailService) { + + this._pagination = { + maxItems: this.maxItems || this.preferences.paginationSize, + skipCount: 0, + totalItems: 0, + hasMoreItems: false + }; } getContextActions(node: NodeEntry) { @@ -310,17 +324,10 @@ export class DocumentListComponent implements OnInit, OnChanges, OnDestroy, Afte } get pagination(): BehaviorSubject { - if (!this._pagination) { - let maxItems = this.maxItems || this.preferences.paginationSize; - let defaultPagination = { - maxItems: maxItems, - skipCount: 0, - totalItems: 0, - hasMoreItems: false - }; - this._pagination = new BehaviorSubject(defaultPagination); + if (!this.$pagination) { + this.$pagination = new BehaviorSubject(this._pagination); } - return this._pagination; + return this.$pagination; } isMobile(): boolean { @@ -412,7 +419,9 @@ export class DocumentListComponent implements OnInit, OnChanges, OnDestroy, Afte this.loadFolder(); } else if (this.data) { if (changes.node && changes.node.currentValue) { - this.data.loadPage(changes.node.currentValue); + let merge = this._pagination ? this._pagination.merge : false; + + this.data.loadPage(changes.node.currentValue, merge); this.onDataReady(changes.node.currentValue); } else if (changes.imageResolver) { this.data.setImageResolver(changes.imageResolver.currentValue); @@ -424,7 +433,7 @@ export class DocumentListComponent implements OnInit, OnChanges, OnDestroy, Afte this.ngZone.run(() => { this.resetSelection(); if (this.node) { - this.data.loadPage(this.node, this.pagination.getValue().merge); + this.data.loadPage(this.node, this._pagination.merge); this.onDataReady(this.node); } else { this.loadFolder(); @@ -579,7 +588,7 @@ export class DocumentListComponent implements OnInit, OnChanges, OnDestroy, Afte } loadFolder() { - if (!this.pagination.getValue().merge) { + if (!this._pagination.merge) { this.setLoadingState(true); } @@ -593,33 +602,36 @@ export class DocumentListComponent implements OnInit, OnChanges, OnDestroy, Afte loadFolderByNodeId(nodeId: string) { if (this.customResourcesService.isCustomSource(nodeId)) { this.updateCustomSourceData(nodeId); - this.customResourcesService.loadFolderByNodeId(nodeId, this.pagination.getValue(), this.includeFields) - .subscribe((page: NodePaging) => { - this.onPageLoaded(page); + this.customResourcesService.loadFolderByNodeId(nodeId, this._pagination, this.includeFields) + .subscribe((nodePaging: NodePaging) => { + this.onPageLoaded(nodePaging); }, (err) => { this.error.emit(err); }); } else { - let pagination = this.pagination.getValue(); + this.documentListService.getFolder(null, { - maxItems: pagination.maxItems, - skipCount: pagination.skipCount, - rootFolderId: nodeId + maxItems: this._pagination.maxItems, + skipCount: this._pagination.skipCount, + rootFolderId: nodeId, + where: this.where }, this.includeFields) .subscribe((nodePaging: NodePaging) => { - this.data.loadPage(nodePaging, this.pagination.getValue().merge); - this.setLoadingState(false); - this.onDataReady(nodePaging); - this.documentListService.getFolderNode(nodeId, this.includeFields).subscribe((nodeEntry: NodeEntry) => { - this.folderNode = nodeEntry.entry; - this.$folderNode.next(this.folderNode); - }); + this.onPageLoaded(nodePaging); + this.getSourceNodeWithPath(nodeId); }, (err) => { this.handleError(err); }); } } + getSourceNodeWithPath(nodeId: string) { + this.documentListService.getFolderNode(nodeId, this.includeFields).subscribe((nodeEntry: NodeEntry) => { + this.folderNode = nodeEntry.entry; + this.$folderNode.next(this.folderNode); + }); + } + resetSelection() { this.dataTable.resetSelection(); this.selection = []; @@ -628,7 +640,7 @@ export class DocumentListComponent implements OnInit, OnChanges, OnDestroy, Afte onPageLoaded(nodePaging: NodePaging) { if (nodePaging) { - this.data.loadPage(nodePaging, this.pagination.getValue().merge); + this.data.loadPage(nodePaging, this._pagination.merge); this.setLoadingState(false); this.onDataReady(nodePaging); } @@ -787,12 +799,13 @@ export class DocumentListComponent implements OnInit, OnChanges, OnDestroy, Afte private onDataReady(nodePaging: NodePaging) { this.ready.emit(nodePaging); - this.pagination.next(nodePaging.list.pagination); } - updatePagination(pagination: PaginationModel) { - this.pagination.next(pagination); + updatePagination(requestPaginationModel: RequestPaginationModel) { + this._pagination.maxItems = requestPaginationModel.maxItems; + this._pagination.merge = requestPaginationModel.merge; + this._pagination.skipCount = requestPaginationModel.skipCount; this.reload(); } diff --git a/lib/content-services/document-list/data/share-datatable-adapter.ts b/lib/content-services/document-list/data/share-datatable-adapter.ts index ffd3430114..4e534da787 100644 --- a/lib/content-services/document-list/data/share-datatable-adapter.ts +++ b/lib/content-services/document-list/data/share-datatable-adapter.ts @@ -245,11 +245,11 @@ export class ShareDataTableAdapter implements DataTableAdapter { } } - public loadPage(page: NodePaging, merge: boolean = false) { + public loadPage(nodePaging: NodePaging, merge: boolean = false) { let shareDataRows: ShareDataRow[] = []; - if (page && page.list) { - let nodeEntries: NodeEntry[] = page.list.entries; + if (nodePaging && nodePaging.list) { + let nodeEntries: NodeEntry[] = nodePaging.list.entries; if (nodeEntries && nodeEntries.length > 0) { shareDataRows = nodeEntries.map((item) => new ShareDataRow(item, this.contentService, this.permissionsStyle, this.thumbnailService)); diff --git a/lib/content-services/document-list/services/document-list.service.ts b/lib/content-services/document-list/services/document-list.service.ts index eaacb8c6a3..a6db0faece 100644 --- a/lib/content-services/document-list/services/document-list.service.ts +++ b/lib/content-services/document-list/services/document-list.service.ts @@ -105,6 +105,9 @@ export class DocumentListService { if (opts.skipCount) { params.skipCount = opts.skipCount; } + if (opts.where) { + params.where = opts.where; + } } return from(this.apiService.getInstance().nodes.getNodeChildren(rootNodeId, params)).pipe( diff --git a/lib/core/models/pagination.model.ts b/lib/core/models/pagination.model.ts index 071e46e320..921f9ba11c 100644 --- a/lib/core/models/pagination.model.ts +++ b/lib/core/models/pagination.model.ts @@ -20,15 +20,15 @@ import { Pagination } from '@alfresco/js-api'; export class PaginationModel extends Pagination { merge?: boolean; - constructor(obj?: any) { - super(obj); - if (obj) { - this.count = obj.count; - this.hasMoreItems = obj.hasMoreItems ? obj.hasMoreItems : false; - this.merge = obj.merge ? obj.merge : false; - this.totalItems = obj.totalItems; - this.skipCount = obj.skipCount; - this.maxItems = obj.maxItems; + constructor(input?: any) { + super(input); + if (input) { + this.count = input.count; + this.hasMoreItems = input.hasMoreItems ? input.hasMoreItems : false; + this.merge = input.merge ? input.merge : false; + this.totalItems = input.totalItems; + this.skipCount = input.skipCount; + this.maxItems = input.maxItems; } } } diff --git a/lib/core/models/public-api.ts b/lib/core/models/public-api.ts index a2ddf3dee6..37f6e7e915 100644 --- a/lib/core/models/public-api.ts +++ b/lib/core/models/public-api.ts @@ -24,3 +24,4 @@ export * from './ecm-company.model'; export * from './redirection.model'; export * from './pagination.model'; export * from './oauth-config.model'; +export * from './request-pagination.model'; diff --git a/lib/core/models/request-pagination.model.ts b/lib/core/models/request-pagination.model.ts new file mode 100644 index 0000000000..5ca62b0d5c --- /dev/null +++ b/lib/core/models/request-pagination.model.ts @@ -0,0 +1,31 @@ +/*! + * @license + * Copyright 2016 Alfresco Software, Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export class RequestPaginationModel { + + skipCount?: number; + + maxItems?: number; + + merge?: boolean = false; + + constructor(input?: any) { + if (input) { + Object.assign(this, input); + } + } +} diff --git a/lib/core/pagination/infinite-pagination.component.spec.ts b/lib/core/pagination/infinite-pagination.component.spec.ts index 1c314712eb..650a76f060 100644 --- a/lib/core/pagination/infinite-pagination.component.spec.ts +++ b/lib/core/pagination/infinite-pagination.component.spec.ts @@ -111,6 +111,36 @@ describe('InfinitePaginationComponent', () => { expect(loadMoreButton).not.toBeNull(); }); + it('should NOT show the load more button if there are no more elements to load', (done) => { + pagination = { maxItems: 444, skipCount: 25, totalItems: 30, hasMoreItems: false }; + + component.target.pagination.next(pagination); + + fixture.detectChanges(); + + component.onLoadMore(); + + fixture.whenStable().then(() => { + let loadMoreButton = fixture.debugElement.query(By.css('[data-automation-id="adf-infinite-pagination-button"]')); + expect(loadMoreButton).toBeNull(); + done(); + }); + }); + + it('should show the load more button if there are more elements to load', (done) => { + pagination = { maxItems: 444, skipCount: 25, totalItems: 55, hasMoreItems: true }; + + component.target.pagination.next(pagination); + + fixture.detectChanges(); + + fixture.whenStable().then(() => { + let loadMoreButton = fixture.debugElement.query(By.css('[data-automation-id="adf-infinite-pagination-button"]')); + expect(loadMoreButton).not.toBeNull(); + done(); + }); + }); + it('should NOT show anything if pagination has NO more items', () => { pagination.hasMoreItems = false; component.target.updatePagination(pagination); @@ -123,15 +153,16 @@ describe('InfinitePaginationComponent', () => { }); it('should trigger the loadMore event with the proper pagination object', (done) => { - pagination.hasMoreItems = true; - pagination.skipCount = 5; - component.target.updatePagination(pagination); + pagination = { maxItems: 444, skipCount: 25, totalItems: 55, hasMoreItems: true }; + + component.target.pagination.next(pagination); + component.isLoading = false; component.pageSize = 5; fixture.detectChanges(); component.loadMore.subscribe((newPagination: Pagination) => { - expect(newPagination.skipCount).toBe(10); + expect(newPagination.skipCount).toBe(5); done(); }); @@ -166,10 +197,9 @@ describe('InfinitePaginationComponent', () => { component.onLoadMore(); expect(spyTarget).toHaveBeenCalledWith({ - maxItems: 444, skipCount: 25, - totalItems: 888, - hasMoreItems: true, + maxItems: 25, + hasMoreItems: false, merge: true }); }); @@ -182,10 +212,9 @@ describe('InfinitePaginationComponent', () => { component.onLoadMore(); expect(spyTarget).toHaveBeenCalledWith({ - maxItems: 444, + maxItems: 7, skipCount: 7, - totalItems: 888, - hasMoreItems: true, + hasMoreItems: false, merge: true }); }); diff --git a/lib/core/pagination/infinite-pagination.component.ts b/lib/core/pagination/infinite-pagination.component.ts index 22ca6c6c0c..4387cebedc 100644 --- a/lib/core/pagination/infinite-pagination.component.ts +++ b/lib/core/pagination/infinite-pagination.component.ts @@ -27,6 +27,7 @@ import { PaginatedComponent } from './paginated-component.interface'; import { Subscription } from 'rxjs'; import { PaginationComponentInterface } from './pagination-component.interface'; import { PaginationModel } from '../models/pagination.model'; +import { RequestPaginationModel } from '../models/request-pagination.model'; import { UserPreferencesService, UserPreferenceValues } from '../services/user-preferences.service'; @Component({ @@ -39,12 +40,6 @@ import { UserPreferencesService, UserPreferenceValues } from '../services/user-p }) export class InfinitePaginationComponent implements OnInit, OnDestroy, PaginationComponentInterface { - static DEFAULT_PAGINATION: PaginationModel = { - skipCount: 0, - hasMoreItems: false, - merge: true - }; - /** Component that provides custom pagination support. */ @Input() target: PaginatedComponent; @@ -59,10 +54,15 @@ export class InfinitePaginationComponent implements OnInit, OnDestroy, Paginatio /** Emitted when the "Load More" button is clicked. */ @Output() - loadMore: EventEmitter = new EventEmitter(); + loadMore: EventEmitter = new EventEmitter(); pagination: PaginationModel; + requestPaginationModel: RequestPaginationModel = { + skipCount: 0, + merge: true + }; + private paginationSubscription: Subscription; constructor(private cdr: ChangeDetectorRef, private userPreferencesService: UserPreferencesService) { @@ -73,6 +73,11 @@ export class InfinitePaginationComponent implements OnInit, OnDestroy, Paginatio this.paginationSubscription = this.target.pagination.subscribe((pagination) => { this.isLoading = false; this.pagination = pagination; + + if (!this.pagination.hasMoreItems) { + this.pagination.hasMoreItems = false; + } + this.cdr.detectChanges(); }); } @@ -80,26 +85,18 @@ export class InfinitePaginationComponent implements OnInit, OnDestroy, Paginatio this.userPreferencesService.select(UserPreferenceValues.PaginationSize).subscribe((pagSize) => { this.pageSize = this.pageSize || pagSize; }); - - if (!this.pagination) { - this.pagination = InfinitePaginationComponent.DEFAULT_PAGINATION; - } } onLoadMore() { - this.pagination.skipCount += this.pageSize; - this.pagination.merge = true; - this.loadMore.next(this.pagination); + this.requestPaginationModel.skipCount += this.pageSize; + this.requestPaginationModel.merge = true; + this.requestPaginationModel.maxItems = this.pageSize; - if (this.pagination.skipCount >= this.pagination.totalItems || !this.pagination.hasMoreItems) { - this.pagination.hasMoreItems = false; - } + this.loadMore.next(this.requestPaginationModel); if (this.target) { - this.target.pagination.value.merge = this.pagination.merge; - this.target.pagination.value.skipCount = this.pagination.skipCount; this.isLoading = true; - this.target.updatePagination( this.pagination); + this.target.updatePagination( this.requestPaginationModel); } } diff --git a/lib/core/pagination/paginated-component.interface.ts b/lib/core/pagination/paginated-component.interface.ts index e25ee5cffc..aa7e08867d 100644 --- a/lib/core/pagination/paginated-component.interface.ts +++ b/lib/core/pagination/paginated-component.interface.ts @@ -17,8 +17,9 @@ import { PaginationModel } from '../models/pagination.model'; import { BehaviorSubject } from 'rxjs'; +import { RequestPaginationModel } from '../models/request-pagination.model'; export interface PaginatedComponent { pagination: BehaviorSubject; - updatePagination(pagination: PaginationModel); + updatePagination(requestPaginationModel: RequestPaginationModel); }