Angular matSort не работает

У меня возникли проблемы с импортом matSort в мой matTable.

Я предоставляю вам свой код:

dashboard.component.ts

import {Component, ViewChild} from '@angular/core';
import {UserService} from "../user.service";
import {DohvatMantisaService} from "../dohvat-mantisa.service";
import {Mantisi} from "../Mantisi";
import {Observable} from "rxjs/Observable";
import {DataSource} from "@angular/cdk/collections";
import 'rxjs/add/observable/of';
import 'rxjs/add/operator/startWith';
import 'rxjs/add/observable/merge';
import {MatSort} from '@angular/material';
@Component({
  selector: 'app-dashboard',
  templateUrl: './dashboard.component.html',
  styleUrls: ['./dashboard.component.css']
})
export class DashboardComponent{
  displayedColumns = ['projekt', 'manits', 'kategorija','ozbiljnost','datum_prijave','postavio','odjel','postavio','naziv','status','planirana_isporuka'];
  constructor(private user:UserService, private dohvatMantisa:DohvatMantisaService) {
  }
  dataSource: TableDataSource | null;
  mantisi: Mantisi[];
  name = this.user.getUsername();
  @ViewChild(MatSort) sort: MatSort;
  ngOnInit() {
    this.dataSource = new TableDataSource(this.dohvatMantisa,this.sort);
  }
}

export class TableDataSource extends DataSource<Mantisi>{
  constructor(private mantisiDS: DohvatMantisaService,private sort: MatSort){
    super();
  }
  connect(): Observable<Mantisi[]> {
    const displayDataChanges = [
      this.mantisiDS.dohvatiMantise(),
      this.sort.sortChange,
    ];

    return Observable.merge(...displayDataChanges).map(() => {
      return this.getSortedData();
    });
  }
  disconnect() {}

  getSortedData(): Mantisi[]{
    const data = this.mantisiDS.prikazMantisa().slice();
    if (!this.sort.active || this.sort.direction == '') { return data; }
    return data.sort((a, b) => {
      let propertyA: number|string = '';
      let propertyB: number|string = '';
      switch (this.sort.active) {
        case 'projekt': [propertyA, propertyB] = [a.projekt, b.projekt]; break;
        case 'manits': [propertyA, propertyB] = [a.manits, b.manits]; break;
        case 'kategorija': [propertyA, propertyB] = [a.kategorija, b.kategorija]; break;
        case 'ozbiljnost': [propertyA, propertyB] = [a.ozbiljnost, b.ozbiljnost]; break;
        case 'datum_prijave': [propertyA, propertyB] = [a.datum_prijave, b.datum_prijave]; break;
        case 'postavio': [propertyA, propertyB] = [a.postavio, b.postavio]; break;
        case 'naziv': [propertyA, propertyB] = [a.naziv, b.naziv]; break;
        case 'status': [propertyA, propertyB] = [a.status, b.status]; break;
        case 'planirana_isporuka': [propertyA, propertyB] = [a.planirana_isporuka, b.planirana_isporuka]; break;
      }

      let valueA = isNaN(+propertyA) ? propertyA : +propertyA;
      let valueB = isNaN(+propertyB) ? propertyB : +propertyB;

      return (valueA < valueB ? -1 : 1) * (this.sort.direction == 'asc' ? 1 : -1);
    });
  }
}

Это мой сервис: dohvatMantisaService

import { Injectable } from '@angular/core';
import {Http, Response, RequestOptions, Headers} from "@angular/http";
import {Observable} from "rxjs/Observable";
import {Mantisi} from "./Mantisi";
import 'rxjs';
import 'rxjs/operator/map';
import 'rxjs/operator/do';

@Injectable()
export class DohvatMantisaService {
  constructor(public http:Http) { }
  result: Mantisi[];
  dohvatiMantise(): Observable<Mantisi[]>{
    return this.http.get('/mantis/getMantisReport').map(this.extractData);
  }
  private extractData(response: Response) {
    let body = response.json();
    return body || {};
  }

  prikazMantisa(): Mantisi[]{
    this.dohvatiMantise().subscribe(res => this.result = res);
    return this.result;
  }
}

И im, предоставляющий вам строку dashboard.component.html matSort:

<mat-table #table [dataSource]="dataSource" class="example-table" matSort>

Основная проблема заключается в том, что данные загружаются и получаются из HTTP-запроса, но я получаю ошибки в консоли, например:

Невозможно прочитать свойство sortChange из undefined    в TableDataSource.webpackJsonp.../../../../../src/app/dashboard/dashboard.component.ts.TableDataSource.connect(dashboard.component.ts: 36)     в MatTable.webpackJsonp.../../../cdk/esm5/table.es5.js.CdkTable._observeRenderChanges (table.es5.js: 602)     в MatTable.webpackJsonp.../../../cdk/esm5/table.es5.js.CdkTable.ngAfterContentChecked(table.es5.js: 522)

Он сообщает мне, что this.sort.sortChange undefined. Я искал все бит Интернета и не мог найти правильный ответ. Надеюсь, ты сможешь помочь мне в этом.

Ответ 1

Я нашел решение этой проблемы.

Основная проблема заключалась в том, что таблица была отображена до поступления данных. Сначала я загрузил данные и отправил их как источник в конструктор dataSource. После этого проблема исчезла. Это вещь с асинхронным http.get и сервисами.

Спасибо за помощь.

fetchData(id){
  this.fetch.getProcesses(id).subscribe(result => {
    this.processes = result;
    this.dataSource = new TableDataSource(this.processes);
    Observable.fromEvent(this.filter.nativeElement, 'keyup')
      .debounceTime(150)
      .distinctUntilChanged()
      .subscribe(() => {
        if (!this.dataSource) {
          return;
        }
        this.dataSource.filter = this.filter.nativeElement.value;
      });
  });
}




export class TableDataSource extends DataSource<Proces>{
  _filterChange = new BehaviorSubject('');
  get filter(): string {
    return this._filterChange.value;
  }
  set filter(filter: string) {
    this._filterChange.next(filter);
  }
  filteredData: any =[];

  constructor(private processes:Proces[]){
    super();
  }
  connect(): Observable<Proces[]>{
    const displayDataChanges = [
      this.processes,
      this._filterChange,
    ];
    return Observable.merge(...displayDataChanges).map(() => {
      this.filteredData = this.processes.slice().filter((item: Proces) => {
        const searchStr = (item.name).toLowerCase();
        return searchStr.indexOf(this.filter.toLowerCase()) !== -1;

      });
      return this.filteredData;
    })
  }
  disconnect(){}
}



      <div class="header">
    <mat-form-field floatPlaceholder="never">
      <input matInput #filter placeholder="Pretraži procese">
    </mat-form-field>
  </div>
  <mat-table #table [dataSource]="dataSource" class="table-mantis" matSort>

    <ng-container matColumnDef="col1">
      <mat-header-cell *matHeaderCellDef class="table-header-cell" mat-sort-header> Name</mat-header-cell>
      <mat-cell *matCellDef="let row" class="example-cell" > {{row.nazivProcesa}} </mat-cell>
    </ng-container>

    <ng-container matColumnDef="col2">
      <mat-header-cell *matHeaderCellDef class="table-header-cell" mat-sort-header> Cell 2</mat-header-cell>
      <mat-cell *matCellDef="let row" class="example-cell" > {{row.nazivVlasnika
        }} </mat-cell>
    </ng-container>

    <ng-container matColumnDef="col3">
      <mat-header-cell *matHeaderCellDef class="table-header-cell" mat-sort-header> Cell 3</mat-header-cell>
      <mat-cell *matCellDef="let row" class="example-cell" > {{row.interniAkt}} </mat-cell>
    </ng-container>

    <mat-header-row *matHeaderRowDef="displayedColumns" class="example-header-row"></mat-header-row>
    <mat-row class="example-row" *matRowDef="let row; columns: displayedColumns;"></mat-row>
  </mat-table>

Ответ 2

Если вы используете "* ngIf" для первоначального скрытия контейнера таблицы в вашем шаблоне, viewchield будет undefined.

Ответ 3

У меня была эта проблема, и все, что мне нужно было сделать, это убедиться, что вы правильно ссылаетесь на свой matSort viewChild и убедитесь, что вы добавили MatSortModule в файл module.ts в области импорта.

Как показано ниже: @NgModule({ imports: [ MatSortModule, MatTableModule, ] )}

Ответ 4

У меня была та же проблема, и я исправил ее, добавив ниже:

@ViewChild(MatSort) set matSort(ms: MatSort) {
this.sort = ms;
this.setDataSourceAttributes();
 }

@ViewChild(MatPaginator) set matPaginator(mp: MatPaginator) {
 this.paginator = mp;
this.setDataSourceAttributes();
 }

setDataSourceAttributes() {
  this.dataSource.paginator = this.paginator;
  this.dataSource.sort = this.sort;
  }

Ответ 5

У меня была похожая проблема при передаче асинхронных данных из слоя контейнера в слой компонента. Здесь мое решение

Поскольку элемент начинался без данных, нет смысла помещать конструктор или OnInit, как это делает оригинальный пример. Только после того, как поток данных поступит (асинхронно), я реализую сортировку и разбиение на страницы в установщике, и все заработало отлично.

В моем шаблоне:

<div class="card">

    <div class="card-header">
      <h4>{{pageTitle}}</h4>
      <mat-form-field>
        <input matInput (keyup)="applyFilter($event.target.value)" placeholder="Filter">
      </mat-form-field>
    </div>

    <div class="card-body p-0">    
      <div class="mat-elevation-z8">
        <table mat-table [dataSource]="dataSource" matSort>

          <ng-container matColumnDef="projectNumber">
            <th mat-header-cell *matHeaderCellDef mat-sort-header> Werf Nummer </th>
            <td mat-cell *matCellDef="let row"> W{{row.projectNumber}} </td>
          </ng-container>

          <ng-container matColumnDef="description">
            <th mat-header-cell *matHeaderCellDef mat-sort-header> Omschrijving </th>
            <td mat-cell *matCellDef="let row"> {{row.description}} </td>
          </ng-container>

          <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
          <tr mat-row *matRowDef="let row; columns: displayedColumns;">
          </tr>
        </table>

        <mat-paginator [pageSizeOptions]="[10,20,100]"></mat-paginator>
      </div>      
    </div>
  </div>

В моем классе angular компонентов у меня есть

    export class ProjectListComponent {
      pageTitle = 'Projects';
      displayedColumns: string[] = ['projectNumber', 'description'];
      dataSource: MatTableDataSource<Project>;

      @ViewChild(MatPaginator, {static: true}) paginator: MatPaginator;
      @ViewChild(MatSort, {static: true}) sort: MatSort;
      @Input() set projects(value: Project[]){
        this.dataSource = new MatTableDataSource(value);
        this.dataSource.paginator = this.paginator;
        this.dataSource.sort = this.sort;
      }
      @Input() selectedProject: Project;
      @Output() selected = new EventEmitter<Project>();

      applyFilter(filterValue: string) {
        this.dataSource.filter = filterValue.trim().toLowerCase();

        if (this.dataSource.paginator) {
          this.dataSource.paginator.firstPage();
        }
      }
    }

Я должен упомянуть еще кое-что действительно интересное о параметрах фильтрации. Обратите внимание, что проект здесь используется только для двух полей, но интерфейс на самом деле больше полей, чем, например, он имеет идентификатор. Несмотря на то, что я не показываю идентификатор, я могу фильтровать по идентификатору с помощью приведенного выше кода, что довольно здорово.

    export interface Project {
        id: string;
        projectNumber: string;
        description: string;
        activePeriods: ProjectActivePeriod[];
    }

Также обратите внимание, что обязательным условием является то, что ссылка matColumnDef = "projectNumber" в отображаемых вами столбцах должна быть точным именем свойства в интерфейсе. Если бы я использовал ProjectNumber ', сортировка не сработала бы.

Я строю это на основе информации

Ответ 6

Так происходит из-за того, что ваш метод this.sort.sortChange вызывается до того, как ваш дочерний элемент просматривает Initialize

Вам просто нужно реализовать свой класс из AfterViewInit вместо OnInit

И использовать ngAfterViewInit метод жизненного цикла

export class ApplicantListComponent implements AfterViewInit {
    @ViewChild(MatPaginator, { static: false }) paginator: MatPaginator;
    @ViewChild(MatSort, { static: false }) sort: MatSort;

    constructor() {}

    ngAfterViewInit() {
        this.sort.sortChange.subscribe(() => {
            this.paginator.pageIndex = 0;
            this.paginator.pageSize = this.pageSize;
        });
}