1

当我单击转到页面时,它会一直转到第 1 页。我基于现有功能。对我来说,Angular 4 是相当新的。不久前我从 Angular 1 开始。

奇怪的是页面组件中的 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>

在组件.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)}`);
  }
}
4

1 回答 1

1

我发现了错误。该应用程序要求。因为页面加载。

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

感谢大家的好评。只要始终确保您查看网络以查看进行了哪些呼叫。

于 2017-09-21T12:37:26.213 回答