1
0

55693: (incomplete) store interraction for selecting items

This commit is contained in:
Kristof De Langhe
2018-09-26 17:30:24 +02:00
parent 9440401ca3
commit 5040d230fb
7 changed files with 279 additions and 5 deletions

View File

@@ -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;

View File

@@ -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,

View 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 */

View 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>

View File

@@ -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);
}
}

View 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;
}
}
}

View 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;
}
});
}