简体   繁体   中英

Angular - Populating Material Table with JSON

I'm trying to build up my first website and I need something quite simple, table with CRUD operations. I'm absolute beginner with Angular and JS in general but after watching few tutorials I managed to work something out. Now I'm stuck while trying to populate material table with JSON data. If I manually set data it works (code below), but I can't figure out how to do it with JSON. Since I need filter, sort and paging as template I've used "rich table" demo example and some code from this question.

I don't think you need html part, but if you do I can update:

import {Component, ElementRef, OnInit, ViewChild} from '@angular/core';
import {DataSource} from '@angular/cdk/collections';
import {Http, Response, RequestOptions, Headers, Request, RequestMethod} from '@angular/http';
import {MatPaginator, MatSort} from '@angular/material';
import {SelectionModel} from '@angular/cdk/collections';
import {BehaviorSubject} from 'rxjs/BehaviorSubject';
import {Observable} from 'rxjs/Observable';
import 'rxjs/add/operator/startWith';
import 'rxjs/add/observable/merge';
import 'rxjs/add/observable/fromEvent';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/distinctUntilChanged';
import 'rxjs/add/operator/debounceTime';
import 'rxjs/add/observable/of';
import 'rxjs/add/operator/catch';
import 'rxjs/add/operator/startWith';
import 'rxjs/add/operator/switchMap';

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

export class PregledComponent implements OnInit {
  displayedColumns = ['userId', 'userName', 'progress', 'color'];
  exampleDatabase: ExampleDatabase | null;
  selection = new SelectionModel<string>(true, []);
  dataSource: ExampleDataSource | null;


  constructor(private http: Http) {
  }

  @ViewChild(MatPaginator) paginator: MatPaginator;
  @ViewChild(MatSort) sort: MatSort;
  @ViewChild('filter') filter: ElementRef;

  ngOnInit() {
    this.loadData();
  }

  loadData() {
    this.exampleDatabase = new ExampleDatabase(this.http);
    this.dataSource = new ExampleDataSource(this.exampleDatabase, this.paginator, this.sort);
    Observable.fromEvent(this.filter.nativeElement, 'keyup')
      .debounceTime(150)
      .distinctUntilChanged()
      .subscribe(() => {
        if (!this.dataSource) {
          return;
        }
        this.dataSource.filter = this.filter.nativeElement.value;
      });
  }

  isAllSelected(): boolean {
    if (!this.dataSource) { return false; }
    if (this.selection.isEmpty()) { return false; }

    if (this.filter.nativeElement.value) {
      return this.selection.selected.length === this.dataSource.renderedData.length;
    } else {
      return this.selection.selected.length === this.exampleDatabase.data.length;
    }
  }

  masterToggle() {
    if (!this.dataSource) { return; }

    if (this.isAllSelected()) {
      this.selection.clear();
    } else if (this.filter.nativeElement.value) {
      this.dataSource.renderedData.forEach(data => this.selection.select(data.id));
    } else {
      this.exampleDatabase.data.forEach(data => this.selection.select(data.id));
    }
  }
}

export interface UserData {
  id: string;
  location: string;
  title: string;
  color: string;
}

/** An example database that the data source uses to retrieve data for the table. */
export class ExampleDatabase {
  /** Stream that emits whenever the data has been modified. */
  dataChange: BehaviorSubject<UserData[]> = new BehaviorSubject<UserData[]>([]);

  private issuesUrl = 'https://mydemourl...com/angular/getdata.php';  // URL to web API
  getRepoIssues(): Observable<UserData[]> {
    return this.http.get(this.issuesUrl)
      .map(this.extractData);
  }

  extractData(result: Response): UserData[] {
    return result.json().map(issue => {
      return {
        id: issue.id,
        location: issue.location,
        title: issue.title,
        color: issue.color,
      };
    });
  }


/*THIS IS A ISSUE HERE
My best guess was:

get data(): UserData [] {return.this.getRepoIssues}

but I get error with observables...

*/

  get data(): UserData [] {
    const data = [
      {
        "id": "17-July-2017",
        "location": "10:00 AM",
        "title": "06:00 PM",
        "color": "8 Hours",
      },
      {
        "id": "17-July1-2017",
        "location": "10:00 AM",
        "title": "06:00 PM",
        "color": "8 Hours",
      },
      {
        "id": "test",
        "location": "123",
        "title": "06:00 PM",
        "color": "bluee",
      }]

    return data;
  }

  constructor(private http: Http) {

    this.dataChange.next(this.data);
  }

}

/**

/** An example database that the data source uses to retrieve data for the table.
export class ExampleDatabase {

  dataChange: BehaviorSubject<UserData[]> = new BehaviorSubject<UserData[]>([]);
  // get data(): UserData[] { return this.dataChange.value; }
  get data(): UserData[]{
    let data = [this.getRepoIssues()];
    return this.dataChange.value;
  }
  private issuesUrl = 'https://antonio1991.000webhostapp.com/angular/getdata.php';  // URL to web API

  getRepoIssues(): Observable<UserData[]> {
    return this.http.get(this.issuesUrl)
      .map(this.extractData);
  }

  extractData(result: Response): UserData[] {
    return result.json().map(issue => {
      return {
        id: issue.id,
        location: issue.location,
        title: issue.title,
        color: issue.color,
      };
    });
  }

  constructor(private http: Http) {}

}
*/



/**
 * Data source to provide what data should be rendered in the table. Note that the data source
 * can retrieve its data in any way. In this case, the data source is provided a reference
 * to a common data base, ExampleDatabase. It is not the data source's responsibility to manage
 * the underlying data. Instead, it only needs to take the data and send the table exactly what
 * should be rendered.
 */
export class ExampleDataSource extends DataSource<UserData> {
  _filterChange = new BehaviorSubject('');
  get filter(): string { return this._filterChange.value; }
  set filter(filter: string) { this._filterChange.next(filter); }

  filteredData: UserData[] = [];
  renderedData: UserData[] = [];

  constructor(private _exampleDatabase: ExampleDatabase,
              private _paginator: MatPaginator,
              private _sort: MatSort) {
    super();
    // Reset to the first page when the user changes the filter.
    this._filterChange.subscribe(() => this._paginator.pageIndex = 0);
  }

  /** Connect function called by the table to retrieve one stream containing the data to render. */
  connect(): Observable<UserData[]> {
    // Listen for any changes in the base data, sorting, filtering, or pagination
    const displayDataChanges = [
      this._sort.sortChange,
      this._filterChange,
      this._paginator.page,
    ];

    return Observable.merge(...displayDataChanges).map(() => {
      // Filter data
      this.filteredData = this._exampleDatabase.data.slice().filter((item: UserData) => {
        const searchStr = (item.id + item.location).toLowerCase();
        return searchStr.indexOf(this.filter.toLowerCase()) !== -1;
      });

      // Sort filtered data
      const sortedData = this.sortData(this.filteredData.slice());

      // Grab the page's slice of the filtered sorted data.
      const startIndex = this._paginator.pageIndex * this._paginator.pageSize;
      this.renderedData = sortedData.splice(startIndex, this._paginator.pageSize);
      return this.renderedData;
    });
  }

  disconnect() {}

  /** Returns a sorted copy of the database data. */
  sortData(data: UserData[]): UserData[] {
    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 'userId': [propertyA, propertyB] = [a.id, b.id]; break;
        case 'userName': [propertyA, propertyB] = [a.location, b.location]; break;
        case 'progress': [propertyA, propertyB] = [a.title, b.title]; break;
        case 'color': [propertyA, propertyB] = [a.color, b.color]; break;
      }

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

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

The angular material documentation leaves much to be desired. There are some links out there that helped me put something like this together:

The problem is that you are trying to do the HTTP get in the getter method for data, which should return a list of UserData and not a http response. Also, you only want to call your request once and not every time an observer connects to the datasource.

If you want to do the ajax call on init of your component, you can put it in the connect() method of the DataSource.

Add the ExampleDatabase dataChange Observable to the displayDataChanges list and call the http GET in the connector of the DataSource:

connect(): Observable<UserData[]> {
    // Listen for any changes in the base data, sorting, filtering, or pagination
    const displayDataChanges = [
      this._exampleDatabase.dataChange,
      this._sort.sortChange,
      this._filterChange,
      this._paginator.page,
    ];

    this._exampleDatabase. getRepoIssues()

In your exampleDatabase, getRepoIssues does the HTTP call and sets the data in the Observable dataChange , while the getter data returns the value of the Observable:

export class ExampleDatabase {
  /** Stream that emits whenever the data has been modified. */
  dataChange: BehaviorSubject<UserData[]> = new BehaviorSubject<UserData[]>([]);

  get data(): UserData[]{
    return this.dataChange.value;
  }

  private issuesUrl = 'https://mydemourl...com/angular/getdata.php';  // URL to web API
  getRepoIssues(): void {
    const myData = this.http.get(this.issuesUrl)
                   .map(this.extractData);

    myData.subscribe(
        result => {
            this.dataChange.next(result);
        },
    );
  }

  extractData(result: Response): UserData[] {
    return result.json().map(issue => {
      return {
        id: issue.id,
        location: issue.location,
        title: issue.title,
        color: issue.color,
      };
    });
  }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM