MD Paginator错误:data.slice不是MapSubscriber的函数

时间:2017-08-01 00:05:45

标签: angular angular-material2

网上没有关于此错误的内容: - (

在MD数据表的MD Paginator中有两个动作。

1)当我点击下一页的箭头时,

2)当我尝试更改页面大小时,例如从5行到10行。

该应用程序因此错误而崩溃。除此之外,数据表显示一切都很好。它显示数据库中的5个第一项,分页部分底部的项目计数为8,即数据库中的总数。

知道发生了什么事并修复了吗?我正在尝试在服务中放入尽可能多的代码,因为其他组件将使用它。

首先是html。请注意,我在最后两列的行中添加了按钮,并隐藏了最后一列中该行的内容。按钮和隐藏的行工作正常。我希望他们不是问题,因为我需要他们。我在html和component.ts中相关的displayedColumns中对它们进行了评论,没有任何帮助。

<md-table #table [dataSource]="dataSource">

      <!-- First Name Column -->
      <ng-container cdkColumnDef="firstName">
        <md-header-cell *cdkHeaderCellDef> Name </md-header-cell>
        <md-cell *cdkCellDef="let row"> {{row.firstName}} </md-cell>
      </ng-container>

      <!-- Las Name Column -->
      <ng-container cdkColumnDef="lastName">
        <md-header-cell *cdkHeaderCellDef> Last Name </md-header-cell>
        <md-cell *cdkCellDef="let row">  {{row.lastName}} </md-cell>
      </ng-container>

      <!-- Title Column -->
      <ng-container cdkColumnDef="mainSkillTitle">
        <md-header-cell *cdkHeaderCellDef> Title </md-header-cell>
        <md-cell *cdkCellDef="let row"> {{row.mainSkillTitle}} </md-cell>
      </ng-container>

      <!-- Main Skills Column -->
      <ng-container cdkColumnDef="mainSkills">
        <md-header-cell *cdkHeaderCellDef> Main Skills </md-header-cell>
        <md-cell *cdkCellDef="let row"> {{row.mainSkills}} </md-cell>
      </ng-container>

      <!-- Delete Buttons Column -->
      <ng-container cdkColumnDef="delete">
        <md-header-cell *cdkHeaderCellDef> Delete </md-header-cell>
        <md-cell *cdkCellDef="let row">
          <button (click)="deleteMember(row.$key)">Delete</button> </md-cell>
      </ng-container>

      <!-- Edit button Column -->
      <ng-container cdkColumnDef="edit">
        <md-header-cell *cdkHeaderCellDef> Edit </md-header-cell>
        <md-cell *cdkCellDef="let row">
          <button (click)="goToDetailPage(row.$key)">Edit</button> </md-cell>
      </ng-container>

      <!-- Database key Column -->

      <ng-container cdkColumnDef="key">
        <md-header-cell *cdkHeaderCellDef class="hiddenField"> Key </md-header-cell>
        <md-cell *cdkCellDef="let row" class="hiddenField"> {{row.$key}} </md-cell>
      </ng-container>


      <md-header-row *cdkHeaderRowDef="displayedColumns"></md-header-row>
      <md-row *cdkRowDef="let row; columns: displayedColumns;"></md-row>


    </md-table>
    <md-paginator #paginator
                  [length]="dataLength?.length"
                  [pageIndex]="0"
                  [pageSize]="5"
                  [pageSizeOptions]="[5, 10, 25, 100]">
    </md-paginator>

带有console.log分析的service.ts中的代码。

// Data Table imports.
import { MdPaginator } from '@angular/material';
import { DataSource } from '@angular/cdk';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/observable/of';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
import 'rxjs/add/operator/startWith';
import 'rxjs/add/observable/merge';
import 'rxjs/add/operator/map';


@Injectable()
export class MembersAdminService {

  private membersData$: FirebaseListObservable<MemberModel[]>;


  constructor(
    private af: AngularFireDatabase,
    private successService: SuccessService,

    @Inject(FirebaseApp) fb) {
      this.membersData$ = af.list('Members');
  }


  public getMembers(): FirebaseListObservable<any> {
    return this.af.list('Members', {
      query: {
        orderByChild: 'lastName'
      }
    });
  }

... CRUD functions
}  

export class MembersAdminSource extends DataSource<MemberModel> {

    constructor(
        private memberDatabase: MembersAdminService,
        private paginator: MdPaginator) {
        super();
    }

connect(): Observable<MemberModel[]> {
      const displayDataChanges = [
          this.memberDatabase.getMembers(),
          this.paginator.page
      ];
        // return Observable.of(this.memberDatabase); Old code that worked.

      return Observable.merge(...displayDataChanges).map((data, page) => {
          const dataSlice = data.slice();
          console.log('dataSlice', dataSlice); // console.log => Array of 3 objects, 8 total in db.

          // Get the page's slice per pageSize setting.
          const startIndex = this.paginator.pageIndex * this.paginator.pageSize;
          const dataLength = this.paginator.length;
          console.log('dataLength', this.paginator.length); // console.log => 8 = correct.
          console.log('pageIndex', this.paginator.pageIndex); // console.log => 0 = correct.
          console.log('pageSize', this.paginator.pageSize); // console.log => 5 = correct.
          console.log('startIndex', startIndex);  // 0 = correct.
          return dataSlice.splice(startIndex, this.paginator.pageSize);
            // console.log => an array of 5 objects.  Seems to be correct. 3 more in db.
      });

    }

    disconnect() {}
}

component.ts中的相关代码

export class AllMembersComponent implements OnInit {

  membersData: MemberModel[];
  private result: boolean;
  allMembers: MemberModel[];

  // For search
  startAt = new Subject();
  endAt = new Subject();
  lastKeypress: 0;

  // For MD data table.
  private dataSource: MembersAdminSource | null;
  private displayedColumns = [
      'firstName',
      'lastName',
      'mainSkillTitle',
      'mainSkills',
      'delete',
      'edit',
      'key'
  ];

  @ViewChild(MdPaginator)
  paginator: MdPaginator;

  public dataLength: any;
  private dataArray: any;

  constructor(
      private membersAdminService: MembersAdminService,
      private router: Router,
      private confirmService: ConfirmService,
      private memberDatabase: MembersAdminService
  ) {}

  ngOnInit() {

    this.membersAdminService.getMembers()
        .subscribe(members => {
            this.dataSource = new MembersAdminSource(this.memberDatabase, this.paginator);
            this.dataLength = members;
        });
}
... More stuff

3 个答案:

答案 0 :(得分:1)

简单地说,看起来data不是数组(或具有slice方法的对象),您可以在方法中调用data.slice() {39}} ;转到.map()

违规行:

const dataSlice = data.slice();

一个修复:

const dataSlice = Array.isArray(data) ? data.slice() : [];

如果data不是数组,那真的取决于你想要做什么。

答案 1 :(得分:1)

Observable.merge未同时返回datapage。相反,它仅返回最后一个发射值的值。当数据库发送事件时,您将结果存储到data并正确切片。当分页器发送事件时,您将页面事件存储到data并错误地将其分割。

要解决此问题,您有几种选择。一,您可以使用另一个合并函数,如combineLatest,它将提供您列出(data, page)的值。问题是它会等到所有流都至少发出一次,但是只有与之交互后,分页器才会发出。您可以通过this.paginator.page.startsWith(null)添加一个起始值,以使用发射值将其启动。

其次,您可以拥有一个存储数据的单独订阅

  this.memberDatabase.getMembers().subscribe(data => this.data = data)

然后进行合并,但不是捕获任何值,只需使用this.data表示数据,this.paginator表示当前页面值。

答案 2 :(得分:1)

经过几天的实验和研究,但我找到了答案。这是如何使用MD Paginator设置MD表并从外部源(如Firebase)导入数据。我为源和数据库类使用服务,大部分代码都进入服务。我收到错误的原因是因为我还没弄清楚如何连接MemberDatabase类的前两行。这需要创建数据库类并重构源类。本或安德鲁的解决方案都没有奏效,我尝试了许多排列。然而,安德鲁解决方案的第一段给了我自己解决这个问题的洞察力。非常感谢Andrew !!

构件-admin.service.ts

import { AngularFireDatabase, FirebaseListObservable } from 'angularfire2/database';
import { FirebaseApp } from 'angularfire2';
import { Inject, Injectable } from '@angular/core';

import { MemberModel } from './member-admin.model';
import { SuccessService } from '../../../shared/success.service';

// Data Table imports.
import { MdPaginator } from '@angular/material';
import { DataSource } from '@angular/cdk';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/observable/of';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
import 'rxjs/add/operator/startWith';
import 'rxjs/add/observable/merge';
import 'rxjs/add/observable/combineLatest';
import 'rxjs/add/operator/map';


@Injectable()
export class MembersAdminService {

  private membersData$: FirebaseListObservable<MemberModel[]>;

  constructor(
    public af: AngularFireDatabase,
    private successService: SuccessService,

    // For Create and Update functions.
    @Inject(FirebaseApp) fb) {
      this.membersData$ = af.list('Members');
    }

// ... CRUD stuff not relevant to the MD Table ...


// *** MD DATA TABLE SERVICES. ***


@Injectable()
export class MemberDatabase {

    /* Stream that emits whenever the data has been modified. */
    public dataChange: BehaviorSubject<MemberModel[]> = new BehaviorSubject<MemberModel[]>([]);
    get data(): MemberModel[] {
        return this.dataChange.value; }

    // Connection to remote db.
    private database = this.memberAdminService.af.list('Members', {
        query: {
            orderByChild: 'lastName'
        }
    });
    public getMembers(): FirebaseListObservable<MemberModel[]> {
        return this.database;
    }


    constructor(private memberAdminService: MembersAdminService) {
        this.getMembers()
            .subscribe(data => this.dataChange.next(data));
    }
}


@Injectable()
export class MembersAdminSource extends DataSource<MemberModel> {


    constructor(
        private memberDatabase: MemberDatabase,
        private paginator: MdPaginator) {
        super();
    }


    /** Connect function called by the table to retrieve one stream containing the data to render. */
    connect(): Observable<MemberModel[]> {

      const displayDataChanges = [
          this.memberDatabase.dataChange,
          this.paginator.page,
      ];

      return Observable
          .merge(...displayDataChanges) // Convert object to array with spread syntax.
          .map(() => {
              const dataSlice = this.memberDatabase.data.slice(); // Data removed from viewed page.

              // Get the page's slice per pageSize setting.
              const startIndex = this.paginator.pageIndex * this.paginator.pageSize;

              const dataLength = this.paginator.length;  // This is for the counter on the DOM.

              return dataSlice.splice(startIndex, this.paginator.pageSize);
          });
    }
    disconnect() {}
}

全members.component.ts 在ngOnInit和类属性中进行了一些重构。

import { Component, OnInit, ViewChild } from '@angular/core';
import { Router } from '@angular/router';
import { Subject } from 'rxjs/Subject';

// For MD Data Table.
import { MdPaginator } from '@angular/material';
import { MembersAdminService, MembersAdminSource, MemberDatabase } from './member-admin.service';

import { ConfirmService } from '../../../shared/confirm.service';
import { MemberModel } from './member-admin.model';


@Component({
  selector: 'app-all-members',
  templateUrl: './all-members.component.html'
})


export class AllMembersComponent implements OnInit {

  membersData: MemberModel[];
  private result: boolean;
  allMembers: MemberModel[];

  // For search
  startAt = new Subject();
  endAt = new Subject();
  lastKeypress: 0;

    // For MD data table.

  // private memberDatabase = new MemberDatabase();  // Requires a param but? Moved to constructor.
  private dataSource: MembersAdminSource | null;
  private displayedColumns = [
      'firstName',
      'lastName',
      'mainSkillTitle',
      'mainSkills',
      'delete',
      'key'
  ];

  @ViewChild(MdPaginator)
  paginator: MdPaginator;

  public dataLength: any; // For member counter on DOM.

  constructor(
      private membersAdminService: MembersAdminService,
      private memberDatabase: MemberDatabase,
      private router: Router,
      private confirmService: ConfirmService
  ) {}

  ngOnInit() {

      this.memberDatabase.getMembers()
          .subscribe(members => {
              this.dataSource = new MembersAdminSource(this.memberDatabase, this.paginator);
              this.dataLength = members;
          });
    }

模板没有变化。