1
當我點擊轉到頁面它繼續轉到第1頁。我基於現有的功能。對我來說Angular 4是相當新的。我剛從Angular 1開始。角4數據表頁面不起作用。 NGX商店,效果
奇怪的是,頁面組件中的console.log被多次觸發。第一次使用正確的頁碼。但隨後與編號1在一起。我一直在尋找它。但它是完全奇怪的。
在我的網頁我有:
<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>
在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)); }
}
在我的影響,我有:
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;
}
}
在我的行動,我有:
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() {}
}
減速機:
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),
};
這是我的服務
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)}`);
}
}
你可以把它放在一個plunkr? – Surreal
問題是這個龐大的項目。我可以嘗試,但是對於Angular的人來說,這很容易被發現? – user3806549