首页 > 解决方案 > 角垫表在点击时不更新

问题描述

您好,我有一个问题,在订阅检索特定站点所有详细信息的服务后,我的 mat-table 没有更新。我有一个结构,它由一个父组件(DepotSelectionComponent)和两个子组件 InteractiveMapComponent、MapResultComponent 组成。我正在从 InteractiveMapComponent 检索单击事件,该事件冒泡到父(DepotSelectionComponent),然后调用服务,然后更新 MapResultComponent 中的数据源。

当前行为:

点击后表格不会更新,只会在我点击 InteractiveMapComponent 中的后退按钮时更新,这对我来说毫无意义。

预期行为:

单击时表格更新,表格立即更新。

父(DepotSelectionComponent)TS:

import { Component, OnInit, ViewChild, NgZone } from '@angular/core';
import { MapResultComponent } from '../map-result/map-result.component';
import { InteractiveMapComponent } from '../interactive-map/interactive-map.component';
import { SiteService } from '../shared/services/site-service';
import { siteDetails } from '../shared/interfaces/site-details.interface';
import { MatTableDataSource } from '@angular/material';

@Component({
  selector: 'app-depot-selection',
  templateUrl: './depot-selection.component.html',
  styleUrls: ['./depot-selection.component.scss']
})
export class DepotSelectionComponent implements OnInit {

  page:string;
  countyName: string;
  @ViewChild(MapResultComponent, {static: false})
  private MapResultComponent: MapResultComponent;

  constructor(private zone: NgZone,
    private _siteService: SiteService) { }

  ngOnInit() {
    this.page = "siteLandingPage";
  }

  home() {
    this.page = "siteLandingPage";
  }

  getDetailsPage() {
    this.page = "siteMoreDetails";
  }

  depotMoreDetails() {
    this.page = "depotMoreDetails"
  }

  populateResults(countyName) {
    this.MapResultComponent.mapResultHeader = countyName.dataObj.label;
    this.MapResultComponent.siteResults.length = 0;
    this._siteService.getSites(countyName.dataObj.label)
    .subscribe((data: any[]) => {
      data.forEach(e => {
      this.MapResultComponent.siteResults.push(new siteDetails(e.Site_ID, e.Address1, e.Address2, e.Town, e.County, e.PostCode, e.Name, e.Description, e.TelephoneNumber));
      })     
    }); 

  this.MapResultComponent.dataSource = new MatTableDataSource(this.MapResultComponent.siteResults);   
  console.log(this.MapResultComponent.dataSource)
}

}

父(DepotSelectionComponent)html:

  <div class="main">  

    <div [ngSwitch]="page">

        <div *ngSwitchCase="'siteLandingPage'">
            <div class="interactiveMap">
                <app-interactive-map (siteDetailTable)="populateResults($event)"></app-interactive-map>
            </div>
            <div class="mapResult">
                <app-map-result (moreDetails)="getDetailsPage($event)"></app-map-result>
            </div>
        </div>

        <div *ngSwitchCase="'siteMoreDetails'">
            <div>
                <button mat-raised-button (click)="home()">Back</button>
            </div>
            <div class="clearFloat"></div>
            <app-site-details (depotMoreDetails)="depotMoreDetails($event)"></app-site-details>
        </div>

        <div *ngSwitchCase="'depotMoreDetails'">
            <div>
                <button mat-raised-button (click)="getDetailsPage()">Back</button>
            </div>
            <div class="clearFloat"></div>
            <app-depot-parent></app-depot-parent>
        </div>
    </div>

</div>

子(InteractiveMapComponent)TS:

import { Component, OnInit, NgZone, EventEmitter, Output, ChangeDetectorRef } from '@angular/core';
import { FusionCharts } from 'fusioncharts';
import { InteractiveMapService } from './interactive-map.service';
import { Observable } from 'rxjs/Rx';
import { CountyDetails } from './county-details.interface';

@Component({
  selector: 'app-interactive-map',
  templateUrl: './interactive-map.component.html',
  styleUrls: ['./interactive-map.component.scss']
})

export class InteractiveMapComponent implements OnInit {

  dataSource: Object;
  map: string;
  mapType:string;
  FusionCharts: FusionCharts;
  test: Array<{id: string, value: string, color: string}> = [];

  @Output() siteDetailTable = new EventEmitter();

  constructor(private _interactiveMapService: InteractiveMapService,
    private zone: NgZone) { }

  ngOnInit() {   
    this.getUKMapData();

    this.mapType = "Site Locations";

    this.dataSource = {
      chart: {
        includevalueinlabels: "1",
        labelsepchar: ": ",
        entityFillHoverColor: "#EE204D",
        theme: "fint",
      }, 
      // Source data as JSON --> id represents counties of england.
      data : this.test,   
    };

  }

  ukMap() {
   this.mapType = "Site Locations";
   this.getUKMapData();
  }

  selectMap(map) {
    switch(map) {
      case "England":
          this.getEnglandMapData();
          this.mapType="England";
      break;

      case "Wales":
        this.getWalesMapData();
        this.mapType="Wales";
      break;

      case "Scotland":
          this.getScotlandMapData();
          this.mapType="Scotland";
      break;

      case "North Ireland":
          this.mapType="Northern Ireland";
      break;

      default:
        ;
    }
  }

  getGBMapData() {  
    this.test.length = 0;   
    this._interactiveMapService.getGBMapData()
    .subscribe((data: any[]) => {
      data.forEach(e => {
        this.test.push(new CountyDetails(e.Map_Data_ID.toString(), e.County, e.Color))    
      })
    })
  }

  getUKMapData() {
    this.test.length = 0;
    this._interactiveMapService.getUKMapData()
    .subscribe((data: any[]) => {
      data.forEach(e => {
        this.test.push(new CountyDetails(e.Map_Data_ID.toString(), e.Country, e.Color));      
      }) 
    });
  }

  getScotlandMapData() {
    this.test.length = 0;
    this._interactiveMapService.getScotlandMapData()
    .subscribe((data: any[]) => {
      data.forEach(e => {
        this.test.push(new CountyDetails(e.Map_Data_ID.toString(), e.County, e.Color));
      })
    })
  }

  getWalesMapData() {
    this.test.length = 0;
    this._interactiveMapService.getWalesMapData()
    .subscribe((data: any[]) => {
      data.forEach(e => {
        this.test.push(new CountyDetails(e.Map_Data_ID.toString(), e.County, e.Color));
      })
    })
  }

  getEnglandMapData() {
    this.test.length = 0;
    this._interactiveMapService.getEnglandMapData()
    .subscribe((data: any[]) => {
      data.forEach(e => {
        this.test.push(new CountyDetails(e.Map_Data_ID.toString(), e.County, e.Color));
      })
    })
  }

  populateResultsTable(event: string) { 
    this.siteDetailTable.emit(event);
  }

  update($event) {
    // Run inside angular context
    this.zone.run(() => {
      this.selectMap($event.dataObj.label);
    })
  }

}

子(InteractiveMapComponent)html:

    <div class="mapContainer">
    <div class="interactiveMapHeader">
        <span>{{mapType}}</span>
        <div *ngIf="mapType != 'Site Locations'">
            <button mat-raised-button (click)="ukMap()">Back</button>
        </div>
    </div>

    <div [ngSwitch]="mapType">
        <div *ngSwitchCase="'Scotland'">
            <fusioncharts
            width = "500"
            height = "900"
            type="maps/scotland"
            [dataSource]="dataSource"
            (entityClick)="populateResultsTable($event)"
            >
            </fusioncharts>
        </div>

        <div *ngSwitchCase="'Wales'">
            <fusioncharts
            width = "500"
            height = "900"
            type="maps/wales"
            [dataSource]="dataSource"
            (entityClick)="populateResultsTable($event)"
            >
            </fusioncharts>
        </div>

        <div *ngSwitchCase="'England'">     
            <fusioncharts
            width = "500"
            height = "900"
            type="maps/england"
            [dataSource]="dataSource"
            (entityClick)="populateResultsTable($event)"
            >
            </fusioncharts>
        </div>

        <div *ngSwitchCase="'Northern Ireland'">
            <fusioncharts
            width = "500"
            height = "900"
            type="maps/northernireland"
            [dataSource]="dataSource"
            (entityClick)="populateResultsTable($event)"
            >
            </fusioncharts>
        </div>

        <div *ngSwitchDefault>
            <fusioncharts
            width = "500"
            height = "1000"
            type="maps/uk7"
            [dataSource]="dataSource"
            (entityClick)="update($event)"
            >
            </fusioncharts>
        </div>
    </div>          

</div>

子(MapResultComponent)TS:

import { Component, OnInit, ViewChild, Output, EventEmitter, ChangeDetectorRef, Input, NgZone } from '@angular/core';
import { MatTableDataSource, MatPaginator, MatDialogConfig, MatDialog } from '@angular/material';
import { NewSiteDialogComponent } from '../new-site-dialog/new-site-dialog.component';
import { siteDetails } from '../shared/interfaces/site-details.interface';
import { SiteService } from '../shared/services/site-service';

@Component({
  selector: 'app-map-result',
  templateUrl: './map-result.component.html',
  styleUrls: ['./map-result.component.scss']
})
export class MapResultComponent implements OnInit {

  displayedColumns: string[] = ['name', 'address1', 'moreDetails'];
  number: number;
  mapResultHeader: string;
  result : string;
  mapResults: Array<{name: string, address: string}> = [];
  dataSource : MatTableDataSource<any>;
  siteResults: Array<{Site_ID: number, Address1: string, Address2: string, Town: string, County: string, PostCode: string, Name: string, Description: string, TelephoneNumber: string}> = [];

  @Output() moreDetails = new EventEmitter();

  @ViewChild(MatPaginator, {static: true}) paginator: MatPaginator;

  constructor(public dialog: MatDialog,
    private _siteService: SiteService) {
  }

  loadMoreDetailsComp(changed: string) {
    this.moreDetails.emit(changed);
  }

  ngOnInit() {
    this.dataSource = new MatTableDataSource();
    this.mapResultHeader = "Kent";
  }

    /**
   * Set the paginator and sort after the view init since this component will
   * be able to query its view for the initialized paginator and sort.
   */
  ngAfterViewInit() {
    this.dataSource.paginator = this.paginator;
  }

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

  openNewSiteDialog(){
    const dialogConfig = new MatDialogConfig();

    dialogConfig.autoFocus = true;
    dialogConfig.disableClose = true;

    this.dialog.open(NewSiteDialogComponent, dialogConfig);
  }

}

export interface Data {
  name: string;
  town: string;
  address: string;
  telephone: string;
}

子(MapResultComponent)html:

<div class="resultsContainer">
    <div class="mapResultHeader">
        <span>{{mapResultHeader}}</span>
    </div>

    <div class="mat-elevation-z8">

            <mat-form-field class="filter">
                <input matInput (keyup)="applyFilter($event.target.value)" placeholder="Filter">
            </mat-form-field>

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

            <ng-container matColumnDef="name">
                <th mat-header-cell *matHeaderCellDef> Site Name </th>
                <td mat-cell *matCellDef="let element"> {{element.Name}}</td>
            </ng-container>

            <ng-container matColumnDef="address1">
                <th mat-header-cell *matHeaderCellDef> Address </th>
                <td mat-cell *matCellDef="let element"> {{element.Address1}}</td>
            </ng-container>

            <ng-container matColumnDef="moreDetails">
                <th mat-header-cell *matHeaderCellDef> More Details </th>
                <td mat-cell *matCellDef="let element"> 
                    <button mat-raised-button (click)="loadMoreDetailsComp($event)">More Details</button> 
                </td>
            </ng-container>

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

            <mat-paginator [pageSize]="[5]" showFirstLastButtons></mat-paginator>

    </div>

    <div class="addNewSite">
        <button mat-raised-button (click)="openNewSiteDialog()">Add New Site</button>
    </div>
</div>

我对此感到非常困惑,我假设它与异步行为有关,但我无法弄清楚

标签: javascripthtmlangularangular-material

解决方案


看起来数据源是在订阅块之外设置的,这导致它在数据加载之前被设置。您应该能够像这样将该行移动到订阅块中:

populateResults(countyName) {
    this.MapResultComponent.mapResultHeader = countyName.dataObj.label;
    this.MapResultComponent.siteResults.length = 0;
    this._siteService.getSites(countyName.dataObj.label)
        .subscribe((data: any[]) => {
            data.forEach(e => {
                this.MapResultComponent.siteResults.push(new siteDetails(e.Site_ID, e.Address1, e.Address2, e.Town, e.County, e.PostCode, e.Name, e.Description, e.TelephoneNumber));
            });
            this.MapResultComponent.dataSource = new MatTableDataSource(this.MapResultComponent.siteResults);
        });
}

如果您收到“更改检测后 x 值已更改”错误,您之后可能还需要changeDetectorRef.detectChanges()致电,因为这看起来确实是一连串更新。


推荐阅读