When I click on the go to page It keeps going to page 1. I based myself on an existing feature. For me Angular 4 is quite new. I started with Angular 1 a while back.
The weird thing is that the console.log in the page component is fired more then once. First time with the right page number. But then aigain with number 1. I have been looking all day at it. But it's totally wierd.
In my page I have:
<div class="portlet light">
<div class="portlet-title">
<div class="caption">
<div class="caption-subject bold uppercase" translate>PLAYER_INVENTORY</div>
</div>
</div>
<div class="portlet-body">
<div class="table-responsive">
<storever-datatable [striped]="true"
[hover]="true"
[data]="inventory$ | async"
[count]="count$ | async"
[currentPage]="page$ | async"
[itemsPerPage]="pageSize$ | async"
[orderBy]="orderBy$ | async"
(pageChange)="pageChanged($event)"
(pageSizeChange)="pageSizeChanged($event)"
(orderByChange)="orderByChanged($event)">
<storever-datatable-column [name]="'AUDIO_PLANNINGS_NAME' | translate:lang"
[prop]="'media.name'"></storever-datatable-column>
<storever-datatable-column [name]="'PLAYER_PLAY_LOGS_FILENAME' | translate:lang"
[prop]="'media.fileName'"></storever-datatable-column>
<storever-datatable-column [name]="'USERS_LIST_COLUMN_STATUS' | translate:lang"
[prop]="'status'"></storever-datatable-column>
<storever-datatable-column [name]="'INVENTORY_LAST_MODIFICATION' | translate:lang"
[prop]="'lastModified'">
<ng-template let-value="value" storeverDatatableColumnCell>
{{ value | moment:lang:dateTimeWithSecondsAndTimezoneFormat:(inventory$ | async)?.timeZone }}
</ng-template>
</storever-datatable-column>
<!-- PREVIEW-->
<storever-datatable-column *ngIf="showVideoPreview"
[name]="' '"
[prop]="'media'">
<ng-template let-value="value" storeverDatatableColumnCell>
<i class="fa fa-play fa-lg" aria-hidden="true" (click)="showPreviewDialog(value)"></i>
</ng-template>
</storever-datatable-column>
In component.ts
import {Component, OnDestroy, OnInit, ViewChild} from '@angular/core';
import {ActivatedRoute, convertToParamMap} from '@angular/router';
import {Store} from '@ngrx/store';
import {Translation, TranslationService} from 'angular-l10n';
import * as _ from 'lodash';
import * as moment from 'moment';
import {Observable} from 'rxjs/Observable';
import {Subscription} from 'rxjs/Subscription';
import {DateTimeFormatService, DEFAULT_PAGE_SIZE, replace} from '../../../shared';
import * as fromRoot from '../../../shared/reducers';
import {
ChangeInventoryPageSizeAction,
ClearInventoryAction,
LoadInventoryAction,
OrderInventoryByAction,
PageInventoryToAction,
SearchInventoryAction,
ToggleSearchInventoryAction
} from '../../actions/inventory';
import {VideoPlayerComponent} from '../../components/video-player/video-player.component';
import {Inventory} from '../../models/inventory';
import {InventoryFilterForm} from '../../models/inventory-filter-form';
import {VideoMedia} from '../../models/video-media';
import * as fromInventory from '../../reducers';
@Component({ selector: 'storever-inventory', templateUrl: './inventory.component.html', styleUrls: ['./inventory.component.scss'] })
export class InventoryComponent extends Translation implements OnInit, OnDestroy {
@ViewChild('videoPlayer') videoPlayer: VideoPlayerComponent;
showFilter$: Observable<boolean>;
filter$: Observable<InventoryFilterForm>;
page$: Observable<number>;
pageSize$: Observable<number>;
orderBy$: Observable<string>;
inventory$: Observable<Inventory[]>;
count$: Observable<number>;
showVideoPreview = false;
showAudioPreview = false;
private sub: Subscription;
private dataSub: Subscription;
get dateTimeWithSecondsAndTimezoneFormat(): string { return DateTimeFormatService.display.dateTimeWithSecondsAndTimezone; }
constructor(private store: Store<fromInventory.AppState>, private activatedRoute: ActivatedRoute, translation: TranslationService) {
super(translation);
const query$ = this.store.select(fromRoot.selectors.getRouterState).filter(state => !_.isEmpty(state)).map(state => state.queryParams);
this.inventory$ = this.store.select(fromInventory.selectors.getInventory);
this.showFilter$ = this.store.select(fromInventory.selectors.getInventoryShowFilter);
this.filter$ = query$.withLatestFrom(this.inventory$).map(([query, inventory]) => Object.assign({}, { id: _.get<number>(inventory, [0, 'id']) }, name, query));
this.page$ = query$.map(convertToParamMap).map(query => parseInt(query.get('$page') || '1', 10));
this.pageSize$ = query$.map(convertToParamMap).map(query => parseInt(query.get('$length'), 10) || DEFAULT_PAGE_SIZE);
this.orderBy$ = query$.map(convertToParamMap).map(query => query.get('$orderBy'));
this.count$ = this.store.select(fromInventory.selectors.getInventoryCount);
}
ngOnInit() {
this.store.dispatch(new ClearInventoryAction());
this.sub = this.activatedRoute.queryParams.distinctUntilChanged((x, y) => _.isEqual(x, y)).subscribe(query => {
if (_.isEmpty(query)) {
this.store.dispatch(replace([], { $page: 1, $length: DEFAULT_PAGE_SIZE, day: moment().format(DateTimeFormatService.input.date) }));
} else {
this.store.dispatch(new LoadInventoryAction());
}
});
this.showVideoPreview = true;
/*
this.dataSub = this.activatedRoute.data.distinctUntilChanged((x, y) => _.isEqual(x, y)).map(convertToParamMap).subscribe(data => {
const type = data.get('type');
this.showVideoPreview = type === 'video';
this.showAudioPreview = type === 'audio';
});
*/
}
ngOnDestroy() {
super.cancelParamSubscriptions();
if (this.sub) {
this.sub.unsubscribe();
}
}
showPreviewDialog(media: VideoMedia): void { this.videoPlayer.open(media); }
toggleSearchForm(value: boolean): void { this.store.dispatch(new ToggleSearchInventoryAction(value)); }
applyFilter(form: InventoryFilterForm): void { this.store.dispatch(new SearchInventoryAction(form)); }
pageChanged(page: number): void { console.log('Page TO action Fired'+page);this.store.dispatch(new PageInventoryToAction(page)); }
pageSizeChanged(pageSize: number): void { this.store.dispatch(new ChangeInventoryPageSizeAction(pageSize)); }
orderByChanged(orderBy: string): void { this.store.dispatch(new OrderInventoryByAction(orderBy)); }
}
In my effect I have:
import {Injectable} from '@angular/core';
import {Response} from '@angular/http';
import {convertToParamMap, Params, Router} from '@angular/router';
import {Actions, Effect} from '@ngrx/effects';
import {Action, Store} from '@ngrx/store';
import {TranslationService} from 'angular-l10n';
import * as _ from 'lodash';
import * as moment from 'moment';
import {ArrayResponse, BaseEffect, DateTimeFormatService, DEFAULT_PAGE_SIZE, error, go, SendBackResult} from '../../shared';
import * as fromRoot from '../../shared/reducers';
import {
ChangeInventoryPageSizeAction,
InventoryActionTypes,
LoadInventoryFailAction,
LoadInventorySuccessAction,
OrderInventoryByAction,
PageInventoryToAction,
SearchInventoryAction
} from '../actions/inventory';
import {Inventory} from '../models/inventory';
import {InventoryFilterForm} from '../models/inventory-filter-form';
import * as fromPlayers from '../reducers';
import {InventoryService} from '../services/inventory-service';
@Injectable()
export class InventoryEffect extends BaseEffect {
private routerState$ = this.store.select(fromRoot.selectors.getRouterState).filter(state => !_.isEmpty(state));
@Effect()
pageTo$ = this.actions$.ofType<PageInventoryToAction>(InventoryActionTypes.PAGE_TO)
.withLatestFrom(this.routerState$.map(state => state.queryParams))
.map(([action, query]) => Object.assign({}, query, { $page: action.payload }))
.map(query => go([], query));
@Effect()
changePageSize$ = this.actions$.ofType<ChangeInventoryPageSizeAction>(InventoryActionTypes.CHANGE_PAGE_SIZE)
.withLatestFrom(this.routerState$.map(state => state.queryParams))
.map(([action, query]) => Object.assign({}, query, { $length: action.payload }))
.map(query => go([], query));
@Effect()
orderBy$ = this.actions$.ofType<OrderInventoryByAction>(InventoryActionTypes.ORDER_BY)
.withLatestFrom(this.routerState$.map(state => state.queryParams))
.map(([action, query]) => Object.assign({}, query, { $orderBy: action.payload }))
.map(query => go([], query));
@Effect()
search$ = this.actions$.ofType<SearchInventoryAction>(InventoryActionTypes.SEARCH)
.withLatestFrom(this.routerState$.map(state => state.queryParams))
.map(([action, query]) => Object.assign({}, query, action.payload, { $page: 1 }))
.map(query => go([], query));
@Effect()
load$ = this.actions$.ofType(InventoryActionTypes.LOAD)
.delayWhen(() => this.routerState$)
.debug('Load inventory list action received.')
.withLatestFrom(this.routerState$.map(state => state.queryParams))
.map(([action, query]) => this.mapFilterFormToFilter(query))
.withLatestFrom(this.routerState$.map(state => state.params).map(convertToParamMap).map(params => params.get('playerId')))
.switchMap(([filter, playerId]) => this.inventoryService.getList(playerId, filter)
.map((payload: SendBackResult<ArrayResponse<Inventory>>) => new LoadInventorySuccessAction(payload.data))
.catch((res: Response) => this.catchResponseError(res)));
@Effect()
loadFail$ = this.actions$.ofType(InventoryActionTypes.LOAD_FAIL)
.debug('A server error occurred while retrieving the inventory.')
.map(() => error(this.translation.translate('AUDIO_PLANNINGS_LOAD_ERROR'), this.translation.translate('TOAST_ERROR_TITLE')));
constructor(private actions$: Actions,
private store: Store<fromPlayers.AppState>,
private translation: TranslationService,
private inventoryService: InventoryService,
router: Router) {
super(router);
}
protected handleUnhandledError(response: Response): Action { return new LoadInventoryFailAction(response.status); }
private mapFilterFormToFilter(params: Params): InventoryFilterForm {
const filter: InventoryFilterForm = {
name: _.get<string>(params, 'name'),
day: moment(_.get<string>(params, 'day'), DateTimeFormatService.input.date, true).toDate(),
$page: _.get<number>(params, '$page', 1),
$length: _.get<number>(params, '$length', DEFAULT_PAGE_SIZE),
$orderBy: _.get<string>(params, '$orderBy')
};
return filter;
}
}
In my action I have:
import {Action} from '@ngrx/store';
import {ArrayResponse, type} from '../../shared';
import {Inventory} from '../models/inventory';
import {InventoryFilterForm} from '../models/inventory-filter-form';
export const InventoryActionTypes = {
TOGGLE_SEARCH: type('[Inventory] Toggle Search Form'),
SEARCH: type('[Inventory] Search'),
CHANGE_PAGE_SIZE: type('[Inventory] Change Page Size'),
PAGE_TO: type('[Inventory] Page To'),
ORDER_BY: type('[Inventory] Order By'),
LOAD: type('[Inventory] Load'),
LOAD_SUCCESS: type('[Inventory] Load Success'),
LOAD_FAIL: type('[Inventory] Load Fail'),
CLEAR: type('[Inventory] Clear Data')
};
export class ToggleSearchInventoryAction implements Action {
readonly type = InventoryActionTypes.TOGGLE_SEARCH;
constructor(public payload?: boolean) {}
}
export class SearchInventoryAction implements Action {
readonly type = InventoryActionTypes.SEARCH;
constructor(public payload?: InventoryFilterForm) {}
}
export class ChangeInventoryPageSizeAction implements Action {
readonly type = InventoryActionTypes.CHANGE_PAGE_SIZE;
constructor(public payload?: number) {}
}
export class PageInventoryToAction implements Action {
readonly type = InventoryActionTypes.PAGE_TO;
constructor(public payload?: number) {}
}
export class OrderInventoryByAction implements Action {
readonly type = InventoryActionTypes.ORDER_BY;
constructor(public payload?: string) {}
}
export class LoadInventoryAction implements Action {
readonly type = InventoryActionTypes.LOAD;
constructor() {}
}
export class LoadInventorySuccessAction implements Action {
readonly type = InventoryActionTypes.LOAD_SUCCESS;
constructor(public payload?: ArrayResponse<Inventory>) {}
}
export class LoadInventoryFailAction implements Action {
readonly type = InventoryActionTypes.LOAD_FAIL;
constructor(public payload?: number) {}
}
export class ClearInventoryAction implements Action {
readonly type = InventoryActionTypes.CLEAR;
constructor() {}
}
Reducer:
import {Action} from '@ngrx/store';
import * as _ from 'lodash';
import {DEFAULT_PAGE_SIZE, UserContextActionTypes} from '../../shared';
import {
ChangeInventoryPageSizeAction,
ClearInventoryAction,
InventoryActionTypes,
LoadInventoryAction,
LoadInventoryFailAction,
LoadInventorySuccessAction,
OrderInventoryByAction,
PageInventoryToAction,
SearchInventoryAction,
ToggleSearchInventoryAction
} from '../actions/inventory';
import {Inventory} from '../models/inventory';
import {InventoryFilterForm} from '../models/inventory-filter-form';
import {AudioPlaylistsState} from './audio-playlists';
export interface InventoryState {
showFilter: boolean;
array: Inventory[];
count: number;
}
const initialState: InventoryState = {
showFilter: true,
array: [],
count: 0,
};
export function inventoryReducer(state = initialState, action: Action): InventoryState {
switch (action.type) {
case InventoryActionTypes.TOGGLE_SEARCH:
return handleToggleSearchAction(state, action);
case InventoryActionTypes.CLEAR:
return handleClearAction(state);
case InventoryActionTypes.LOAD_SUCCESS:
return handleLoadSuccessAction(state, action);
case InventoryActionTypes.LOAD_FAIL:
return handleLoadFailAction(state);
case InventoryActionTypes.LOAD:
return handleLoadAction();
case UserContextActionTypes.CHANGE_CLIENT:
return handleChangeClientAction();
default:
return state;
}
}
function handleToggleSearchAction(state: InventoryState, action: ToggleSearchInventoryAction): InventoryState {
const newState: InventoryState = { showFilter: action.payload, array: state.array, count: state.count };
return newState;
}
function handleClearAction(state: InventoryState): InventoryState {
const newState: InventoryState = { showFilter: state.showFilter, array: [], count: 0 };
return newState;
}
function handleLoadSuccessAction(state: InventoryState, action: LoadInventorySuccessAction): InventoryState {
const newState: InventoryState = { showFilter: state.showFilter, array: action.payload.array, count: action.payload.count };
return newState;
}
function handleLoadFailAction(state: InventoryState): InventoryState {
const newState: InventoryState = { showFilter: state.showFilter, array: [], count: 0 };
return newState;
}
function handleChangeClientAction(): InventoryState {
return { showFilter: true, array: [], count: 0 };
}
function handleLoadAction(): InventoryState {
return initialState;
}
export const inventorySelectors = {
showFilter: (state: InventoryState) => _.get<boolean>(state, 'showFilter', true),
array: (state: InventoryState) => _.get<Inventory[]>(state, 'array', []),
count: (state: InventoryState) => _.get<number>(state, 'count', 0),
};
This is my service
import {Injectable} from '@angular/core';
import {Http} from '@angular/http';
import {Observable} from 'rxjs/Observable';
import {AppSettingsService, ArrayResponse, BaseRestService, SendBackResult, serializeQueryString} from '../../shared';
import {Inventory} from '../models/inventory';
import {InventoryFilterForm} from '../models/inventory-filter-form';
@Injectable()
export class InventoryService extends BaseRestService {
constructor(http: Http, appSettingsService: AppSettingsService) { super(http, appSettingsService); }
protected get className(): string { return 'InventoryService'; }
protected ge[![enter image description here][1]][1]t isAnonymous(): boolean { return false; }
getList(playerId: string, filter: InventoryFilterForm): Observable<SendBackResult<ArrayResponse<Inventory>>> {
return this.get(`/logging/devices/${playerId}/inventory/videomedia${serializeQueryString(filter)}`);
}
getAudioList(playerId: string, filter: InventoryFilterForm): Observable<SendBackResult<ArrayResponse<Inventory>>> {
return this.get(`/logging/devices/${playerId}/inventory/audiomedia${serializeQueryString(filter)}`);
}
getVideoList(playerId: string, filter: InventoryFilterForm): Observable<SendBackResult<ArrayResponse<Inventory>>> {
return this.get(`/logging/devices/${playerId}/inventory/videomedia${serializeQueryString(filter)}`);
}
}
I found the erorr. The app made to req. Becuse of the page load.
import {Action} from '@ngrx/store';
import * as _ from 'lodash';
import {DEFAULT_PAGE_SIZE, UserContextActionTypes} from '../../shared';
import {
ChangeInventoryPageSizeAction,
ClearInventoryAction,
InventoryActionTypes,
LoadInventoryAction,
LoadInventoryFailAction,
LoadInventorySuccessAction,
OrderInventoryByAction,
PageInventoryToAction,
SearchInventoryAction,
ToggleSearchInventoryAction
} from '../actions/inventory';
import {Inventory} from '../models/inventory';
import {InventoryFilterForm} from '../models/inventory-filter-form';
import {AudioPlaylistsState} from './audio-playlists';
export interface InventoryState {
showFilter: boolean;
array: Inventory[];
count: number;
}
const initialState: InventoryState = {
showFilter: true,
array: [],
count: 0,
};
export function inventoryReducer(state = initialState, action: Action): InventoryState {
switch (action.type) {
case InventoryActionTypes.TOGGLE_SEARCH:
return handleToggleSearchAction(state, action);
case InventoryActionTypes.CLEAR:
return handleClearAction(state);
case InventoryActionTypes.LOAD_SUCCESS:
return handleLoadSuccessAction(state, action);
case InventoryActionTypes.LOAD_FAIL:
return handleLoadFailAction(state);
/*
case InventoryActionTypes.LOAD:
return handleLoadAction();
*/
case UserContextActionTypes.CHANGE_CLIENT:
return handleChangeClientAction();
default:
return state;
}
}
function handleToggleSearchAction(state: InventoryState, action: ToggleSearchInventoryAction): InventoryState {
const newState: InventoryState = { showFilter: action.payload, array: state.array, count: state.count };
return newState;
}
function handleClearAction(state: InventoryState): InventoryState {
const newState: InventoryState = { showFilter: state.showFilter, array: [], count: 0 };
return newState;
}
function handleLoadSuccessAction(state: InventoryState, action: LoadInventorySuccessAction): InventoryState {
const newState: InventoryState = { showFilter: state.showFilter, array: action.payload.array, count: action.payload.count };
return newState;
}
function handleLoadFailAction(state: InventoryState): InventoryState {
const newState: InventoryState = { showFilter: state.showFilter, array: [], count: 0 };
return newState;
}
function handleChangeClientAction(): InventoryState {
return { showFilter: true, array: [], count: 0 };
}
/*
function handleLoadAction(): InventoryState {
return initialState;
}
*/
export const inventorySelectors = {
showFilter: (state: InventoryState) => _.get<boolean>(state, 'showFilter', true),
array: (state: InventoryState) => _.get<Inventory[]>(state, 'array', []),
count: (state: InventoryState) => _.get<number>(state, 'count', 0),
};
Thanks all for the great feedback. Just always make sure you look in network to see what calls are made.