diff --git a/src/app/+item-page/edit-item-page/item-bitstreams/item-bitstreams.component.scss b/src/app/+item-page/edit-item-page/item-bitstreams/item-bitstreams.component.scss
index 2e5e51b7ee..0400e765de 100644
--- a/src/app/+item-page/edit-item-page/item-bitstreams/item-bitstreams.component.scss
+++ b/src/app/+item-page/edit-item-page/item-bitstreams/item-bitstreams.component.scss
@@ -13,7 +13,7 @@
.row-element {
padding: 12px;
padding: 0.75em;
- border-top: $table-border-width solid $table-border-color;
+ border-bottom: $table-border-width solid $table-border-color;
}
.drag-handle {
diff --git a/src/app/+item-page/edit-item-page/item-bitstreams/item-edit-bitstream-bundle/item-edit-bitstream-bundle.component.html b/src/app/+item-page/edit-item-page/item-bitstreams/item-edit-bitstream-bundle/item-edit-bitstream-bundle.component.html
index af0a8b809c..57b76dbcfc 100644
--- a/src/app/+item-page/edit-item-page/item-bitstreams/item-edit-bitstream-bundle/item-edit-bitstream-bundle.component.html
+++ b/src/app/+item-page/edit-item-page/item-bitstreams/item-edit-bitstream-bundle/item-edit-bitstream-bundle.component.html
@@ -17,35 +17,27 @@
-
-
+
batchSize}"
+ *ngVar="((updates$ | async) | dsObjectValues) as updateValues" cdkDropList (cdkDropListDropped)="drop($event)">
+
-
-
-
-
-
-
-
-
- {{'item.edit.bitstreams.bundle.displaying' | translate:{ amount: bitstreamsRD?.payload?.elementsPerPage, total: bitstreamsRD?.payload?.totalElements } }}
-
-
-
- {{'loading.bitstreams' | translate}}
-
-
+
+
+
-
-
+
+
diff --git a/src/app/+item-page/edit-item-page/item-bitstreams/item-edit-bitstream-bundle/item-edit-bitstream-bundle.component.ts b/src/app/+item-page/edit-item-page/item-bitstreams/item-edit-bitstream-bundle/item-edit-bitstream-bundle.component.ts
index 4b102f66ec..baff48ce5c 100644
--- a/src/app/+item-page/edit-item-page/item-bitstreams/item-edit-bitstream-bundle/item-edit-bitstream-bundle.component.ts
+++ b/src/app/+item-page/edit-item-page/item-bitstreams/item-edit-bitstream-bundle/item-edit-bitstream-bundle.component.ts
@@ -13,8 +13,9 @@ import { PaginatedList } from '../../../../core/data/paginated-list';
import { BundleDataService } from '../../../../core/data/bundle-data.service';
import { BehaviorSubject } from 'rxjs/internal/BehaviorSubject';
import { combineLatest as observableCombineLatest } from 'rxjs';
-import { hasNoValue } from '../../../../shared/empty.util';
+import { hasNoValue, isEmpty } from '../../../../shared/empty.util';
import { PaginatedSearchOptions } from '../../../../shared/search/paginated-search-options.model';
+import { PaginationComponentOptions } from '../../../../shared/pagination/pagination-component-options.model';
@Component({
selector: 'ds-item-edit-bitstream-bundle',
@@ -55,33 +56,26 @@ export class ItemEditBitstreamBundleComponent implements OnInit {
* The amount of one bitstreams one "batch" resembles
* The user is able to increase the amount of bitstreams displayed per bundle by this batch size until all are shown
*/
- batchSize = 10;
+ batchSize = 2;
/**
* The page options to use for fetching the bitstreams
*/
- bitstreamsOptions = {
+ bitstreamsOptions = Object.assign(new PaginationComponentOptions(),{
id: 'bitstreams-pagination-options',
currentPage: 1,
pageSize: this.batchSize
- } as any;
+ });
/**
- * The current amount of bitstreams to display for this bundle
- * Starts off with just one batch
+ * The current page we're displaying for this bundle
*/
- currentSize$ = new BehaviorSubject
(this.batchSize);
+ currentPage$ = new BehaviorSubject(1);
/**
- * Are we currently loading more bitstreams?
+ * A list of pages that have been initialized in the field-update store
*/
- isLoadingMore$: Observable;
-
- /**
- * What size were the object updates last initialized with?
- * Used to check if the object updates need to be re-initialized when loading more bitstreams
- */
- lastInitializedWithSize: number;
+ initializedPages: number[] = [];
constructor(private objectUpdatesService: ObjectUpdatesService,
private bundleService: BundleDataService,
@@ -89,27 +83,39 @@ export class ItemEditBitstreamBundleComponent implements OnInit {
}
ngOnInit(): void {
- this.bitstreamsRD$ = this.currentSize$.pipe(
- switchMap((size: number) => this.bundleService.getBitstreams(this.bundle.id,
- new PaginatedSearchOptions({pagination: Object.assign({}, this.bitstreamsOptions, { pageSize: size })})))
+ this.bitstreamsRD$ = this.currentPage$.pipe(
+ switchMap((page: number) => this.bundleService.getBitstreams(this.bundle.id,
+ new PaginatedSearchOptions({pagination: Object.assign({}, this.bitstreamsOptions, { currentPage: page })})))
);
this.updates$ = this.bitstreamsRD$.pipe(
toBitstreamsArray(),
tap((bitstreams: Bitstream[]) => {
- if (hasNoValue(this.lastInitializedWithSize) || this.currentSize$.value !== this.lastInitializedWithSize) {
- this.objectUpdatesService.initialize(this.bundle.self, bitstreams, new Date(), true);
- this.lastInitializedWithSize = this.currentSize$.value;
+ // Pages in the field-update store are indexed starting at 0 (because they're stored in an array of pages)
+ const updatesPage = this.currentPage$.value - 1;
+ if (isEmpty(this.initializedPages)) {
+ // No updates have been initialized yet for this bundle, initialize the first page
+ this.objectUpdatesService.initializeWithCustomOrder(this.bundle.self, bitstreams, new Date(), this.batchSize, updatesPage);
+ this.initializedPages.push(updatesPage);
+ } else if (this.initializedPages.indexOf(this.currentPage$.value) < 0) {
+ // Updates were initialized for this bundle, but not the page we're on. Add the current page to the field-update store for this bundle
+ this.objectUpdatesService.addPageToCustomOrder(this.bundle.self, bitstreams, updatesPage);
+ this.initializedPages.push(updatesPage);
}
}),
- switchMap((bitstreams: Bitstream[]) => this.objectUpdatesService.getFieldUpdatesByCustomOrder(this.bundle.self, bitstreams))
- );
- this.isLoadingMore$ = observableCombineLatest(this.currentSize$, this.bitstreamsRD$).pipe(
- map(([size, bitstreamsRD]: [number, RemoteData>]) => size > bitstreamsRD.payload.page.length)
+ switchMap((bitstreams: Bitstream[]) => this.objectUpdatesService.getFieldUpdatesByCustomOrder(this.bundle.self, bitstreams, this.currentPage$.value - 1))
);
this.viewContainerRef.createEmbeddedView(this.bundleView);
}
+ /**
+ * Update the current page
+ * @param page
+ */
+ switchPage(page: number) {
+ this.currentPage$.next(page);
+ }
+
/**
* A bitstream was moved, send updates to the store
* @param event
@@ -117,18 +123,4 @@ export class ItemEditBitstreamBundleComponent implements OnInit {
drop(event: CdkDragDrop) {
this.objectUpdatesService.saveMoveFieldUpdate(this.bundle.self, event.previousIndex, event.currentIndex);
}
-
- /**
- * Load more bitstreams (current size + batchSize)
- */
- loadMore() {
- this.currentSize$.next(this.currentSize$.value + this.batchSize);
- }
-
- /**
- * Load all bitstreams
- */
- loadAll() {
- this.currentSize$.next(9999);
- }
}
diff --git a/src/app/core/data/array-move-change-analyzer.service.ts b/src/app/core/data/array-move-change-analyzer.service.ts
index cb8f0548d7..91dd7809e0 100644
--- a/src/app/core/data/array-move-change-analyzer.service.ts
+++ b/src/app/core/data/array-move-change-analyzer.service.ts
@@ -5,6 +5,7 @@ import { Injectable } from '@angular/core';
import { CacheableObject } from '../cache/object-cache.reducer';
import { NormalizedObject } from '../cache/models/normalized-object.model';
import { moveItemInArray } from '@angular/cdk/drag-drop';
+import { hasValue } from '../../shared/empty.util';
/**
* A class to determine move operations between two arrays
@@ -22,15 +23,17 @@ export class ArrayMoveChangeAnalyzer {
const result = [];
const moved = [...array1];
array1.forEach((value: T, index: number) => {
- const otherIndex = array2.indexOf(value);
- const movedIndex = moved.indexOf(value);
- if (index !== otherIndex && movedIndex !== otherIndex) {
- moveItemInArray(moved, movedIndex, otherIndex);
- result.push(Object.assign({
- op: 'move',
- from: '/' + movedIndex,
- path: '/' + otherIndex
- }) as MoveOperation)
+ if (hasValue(value)) {
+ const otherIndex = array2.indexOf(value);
+ const movedIndex = moved.indexOf(value);
+ if (index !== otherIndex && movedIndex !== otherIndex) {
+ moveItemInArray(moved, movedIndex, otherIndex);
+ result.push(Object.assign({
+ op: 'move',
+ from: '/' + movedIndex,
+ path: '/' + otherIndex
+ }) as MoveOperation)
+ }
}
});
return result;
diff --git a/src/app/core/data/object-updates/object-updates.actions.ts b/src/app/core/data/object-updates/object-updates.actions.ts
index d48d66dbf8..045ce97974 100644
--- a/src/app/core/data/object-updates/object-updates.actions.ts
+++ b/src/app/core/data/object-updates/object-updates.actions.ts
@@ -8,6 +8,7 @@ import { INotification } from '../../../shared/notifications/models/notification
*/
export const ObjectUpdatesActionTypes = {
INITIALIZE_FIELDS: type('dspace/core/cache/object-updates/INITIALIZE_FIELDS'),
+ ADD_PAGE_TO_CUSTOM_ORDER: type('dspace/core/cache/object-updates/ADD_PAGE_TO_CUSTOM_ORDER'),
SET_EDITABLE_FIELD: type('dspace/core/cache/object-updates/SET_EDITABLE_FIELD'),
SET_VALID_FIELD: type('dspace/core/cache/object-updates/SET_VALID_FIELD'),
ADD_FIELD: type('dspace/core/cache/object-updates/ADD_FIELD'),
@@ -39,7 +40,9 @@ export class InitializeFieldsAction implements Action {
url: string,
fields: Identifiable[],
lastModified: Date,
- order: string[]
+ order: string[],
+ pageSize: number,
+ page: number
};
/**
@@ -50,14 +53,48 @@ export class InitializeFieldsAction implements Action {
* @param fields The identifiable fields of which the updates are kept track of
* @param lastModified The last modified date of the object that belongs to the page
* @param order A custom order to keep track of objects moving around
+ * @param pageSize The page size used to fill empty pages for the custom order
+ * @param page The first page to populate in the custom order
*/
constructor(
url: string,
fields: Identifiable[],
lastModified: Date,
- order: string[] = []
+ order: string[] = [],
+ pageSize: number = 9999,
+ page: number = 0
) {
- this.payload = { url, fields, lastModified, order };
+ this.payload = { url, fields, lastModified, order, pageSize, page };
+ }
+}
+
+/**
+ * An ngrx action to initialize a new page's fields in the ObjectUpdates state
+ */
+export class AddPageToCustomOrderAction implements Action {
+ type = ObjectUpdatesActionTypes.ADD_PAGE_TO_CUSTOM_ORDER;
+ payload: {
+ url: string,
+ fields: Identifiable[],
+ order: string[],
+ page: number
+ };
+
+ /**
+ * Create a new AddPageToCustomOrderAction
+ *
+ * @param url The unique url of the page for which the fields are being added
+ * @param fields The identifiable fields of which the updates are kept track of
+ * @param order A custom order to keep track of objects moving around
+ * @param page The page to populate in the custom order
+ */
+ constructor(
+ url: string,
+ fields: Identifiable[],
+ order: string[] = [],
+ page: number = 0
+ ) {
+ this.payload = { url, fields, order, page };
}
}
@@ -254,7 +291,9 @@ export class MoveFieldUpdateAction implements Action {
payload: {
url: string,
from: number,
- to: number
+ to: number,
+ fromPage: number,
+ toPage: number
};
/**
@@ -262,15 +301,19 @@ export class MoveFieldUpdateAction implements Action {
*
* @param url
* the unique url of the page for which a field's change should be removed
- * @param from The index of the object to move
- * @param to The index to move the object to
+ * @param from The index of the object to move
+ * @param to The index to move the object to
+ * @param fromPage The page to move the object from
+ * @param toPage The page to move the object to
*/
constructor(
url: string,
from: number,
- to: number
+ to: number,
+ fromPage: number,
+ toPage: number
) {
- this.payload = { url, from, to };
+ this.payload = { url, from, to, fromPage, toPage };
}
}
@@ -286,4 +329,5 @@ export type ObjectUpdatesAction
| ReinstateObjectUpdatesAction
| RemoveObjectUpdatesAction
| RemoveFieldUpdateAction
- | MoveFieldUpdateAction;
+ | MoveFieldUpdateAction
+ | AddPageToCustomOrderAction;
diff --git a/src/app/core/data/object-updates/object-updates.reducer.ts b/src/app/core/data/object-updates/object-updates.reducer.ts
index 7350ba766f..47756c58d3 100644
--- a/src/app/core/data/object-updates/object-updates.reducer.ts
+++ b/src/app/core/data/object-updates/object-updates.reducer.ts
@@ -1,5 +1,5 @@
import {
- AddFieldUpdateAction,
+ AddFieldUpdateAction, AddPageToCustomOrderAction,
DiscardObjectUpdatesAction,
FieldChangeType,
InitializeFieldsAction, MoveFieldUpdateAction,
@@ -9,8 +9,9 @@ import {
RemoveFieldUpdateAction,
RemoveObjectUpdatesAction, SetEditableFieldUpdateAction, SetValidFieldUpdateAction
} from './object-updates.actions';
-import { hasNoValue, hasValue, isNotEmpty } from '../../../shared/empty.util';
-import { moveItemInArray } from '@angular/cdk/drag-drop';
+import { hasNoValue, hasValue, isEmpty, isNotEmpty } from '../../../shared/empty.util';
+import { moveItemInArray, transferArrayItem } from '@angular/cdk/drag-drop';
+import { from } from 'rxjs/internal/observable/from';
/**
* Path where discarded objects are saved
@@ -59,11 +60,16 @@ export interface FieldUpdates {
* A custom order given to the list of objects
*/
export interface CustomOrder {
- initialOrder: string[],
- newOrder: string[],
+ initialOrderPages: OrderPage[],
+ newOrderPages: OrderPage[],
+ pageSize: number;
changed: boolean
}
+export interface OrderPage {
+ order: string[]
+}
+
/**
* The updated state of a single page
*/
@@ -104,6 +110,9 @@ export function objectUpdatesReducer(state = initialState, action: ObjectUpdates
case ObjectUpdatesActionTypes.INITIALIZE_FIELDS: {
return initializeFieldsUpdate(state, action as InitializeFieldsAction);
}
+ case ObjectUpdatesActionTypes.ADD_PAGE_TO_CUSTOM_ORDER: {
+ return addPageToCustomOrder(state, action as AddPageToCustomOrderAction);
+ }
case ObjectUpdatesActionTypes.ADD_FIELD: {
return addFieldUpdate(state, action as AddFieldUpdateAction);
}
@@ -147,18 +156,47 @@ function initializeFieldsUpdate(state: any, action: InitializeFieldsAction) {
const fields: Identifiable[] = action.payload.fields;
const lastModifiedServer: Date = action.payload.lastModified;
const order = action.payload.order;
+ const pageSize = action.payload.pageSize;
+ const page = action.payload.page;
const fieldStates = createInitialFieldStates(fields);
+ const initialOrderPages = addOrderToPages([], order, pageSize, page);
const newPageState = Object.assign(
{},
state[url],
{ fieldStates: fieldStates },
{ fieldUpdates: {} },
{ lastModified: lastModifiedServer },
- { customOrder: { initialOrder: order, newOrder: order, changed: false } }
+ { customOrder: {
+ initialOrderPages: initialOrderPages,
+ newOrderPages: initialOrderPages,
+ pageSize: 9999,
+ changed: false }
+ }
);
return Object.assign({}, state, { [url]: newPageState });
}
+/**
+ * Add a page of objects to the state of a specific url and update a specific page of the custom order
+ * @param state The current state
+ * @param action The action to perform on the current state
+ */
+function addPageToCustomOrder(state: any, action: AddPageToCustomOrderAction) {
+ const url: string = action.payload.url;
+ const fields: Identifiable[] = action.payload.fields;
+ const order = action.payload.order;
+ const page = action.payload.page;
+ const pageState: ObjectUpdatesEntry = state[url] || {};
+ const newPageState = Object.assign({}, pageState, {
+ fieldStates: Object.assign({}, pageState.fieldStates, fields),
+ customOrder: Object.assign({}, pageState.customOrder, {
+ newOrderPages: addOrderToPages(pageState.customOrder.newOrderPages, order, pageState.customOrder.pageSize, page),
+ initialOrderPages: addOrderToPages(pageState.customOrder.initialOrderPages, order, pageState.customOrder.pageSize, page)
+ })
+ });
+ return Object.assign({}, state, { [url]: newPageState });
+}
+
/**
* Add a new update for a specific field to the store
* @param state The current state
@@ -224,9 +262,9 @@ function discardObjectUpdatesFor(url: string, state: any) {
const newCustomOrder = Object.assign({}, pageState.customOrder);
if (pageState.customOrder.changed) {
- const initialOrder = pageState.customOrder.initialOrder;
+ const initialOrder = pageState.customOrder.initialOrderPages;
if (isNotEmpty(initialOrder)) {
- newCustomOrder.newOrder = initialOrder;
+ newCustomOrder.newOrderPages = initialOrder;
newCustomOrder.changed = false;
}
}
@@ -389,6 +427,17 @@ function createInitialFieldStates(fields: Identifiable[]) {
return fieldStates;
}
+/**
+ * Method to add a list of objects to an existing FieldStates object
+ * @param fieldStates FieldStates to add states to
+ * @param fields Identifiable objects The list of objects to add to the FieldStates
+ */
+function addFieldStates(fieldStates: FieldStates, fields: Identifiable[]) {
+ const uuids = fields.map((field: Identifiable) => field.uuid);
+ uuids.forEach((uuid: string) => fieldStates[uuid] = initialFieldState);
+ return fieldStates;
+}
+
/**
* Move an object within the custom order of a page state
* @param state The current state
@@ -396,23 +445,64 @@ function createInitialFieldStates(fields: Identifiable[]) {
*/
function moveFieldUpdate(state: any, action: MoveFieldUpdateAction) {
const url = action.payload.url;
- const from = action.payload.from;
- const to = action.payload.to;
+ const fromIndex = action.payload.from;
+ const toIndex = action.payload.to;
+ const fromPage = action.payload.fromPage;
+ const toPage = action.payload.toPage;
const pageState: ObjectUpdatesEntry = state[url];
- const initialOrder = pageState.customOrder.initialOrder;
- const customOrder = [...pageState.customOrder.newOrder];
- if (isNotEmpty(customOrder) && isNotEmpty(customOrder[from]) && isNotEmpty(customOrder[to])) {
- moveItemInArray(customOrder, from, to);
+ const initialOrderPages = pageState.customOrder.initialOrderPages;
+ const customOrderPages = [...pageState.customOrder.newOrderPages];
+ if (fromPage === toPage) {
+ if (isNotEmpty(customOrderPages[fromPage]) && isNotEmpty(customOrderPages[fromPage].order[fromIndex]) && isNotEmpty(customOrderPages[fromPage].order[toIndex])) {
+ moveItemInArray(customOrderPages[fromPage].order, fromIndex, toIndex);
+ }
+ } else {
+ if (isNotEmpty(customOrderPages[fromPage]) && isNotEmpty(customOrderPages[toPage]) && isNotEmpty(customOrderPages[fromPage].order[fromIndex]) && isNotEmpty(customOrderPages[toPage].order[toIndex])) {
+ transferArrayItem(customOrderPages[fromPage].order, customOrderPages[toPage].order, fromIndex, toIndex);
+ }
}
let changed = false;
- initialOrder.forEach((id: string, index: number) => {
- if (id !== customOrder[index]) {
- changed = true;
- return;
+ initialOrderPages.forEach((orderPage: OrderPage, page: number) => {
+ if (isNotEmpty(orderPage) && isNotEmpty(orderPage.order) && isNotEmpty(customOrderPages[page]) && isNotEmpty(customOrderPages[page].order)) {
+ orderPage.order.forEach((id: string, index: number) => {
+ if (id !== customOrderPages[page].order[index]) {
+ changed = true;
+ return;
+ }
+ });
+ if (changed) {
+ return;
+ }
}
});
- return Object.assign({}, state, { [url]: Object.assign({}, pageState, { customOrder: Object.assign({}, pageState.customOrder, { newOrder: customOrder, changed: changed }) }) })
+ return Object.assign({}, state, { [url]: Object.assign({}, pageState, { customOrder: Object.assign({}, pageState.customOrder, { newOrderPages: customOrderPages, changed: changed }) }) })
+}
+
+/**
+ * Initialize a custom order page by providing the list of all pages, a list of UUIDs, pageSize and the page to populate
+ * @param initialPages The initial list of OrderPage objects
+ * @param order The list of UUIDs to create a page for
+ * @param pageSize The pageSize used to populate empty spacer pages
+ * @param page The index of the page to add
+ */
+function addOrderToPages(initialPages: OrderPage[], order: string[], pageSize: number, page: number): OrderPage[] {
+ const result = [...initialPages];
+ const orderPage: OrderPage = { order: order };
+ if (page < result.length) {
+ // The page we're trying to add already exists in the list. Overwrite it.
+ result[page] = orderPage;
+ } else if (page === result.length) {
+ // The page we're trying to add is the next page in the list, add it.
+ result.push(orderPage);
+ } else {
+ // The page we're trying to add is at least one page ahead of the list, fill the list with empty pages before adding the page.
+ const emptyOrderPage: OrderPage = { order: [] };
+ emptyOrderPage.order.fill(undefined, 0, pageSize);
+ result.fill(emptyOrderPage, result.length, page - 1);
+ result.push(orderPage);
+ }
+ return result;
}
diff --git a/src/app/core/data/object-updates/object-updates.service.ts b/src/app/core/data/object-updates/object-updates.service.ts
index 42ca0eedab..6e1d8d1fc1 100644
--- a/src/app/core/data/object-updates/object-updates.service.ts
+++ b/src/app/core/data/object-updates/object-updates.service.ts
@@ -8,11 +8,11 @@ import {
Identifiable,
OBJECT_UPDATES_TRASH_PATH,
ObjectUpdatesEntry,
- ObjectUpdatesState
+ ObjectUpdatesState, OrderPage
} from './object-updates.reducer';
import { Observable } from 'rxjs';
import {
- AddFieldUpdateAction,
+ AddFieldUpdateAction, AddPageToCustomOrderAction,
DiscardObjectUpdatesAction,
FieldChangeType,
InitializeFieldsAction,
@@ -28,6 +28,7 @@ import { INotification } from '../../../shared/notifications/models/notification
import { Operation } from 'fast-json-patch';
import { ArrayMoveChangeAnalyzer } from '../array-move-change-analyzer.service';
import { MoveOperation } from 'fast-json-patch/lib/core';
+import { flatten } from '@angular/compiler';
function objectUpdatesStateSelector(): MemoizedSelector {
return createSelector(coreSelector, (state: CoreState) => state['cache/object-updates']);
@@ -56,10 +57,25 @@ export class ObjectUpdatesService {
* @param url The page's URL for which the changes are being mapped
* @param fields The initial fields for the page's object
* @param lastModified The date the object was last modified
- * @param addCustomOrder Add a custom order list to track move changes
*/
- initialize(url, fields: Identifiable[], lastModified: Date, addCustomOrder?: boolean): void {
- this.store.dispatch(new InitializeFieldsAction(url, fields, lastModified, addCustomOrder ? fields.map((field) => field.uuid) : []));
+ initialize(url, fields: Identifiable[], lastModified: Date): void {
+ this.store.dispatch(new InitializeFieldsAction(url, fields, lastModified));
+ }
+
+ /**
+ * Method to dispatch an InitializeFieldsAction to the store and keeping track of the order objects are stored
+ * @param url The page's URL for which the changes are being mapped
+ * @param fields The initial fields for the page's object
+ * @param lastModified The date the object was last modified
+ * @param pageSize The page size to use for adding pages to the custom order
+ * @param page The first page to populate the custom order with
+ */
+ initializeWithCustomOrder(url, fields: Identifiable[], lastModified: Date, pageSize = 9999, page = 0): void {
+ this.store.dispatch(new InitializeFieldsAction(url, fields, lastModified, fields.map((field) => field.uuid), pageSize, page));
+ }
+
+ addPageToCustomOrder(url, fields: Identifiable[], page: number): void {
+ this.store.dispatch(new AddPageToCustomOrderAction(url, fields, fields.map((field) => field.uuid), page));
}
/**
@@ -140,13 +156,14 @@ export class ObjectUpdatesService {
* sorted by their custom order to create a FieldUpdates object
* @param url The URL of the page for which the FieldUpdates should be requested
* @param initialFields The initial values of the fields
+ * @param page The page to retrieve
*/
- getFieldUpdatesByCustomOrder(url: string, initialFields: Identifiable[]): Observable {
+ getFieldUpdatesByCustomOrder(url: string, initialFields: Identifiable[], page = 0): Observable {
const objectUpdates = this.getObjectEntry(url);
return objectUpdates.pipe(map((objectEntry) => {
const fieldUpdates: FieldUpdates = {};
if (hasValue(objectEntry)) {
- for (const uuid of objectEntry.customOrder.newOrder) {
+ for (const uuid of objectEntry.customOrder.newOrderPages[page].order) {
let fieldUpdate = objectEntry.fieldUpdates[uuid];
if (isEmpty(fieldUpdate)) {
const identifiable = initialFields.find((object: Identifiable) => object.uuid === uuid);
@@ -230,12 +247,14 @@ export class ObjectUpdatesService {
/**
* Dispatches a MoveFieldUpdateAction
- * @param url The page's URL for which the changes are saved
- * @param from The index of the object to move
- * @param to The index to move the object to
+ * @param url The page's URL for which the changes are saved
+ * @param from The index of the object to move
+ * @param to The index to move the object to
+ * @param fromPage The page to move the object from
+ * @param toPage The page to move the object to
*/
- saveMoveFieldUpdate(url: string, from: number, to: number) {
- this.store.dispatch(new MoveFieldUpdateAction(url, from, to));
+ saveMoveFieldUpdate(url: string, from: number, to: number, fromPage = 0, toPage = 0) {
+ this.store.dispatch(new MoveFieldUpdateAction(url, from, to, fromPage, toPage));
}
/**
@@ -350,7 +369,10 @@ export class ObjectUpdatesService {
getMoveOperations(url: string): Observable {
return this.getObjectEntry(url).pipe(
map((objectEntry) => objectEntry.customOrder),
- map((customOrder) => this.comparator.diff(customOrder.initialOrder, customOrder.newOrder))
+ map((customOrder) => this.comparator.diff(
+ flatten(customOrder.initialOrderPages.map((orderPage: OrderPage) => orderPage.order)),
+ flatten(customOrder.newOrderPages.map((orderPage: OrderPage) => orderPage.order)))
+ )
);
}
diff --git a/src/app/shared/pagination-drag-and-drop/pagination-drag-and-drop.component.html b/src/app/shared/pagination-drag-and-drop/pagination-drag-and-drop.component.html
new file mode 100644
index 0000000000..033ef90834
--- /dev/null
+++ b/src/app/shared/pagination-drag-and-drop/pagination-drag-and-drop.component.html
@@ -0,0 +1,11 @@
+
+
+
diff --git a/src/app/shared/pagination-drag-and-drop/pagination-drag-and-drop.component.ts b/src/app/shared/pagination-drag-and-drop/pagination-drag-and-drop.component.ts
new file mode 100644
index 0000000000..2d87aa89d8
--- /dev/null
+++ b/src/app/shared/pagination-drag-and-drop/pagination-drag-and-drop.component.ts
@@ -0,0 +1,55 @@
+import { Component, EventEmitter, Input, Output } from '@angular/core';
+import { PaginationComponentOptions } from '../pagination/pagination-component-options.model';
+import { PaginatedList } from '../../core/data/paginated-list';
+
+@Component({
+ selector: 'ds-pagination-drag-and-drop',
+ templateUrl: './pagination-drag-and-drop.component.html',
+})
+export class PaginationDragAndDropComponent {
+ /**
+ * Configuration for the NgbPagination component.
+ */
+ @Input() paginationOptions: PaginationComponentOptions;
+
+ /**
+ * The paginated list being displayed
+ */
+ @Input() paginatedList: PaginatedList;
+
+ /**
+ * Option for hiding the pagination detail
+ */
+ @Input() public hidePaginationDetail = false;
+
+ /**
+ * Option for hiding the gear
+ */
+ @Input() public hideGear = false;
+
+ /**
+ * Option for hiding the pager when there is less than 2 pages
+ */
+ @Input() public hidePagerWhenSinglePage = true;
+
+ /**
+ * Option for disabling updating and reading route parameters on pagination changes
+ * In other words, changing pagination won't add or update the url parameters on the current page, and the url
+ * parameters won't affect the pagination of this component
+ */
+ @Input() public disableRouteParameterUpdate = false;
+
+ /**
+ * An event fired when the page is changed.
+ * Event's payload equals to the newly selected page.
+ */
+ @Output() pageChange: EventEmitter = new EventEmitter();
+
+ /**
+ * Switch to a different page
+ * @param page Page to switch to
+ */
+ switchPage(page: number) {
+ this.pageChange.emit(page);
+ }
+}
diff --git a/src/app/shared/pagination/pagination.component.ts b/src/app/shared/pagination/pagination.component.ts
index 9c378d1aff..04309b6f9f 100644
--- a/src/app/shared/pagination/pagination.component.ts
+++ b/src/app/shared/pagination/pagination.component.ts
@@ -99,6 +99,13 @@ export class PaginationComponent implements OnDestroy, OnInit {
*/
@Input() public hidePagerWhenSinglePage = true;
+ /**
+ * Option for disabling updating and reading route parameters on pagination changes
+ * In other words, changing pagination won't add or update the url parameters on the current page, and the url
+ * parameters won't affect the pagination of this component
+ */
+ @Input() public disableRouteParameterUpdate = false;
+
/**
* Current page.
*/
@@ -173,20 +180,35 @@ export class PaginationComponent implements OnDestroy, OnInit {
this.checkConfig(this.paginationOptions);
this.initializeConfig();
// Listen to changes
- this.subs.push(this.route.queryParams
- .subscribe((queryParams) => {
- if (this.isEmptyPaginationParams(queryParams)) {
- this.initializeConfig(queryParams);
+ if (!this.disableRouteParameterUpdate) {
+ this.subs.push(this.route.queryParams
+ .subscribe((queryParams) => {
+ this.initializeParams(queryParams);
+ }));
+ }
+ }
+
+ /**
+ * Initialize the route and current parameters
+ * This method will fix any invalid or missing parameters
+ * @param params
+ */
+ private initializeParams(params) {
+ if (this.isEmptyPaginationParams(params)) {
+ this.initializeConfig(params);
+ } else {
+ this.currentQueryParams = params;
+ const fixedProperties = this.validateParams(params);
+ if (isNotEmpty(fixedProperties)) {
+ if (!this.disableRouteParameterUpdate) {
+ this.fixRoute(fixedProperties);
} else {
- this.currentQueryParams = queryParams;
- const fixedProperties = this.validateParams(queryParams);
- if (isNotEmpty(fixedProperties)) {
- this.fixRoute(fixedProperties);
- } else {
- this.setFields();
- }
+ this.initializeParams(fixedProperties);
}
- }));
+ } else {
+ this.setFields();
+ }
+ }
}
private fixRoute(fixedProperties) {
@@ -247,7 +269,7 @@ export class PaginationComponent implements OnDestroy, OnInit {
* The page being navigated to.
*/
public doPageChange(page: number) {
- this.updateRoute({ pageId: this.id, page: page.toString() });
+ this.updateParams(Object.assign({}, this.currentQueryParams, { pageId: this.id, page: page.toString() }));
}
/**
@@ -257,7 +279,7 @@ export class PaginationComponent implements OnDestroy, OnInit {
* The page size being navigated to.
*/
public doPageSizeChange(pageSize: number) {
- this.updateRoute({ pageId: this.id, page: 1, pageSize: pageSize });
+ this.updateParams(Object.assign({}, this.currentQueryParams,{ pageId: this.id, page: 1, pageSize: pageSize }));
}
/**
@@ -267,7 +289,7 @@ export class PaginationComponent implements OnDestroy, OnInit {
* The sort direction being navigated to.
*/
public doSortDirectionChange(sortDirection: SortDirection) {
- this.updateRoute({ pageId: this.id, page: 1, sortDirection: sortDirection });
+ this.updateParams(Object.assign({}, this.currentQueryParams,{ pageId: this.id, page: 1, sortDirection: sortDirection }));
}
/**
@@ -277,7 +299,7 @@ export class PaginationComponent implements OnDestroy, OnInit {
* The sort field being navigated to.
*/
public doSortFieldChange(field: string) {
- this.updateRoute({ pageId: this.id, page: 1, sortField: field });
+ this.updateParams(Object.assign(this.currentQueryParams,{ pageId: this.id, page: 1, sortField: field }));
}
/**
@@ -347,6 +369,20 @@ export class PaginationComponent implements OnDestroy, OnInit {
})
}
+ /**
+ * Update the current query params and optionally update the route
+ * @param params
+ */
+ private updateParams(params: {}) {
+ if (isNotEmpty(difference(params, this.currentQueryParams))) {
+ if (!this.disableRouteParameterUpdate) {
+ this.updateRoute(params);
+ } else {
+ this.initializeParams(params);
+ }
+ }
+ }
+
/**
* Method to update the route parameters
*/
diff --git a/src/app/shared/shared.module.ts b/src/app/shared/shared.module.ts
index cd6417d964..a02b77c0b8 100644
--- a/src/app/shared/shared.module.ts
+++ b/src/app/shared/shared.module.ts
@@ -178,6 +178,7 @@ import { ImportableListItemControlComponent } from './object-collection/shared/i
import { DragDropModule } from '@angular/cdk/drag-drop';
import { ExistingMetadataListElementComponent } from './form/builder/ds-dynamic-form-ui/existing-metadata-list-element/existing-metadata-list-element.component';
import { CustomSwitchComponent } from './form/builder/ds-dynamic-form-ui/models/custom-switch/custom-switch.component';
+import { PaginationDragAndDropComponent } from './pagination-drag-and-drop/pagination-drag-and-drop.component';
const MODULES = [
// Do NOT include UniversalModule, HttpModule, or JsonpModule here
@@ -263,6 +264,7 @@ const COMPONENTS = [
AbstractListableElementComponent,
ObjectCollectionComponent,
PaginationComponent,
+ PaginationDragAndDropComponent,
SearchFormComponent,
PageWithSidebarComponent,
SidebarDropdownComponent,