mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-07 10:04:11 +00:00
55946: Refactored item-select to object-select to allow for easier implementation of collection-select
This commit is contained in:
@@ -14,7 +14,7 @@ import {
|
|||||||
} from './+search-page/search-filters/search-filter/search-filter.reducer';
|
} from './+search-page/search-filters/search-filter/search-filter.reducer';
|
||||||
import { notificationsReducer, NotificationsState } from './shared/notifications/notifications.reducers';
|
import { notificationsReducer, NotificationsState } from './shared/notifications/notifications.reducers';
|
||||||
import { truncatableReducer, TruncatablesState } from './shared/truncatable/truncatable.reducer';
|
import { truncatableReducer, TruncatablesState } from './shared/truncatable/truncatable.reducer';
|
||||||
import { itemSelectionReducer, ItemSelectionsState } from './shared/item-select/item-select.reducer';
|
import { objectSelectionReducer, ObjectSelectionsState } from './shared/object-select/object-select.reducer';
|
||||||
|
|
||||||
export interface AppState {
|
export interface AppState {
|
||||||
router: fromRouter.RouterReducerState;
|
router: fromRouter.RouterReducerState;
|
||||||
@@ -25,7 +25,7 @@ export interface AppState {
|
|||||||
searchSidebar: SearchSidebarState;
|
searchSidebar: SearchSidebarState;
|
||||||
searchFilter: SearchFiltersState;
|
searchFilter: SearchFiltersState;
|
||||||
truncatable: TruncatablesState,
|
truncatable: TruncatablesState,
|
||||||
itemSelection: ItemSelectionsState
|
objectSelection: ObjectSelectionsState
|
||||||
}
|
}
|
||||||
|
|
||||||
export const appReducers: ActionReducerMap<AppState> = {
|
export const appReducers: ActionReducerMap<AppState> = {
|
||||||
@@ -37,7 +37,7 @@ export const appReducers: ActionReducerMap<AppState> = {
|
|||||||
searchSidebar: sidebarReducer,
|
searchSidebar: sidebarReducer,
|
||||||
searchFilter: filterReducer,
|
searchFilter: filterReducer,
|
||||||
truncatable: truncatableReducer,
|
truncatable: truncatableReducer,
|
||||||
itemSelection: itemSelectionReducer
|
objectSelection: objectSelectionReducer
|
||||||
};
|
};
|
||||||
|
|
||||||
export const routerStateSelector = (state: AppState) => state.router;
|
export const routerStateSelector = (state: AppState) => state.router;
|
||||||
|
@@ -64,8 +64,8 @@ import { NotificationsService } from '../shared/notifications/notifications.serv
|
|||||||
import { UploaderService } from '../shared/uploader/uploader.service';
|
import { UploaderService } from '../shared/uploader/uploader.service';
|
||||||
import { BrowseItemsResponseParsingService } from './data/browse-items-response-parsing-service';
|
import { BrowseItemsResponseParsingService } from './data/browse-items-response-parsing-service';
|
||||||
import { DSpaceObjectDataService } from './data/dspace-object-data.service';
|
import { DSpaceObjectDataService } from './data/dspace-object-data.service';
|
||||||
import { ItemSelectService } from '../shared/item-select/item-select.service';
|
|
||||||
import { MappingCollectionsReponseParsingService } from './data/mapping-collections-reponse-parsing.service';
|
import { MappingCollectionsReponseParsingService } from './data/mapping-collections-reponse-parsing.service';
|
||||||
|
import { ObjectSelectService } from '../shared/object-select/object-select.service';
|
||||||
|
|
||||||
const IMPORTS = [
|
const IMPORTS = [
|
||||||
CommonModule,
|
CommonModule,
|
||||||
@@ -131,7 +131,7 @@ const PROVIDERS = [
|
|||||||
UploaderService,
|
UploaderService,
|
||||||
UUIDService,
|
UUIDService,
|
||||||
DSpaceObjectDataService,
|
DSpaceObjectDataService,
|
||||||
ItemSelectService,
|
ObjectSelectService,
|
||||||
// register AuthInterceptor as HttpInterceptor
|
// register AuthInterceptor as HttpInterceptor
|
||||||
{
|
{
|
||||||
provide: HTTP_INTERCEPTORS,
|
provide: HTTP_INTERCEPTORS,
|
||||||
|
@@ -1,75 +0,0 @@
|
|||||||
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 */
|
|
@@ -1,98 +0,0 @@
|
|||||||
import {
|
|
||||||
ItemSelectionDeselectAction, ItemSelectionInitialDeselectAction,
|
|
||||||
ItemSelectionInitialSelectAction, ItemSelectionResetAction,
|
|
||||||
ItemSelectionSelectAction, ItemSelectionSwitchAction
|
|
||||||
} from './item-select.actions';
|
|
||||||
import { itemSelectionReducer } from './item-select.reducer';
|
|
||||||
|
|
||||||
const itemId1 = 'id1';
|
|
||||||
const itemId2 = 'id2';
|
|
||||||
|
|
||||||
class NullAction extends ItemSelectionSelectAction {
|
|
||||||
type = null;
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
super(undefined);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
describe('itemSelectionReducer', () => {
|
|
||||||
|
|
||||||
it('should return the current state when no valid actions have been made', () => {
|
|
||||||
const state = {};
|
|
||||||
state[itemId1] = { checked: true };
|
|
||||||
const action = new NullAction();
|
|
||||||
const newState = itemSelectionReducer(state, action);
|
|
||||||
|
|
||||||
expect(newState).toEqual(state);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should start with an empty object', () => {
|
|
||||||
const state = {};
|
|
||||||
const action = new NullAction();
|
|
||||||
const newState = itemSelectionReducer(undefined, action);
|
|
||||||
|
|
||||||
expect(newState).toEqual(state);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should set checked to true in response to the INITIAL_SELECT action', () => {
|
|
||||||
const action = new ItemSelectionInitialSelectAction(itemId1);
|
|
||||||
const newState = itemSelectionReducer(undefined, action);
|
|
||||||
|
|
||||||
expect(newState[itemId1].checked).toBeTruthy();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should set checked to true in response to the INITIAL_DESELECT action', () => {
|
|
||||||
const action = new ItemSelectionInitialDeselectAction(itemId1);
|
|
||||||
const newState = itemSelectionReducer(undefined, action);
|
|
||||||
|
|
||||||
expect(newState[itemId1].checked).toBeFalsy();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should set checked to true in response to the SELECT action', () => {
|
|
||||||
const state = {};
|
|
||||||
state[itemId1] = { checked: false };
|
|
||||||
const action = new ItemSelectionSelectAction(itemId1);
|
|
||||||
const newState = itemSelectionReducer(state, action);
|
|
||||||
|
|
||||||
expect(newState[itemId1].checked).toBeTruthy();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should set checked to false in response to the DESELECT action', () => {
|
|
||||||
const state = {};
|
|
||||||
state[itemId1] = { checked: true };
|
|
||||||
const action = new ItemSelectionDeselectAction(itemId1);
|
|
||||||
const newState = itemSelectionReducer(state, action);
|
|
||||||
|
|
||||||
expect(newState[itemId1].checked).toBeFalsy();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should set checked from false to true in response to the SWITCH action', () => {
|
|
||||||
const state = {};
|
|
||||||
state[itemId1] = { checked: false };
|
|
||||||
const action = new ItemSelectionSwitchAction(itemId1);
|
|
||||||
const newState = itemSelectionReducer(state, action);
|
|
||||||
|
|
||||||
expect(newState[itemId1].checked).toBeTruthy();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should set checked from true to false in response to the SWITCH action', () => {
|
|
||||||
const state = {};
|
|
||||||
state[itemId1] = { checked: true };
|
|
||||||
const action = new ItemSelectionSwitchAction(itemId1);
|
|
||||||
const newState = itemSelectionReducer(state, action);
|
|
||||||
|
|
||||||
expect(newState[itemId1].checked).toBeFalsy();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should set reset the state in response to the RESET action', () => {
|
|
||||||
const state = {};
|
|
||||||
state[itemId1] = { checked: true };
|
|
||||||
state[itemId2] = { checked: false };
|
|
||||||
const action = new ItemSelectionResetAction(undefined);
|
|
||||||
const newState = itemSelectionReducer(state, action);
|
|
||||||
|
|
||||||
expect(newState).toEqual({});
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
@@ -1,96 +0,0 @@
|
|||||||
import { ItemSelectService } from './item-select.service';
|
|
||||||
import { Store } from '@ngrx/store';
|
|
||||||
import { Observable } from 'rxjs/Observable';
|
|
||||||
import { ItemSelectionsState } from './item-select.reducer';
|
|
||||||
import { AppState } from '../../app.reducer';
|
|
||||||
import {
|
|
||||||
ItemSelectionDeselectAction,
|
|
||||||
ItemSelectionInitialDeselectAction,
|
|
||||||
ItemSelectionInitialSelectAction, ItemSelectionResetAction,
|
|
||||||
ItemSelectionSelectAction, ItemSelectionSwitchAction
|
|
||||||
} from './item-select.actions';
|
|
||||||
|
|
||||||
describe('ItemSelectService', () => {
|
|
||||||
let service: ItemSelectService;
|
|
||||||
|
|
||||||
const mockItemId = 'id1';
|
|
||||||
|
|
||||||
const store: Store<ItemSelectionsState> = jasmine.createSpyObj('store', {
|
|
||||||
/* tslint:disable:no-empty */
|
|
||||||
dispatch: {},
|
|
||||||
/* tslint:enable:no-empty */
|
|
||||||
select: Observable.of(true)
|
|
||||||
});
|
|
||||||
|
|
||||||
const appStore: Store<AppState> = jasmine.createSpyObj('appStore', {
|
|
||||||
/* tslint:disable:no-empty */
|
|
||||||
dispatch: {},
|
|
||||||
/* tslint:enable:no-empty */
|
|
||||||
select: Observable.of(true)
|
|
||||||
});
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
service = new ItemSelectService(store, appStore);
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('when the initialSelect method is triggered', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
service.initialSelect(mockItemId);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('ItemSelectionInitialSelectAction should be dispatched to the store', () => {
|
|
||||||
expect(store.dispatch).toHaveBeenCalledWith(new ItemSelectionInitialSelectAction(mockItemId));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('when the initialDeselect method is triggered', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
service.initialDeselect(mockItemId);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('ItemSelectionInitialDeselectAction should be dispatched to the store', () => {
|
|
||||||
expect(store.dispatch).toHaveBeenCalledWith(new ItemSelectionInitialDeselectAction(mockItemId));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('when the select method is triggered', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
service.select(mockItemId);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('ItemSelectionSelectAction should be dispatched to the store', () => {
|
|
||||||
expect(store.dispatch).toHaveBeenCalledWith(new ItemSelectionSelectAction(mockItemId));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('when the deselect method is triggered', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
service.deselect(mockItemId);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('ItemSelectionDeselectAction should be dispatched to the store', () => {
|
|
||||||
expect(store.dispatch).toHaveBeenCalledWith(new ItemSelectionDeselectAction(mockItemId));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('when the switch method is triggered', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
service.switch(mockItemId);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('ItemSelectionSwitchAction should be dispatched to the store', () => {
|
|
||||||
expect(store.dispatch).toHaveBeenCalledWith(new ItemSelectionSwitchAction(mockItemId));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('when the reset method is triggered', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
service.reset();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('ItemSelectionInitialSelectAction should be dispatched to the store', () => {
|
|
||||||
expect(store.dispatch).toHaveBeenCalledWith(new ItemSelectionResetAction(null));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
@@ -1,119 +0,0 @@
|
|||||||
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';
|
|
||||||
import { map } from 'rxjs/operators';
|
|
||||||
import { AppState } from '../../app.reducer';
|
|
||||||
|
|
||||||
const selectionStateSelector = (state: ItemSelectionsState) => state.itemSelection;
|
|
||||||
const itemSelectionsStateSelector = (state: AppState) => state.itemSelection;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Service that takes care of selecting and deselecting items
|
|
||||||
*/
|
|
||||||
@Injectable()
|
|
||||||
export class ItemSelectService {
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
private store: Store<ItemSelectionsState>,
|
|
||||||
private appStore: Store<AppState>
|
|
||||||
) {
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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)).pipe(
|
|
||||||
map((object: ItemSelectionState) => {
|
|
||||||
if (object) {
|
|
||||||
return object.checked;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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
|
|
||||||
*/
|
|
||||||
getAllSelected(): Observable<string[]> {
|
|
||||||
return this.appStore.select(itemSelectionsStateSelector).pipe(
|
|
||||||
map((state: ItemSelectionsState) => Object.keys(state).filter((key) => state[key].checked))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
@@ -1,30 +1,25 @@
|
|||||||
import { ItemSelectComponent } from './item-select.component';
|
import { ItemSelectComponent } from './item-select.component';
|
||||||
import { async, ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing';
|
import { async, ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing';
|
||||||
import { SharedModule } from '../shared.module';
|
|
||||||
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
|
||||||
import { ItemSelectService } from './item-select.service';
|
|
||||||
import { ItemSelectServiceStub } from '../testing/item-select-service-stub';
|
|
||||||
import { Observable } from 'rxjs/Observable';
|
|
||||||
import { RemoteData } from '../../core/data/remote-data';
|
|
||||||
import { PaginatedList } from '../../core/data/paginated-list';
|
|
||||||
import { PageInfo } from '../../core/shared/page-info.model';
|
|
||||||
import { Item } from '../../core/shared/item.model';
|
|
||||||
import { By } from '@angular/platform-browser';
|
|
||||||
import { ActivatedRoute, Route, Router } from '@angular/router';
|
|
||||||
import { ActivatedRouteStub } from '../testing/active-router-stub';
|
|
||||||
import { RouterStub } from '../testing/router-stub';
|
|
||||||
import { HostWindowService } from '../host-window.service';
|
|
||||||
import { HostWindowServiceStub } from '../testing/host-window-service-stub';
|
|
||||||
import { PaginationComponentOptions } from '../pagination/pagination-component-options.model';
|
|
||||||
import { LocationStrategy } from '@angular/common';
|
|
||||||
import { MockLocationStrategy } from '@angular/common/testing';
|
|
||||||
import { RouterTestingModule } from '@angular/router/testing';
|
import { RouterTestingModule } from '@angular/router/testing';
|
||||||
|
import { Item } from '../../../core/shared/item.model';
|
||||||
|
import { Observable } from 'rxjs/Observable';
|
||||||
|
import { RemoteData } from '../../../core/data/remote-data';
|
||||||
|
import { PaginatedList } from '../../../core/data/paginated-list';
|
||||||
|
import { PageInfo } from '../../../core/shared/page-info.model';
|
||||||
|
import { PaginationComponentOptions } from '../../pagination/pagination-component-options.model';
|
||||||
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
import { SharedModule } from '../../shared.module';
|
||||||
|
import { ObjectSelectServiceStub } from '../../testing/object-select-service-stub';
|
||||||
|
import { ObjectSelectService } from '../object-select.service';
|
||||||
|
import { HostWindowService } from '../../host-window.service';
|
||||||
|
import { HostWindowServiceStub } from '../../testing/host-window-service-stub';
|
||||||
|
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
||||||
|
import { By } from '@angular/platform-browser';
|
||||||
|
|
||||||
describe('ItemSelectComponent', () => {
|
describe('ItemSelectComponent', () => {
|
||||||
let comp: ItemSelectComponent;
|
let comp: ItemSelectComponent;
|
||||||
let fixture: ComponentFixture<ItemSelectComponent>;
|
let fixture: ComponentFixture<ItemSelectComponent>;
|
||||||
let itemSelectService: ItemSelectService;
|
let itemSelectService: ObjectSelectService;
|
||||||
|
|
||||||
const mockItemList = [
|
const mockItemList = [
|
||||||
Object.assign(new Item(), {
|
Object.assign(new Item(), {
|
||||||
@@ -70,7 +65,7 @@ describe('ItemSelectComponent', () => {
|
|||||||
imports: [TranslateModule.forRoot(), SharedModule, RouterTestingModule.withRoutes([])],
|
imports: [TranslateModule.forRoot(), SharedModule, RouterTestingModule.withRoutes([])],
|
||||||
declarations: [],
|
declarations: [],
|
||||||
providers: [
|
providers: [
|
||||||
{ provide: ItemSelectService, useValue: new ItemSelectServiceStub() },
|
{ provide: ObjectSelectService, useValue: new ObjectSelectServiceStub() },
|
||||||
{ provide: HostWindowService, useValue: new HostWindowServiceStub(0) }
|
{ provide: HostWindowService, useValue: new HostWindowServiceStub(0) }
|
||||||
],
|
],
|
||||||
schemas: [NO_ERRORS_SCHEMA]
|
schemas: [NO_ERRORS_SCHEMA]
|
@@ -1,12 +1,11 @@
|
|||||||
import { Component, EventEmitter, Input, OnDestroy, OnInit, Output } from '@angular/core';
|
import { Component, EventEmitter, Input, OnDestroy, OnInit, Output } from '@angular/core';
|
||||||
import { ItemDataService } from '../../core/data/item-data.service';
|
|
||||||
import { PaginatedList } from '../../core/data/paginated-list';
|
|
||||||
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';
|
|
||||||
import { take } from 'rxjs/operators';
|
import { take } from 'rxjs/operators';
|
||||||
|
import { Observable } from 'rxjs/Observable';
|
||||||
|
import { Item } from '../../../core/shared/item.model';
|
||||||
|
import { RemoteData } from '../../../core/data/remote-data';
|
||||||
|
import { PaginatedList } from '../../../core/data/paginated-list';
|
||||||
|
import { PaginationComponentOptions } from '../../pagination/pagination-component-options.model';
|
||||||
|
import { ObjectSelectService } from '../object-select.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-item-select',
|
selector: 'ds-item-select',
|
||||||
@@ -50,11 +49,11 @@ export class ItemSelectComponent implements OnInit {
|
|||||||
*/
|
*/
|
||||||
selectedIds$: Observable<string[]>;
|
selectedIds$: Observable<string[]>;
|
||||||
|
|
||||||
constructor(private itemSelectService: ItemSelectService) {
|
constructor(private objectelectService: ObjectSelectService) {
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.selectedIds$ = this.itemSelectService.getAllSelected();
|
this.selectedIds$ = this.objectelectService.getAllSelected();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -62,7 +61,7 @@ export class ItemSelectComponent implements OnInit {
|
|||||||
* @param {string} id
|
* @param {string} id
|
||||||
*/
|
*/
|
||||||
switch(id: string) {
|
switch(id: string) {
|
||||||
this.itemSelectService.switch(id);
|
this.objectelectService.switch(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -71,7 +70,7 @@ export class ItemSelectComponent implements OnInit {
|
|||||||
* @returns {Observable<boolean>}
|
* @returns {Observable<boolean>}
|
||||||
*/
|
*/
|
||||||
getSelected(id: string): Observable<boolean> {
|
getSelected(id: string): Observable<boolean> {
|
||||||
return this.itemSelectService.getSelected(id);
|
return this.objectelectService.getSelected(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -83,7 +82,7 @@ export class ItemSelectComponent implements OnInit {
|
|||||||
take(1)
|
take(1)
|
||||||
).subscribe((ids: string[]) => {
|
).subscribe((ids: string[]) => {
|
||||||
this.confirm.emit(ids);
|
this.confirm.emit(ids);
|
||||||
this.itemSelectService.reset();
|
this.objectelectService.reset();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
75
src/app/shared/object-select/object-select.actions.ts
Normal file
75
src/app/shared/object-select/object-select.actions.ts
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
import { type } from '../ngrx/type';
|
||||||
|
import { Action } from '@ngrx/store';
|
||||||
|
|
||||||
|
export const ObjectSelectionActionTypes = {
|
||||||
|
INITIAL_DESELECT: type('dspace/object-select/INITIAL_DESELECT'),
|
||||||
|
INITIAL_SELECT: type('dspace/object-select/INITIAL_SELECT'),
|
||||||
|
SELECT: type('dspace/object-select/SELECT'),
|
||||||
|
DESELECT: type('dspace/object-select/DESELECT'),
|
||||||
|
SWITCH: type('dspace/object-select/SWITCH'),
|
||||||
|
RESET: type('dspace/object-select/RESET')
|
||||||
|
};
|
||||||
|
|
||||||
|
export class ObjectSelectionAction implements Action {
|
||||||
|
/**
|
||||||
|
* UUID of the object a select action can be performed on
|
||||||
|
*/
|
||||||
|
id: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Type of action that will be performed
|
||||||
|
*/
|
||||||
|
type;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize with the object's UUID
|
||||||
|
* @param {string} id of the object
|
||||||
|
*/
|
||||||
|
constructor(id: string) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* tslint:disable:max-classes-per-file */
|
||||||
|
/**
|
||||||
|
* Used to set the initial state to deselected
|
||||||
|
*/
|
||||||
|
export class ObjectSelectionInitialDeselectAction extends ObjectSelectionAction {
|
||||||
|
type = ObjectSelectionActionTypes.INITIAL_DESELECT;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to set the initial state to selected
|
||||||
|
*/
|
||||||
|
export class ObjectSelectionInitialSelectAction extends ObjectSelectionAction {
|
||||||
|
type = ObjectSelectionActionTypes.INITIAL_SELECT;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to select an object
|
||||||
|
*/
|
||||||
|
export class ObjectSelectionSelectAction extends ObjectSelectionAction {
|
||||||
|
type = ObjectSelectionActionTypes.SELECT;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to deselect an object
|
||||||
|
*/
|
||||||
|
export class ObjectSelectionDeselectAction extends ObjectSelectionAction {
|
||||||
|
type = ObjectSelectionActionTypes.DESELECT;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to switch an object between selected and deselected
|
||||||
|
*/
|
||||||
|
export class ObjectSelectionSwitchAction extends ObjectSelectionAction {
|
||||||
|
type = ObjectSelectionActionTypes.SWITCH;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to reset all objects selected to be deselected
|
||||||
|
*/
|
||||||
|
export class ObjectSelectionResetAction extends ObjectSelectionAction {
|
||||||
|
type = ObjectSelectionActionTypes.RESET;
|
||||||
|
}
|
||||||
|
/* tslint:enable:max-classes-per-file */
|
98
src/app/shared/object-select/object-select.reducer.spec.ts
Normal file
98
src/app/shared/object-select/object-select.reducer.spec.ts
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
import {
|
||||||
|
ObjectSelectionDeselectAction, ObjectSelectionInitialDeselectAction,
|
||||||
|
ObjectSelectionInitialSelectAction, ObjectSelectionResetAction,
|
||||||
|
ObjectSelectionSelectAction, ObjectSelectionSwitchAction
|
||||||
|
} from './object-select.actions';
|
||||||
|
import { objectSelectionReducer } from './object-select.reducer';
|
||||||
|
|
||||||
|
const objectId1 = 'id1';
|
||||||
|
const objectId2 = 'id2';
|
||||||
|
|
||||||
|
class NullAction extends ObjectSelectionSelectAction {
|
||||||
|
type = null;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super(undefined);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('objectSelectionReducer', () => {
|
||||||
|
|
||||||
|
it('should return the current state when no valid actions have been made', () => {
|
||||||
|
const state = {};
|
||||||
|
state[objectId1] = { checked: true };
|
||||||
|
const action = new NullAction();
|
||||||
|
const newState = objectSelectionReducer(state, action);
|
||||||
|
|
||||||
|
expect(newState).toEqual(state);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should start with an empty object', () => {
|
||||||
|
const state = {};
|
||||||
|
const action = new NullAction();
|
||||||
|
const newState = objectSelectionReducer(undefined, action);
|
||||||
|
|
||||||
|
expect(newState).toEqual(state);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should set checked to true in response to the INITIAL_SELECT action', () => {
|
||||||
|
const action = new ObjectSelectionInitialSelectAction(objectId1);
|
||||||
|
const newState = objectSelectionReducer(undefined, action);
|
||||||
|
|
||||||
|
expect(newState[objectId1].checked).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should set checked to true in response to the INITIAL_DESELECT action', () => {
|
||||||
|
const action = new ObjectSelectionInitialDeselectAction(objectId1);
|
||||||
|
const newState = objectSelectionReducer(undefined, action);
|
||||||
|
|
||||||
|
expect(newState[objectId1].checked).toBeFalsy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should set checked to true in response to the SELECT action', () => {
|
||||||
|
const state = {};
|
||||||
|
state[objectId1] = { checked: false };
|
||||||
|
const action = new ObjectSelectionSelectAction(objectId1);
|
||||||
|
const newState = objectSelectionReducer(state, action);
|
||||||
|
|
||||||
|
expect(newState[objectId1].checked).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should set checked to false in response to the DESELECT action', () => {
|
||||||
|
const state = {};
|
||||||
|
state[objectId1] = { checked: true };
|
||||||
|
const action = new ObjectSelectionDeselectAction(objectId1);
|
||||||
|
const newState = objectSelectionReducer(state, action);
|
||||||
|
|
||||||
|
expect(newState[objectId1].checked).toBeFalsy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should set checked from false to true in response to the SWITCH action', () => {
|
||||||
|
const state = {};
|
||||||
|
state[objectId1] = { checked: false };
|
||||||
|
const action = new ObjectSelectionSwitchAction(objectId1);
|
||||||
|
const newState = objectSelectionReducer(state, action);
|
||||||
|
|
||||||
|
expect(newState[objectId1].checked).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should set checked from true to false in response to the SWITCH action', () => {
|
||||||
|
const state = {};
|
||||||
|
state[objectId1] = { checked: true };
|
||||||
|
const action = new ObjectSelectionSwitchAction(objectId1);
|
||||||
|
const newState = objectSelectionReducer(state, action);
|
||||||
|
|
||||||
|
expect(newState[objectId1].checked).toBeFalsy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should set reset the state in response to the RESET action', () => {
|
||||||
|
const state = {};
|
||||||
|
state[objectId1] = { checked: true };
|
||||||
|
state[objectId2] = { checked: false };
|
||||||
|
const action = new ObjectSelectionResetAction(undefined);
|
||||||
|
const newState = objectSelectionReducer(state, action);
|
||||||
|
|
||||||
|
expect(newState).toEqual({});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
@@ -1,21 +1,21 @@
|
|||||||
import { isEmpty } from '../empty.util';
|
import { isEmpty } from '../empty.util';
|
||||||
import { ItemSelectionAction, ItemSelectionActionTypes } from './item-select.actions';
|
import { ObjectSelectionAction, ObjectSelectionActionTypes } from './object-select.actions';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface that represents the state for a single filters
|
* Interface that represents the state for a single filters
|
||||||
*/
|
*/
|
||||||
export interface ItemSelectionState {
|
export interface ObjectSelectionState {
|
||||||
checked: boolean;
|
checked: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface that represents the state for all available filters
|
* Interface that represents the state for all available filters
|
||||||
*/
|
*/
|
||||||
export interface ItemSelectionsState {
|
export interface ObjectSelectionsState {
|
||||||
[id: string]: ItemSelectionState
|
[id: string]: ObjectSelectionState
|
||||||
}
|
}
|
||||||
|
|
||||||
const initialState: ItemSelectionsState = Object.create(null);
|
const initialState: ObjectSelectionsState = Object.create(null);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Performs a search filter action on the current state
|
* Performs a search filter action on the current state
|
||||||
@@ -23,11 +23,11 @@ const initialState: ItemSelectionsState = Object.create(null);
|
|||||||
* @param {SearchFilterAction} action The action that should be performed
|
* @param {SearchFilterAction} action The action that should be performed
|
||||||
* @returns {SearchFiltersState} The state after the action is performed
|
* @returns {SearchFiltersState} The state after the action is performed
|
||||||
*/
|
*/
|
||||||
export function itemSelectionReducer(state = initialState, action: ItemSelectionAction): ItemSelectionsState {
|
export function objectSelectionReducer(state = initialState, action: ObjectSelectionAction): ObjectSelectionsState {
|
||||||
|
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
|
|
||||||
case ItemSelectionActionTypes.INITIAL_SELECT: {
|
case ObjectSelectionActionTypes.INITIAL_SELECT: {
|
||||||
if (isEmpty(state) || isEmpty(state[action.id])) {
|
if (isEmpty(state) || isEmpty(state[action.id])) {
|
||||||
return Object.assign({}, state, {
|
return Object.assign({}, state, {
|
||||||
[action.id]: {
|
[action.id]: {
|
||||||
@@ -38,7 +38,7 @@ export function itemSelectionReducer(state = initialState, action: ItemSelection
|
|||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
case ItemSelectionActionTypes.INITIAL_DESELECT: {
|
case ObjectSelectionActionTypes.INITIAL_DESELECT: {
|
||||||
if (isEmpty(state) || isEmpty(state[action.id])) {
|
if (isEmpty(state) || isEmpty(state[action.id])) {
|
||||||
return Object.assign({}, state, {
|
return Object.assign({}, state, {
|
||||||
[action.id]: {
|
[action.id]: {
|
||||||
@@ -49,7 +49,7 @@ export function itemSelectionReducer(state = initialState, action: ItemSelection
|
|||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
case ItemSelectionActionTypes.SELECT: {
|
case ObjectSelectionActionTypes.SELECT: {
|
||||||
return Object.assign({}, state, {
|
return Object.assign({}, state, {
|
||||||
[action.id]: {
|
[action.id]: {
|
||||||
checked: true
|
checked: true
|
||||||
@@ -57,7 +57,7 @@ export function itemSelectionReducer(state = initialState, action: ItemSelection
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
case ItemSelectionActionTypes.DESELECT: {
|
case ObjectSelectionActionTypes.DESELECT: {
|
||||||
return Object.assign({}, state, {
|
return Object.assign({}, state, {
|
||||||
[action.id]: {
|
[action.id]: {
|
||||||
checked: false
|
checked: false
|
||||||
@@ -65,7 +65,7 @@ export function itemSelectionReducer(state = initialState, action: ItemSelection
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
case ItemSelectionActionTypes.SWITCH: {
|
case ObjectSelectionActionTypes.SWITCH: {
|
||||||
return Object.assign({}, state, {
|
return Object.assign({}, state, {
|
||||||
[action.id]: {
|
[action.id]: {
|
||||||
checked: (isEmpty(state) || isEmpty(state[action.id])) ? true : !state[action.id].checked
|
checked: (isEmpty(state) || isEmpty(state[action.id])) ? true : !state[action.id].checked
|
||||||
@@ -73,7 +73,7 @@ export function itemSelectionReducer(state = initialState, action: ItemSelection
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
case ItemSelectionActionTypes.RESET: {
|
case ObjectSelectionActionTypes.RESET: {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
96
src/app/shared/object-select/object-select.service.spec.ts
Normal file
96
src/app/shared/object-select/object-select.service.spec.ts
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
import { ObjectSelectService } from './object-select.service';
|
||||||
|
import { Store } from '@ngrx/store';
|
||||||
|
import { Observable } from 'rxjs/Observable';
|
||||||
|
import { ObjectSelectionsState } from './object-select.reducer';
|
||||||
|
import { AppState } from '../../app.reducer';
|
||||||
|
import {
|
||||||
|
ObjectSelectionDeselectAction,
|
||||||
|
ObjectSelectionInitialDeselectAction,
|
||||||
|
ObjectSelectionInitialSelectAction, ObjectSelectionResetAction,
|
||||||
|
ObjectSelectionSelectAction, ObjectSelectionSwitchAction
|
||||||
|
} from './object-select.actions';
|
||||||
|
|
||||||
|
describe('ObjectSelectService', () => {
|
||||||
|
let service: ObjectSelectService;
|
||||||
|
|
||||||
|
const mockObjectId = 'id1';
|
||||||
|
|
||||||
|
const store: Store<ObjectSelectionsState> = jasmine.createSpyObj('store', {
|
||||||
|
/* tslint:disable:no-empty */
|
||||||
|
dispatch: {},
|
||||||
|
/* tslint:enable:no-empty */
|
||||||
|
select: Observable.of(true)
|
||||||
|
});
|
||||||
|
|
||||||
|
const appStore: Store<AppState> = jasmine.createSpyObj('appStore', {
|
||||||
|
/* tslint:disable:no-empty */
|
||||||
|
dispatch: {},
|
||||||
|
/* tslint:enable:no-empty */
|
||||||
|
select: Observable.of(true)
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
service = new ObjectSelectService(store, appStore);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when the initialSelect method is triggered', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
service.initialSelect(mockObjectId);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('ObjectSelectionInitialSelectAction should be dispatched to the store', () => {
|
||||||
|
expect(store.dispatch).toHaveBeenCalledWith(new ObjectSelectionInitialSelectAction(mockObjectId));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when the initialDeselect method is triggered', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
service.initialDeselect(mockObjectId);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('ObjectSelectionInitialDeselectAction should be dispatched to the store', () => {
|
||||||
|
expect(store.dispatch).toHaveBeenCalledWith(new ObjectSelectionInitialDeselectAction(mockObjectId));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when the select method is triggered', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
service.select(mockObjectId);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('ObjectSelectionSelectAction should be dispatched to the store', () => {
|
||||||
|
expect(store.dispatch).toHaveBeenCalledWith(new ObjectSelectionSelectAction(mockObjectId));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when the deselect method is triggered', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
service.deselect(mockObjectId);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('ObjectSelectionDeselectAction should be dispatched to the store', () => {
|
||||||
|
expect(store.dispatch).toHaveBeenCalledWith(new ObjectSelectionDeselectAction(mockObjectId));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when the switch method is triggered', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
service.switch(mockObjectId);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('ObjectSelectionSwitchAction should be dispatched to the store', () => {
|
||||||
|
expect(store.dispatch).toHaveBeenCalledWith(new ObjectSelectionSwitchAction(mockObjectId));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when the reset method is triggered', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
service.reset();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('ObjectSelectionInitialSelectAction should be dispatched to the store', () => {
|
||||||
|
expect(store.dispatch).toHaveBeenCalledWith(new ObjectSelectionResetAction(null));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
119
src/app/shared/object-select/object-select.service.ts
Normal file
119
src/app/shared/object-select/object-select.service.ts
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { createSelector, MemoizedSelector, Store } from '@ngrx/store';
|
||||||
|
import { ObjectSelectionsState, ObjectSelectionState } from './object-select.reducer';
|
||||||
|
import {
|
||||||
|
ObjectSelectionDeselectAction,
|
||||||
|
ObjectSelectionInitialDeselectAction,
|
||||||
|
ObjectSelectionInitialSelectAction, ObjectSelectionResetAction,
|
||||||
|
ObjectSelectionSelectAction, ObjectSelectionSwitchAction
|
||||||
|
} from './object-select.actions';
|
||||||
|
import { Observable } from 'rxjs/Observable';
|
||||||
|
import { hasValue } from '../empty.util';
|
||||||
|
import { map } from 'rxjs/operators';
|
||||||
|
import { AppState } from '../../app.reducer';
|
||||||
|
|
||||||
|
const selectionStateSelector = (state: ObjectSelectionsState) => state.objectSelection;
|
||||||
|
const objectSelectionsStateSelector = (state: AppState) => state.objectSelection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Service that takes care of selecting and deselecting objects
|
||||||
|
*/
|
||||||
|
@Injectable()
|
||||||
|
export class ObjectSelectService {
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private store: Store<ObjectSelectionsState>,
|
||||||
|
private appStore: Store<AppState>
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request the current selection of a given object
|
||||||
|
* @param {string} id The UUID of the object
|
||||||
|
* @returns {Observable<boolean>} Emits the current selection state of the given object, if it's unavailable, return false
|
||||||
|
*/
|
||||||
|
getSelected(id: string): Observable<boolean> {
|
||||||
|
return this.store.select(selectionByIdSelector(id)).pipe(
|
||||||
|
map((object: ObjectSelectionState) => {
|
||||||
|
if (object) {
|
||||||
|
return object.checked;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request the current selection of a given object
|
||||||
|
* @param {string} id The UUID of the object
|
||||||
|
* @returns {Observable<boolean>} Emits the current selection state of the given object, if it's unavailable, return false
|
||||||
|
*/
|
||||||
|
getAllSelected(): Observable<string[]> {
|
||||||
|
return this.appStore.select(objectSelectionsStateSelector).pipe(
|
||||||
|
map((state: ObjectSelectionsState) => Object.keys(state).filter((key) => state[key].checked))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dispatches an initial select action to the store for a given object
|
||||||
|
* @param {string} id The UUID of the object to select
|
||||||
|
*/
|
||||||
|
public initialSelect(id: string): void {
|
||||||
|
this.store.dispatch(new ObjectSelectionInitialSelectAction(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dispatches an initial deselect action to the store for a given object
|
||||||
|
* @param {string} id The UUID of the object to deselect
|
||||||
|
*/
|
||||||
|
public initialDeselect(id: string): void {
|
||||||
|
this.store.dispatch(new ObjectSelectionInitialDeselectAction(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dispatches a select action to the store for a given object
|
||||||
|
* @param {string} id The UUID of the object to select
|
||||||
|
*/
|
||||||
|
public select(id: string): void {
|
||||||
|
this.store.dispatch(new ObjectSelectionSelectAction(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dispatches a deselect action to the store for a given object
|
||||||
|
* @param {string} id The UUID of the object to deselect
|
||||||
|
*/
|
||||||
|
public deselect(id: string): void {
|
||||||
|
this.store.dispatch(new ObjectSelectionDeselectAction(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dispatches a switch action to the store for a given object
|
||||||
|
* @param {string} id The UUID of the object to select
|
||||||
|
*/
|
||||||
|
public switch(id: string): void {
|
||||||
|
this.store.dispatch(new ObjectSelectionSwitchAction(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dispatches a reset action to the store for all objects
|
||||||
|
*/
|
||||||
|
public reset(): void {
|
||||||
|
this.store.dispatch(new ObjectSelectionResetAction(null));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function selectionByIdSelector(id: string): MemoizedSelector<ObjectSelectionsState, ObjectSelectionState> {
|
||||||
|
return keySelector<ObjectSelectionState>(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function keySelector<T>(key: string): MemoizedSelector<ObjectSelectionsState, T> {
|
||||||
|
return createSelector(selectionStateSelector, (state: ObjectSelectionState) => {
|
||||||
|
if (hasValue(state)) {
|
||||||
|
return state[key];
|
||||||
|
} else {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
@@ -83,7 +83,7 @@ import { InputSuggestionsComponent } from './input-suggestions/input-suggestions
|
|||||||
import { CapitalizePipe } from './utils/capitalize.pipe';
|
import { CapitalizePipe } from './utils/capitalize.pipe';
|
||||||
import { MomentModule } from 'angular2-moment';
|
import { MomentModule } from 'angular2-moment';
|
||||||
import { ObjectKeysPipe } from './utils/object-keys-pipe';
|
import { ObjectKeysPipe } from './utils/object-keys-pipe';
|
||||||
import { ItemSelectComponent } from './item-select/item-select.component';
|
import { ItemSelectComponent } from './object-select/item-select/item-select.component';
|
||||||
|
|
||||||
const MODULES = [
|
const MODULES = [
|
||||||
// Do NOT include UniversalModule, HttpModule, or JsonpModule here
|
// Do NOT include UniversalModule, HttpModule, or JsonpModule here
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
import { Observable } from 'rxjs/Observable';
|
import { Observable } from 'rxjs/Observable';
|
||||||
|
|
||||||
export class ItemSelectServiceStub {
|
export class ObjectSelectServiceStub {
|
||||||
|
|
||||||
ids: string[] = [];
|
ids: string[] = [];
|
||||||
|
|
Reference in New Issue
Block a user