2017-11-22 25 views
5

Sau vài tuần của Googling và chỉ có một câu hỏi Stackoverflown cho đến nay cuối cùng tôi đã xây dựng được Angular CRUD App sử dụng Thành phần bảng vật liệu của mình. Nó cho thấy dữ liệu từ backend (JSON) và cho các hoạt động CRUD tôi đang sử dụng các hộp thoại như một hiển thị trên hình ảnh (đây là chỉnh sửa, xin lỗi cho Croatia). Hộp thoại có thể không phải là cách tốt nhất để thực hiện, chỉnh sửa nội tuyến có thể tốt hơn. Tuy nhiên, để thêm mục mới, bạn cần một cái gì đó giống như hộp thoại.Góc - Bảng vật liệu, có thể cập nhật các hàng mà không làm mới toàn bộ bảng không?

enter image description here

Điều cuối cùng tôi bị mắc kẹt là cách cập nhật các trường trong bảng tương ứng. Vì vậy, khi bạn nhấn 'Lưu' trên hộp thoại, dữ liệu sẽ được cập nhật trong chương trình phụ trợ (trong bảng MySQL) nhưng không được đặt trước. Trong thời gian này tôi có cách giải quyết xấu cho việc này, mỗi lần bạn cập nhật, nó cũng làm mới toàn bộ bảng.

Dù sao đây là mã:

phần Bảng:

export class BazaComponent implements OnInit { 
    .... 
    constructor(public httpClient: HttpClient, public dialog: MatDialog) { 
    } 

    ngOnInit() { 
    this.loadData(); 
    } 

    // TODO: Simplfy this... 
    addNew(ident: number, naziv: string, mt: number, kutija: number, 
     komada: number, jm: string, orginal: number, lokacija: number, napomena: string) { 
    console.log('add new clicked'); 
    const dialogRef = this.dialog.open(AddDialogComponent, { 
     data: {ident: ident, naziv: naziv, mt: mt, kutija: kutija, 
     komada: komada, jm: jm, orginal: orginal, lokacija: lokacija, napomena: napomena } 
    }); 

    dialogRef.afterClosed().subscribe(result => { 
     console.log(result); 
     if (result === 1) { 
     this.loadData(); // --> This is a temp workaround, every time when I do CRUD operation just redraw whole thing again 
     } 
    }); 
    } 

    startEdit(id: number, ident: number, naziv: string, mt: number, kutija: number, 
      komada: number, jm: string, orginal: number, lokacija: number, napomena: string) { 

    const dialogRef = this.dialog.open(EditDialogComponent, { 
     data: {id: id, ident: ident, naziv: naziv, mt: mt, kutija: kutija, 
     komada: komada, jm: jm, orginal: orginal, lokacija: lokacija, napomena: napomena} 
    }); 

    dialogRef.afterClosed().subscribe(result => { 
     if (result === 1) { 
     this.loadData(); // --> This is a temp workaround, every time when I do CRUD operation just redraw whole thing again 
     } 
    }); 
    } 

    deleteItem(id: number, ident: number, naziv: string, mt: number) { 
    const dialogRef = this.dialog.open(DeleteDialogComponent, { 
     data: {id: id, ident: ident, naziv: naziv, mt: mt} 
    }); 

    dialogRef.afterClosed().subscribe(result => { 
     if (result === 1) { 
     this.loadData(); 
     } 
    }); 
    } 


    public loadData() { 
    this.exampleDatabase = new DataService(this.httpClient); 
    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; 
     }); 
    } 
} 


export class ExampleDataSource extends DataSource<Baza> { 
    _filterChange = new BehaviorSubject(''); 

    get filter(): string { 
    return this._filterChange.value; 
    } 

    set filter(filter: string) { 
    this._filterChange.next(filter); 
    } 

    filteredData: Baza[] = []; 
    renderedData: Baza[] = []; 

    constructor(private _exampleDatabase: DataService, 
       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<Baza[]> { 
    // 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.getAllItems(); 

    return Observable.merge(...displayDataChanges).map(() => { 
     // Filter data 
     this.filteredData = this._exampleDatabase.data.slice().filter((item: Baza) => { 
     const searchStr = (item.ident + item.naziv + item.mt + item.lokacija + item.napomena).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: Baza[]): Baza[] { 
    ... sort stuff 
} 

Dưới đây là DataService nơi tôi đoán tôi nên làm cập nhật lĩnh vực:

import { Injectable } from '@angular/core'; 
import { HttpClient, HttpErrorResponse, HttpHeaders} from '@angular/common/http'; 
import { Baza } from '../models/kanban.baza'; 
import { BehaviorSubject } from 'rxjs/BehaviorSubject'; 

    @Injectable() 
    export class DataService { 
     private readonly API_URL = 'http://localhost/api/' 

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

     constructor(private httpClient: HttpClient) { 
     } 

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

     getAllItems(): void { 
     this.httpClient.get<Baza[]>(this.API_URL).subscribe(data => { 
      this.dataChange.next(data['items']); 
     }); 
     } 

    addItem(baza: Baza): void { 
     this.httpClient.post(this.API_URL, Baza).subscribe(data => { 
      //THIS WAS MY BEST TRY BUT IT DOESN'T WORK :(
      const copiedData = this.data.slice(); 
      copiedData.push(baza); 
      console.log(copiedData); 
      this.dataChange.next(copiedData); 
     }); 
    } 


     updateItem(baza: Baza): void { 
     this.httpClient.put(this.API_URL + baza.id, baza).subscribe(); 
     } 

     deleteItem(id: number): void { 
     this.httpClient.delete(this.API_URL + id, {headers: new HttpHeaders().set('Access-Control-Allow-Origin', '*')}).subscribe(); 
    } 
} 

CẬP NHẬT 2017/11/27:

Được rồi, cuối cùng tôi đã tìm ra cách để t rigger thêm hàng mới. Tôi đã phải gọi dataChange.value bên trong thành phần bảng. Khi bạn tải nó với một số dòng dữ liệu mới sẽ xuất hiện tức thời.

const data = {id: 208, ident: 233, naziv: 'test', mt: 291, komada: 2, jm: 'a', orginal: 100, lokacija: 3, napomena: 'pls work'}; 
this.exampleDatabase.dataChange.value.push(data); 

Cùng một điều trong DataService sẽ không làm việc:

this.dataChange.value.push(data); 

Plunker là ở đây:

https://plnkr.co/edit/IWCVsBRl54F7ylGNIJJ3?p=info

EDIT 2017/11/28:

Bây giờ chỉ điều bên trái là xây dựng lo gic để thêm, chỉnh sửa và xóa. Để thêm vào dễ dàng, nó chỉ là `value.push (dữ liệu) '. Cảm ơn vì đã giúp mọi người.

Trả lời

7

Hãy cho tôi một chút thời gian nhưng cuối cùng tôi đã có mọi thứ hoạt động. Câu trả lời của bạn và các cách tiếp cận khác nhau đã giúp ích. Vì vậy, đây là thực hiện CRUD của tôi nếu có ai gặp rắc rối với điều này:

https://github.com/marinantonio/angular-mat-table-crud

Ảnh chụp màn hình: Alt Text

Hoặc bạn có thể kiểm tra bản demo dự án: https://marinantonio.github.io/angular-mat-table-crud/

phần chính là trong bảng. ts tệp:

.... 
addNew(issue: Issue) { 
    const dialogRef = this.dialog.open(AddDialogComponent, { 
     data: {issue: issue } 
    }); 

    dialogRef.afterClosed().subscribe(result => { 
     if (result === 1) { 
     this.exampleDatabase.dataChange.value.push(this.dataService.getDialogData()); 
     this.refreshTable(); 
     } 
    }); 
    } 

    startEdit(i: number, id: number, title: string, state: string, url: string, created_at: string, updated_at: string) { 
    this.index = i; 
    this.id2 = id; 
    console.log(this.index); 
    const dialogRef = this.dialog.open(EditDialogComponent, { 
     data: {id: id, title: title, state: state, url: url, created_at: created_at, updated_at: updated_at} 
    }); 

    dialogRef.afterClosed().subscribe(result => { 
     if (result === 1) { 
     // Part where we do frontend update, first you need to find record using id 
     const foundIndex = this.exampleDatabase.dataChange.value.findIndex(x => x.id === this.id2); 
     // Then you update that record using dialogData 
     this.exampleDatabase.dataChange.value[foundIndex] = this.dataService.getDialogData(); 
     // And lastly refresh table 
     this.refreshTable(); 
     } 
    }); 
    } 

    deleteItem(i: number, id: number, title: string, state: string, url: string) { 
    this.index = i; 
    this.id2 = id; 
    const dialogRef = this.dialog.open(DeleteDialogComponent, { 
     data: {id: id, title: title, state: state, url: url} 
    }); 

    dialogRef.afterClosed().subscribe(result => { 
     if (result === 1) { 
     const foundIndex = this.exampleDatabase.dataChange.value.findIndex(x => x.id === this.id2); 
     this.exampleDatabase.dataChange.value.splice(foundIndex, 1); 
     this.refreshTable(); 
     } 
    }); 
    } 


    private refreshTable() { 
    // If there's no data in filter we do update using pagination, next page or previous page 
    if (this.dataSource._filterChange.getValue() === '') { 
     if (this.dataSource._paginator.pageIndex === 0) { 
     this.dataSource._paginator.nextPage(); 
     this.dataSource._paginator.previousPage(); 
     } else { 
     this.dataSource._paginator.previousPage(); 
     this.dataSource._paginator.nextPage(); 
     } 
     // If there's something in filter, we reset it to 0 and then put back old value 
    } else { 
     this.dataSource.filter = ''; 
     this.dataSource.filter = this.filter.nativeElement.value; 
    } 
} 
.... 
+0

Tuyệt vời. Chỉ cần đề cập đến, bạn có thể xác định bản dịch của croatian cho toàn bộ ứng dụng trong phần nhà cung cấp app.module.ts như sau: {cung cấp: MatPaginatorIntl, useClass: MatPaginatorIntlCro}. Nếu bạn quan tâm đến việc thực hiện MatPaginatorIntlCro của tôi, tôi có thể đăng nó. – zszep

+0

Giới thiệu về bản dịch, chắc chắn :). Gần đây tôi đã tìm ra toàn bộ logic mới cho vấn đề cập nhật bảng fronted vì vậy tôi sẽ viết ứng dụng mẫu và đưa vào github. ;) – besthiroeu

4

Như tôi đã nhìn thấy từ code của bạn mà bạn đang sử dụng pagination, bạn có thể làm như sau sau ca phẫu thuật crud:

this.dataSource.paginator = this.paginator; 

này sẽ làm mới trang hiện tại. Và, vui mừng một người nào đó từ Croatia đang sử dụng vật liệu góc cạnh.

Dưới đây là một phần quan trọng từ mã của tôi:

dialogRef.afterClosed().subscribe(result => { 
    if (result === null) { return; } 
    switch (mode) {    // add new 
     case 'C': { 
      data.push(result.vendor); 
      this.refreshTable(); 
      break; 
     } 
     case 'U': {    // update 
      const index = data.findIndex((item) => item.buFmisVendorId === result.vendor.buFmisVendorId); 
      if (index > -1) { 
       data[index] = vendor; 
       this.refreshTable(); 
      } 
      break; 
     } 

    } 
}); 

private refreshTable() { 
    this.dataSource.paginator = this.paginator; 
} 
+0

Tôi thực sự là người phát triển Android, đó là tại sao tôi ít bị lạc ở đây. : D Dù sao refreshTable không làm việc beacuse tôi có thể xem trang flickers cho một thứ hai nhưng tôi vẫn có vấn đề với: "data.push (result.vendor);". Đoán tốt nhất của tôi là tôi nên làm data.push bên trong dataService.ts của tôi kể từ khi tôi nhận được các giá trị từ các hộp thoại ở đó. Bảng behaviorSubject cũng được định nghĩa bên trong DataServices.ts. – besthiroeu

+0

Tôi đang làm điều này trong thành phần của tôi, không phải là dịch vụ. Với tôi nó có vẻ không phải là một mối quan tâm kinh doanh (dịch vụ) nhưng một mối quan tâm hiển thị (thành phần). Dù sao, nó hoạt động rất độc đáo (siêu nhanh mà không có yêu cầu http mới). Nếu bạn có thể tái tạo một kẻ buôn bán, có lẽ tôi có thể giúp.Nếu nó không hoạt động, thì có thể bạn đã không cập nhật nguồn dữ liệu chính xác. – zszep

+0

Cập nhật nhanh, tôi đã quản lý để làm điều đó. Bây giờ tôi chỉ phải xây dựng logic để xóa, chỉnh sửa. Mã của bạn sẽ hữu ích cho điều đó. – besthiroeu

1

Một cách tiếp cận ít khác nhau để xóa một mục và làm mới bảng dữ liệu. Nó gọi api một lần nữa nhưng điều này có thể làm việc cho các tập dữ liệu nhỏ hơn.

public deleteMember(memberId) { 
     // Call the confirm dialog component 
     this.confirmService.confirm('Confirm Delete', 'This action is final. Gone forever!') 
      .switchMap(res => {if (res === true) { 
       return this.appService.deleteItem(this.dbTable, memberId); 
      }}) 
      .subscribe(
       result => { 
       this.success(); 
       // Refresh DataTable to remove row. This solution calls the db and is a hack. 
       this.ngAfterViewInit(); 
       }, 
       (err: HttpErrorResponse) => { 
        console.log(err.error); 
        console.log(err.message); 
       this.messagesService.openDialog('Error', 'Delete did not happen.'); 
       } 
     ); 
    } 

Điều này được gọi gần đầu thành phần của khóa học nhưng được bao gồm ở đây để tham khảo.

private dbTable = 'members'; 
dataSource = new MatTableDataSource(); 

ngAfterViewInit() { 
    this.appService = new AppService(this.http); 
    this.dataSource.sort = this.sort; 
    this.dataSource.paginator = this.paginator; 


    // Populate the Material2 DataTable. 
    Observable.merge(this.paginator.page) 
     .startWith(null) // Delete this and no data is downloaded. 
     .switchMap(() => { 
     return this.appService.getItems(this.dbTable, 
      this.paginator.pageIndex); 
     }) 
     .map(data => { 
     return data.resource; 
     }) 
     .subscribe(data => { 
     this.dataLength = data.length; 
     this.dataSource.data = data; 
     }); 
    } 
+0

Mã của bạn có giống như giải pháp tạm thời của tôi không? Sau khi tôi thực hiện thao tác CRUD, tôi chỉ gọi "getAllData" bằng cách sử dụng "this.loadData();". – besthiroeu

+0

Có, cuộc gọi để tải lại dữ liệu là cùng một ý tưởng. Tuy nhiên, mã của tôi là khác nhau và có thể giúp người khác với cách tiếp cận khác. Tôi hy vọng :-) – Preston

1

Bạn có thể có một cái nhìn tại

addItem(baza: Baza): void { 
    this.httpClient.post(this.API_URL, Baza).subscribe(data => { 
     //THIS WAS MY BEST TRY BUT IT DOESN'T WORK :(
     const copiedData = this.data.slice(); 
     copiedData.push(baza); 
     console.log(copiedData); 
     this.dataChange.next(copiedData); 
    }); 
} 

Sản phẩm yêu cầu POST làm việc và gửi dữ liệu? Bạn tham chiếu Baza trong yêu cầu POST phải là 'baza' (chữ thường B). Có thể yêu cầu thất bại vì điều này và đăng ký quan sát không bao giờ được hoàn thành ... bạn có thể kiểm tra lại lý thuyết đó với một trình xử lý lỗi trên đăng ký.

addItem(baza: Baza): void { 
    this.httpClient.post(this.API_URL, baza).subscribe(data => { 
     const copiedData = this.data.slice(); 
     copiedData.push(baza); 
     console.log(copiedData); 
     this.dataChange.next(copiedData); 
    }, (errror) => { 
    console.log(error); 
    }); 
} 

Cuối cùng là để chỉnh sửa, cách tiếp cận của tôi sẽ khác đôi chút. Tiêm cùng một thể hiện của DataService vào thành phần và chuyển cùng tham chiếu này tới bảng DataSource chứ không phải một cá thể mới. Tiếp theo, chuyển toàn bộ đối tượng baza vào hộp thoại chỉnh sửa, không chỉ các thuộc tính của nó. Tiếp theo, trên hộp thoại đóng, vượt qua bản gốc (đối tượng chưa được chỉnh sửa) cũng như các thuộc tính mới (hoặc tốt hơn, một đối tượng mới của lớp Baza với các trường đã chỉnh sửa). Gửi chúng đến dịch vụ dữ liệu của chúng tôi với phương pháp "chỉnh sửa/cập nhật". Phương thức chỉnh sửa/cập nhật sẽ lọc bộ dữ liệu hiện có đang tìm kiếm bất kỳ mục nhập nào khớp với đối tượng chưa được chỉnh sửa của chúng tôi và đặt chúng bằng với đối tượng mới của chúng tôi. Ví dụ trừu tượng được đưa ra dưới đây

// ví dụ: Thành phần

export class BazaComponent implements OnInit { 
    .... 
    constructor(
    public httpClient: HttpClient, 
    public dialog: MatDialog, 
    public dataService: DataService 
){} 
    .... 
    public loadData() { 
    this.dataSource = new ExampleDataSource(this.dataService, 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; 
     }); 
    } 
    .... 
    startEdit(baza: Baza) { 
    const dialogRef = this.dialog.open(EditDialogComponent, { 
     data: { 
     baza: baza 
     } 
    }); 

    dialogRef.afterClosed().subscribe(result => { 
     // result will be simple array of our 'old' baza object that we passed in, and the 'new' baza object that contains the edits 
     this.dataService.updateItem(result[0], result[1]); 
    }); 
    } 

    dialogRef.close(['close',editBaza,baza]); 

// ví dụ: dịch vụ

export class DataService { 
    .... 
    set data(data: Baza[]) { 
    this.dataChange.next(data); 
    } 
    .... 
    updateItem(oldBaza: Baza, newBaza: Baza){ 
    this.data = this.data.map((baza: Baza) => { 
     if(baza === oldBaza) return newBaza; 
     return baza; 
    }); 
    } 
+0

Có, một phần công việc, thực sự tất cả các hoạt động CRUD hoạt động. :) Baza chỉ là một mô hình: nhập {Baza} từ '../models/baza' ;. Không có gì lạ mắt đang diễn ra ở đó, tôi có thể chỉnh sửa câu hỏi nếu bạn không muốn nhìn vào nó. Tôi chỉ gặp vấn đề với Mat Table và vì mọi thứ đều mới mẻ nên không có mẫu crud làm việc nào trên web. – besthiroeu

+0

Tôi đang tìm kiếm câu trả lời của bạn, có lỗi trong DataService, hàm updateItem: ERROR trong src/app/services/data.service.ts (40,10): lỗi TS2540: Không thể gán cho 'dữ liệu' vì nó là hằng số hoặc thuộc tính chỉ đọc. – besthiroeu

+0

Vì chúng tôi đang thay đổi BehaviourSubject không phải là một mảng dữ liệu đơn giản, tôi đã cập nhật câu trả lời để bao gồm một phương thức dữ liệu đã đặt. Bạn có thể thử với điều đó được thêm vào không. –

1

Giải pháp này sử dụng mã xóa hiện tại của tôi nhưng giống với mã cập nhật. Vấn đề chính là tìm chỉ mục mảng cho mục được chỉnh sửa hoặc xóa. Lưu ý rằng khi kết quả thành công, tôi gọi một phương thức thành công để thông báo cho người dùng sau đó gọi một hàm để xóa hàng khỏi bảng dữ liệu. Hoặc bạn có thể cập nhật dữ liệu trong hàng đó bằng một mã nhỏ khác như đẩy dữ liệu vào mảng đối tượng. Bằng cách này, chúng tôi không phải tải xuống lại tất cả dữ liệu.

public deleteMember(memberId) { 
     // Call the confirm dialog component 
     this.confirmService.confirm('Confirm Delete', 'This action is final. Gone forever!') 
      .switchMap(res => {if (res === true) { 
       return this.appService.deleteItem(this.dbTable, memberId); 
      }}) 
      .subscribe(
       result => { 
       this.success(); 
       // Refresh DataTable to remove row. 
       this.updateDataTable (memberId); 
       }, 
       (err: HttpErrorResponse) => { 
        console.log(err.error); 
        console.log(err.message); 
       this.messagesService.openDialog('Error', 'Delete did not happen.'); 
       } 
     ); 
    } 

Bây giờ, hãy xóa hoặc cập nhật, hàng đã xóa hoặc chỉnh sửa.

private dsData: any; 
    // Remove the deleted row from the data table. Need to remove from the downloaded data first. 
    private updateDataTable (itemId) { 
    this.dsData = this.dataSource.data; 
    if (this.dsData.length > 0) { 
     for (let i = 0; i < this.dsData.length; i++) { 
     if (this.dsData[i].member_id === itemId) { 
      this.dataSource.data.splice(i, 1); 
     } 
     } 
    } 
    this.dataSource.paginator = this.paginator; 
    } 
+0

Câu trả lời của bạn thực sự đã giúp tôi tìm ra một vài điều. this.dataSource.data; sẽ không làm việc tho. Tôi đã cập nhật câu hỏi. – besthiroeu

Các vấn đề liên quan