mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-07 01:54:15 +00:00
55693: (incomplete) store interraction for selecting items
This commit is contained in:
@@ -14,6 +14,7 @@ import {
|
||||
} from './+search-page/search-filters/search-filter/search-filter.reducer';
|
||||
import { notificationsReducer, NotificationsState } from './shared/notifications/notifications.reducers';
|
||||
import { truncatableReducer, TruncatablesState } from './shared/truncatable/truncatable.reducer';
|
||||
import { itemSelectionReducer, ItemSelectionsState } from './shared/item-select/item-select.reducer';
|
||||
|
||||
export interface AppState {
|
||||
router: fromRouter.RouterReducerState;
|
||||
@@ -23,7 +24,8 @@ export interface AppState {
|
||||
notifications: NotificationsState;
|
||||
searchSidebar: SearchSidebarState;
|
||||
searchFilter: SearchFiltersState;
|
||||
truncatable: TruncatablesState;
|
||||
truncatable: TruncatablesState,
|
||||
itemSelection: ItemSelectionsState
|
||||
}
|
||||
|
||||
export const appReducers: ActionReducerMap<AppState> = {
|
||||
@@ -34,7 +36,8 @@ export const appReducers: ActionReducerMap<AppState> = {
|
||||
notifications: notificationsReducer,
|
||||
searchSidebar: sidebarReducer,
|
||||
searchFilter: filterReducer,
|
||||
truncatable: truncatableReducer
|
||||
truncatable: truncatableReducer,
|
||||
itemSelection: itemSelectionReducer
|
||||
};
|
||||
|
||||
export const routerStateSelector = (state: AppState) => state.router;
|
||||
|
@@ -64,6 +64,7 @@ import { NotificationsService } from '../shared/notifications/notifications.serv
|
||||
import { UploaderService } from '../shared/uploader/uploader.service';
|
||||
import { BrowseItemsResponseParsingService } from './data/browse-items-response-parsing-service';
|
||||
import { DSpaceObjectDataService } from './data/dspace-object-data.service';
|
||||
import { ItemSelectService } from '../shared/item-select/item-select.service';
|
||||
|
||||
const IMPORTS = [
|
||||
CommonModule,
|
||||
@@ -128,6 +129,7 @@ const PROVIDERS = [
|
||||
UploaderService,
|
||||
UUIDService,
|
||||
DSpaceObjectDataService,
|
||||
ItemSelectService,
|
||||
// register AuthInterceptor as HttpInterceptor
|
||||
{
|
||||
provide: HTTP_INTERCEPTORS,
|
||||
|
75
src/app/shared/item-select/item-select.actions.ts
Normal file
75
src/app/shared/item-select/item-select.actions.ts
Normal file
@@ -0,0 +1,75 @@
|
||||
import { type } from '../ngrx/type';
|
||||
import { Action } from '@ngrx/store';
|
||||
|
||||
export const ItemSelectionActionTypes = {
|
||||
INITIAL_DESELECT: type('dspace/item-select/INITIAL_DESELECT'),
|
||||
INITIAL_SELECT: type('dspace/item-select/INITIAL_SELECT'),
|
||||
SELECT: type('dspace/item-select/SELECT'),
|
||||
DESELECT: type('dspace/item-select/DESELECT'),
|
||||
SWITCH: type('dspace/item-select/SWITCH'),
|
||||
RESET: type('dspace/item-select/RESET')
|
||||
};
|
||||
|
||||
export class ItemSelectionAction implements Action {
|
||||
/**
|
||||
* UUID of the item a select action can be performed on
|
||||
*/
|
||||
id: string;
|
||||
|
||||
/**
|
||||
* Type of action that will be performed
|
||||
*/
|
||||
type;
|
||||
|
||||
/**
|
||||
* Initialize with the item's UUID
|
||||
* @param {string} id of the item
|
||||
*/
|
||||
constructor(id: string) {
|
||||
this.id = id;
|
||||
}
|
||||
}
|
||||
|
||||
/* tslint:disable:max-classes-per-file */
|
||||
/**
|
||||
* Used to set the initial state to deselected
|
||||
*/
|
||||
export class ItemSelectionInitialDeselectAction extends ItemSelectionAction {
|
||||
type = ItemSelectionActionTypes.INITIAL_DESELECT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to set the initial state to selected
|
||||
*/
|
||||
export class ItemSelectionInitialSelectAction extends ItemSelectionAction {
|
||||
type = ItemSelectionActionTypes.INITIAL_SELECT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to select an item
|
||||
*/
|
||||
export class ItemSelectionSelectAction extends ItemSelectionAction {
|
||||
type = ItemSelectionActionTypes.SELECT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to deselect an item
|
||||
*/
|
||||
export class ItemSelectionDeselectAction extends ItemSelectionAction {
|
||||
type = ItemSelectionActionTypes.DESELECT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to switch an item between selected and deselected
|
||||
*/
|
||||
export class ItemSelectionSwitchAction extends ItemSelectionAction {
|
||||
type = ItemSelectionActionTypes.SWITCH;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to reset all item's selected to be deselected
|
||||
*/
|
||||
export class ItemSelectionResetAction extends ItemSelectionAction {
|
||||
type = ItemSelectionActionTypes.RESET;
|
||||
}
|
||||
/* tslint:enable:max-classes-per-file */
|
@@ -16,8 +16,8 @@
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr *ngFor="let item of (itemsRD$ | async)?.payload?.page ; let i = index">
|
||||
<td><input [(ngModel)]="checked[i]" type="checkbox"></td>
|
||||
<tr *ngFor="let item of (itemsRD$ | async)?.payload?.page">
|
||||
<td><input [ngModel]="getSelected(item.id) | async" (change)="switch(item.id)" type="checkbox"></td>
|
||||
<td><a [routerLink]="['/items', item.id]">{{(item.owningCollection | async)?.payload?.name}}</a></td>
|
||||
<td><a *ngIf="item.filterMetadata(['dc.contributor.author', 'dc.creator', 'dc.contributor.*']).length > 0" [routerLink]="['/items', item.id]">{{item.filterMetadata(['dc.contributor.author', 'dc.creator', 'dc.contributor.*'])[0].value}}</a></td>
|
||||
<td><a [routerLink]="['/items', item.id]">{{item.findMetadata("dc.title")}}</a></td>
|
||||
|
@@ -5,6 +5,7 @@ import { RemoteData } from '../../core/data/remote-data';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import { Item } from '../../core/shared/item.model';
|
||||
import { PaginationComponentOptions } from '../pagination/pagination-component-options.model';
|
||||
import { ItemSelectService } from './item-select.service';
|
||||
|
||||
@Component({
|
||||
selector: 'ds-item-select',
|
||||
@@ -22,11 +23,19 @@ export class ItemSelectComponent implements OnInit {
|
||||
|
||||
checked: boolean[] = [];
|
||||
|
||||
constructor(private itemDataService: ItemDataService) {
|
||||
constructor(private itemSelectService: ItemSelectService) {
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.itemsRD$.subscribe((value) => console.log(value));
|
||||
}
|
||||
|
||||
switch(id: string) {
|
||||
this.itemSelectService.switch(id);
|
||||
}
|
||||
|
||||
getSelected(id: string): Observable<boolean> {
|
||||
return this.itemSelectService.getSelected(id);
|
||||
}
|
||||
|
||||
}
|
||||
|
84
src/app/shared/item-select/item-select.reducer.ts
Normal file
84
src/app/shared/item-select/item-select.reducer.ts
Normal file
@@ -0,0 +1,84 @@
|
||||
import { isEmpty } from '../empty.util';
|
||||
import { ItemSelectionAction, ItemSelectionActionTypes } from './item-select.actions';
|
||||
|
||||
/**
|
||||
* Interface that represents the state for a single filters
|
||||
*/
|
||||
export interface ItemSelectionState {
|
||||
checked: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface that represents the state for all available filters
|
||||
*/
|
||||
export interface ItemSelectionsState {
|
||||
[id: string]: ItemSelectionState
|
||||
}
|
||||
|
||||
const initialState: ItemSelectionsState = Object.create(null);
|
||||
|
||||
/**
|
||||
* Performs a search filter action on the current state
|
||||
* @param {SearchFiltersState} state The state before the action is performed
|
||||
* @param {SearchFilterAction} action The action that should be performed
|
||||
* @returns {SearchFiltersState} The state after the action is performed
|
||||
*/
|
||||
export function itemSelectionReducer(state = initialState, action: ItemSelectionAction): ItemSelectionsState {
|
||||
|
||||
switch (action.type) {
|
||||
|
||||
case ItemSelectionActionTypes.INITIAL_SELECT: {
|
||||
if (isEmpty(state) || isEmpty(state[action.id])) {
|
||||
return Object.assign({}, state, {
|
||||
[action.id]: {
|
||||
checked: true
|
||||
}
|
||||
});
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
||||
case ItemSelectionActionTypes.INITIAL_DESELECT: {
|
||||
if (isEmpty(state) || isEmpty(state[action.id])) {
|
||||
return Object.assign({}, state, {
|
||||
[action.id]: {
|
||||
checked: false
|
||||
}
|
||||
});
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
||||
case ItemSelectionActionTypes.SELECT: {
|
||||
return Object.assign({}, state, {
|
||||
[action.id]: {
|
||||
checked: true
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
case ItemSelectionActionTypes.DESELECT: {
|
||||
return Object.assign({}, state, {
|
||||
[action.id]: {
|
||||
checked: false
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
case ItemSelectionActionTypes.SWITCH: {
|
||||
return Object.assign({}, state, {
|
||||
[action.id]: {
|
||||
checked: !state.checked
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
case ItemSelectionActionTypes.RESET: {
|
||||
return {};
|
||||
}
|
||||
|
||||
default: {
|
||||
return state;
|
||||
}
|
||||
}
|
||||
}
|
101
src/app/shared/item-select/item-select.service.ts
Normal file
101
src/app/shared/item-select/item-select.service.ts
Normal file
@@ -0,0 +1,101 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { createSelector, MemoizedSelector, Store } from '@ngrx/store';
|
||||
import { ItemSelectionsState, ItemSelectionState } from './item-select.reducer';
|
||||
import {
|
||||
ItemSelectionDeselectAction,
|
||||
ItemSelectionInitialDeselectAction,
|
||||
ItemSelectionInitialSelectAction, ItemSelectionResetAction,
|
||||
ItemSelectionSelectAction, ItemSelectionSwitchAction
|
||||
} from './item-select.actions';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import { hasValue } from '../empty.util';
|
||||
|
||||
const selectionStateSelector = (state: ItemSelectionsState) => state.selectionItem;
|
||||
|
||||
/**
|
||||
* Service that takes care of selecting and deselecting items
|
||||
*/
|
||||
@Injectable()
|
||||
export class ItemSelectService {
|
||||
|
||||
constructor(private store: Store<ItemSelectionsState>) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Request the current selection of a given item
|
||||
* @param {string} id The UUID of the item
|
||||
* @returns {Observable<boolean>} Emits the current selection state of the given item, if it's unavailable, return false
|
||||
*/
|
||||
getSelected(id: string): Observable<boolean> {
|
||||
return this.store.select(selectionByIdSelector(id))
|
||||
.map((object: ItemSelectionState) => {
|
||||
if (object) {
|
||||
return object.checked;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatches an initial select action to the store for a given item
|
||||
* @param {string} id The UUID of the item to select
|
||||
*/
|
||||
public initialSelect(id: string): void {
|
||||
this.store.dispatch(new ItemSelectionInitialSelectAction(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatches an initial deselect action to the store for a given item
|
||||
* @param {string} id The UUID of the item to deselect
|
||||
*/
|
||||
public initialDeselect(id: string): void {
|
||||
this.store.dispatch(new ItemSelectionInitialDeselectAction(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatches a select action to the store for a given item
|
||||
* @param {string} id The UUID of the item to select
|
||||
*/
|
||||
public select(id: string): void {
|
||||
this.store.dispatch(new ItemSelectionSelectAction(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatches a deselect action to the store for a given item
|
||||
* @param {string} id The UUID of the item to deselect
|
||||
*/
|
||||
public deselect(id: string): void {
|
||||
this.store.dispatch(new ItemSelectionDeselectAction(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatches a switch action to the store for a given item
|
||||
* @param {string} id The UUID of the item to select
|
||||
*/
|
||||
public switch(id: string): void {
|
||||
this.store.dispatch(new ItemSelectionSwitchAction(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatches a reset action to the store for all items
|
||||
*/
|
||||
public reset(): void {
|
||||
this.store.dispatch(new ItemSelectionResetAction(null));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function selectionByIdSelector(id: string): MemoizedSelector<ItemSelectionsState, ItemSelectionState> {
|
||||
return keySelector<ItemSelectionState>(id);
|
||||
}
|
||||
|
||||
export function keySelector<T>(key: string): MemoizedSelector<ItemSelectionsState, T> {
|
||||
return createSelector(selectionStateSelector, (state: ItemSelectionState) => {
|
||||
if (hasValue(state)) {
|
||||
return state[key];
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
});
|
||||
}
|
Reference in New Issue
Block a user