Angular dataSource не загружается в HTML шаблоне - PullRequest
0 голосов
/ 06 августа 2020

Я пытаюсь загрузить данные из файла Excel в приложение angular. Я могу загрузить данные Excel, проанализировать их, чтобы получить только нужные мне столбцы, и сохранить их в массиве. Это сделано в моем файле service.ts.

Служба вызывается из файла component.ts в качестве возврата. Я подтверждаю, что получаю свои данные с помощью console.log ().

Я использую @Output () для отправки данных дочернему компоненту для отображения данных как MatTableDataSource.

Я знаю, что шаблон html работает, поскольку я поместил данные на json -сервер и вызвал его оттуда, используя службу для возврата данных.

Я пробовал несколько разных модов, чтобы увидеть почему источник данных не отображается в html. Мой экран html показывает, что у меня есть несколько записей, но не отображаются строки данных.

service.ts

import { Injectable, EventEmitter, Output } from '@angular/core';
import * as XLSX from 'xlsx';
import { BoMItem } from '../_models/bomItem';
import { Observable, of } from 'rxjs';

const BOM_DATA: BoMItem[] = [];

@Injectable({
  providedIn: 'root'
})
export class ExcelService {

  fileUploaded: File;
  storeData: any;
  worksheet: any;
  jsonData: any;

  myData = {} as BoMItem;

  constructor() { }

  readExcel(filesUploaded: FileList): Observable<any> {
    // console.log(filesUploaded);
    for (const file in filesUploaded) {
      if (Object.prototype.hasOwnProperty.call(filesUploaded, file)) {
        let importFile: File = filesUploaded[file];
        // console.log('File: ', importFile);
        let readFile = new FileReader();
        readFile.onload = (e) => {
          this.storeData = readFile.result;
          let data = new Uint8Array(this.storeData);
          let arr = new Array();
          for (let i = 0; i !== data.length; ++i) {
            arr[i] = String.fromCharCode(data[i]);
          }
          let bstr = arr.join('');
          let workbook = XLSX.read(bstr, { type: 'binary' });
          let firstSheetName = workbook.SheetNames[0];
          this.worksheet = workbook.Sheets[firstSheetName];
          this.createJsonData();
        };
        readFile.readAsArrayBuffer(importFile);
      }
    }
    return of(BOM_DATA);
  }

  createJsonData() {
    // : Observable<BoMItem[]>
    this.jsonData = XLSX.utils.sheet_to_json(this.worksheet, { raw: false });

    this.jsonData.forEach(element => {
      this.myData = {} as BoMItem;
      for (const key in element) {
        if (Object.prototype.hasOwnProperty.call(element, key)) {
          const item = element[key];
          if (key === 'DESC' || key === 'Description' || key === 'Disc') {
            this.myData.desc = item;
          }
          if (key === 'PART NUMBER' || key === 'MFG PART NUMBER' || key === 'Catalog') {
            this.myData.partNumber = item;
          }
          if (key === 'MFG' || key === 'Manufacturer' || key === 'mfg') {
            this.myData.mfg = item;
          }
          if (key === 'QTY' || key === 'qty' || key === 'Quantity') {
            this.myData.qty = item;
          }
        }
      }
      BOM_DATA.push(this.myData);
    });
    // console.log('BOM_DATA: ', BOM_DATA);
    return of(BOM_DATA);
  }

model.ts

export interface BoMItem {
    desc: string;
    partNumber: string;
    mfg: string;
    qty: string;
    supplierName?: string;
  }

parent component.ts

import { Component, OnInit, Output } from '@angular/core';
import { ExcelService } from 'src/app/_services/excel.service';

@Component({
  selector: 'app-bom-load',
  templateUrl: './bom-load.component.html',
  styleUrls: ['./bom-load.component.css']
})
export class BomLoadComponent implements OnInit {
  @Output()
  data: any;

  constructor(
    private excelService: ExcelService
  ) { }

  ngOnInit(): void {
  }

  uploadedFile(evt) {
    return this.excelService.readExcel(evt.target.files).subscribe((response: any) => {
      this.data = response;
      console.log('bom-load: ', this.data);
    });
  }
}

child component.ts

import { Component, ViewChild, OnInit, Input } from '@angular/core';
import { MatPaginator } from '@angular/material/paginator';
import { MatTableDataSource } from '@angular/material/table';
import { NgForm } from '@angular/forms';
import * as _ from 'lodash';
import { BoMItem } from 'src/app/_models/bomItem';
import { HttpClient } from '@angular/common/http';
import { BomService } from 'src/app/_services/bom.service';
import { MatSort } from '@angular/material/sort';



@Component({
  selector: 'app-bom-list',
  templateUrl: './bom-list.component.html',
  styleUrls: ['./bom-list.component.css']
})
export class BomListComponent implements OnInit {
  @ViewChild('bomForm', { static: false})
  bomForm: NgForm;

  bomData: BoMItem;

  @ViewChild(MatPaginator, { static: true }) paginator: MatPaginator;
  @ViewChild(MatSort, {static: true}) sort: MatSort;
  displayedColumns: string[] = ['desc', 'partNumber', 'mfg', 'qty', 'supName', 'actions'];
  // displayedColumns: string[] = ['desc', 'partNumber', 'mfg', 'qty', 'actions'];

  @Input()
  data: any[];

  dataSource: MatTableDataSource<BoMItem>;

  isEditMode = false;

  constructor(private http: HttpClient, private bomService: BomService) {
    this.bomData = {} as BoMItem;
  }

  ngOnInit() {
    this.dataSource = new MatTableDataSource(this.data);
    // this.getAllBomItems();
    console.log('bom-list: ', this.dataSource);
    // this.dataSource.paginator = this.paginator;
    // this.dataSource.sort = this.sort;
  }

  getAllBomItems() {
    this.bomService.getList().subscribe((response: any) => {
      this.dataSource.data = response;
    });
  }

  editItem(element) {
    this.bomData = _.cloneDeep(element);
    this.isEditMode = true;
  }

  cancelEdit() {
    this.isEditMode = false;
    this.bomForm.resetForm();
  }

  onSubmit() {
    if (this.bomForm.form.valid) {
      if (this.isEditMode) {
        this.updateBomItem();
      }
      else {
        this.addBomItem();
      }
    } else {
      console.log('Enter valid data!');
    }
  }

  addBomItem() {
    throw new Error('Method not implemented.');
  }

  updateBomItem() {
    this.bomService.updateItem(this.bomData.partNumber, this.bomData).subscribe((response: any) => {
      this.dataSource.data = this.dataSource.data.map((o: BoMItem) => {
        if (o.partNumber === response.partNumber) {
          o = response;
        }
        return o;
      });
      this.getAllBomItems();
    });
  }

}

родительский компонент. html

<mat-toolbar>
    <input type="file" class="form-control" (change)="uploadedFile($event)" placeholder="Upload file"
        accept=".xls,.xlsx" multiple>
</mat-toolbar>

<div *ngIf="data != null">
    <app-bom-list [data]="data"></app-bom-list>
</div>

дочерний компонент. html

<div class="container">
   
        <ng-container *ngIf="isEditMode;">
            <button mat-button color="warn">Update</button>
            <a mat-button color="warn" (click)="cancelEdit()">Cancel</a>
        </ng-container>
    </form> -->

    <table mat-table [dataSource]="dataSource" class="mat-elevation-z8">
        <ng-container matColumnDef="desc">
            <th mat-header-cell *matHeaderCellDef>Description</th>
            <td mat-cell *matCellDef="let element">{{element.desc }}</td>
        </ng-container>

        <ng-container matColumnDef="partNumber">
            <th mat-header-cell *matHeaderCellDef>Part Number</th>
            <td mat-cell *matCellDef="let element"> {{element.partNumber}} </td>
        </ng-container>

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

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

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

        <ng-container matColumnDef="actions">
            <mat-header-cell *matHeaderCellDef>
                <!-- <button mat-icon-button color="primary">
                    <mat-icon aria-label="Example icon-button with a heart icon">add</mat-icon>
                </button> -->
            </mat-header-cell>

            <mat-cell *matCellDef="let element; let i=index;">
                <button mat-icon-button color="accent">
                    <mat-icon aria-label="Edit" (click)="editItem(element)">edit</mat-icon>
                </button>

                <button mat-icon-button color="accent">
                    <mat-icon aria-label="Delete">delete</mat-icon>
                </button>
            </mat-cell>
        </ng-container>

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

    </table>

    <mat-paginator [length]="dataSource?.data.length" [pageSize]="5" [pageSizeOptions]="[5, 10, 25, 100, 500]">
    </mat-paginator>

</div>

Информация журнала консоли:

bom-load:  
[]
0: {qty: "6", mfg: "Mencom", partNumber: "1321-3R8-C", desc: "480V 3-PHASE AC DRIVE REACTORS, OPEN TYPE, 8A, 3 HP Drive"}
1: {qty: "1", mfg: "AB", partNumber: "1756-EN2T", desc: "1756 CONTROL LOGIX ETHERNET / IP BRIDGE MODULE (128 TCP/IP CONNECTIONS)"}
2: {qty: "1", mfg: "AB", partNumber: "1756-EN2TR", desc: "1756 CONTROL LOGIX ETHERNET / IP BRIDGE MODULE (128 TCP/IP CONNECTIONS) REDUNDANT"}
3: {qty: "1", mfg: "AB", partNumber: "1756-L81E", desc: "1756 CONTROL LOGIX CONTROLLER, USER MEMORY 3MB, 1Gb ENET PORT"}
4: {qty: "6", mfg: "AB", partNumber: "25B-D6P0N104", desc: "POWERFLEX 525 VARIABLE FREQUENCY DRIVE 3 HP, 480VAC 3 PH, FRAME A"}
5: {qty: "6", mfg: "AB", partNumber: "25-COMM-E2P", desc: "POWERFLEX 525 DUAL-PORT ETHERNET/IP MODULE"}
length: 6
__proto__: Array(0)
bom-list.component.ts:43 bom-list:  
MatTableDataSource {_renderData: BehaviorSubject, _filter: BehaviorSubject, _internalPageChanges: Subject, _renderChangesSubscription: Subscriber, sortingDataAccessor: ƒ, …}
filterPredicate: (data, filter) => {…}
filteredData: (6) [{…}, {…}, {…}, {…}, {…}, {…}]
sortData: (data, sort) => {…}
sortingDataAccessor: (data, sortHeaderId) => {…}
_data: BehaviorSubject {_isScalar: false, observers: Array(1), closed: false, isStopped: false, hasError: false, …}
_filter: BehaviorSubject {_isScalar: false, observers: Array(1), closed: false, isStopped: false, hasError: false, …}
_internalPageChanges: Subject {_isScalar: false, observers: Array(0), closed: false, isStopped: false, hasError: false, …}
_renderChangesSubscription: Subscriber {closed: false, _parentOrParents: null, _subscriptions: Array(1), syncErrorValue: null, syncErrorThrown: false, …}
_renderData: BehaviorSubject {_isScalar: false, observers: Array(1), closed: false, isStopped: false, hasError: false, …}
data: Array(6)
0: {qty: "6", mfg: "Mencom", partNumber: "1321-3R8-C", desc: "480V 3-PHASE AC DRIVE REACTORS, OPEN TYPE, 8A, 3 HP Drive"}
1: {qty: "1", mfg: "AB", partNumber: "1756-EN2T", desc: "1756 CONTROL LOGIX ETHERNET / IP BRIDGE MODULE (128 TCP/IP CONNECTIONS)"}
2: {qty: "1", mfg: "AB", partNumber: "1756-EN2TR", desc: "1756 CONTROL LOGIX ETHERNET / IP BRIDGE MODULE (128 TCP/IP CONNECTIONS) REDUNDANT"}
3: {qty: "1", mfg: "AB", partNumber: "1756-L81E", desc: "1756 CONTROL LOGIX CONTROLLER, USER MEMORY 3MB, 1Gb ENET PORT"}
4: {qty: "6", mfg: "AB", partNumber: "25B-D6P0N104", desc: "POWERFLEX 525 VARIABLE FREQUENCY DRIVE 3 HP, 480VAC 3 PH, FRAME A"}
5: {qty: "6", mfg: "AB", partNumber: "25-COMM-E2P", desc: "POWERFLEX 525 DUAL-PORT ETHERNET/IP MODULE"}
length: 6
__proto__: Array(0)
filter: (...)
paginator: (...)
sort: (...)
__proto__: DataSource

Изображение html таблицы, пытающейся использовать данные, загруженные из Excel Screen image of table showing pagination count but no rows

Image of static data using this.getAllBomItems Изображение данных stati c с использованием this.getAllBomItems ()

Интересная разработка:

добавлением; как было предложено; ngOnChanges (), теперь я получаю данные, но ТОЛЬКО после нажатия кнопок пагинации.

  ngOnChanges() {
    this.dataSource = new MatTableDataSource(this.data);
    this.dataSource.paginator = this.paginator;
    console.log('bom-list: ', this.dataSource);
    console.log('this.data: ', this.data);
    this.dataSource.paginator = this.paginator;
  }

Ответы [ 2 ]

1 голос
/ 06 августа 2020

Обновить

Вместо использования ngOnInit попробуйте использовать ngOnChanges в своем дочернем компоненте.

И попробуйте использовать ChangeDetectorRef.

constructor (
  ...
  private cdr: ChangeDetectorRef
) {}

ngOnChanges() {
    this.dataSource = new MatTableDataSource(this.data);
    this.dataSource.paginator = this.paginator;
    this.cdr.detectChanges();
}

Примечание

<div *ngIf="data != null">

Измените строку выше на:

<div *ngIf="data">

Значение ваших данных undefined, поэтому *ngIf="data != null" это выражение будет правдой. Таким образом, ngOnInit вашего дочернего компонента будет вызываться без data.

Также вам необходимо обновить приведенный ниже код:

getAllBomItems() {
    this.bomService.getList().subscribe((response: any) => {
      this.dataSource = new MatTableDataSource(response);
      // this.dataSource.data = response; <- your table will not detect change with this line, you must replace the whole dataSource object.
    });
  }
0 голосов
/ 06 августа 2020

попробуйте ввести ChangeDetectorRef в свой компонент, содержащий ваши данные, и вызовите его следующим образом:

  constructor(private cdRef: ChangeDetectorRef) {
  }

//after add new data to your table
this.cdRef.detectChanges();

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...