Угловой коврик не работает

У меня проблемы с импортом 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);
    });
  }
}

Это мой сервис: дохватМантизаСервис

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

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

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

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

Не удается прочитать свойство 'sortChange' из неопределенного в 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 не определен. Я искал всю часть интернета и не мог найти правильный ответ. Надеюсь, вы можете помочь мне с этим.

14 ответов

Решение

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

Основная проблема заключалась в том, что таблица отображалась до поступления данных. Сначала я загрузил данные и отправил их как источник в конструктор 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>

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

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

Как следующее: @NgModule({ imports: [ MatSortModule, MatTableModule, ] )}

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

@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;
  }

В Angular 8 и Material 6.4.1, если вы ждете получения данных от службы, самый простой и лучший способ исправить MatSort и MatPaginator - это установить static: false вместо true, например:

@ViewChild(MatSort, { static: false }) sort: MatSort;
@ViewChild(MatPaginator, { static: false }) paginator: MatPaginator;

В моем случае это вызов данных ngOnChanges.

Итак, это происходит из-за того, что ваш метод 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;
        });
} 

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

Поскольку элемент начинался без данных, нет смысла помещать конструктор или 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>

В моем классе угловых компонентов у меня есть

    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" в отображаемом вами столбце обязательно должна быть точным именем свойства в интерфейсе. Если бы я использовал " P rojectNumber", вид не будет работать.

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

      <table mat-table matSort #empTbSort="matSort" [dataSource]="dataSource">
      @ViewChild('empTbSort') sort = new MatSort();

 
ngOnInit(): void {
  // async call
  this.dataSource = new MatTableDataSource<any>(data);
  // make sure to put you sore after init dataSource 
  this.dataSource.sort = this.sort;
}

Как работает сортировка по мат-таблице? MatTableDataSource сортирует данные, сопоставляя имя столбца (matColumnDef) с именем свойства записи таблицы.

      displayedColumns: string[] = ['ID', 'firstname', 'lastname','EmAil'];

export interface Employee 
{
  ID: number,   
  firstname:string, 
  lastname:string,  
  EmAil:string,
}

Имя свойства должно совпадать с matColumnDef, иначе столбец таблицы не будет сортироваться.

подробнее: https://www.angularjswiki.com/material/mat-table-sort/

В дополнение ко всем другим ответам, если у вас есть несколько таблиц с сортировкой в ​​вашем шаблоне, вам необходимо определить уникальные matSort как показано ниже:

Шаблон:

      <mat-table [dataSource]="firstDataSource" matSort #firstSort="matSort">

Составная часть:

      @ViewChild('firstSort', { static: false }) firstSort: MatSort;

ngAfterViewInit() {
   this.firstDataSource.sort = this.firstSort;
}

У меня была такая же проблема,

У этой проблемы есть несколько причин.

  1. Убедитесь, что вы включили модуль MatSortModule в свой модуль приложения.
 import { MatSortModule, MatPaginatorModule, MatTableModule } from '@angular/material';

 @NgModule({
  imports: [MatSortModule, MatPaginatorModule, MatTableModule],
  exports: []
})
  1. Убедитесь, что вы подписались на изменение сортировки в ngAfterViewInit
  ngAfterViewInit() {
    this.sort.sortChange.subscribe(() => this.paginator.pageIndex = 0 );
  }

Я обновляю свои данные из внешнего источника, и по какой-то причине, когда я изменил свою переменную, таблица не обновлялась. Затем я наблюдал за изменениями вngOnChangesи обновилdataзатем переменная отображала мою таблицу, но не позволяла сортировать. Вот что, наконец, сработало для меня:

        dataSource: MatTableDataSource<BasicTableData>;

  constructor() { }

  ngOnInit(): void {
    this.dataSource = new MatTableDataSource(this.data);
  }

  ngAfterViewInit() {
    this.dataSource.sort = this.sort;
  }

  ngOnChanges(changes: SimpleChanges): void {
    if(changes['data'].currentValue !== changes['data'].previousValue && !changes['data'].firstChange){
      this.dataSource.data = changes['data'].currentValue;
    }
  }

Для меня, хотя я заявил

      dataSource: MatTableDataSource<MyRowEntity>

Я все еще устанавливал его в массив (что потребовало бы явного вызова matTableChild.renderRows())

      // renderRows not called automatically onSortChange
dataSource = [{name: 'hello'}, {name: 'world'}] 

Решение:

      // renderRows called automatically onSortChange
this.dataSource = new MatTableDataSource(myRowEntities); 

PS В исходном вопросе нет этой ошибки, но я подумал, что другие могут

Для меня я забыл поставить matSort подпирать <table/>.

      <table mat-table matSort [dataSource]="dataSource">

Если данные находятся внутри объекта, сортировка также не будет работать. Например:

      interface: Name { firstName: string; lastName: string; }
interface: User { name: Name; }

{{ user.Name.firstName }}

Сортировка в этом случае не работает. Он должен быть плоским, как: {{ user.firstName }}

Кроме того, имя свойства и имена столбцов должны совпадать.

Другие вопросы по тегам