Угловой материал: требуется дополнительное взаимодействие для запуска рендеринга - PullRequest
0 голосов
/ 31 октября 2019

Я сталкиваюсь со странным поведением углового материала: некоторые компоненты нуждаются в дополнительном событии (например, щелчок мыши или движение мыши по элементу интереса), чтобы вызвать фактический рендеринг. Например, в mat-таблице при загрузке новых строк некоторые пустые строки по-прежнему отображаются, и как только я прокручиваю вниз или щелкаю одну из них, они исчезают. Другой пример - диалог: когда я щелкаю за пределами границы, ничего не происходит, но если я нажимаю снова или прокручиваю, он исчезает. Вот моя версия angular-cli. Спасибо


Angular CLI: 8.3.14
Node: 13.0.1
OS: linux x64
Angular: 8.2.12
... animations, common, compiler, compiler-cli, core, forms
... language-service, platform-browser, platform-browser-dynamic
... router

Package                           Version
-----------------------------------------------------------
@angular-devkit/architect         0.803.14
@angular-devkit/build-angular     0.803.14
@angular-devkit/build-optimizer   0.803.14
@angular-devkit/build-webpack     0.803.14
@angular-devkit/core              8.3.14
@angular-devkit/schematics        8.3.14
@angular/cdk                      8.2.3
@angular/cli                      8.3.14
@angular/flex-layout              8.0.0-beta.27
@angular/material                 8.2.3
@ngtools/webpack                  8.3.14
@schematics/angular               8.3.14
@schematics/update                0.803.14
rxjs                              6.5.3
typescript                        3.5.3
webpack                           4.41.2

Прежде всего, спасибо за ответы. Я заметил, что все проблемы связаны с таблицей матов: диалоговое окно, вызывающее проблемы, вызывается кнопкой внутри таблицы, и если я перемещаю его наружу, он работает правильно. Компонент таблицы следующие:


export class MainComponent implements OnInit {

    dataSource : MyTableSource;
    @ViewChild(MatTable, {static:true}) table: MatTable<any>;
    dtOptions: any = { 
            displayedColumns : [ 'colA', 'colB', 'colC', 'colD'],
            search : { value : "" } ,
            order : { name : 'values' , asc : true },
            subset : [] , 
            pageSize : 10,
            pageIndex : 0,
            draw : 0 , 
            recordsTotal : 0, 
            recordsFiltered : 0};

    constructor(public dialog: MatDialog,
            private _snackBar: MatSnackBar,private trackService: TracksService) {
    };
   ngOnInit() {
        this.dataSource = new KmerTableSource(this.trackService);
   }
   refreshTable(event?:any){
        if ( event){
            if ( event.pageSize){
                this.dtOptions.pageSize=event.pageSize;
                this.dtOptions.pageIndex=event.pageIndex;
            }
            if (event.active){
                this.dtOptions.order.name = event.active;
                this.dtOptions.order.asc = event.direction == "asc";
            }
        }

        this.dataSource.loadData(this.dtOptions);
    }

}

HTML

<table mat-table matSort (matSortChange)="refreshTable($event)" [dataSource]="dataSource" >
                        <ng-container matColumnDef="importance">
                            <th mat-header-cell *matHeaderCellDef  mat-sort-header>Col A</th>
                            <td mat-cell *matCellDef="let row">{{row.A}}</td>
                        </ng-container>
                        <ng-container matColumnDef="kmer">
                            <th mat-header-cell *matHeaderCellDef  mat-sort-header>Col B</th>
                            <td mat-cell *matCellDef="let row">{{row.B}}</td>
                        </ng-container>
                        <ng-container matColumnDef="position">
                            <th mat-header-cell *matHeaderCellDef  mat-sort-header>Col C</th>
                            <td mat-cell *matCellDef="let row">{{row.C}}</td>
                        </ng-container>

                        <tr mat-header-row *matHeaderRowDef="dtOptions.displayedColumns;sticky : true;"></tr>
                        <tr mat-row cdk-row *matRowDef="let row; columns: dtOptions.displayedColumns"></tr>
                    </table>


Источник данных


@Injectable({
  providedIn: 'root'
})


export class MyTableSource implements DataSource<DataPoint> {
  private dataSubject = new BehaviorSubject<DataPoint[]>([]);

  constructor(private trackService :TracksService ) { }

  connect(collectionViewer: CollectionViewer): Observable<DataPoint[]> {
      return this.dataSubject.asObservable();
  }

  disconnect(collectionViewer: CollectionViewer): void {
      this.dataSubject.complete();
  }

  loadData(request : any) {
      this.trackService.getDataTable(request).pipe(
              catchError(()=>of([])),
      ).subscribe(response => {
          this.dataSubject.next(response);
       });
  }

}


Служба отслеживания (я использую электрон)


@Injectable( {
    providedIn: 'root'
} )


export class TracksService {
    protected request: number = 0;
    protected ipc : IpcRenderer;
    constructor( protected http: HttpClient ) {
        if ( ( <any>window ).require ) {
            try {
                this.ipc = ( <any>window ).require( "electron" ).ipcRenderer;
            } catch ( error ) {
                throw error;
            }
        } else {
            console.warn( "Could not load electron ipc" );
        }
    }

    getDataTable( dataTablesParameters : any) : Observable<any[]>{
        var id = this.request;
        this.request += 1;
        this.ipc.send( "getData" , id, dataTablesParameters);
        return new Observable<any[]>(observer => {
            this.ipc.once( "getData-" + id, ( event, arg ) => {
                dataTablesParameters.recordsTotal= arg.recordsTotal;
                dataTablesParameters.recordsFiltered=arg.recordsFiltered;
                observer.next(arg.data);
            });
        });
    }

}

Ответы [ 2 ]

0 голосов
/ 04 ноября 2019

Решено с использованием NgZone и ChangeDetectorRef: возможно, вызов был сделан внутри компонента MatTable, и изменения в подписке не были отображены в основном компоненте.

В основном компоненте:

refreshTable(event?:any){
        ...
        this.dataSource.loadData(this.dtOptions).then(()=>this.cd.markForCheck());
    }

LoadData источника данных теперь должен возвращать Promise для запуска markForCheck после загрузки

loadData(request : any) : Promise<void> {
return new Promise(resolve, reject )=>{...})
}

А что касается диалога, он находится в NgZone.run

this.zone.run(()=>{this.dialog.open(...)}));

Спасибо @ gsa.interactive за подсказку ChangeDetectorRef

0 голосов
/ 31 октября 2019

Я столкнулся с чем-то похожим, используя компоненты материала, которые получают свои значения из значения Input (). Необходимо использовать ChangeDetectorRef для ngOnChanges или ngAfterContentInit.

export class MyComponent {
  @Input() data: string[];

  constructor(private cd: ChangeDetectorRef) { }

  ngAfterContentInit() {
    this.cd.detectChanges();
  }
}

Возможно, вам потребуется изменить стратегию для нажатия:

@Component({
...
changeDetection: ChangeDetectionStrategy.OnPush
...
})

...