Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Table] Add example with dynamic data (add/remove/edit rows) #5917

Closed
tyler2cr opened this issue Jul 20, 2017 · 58 comments · Fixed by #22808
Closed

[Table] Add example with dynamic data (add/remove/edit rows) #5917

tyler2cr opened this issue Jul 20, 2017 · 58 comments · Fixed by #22808
Assignees
Labels
area: material/table docs This issue is related to documentation P4 A relatively minor issue that is not relevant to core functions
Projects

Comments

@tyler2cr
Copy link

tyler2cr commented Jul 20, 2017

Bug, feature request, or proposal:

cdk table API docs need instructions and/or an example of updating the dataSource in the table
connecting the table to a data source

What is the expected behavior?

seeing implementation instructions and/or an example of how to send the updated dataSource to the table so that the connect function is called

What is the current behavior?

The docs only cover rendering data on initial load of the component, but if a service updates the data, there isn't a documented way to update the data-table

What are the steps to reproduce?

Providing a Plunker (or similar) is the best way to get the team to see your issue.
Plunker template: https://goo.gl/DlHd6U

What is the use-case or motivation for changing an existing behavior?

the md-table API doc hints at an approach but also lacks clarity

Which versions of Angular, Material, OS, TypeScript, browsers are affected?

angular 4
material 2
typescript 2.4
windows

Is there anything else we should know?

@andrewseguin
Copy link
Contributor

Sounds good to me, I'll make an example that includes adding/removing/editing rows.

@andrewseguin andrewseguin self-assigned this Jul 20, 2017
@andrewseguin andrewseguin added the docs This issue is related to documentation label Jul 20, 2017
@andrewseguin andrewseguin changed the title cdk-data-table docs lack instructions or an example to update data [Table] Add example with dynamic data (add/remove/edit rows) Jul 20, 2017
@Lakston
Copy link

Lakston commented Jul 21, 2017

I hope I can add to this request without creating a new Issue, I've been looking at the example code provided in both the angular material docs and the cdk guide and I'm struggling to understand how to just send my data (a simple array of objects) to the table. Do I need my data to be a BehaviorSubject ? Do I need the typescript get too ?

The example generates data with loops and is quite confusing to me.

A simpler example with just a small simple array would really help ! I'll create a new Issue if needed.

@irowbin
Copy link

irowbin commented Jul 21, 2017

hello @andrewseguin It would be nice if you add collapsible row too.

@ilmars
Copy link

ilmars commented Jul 21, 2017

Hi @andrewseguin,
alos would be nice to have example with server data,
to get server data is OK, but how to triger on new http.get request on paging, sorting and filtering? (i'm stuck with that).

@willshowell
Copy link
Contributor

@Lakston IMO your request deserves a separate issue. While the ExampleDatabase is great as a supplier of 100 rows of random data, it convolutes the Basic Example, and I think the docs could benefit from an even simpler example with maybe a single emission of a hardcoded array of data with Observable.of.

@ilmars luckily an example with an HTTP request just got merged yesterday and will be available on the docs site soon #5766

@tyler2cr
Copy link
Author

@Lakston what I did to accomplish that was to setup a service to provide the data array to the ExampleDatabase and then looped over each item in the array and fed them into the addUser method from the example, after changing it to match my object. So you can just replace what is being iterated over in the for loop from the example by creating a service to call for that data

@tyler2cr
Copy link
Author

@tyler2cr
Copy link
Author

Some help on this would be greatly appreciated :)

https://stackoverflow.com/questions/45382599/angular-2-table-invoke-connect-function

@etovian
Copy link

etovian commented Aug 1, 2017

Agreed. I wept for joy when this component arrived. But after about six hours of fiddling with this thing, I still can't get it to work with a meaningful, real-world example. I expected to just point it at an array, and I'd be off and running. But having to create a datasource and a database is especially cumbersome. If the grid is supposed to be truly unopinionated, imo it shouldn't be coupled to observables. I should be able to just point it at an array of data and an array of column definitions (like most other analogous components).

@willshowell
Copy link
Contributor

@etovian You don't need to create a database. That's just used in the example because the example needs to generate a lot of random data to display.

You barely even have to use Observables. They're used in the examples because they massively simplify manipulating the data in response to user events and are generally very powerful and flexible. If you just want to render an array once, you can use Observable.of(myArrayOfValues) and you don't have to touch rxjs again. If you want to render it more than once,

// component
_dataSubject = new BehaviorSubject<any[]>([])

constructor() {
  this.dataSource = new MyDataSource(this._dataSubject)
}

updateValues(myArray: any[]) {
  this._dataSubject.next(myArray)
}

// MyDataSource
constructor(private _data: BehaviorSubject<any[]>) { }

connect() {
  return this._data.asObservable()
}

@etovian
Copy link

etovian commented Aug 1, 2017

@willshowell Thanks for the tip! I sort of got this working:

export class MyDataSource extends DataSource<any[]> {

  data = [
    { firstName: 'Mike' },
    { firstName: 'Amy' },
    { firstName: 'Jillian' },
    { firstName: 'Juliette' }
  ];

  constructor() {
    super();
  }

  connect (): Observable<any[]> {
    return Observable.of(this.data);
  }

  disconnect (  ): void {

  }

}

The only problem is that the data doesn't load when the component using the dataSource initializes. Oddly, if I click elsewhere on the page (not even inside the component), then table displays the data. Here's all of the code:

import { Component, OnInit, ViewChild } from '@angular/core';
import { CdkTable, DataSource } from '@angular/cdk';
import {Observable} from 'rxjs/Observable';
import 'rxjs/add/operator/startWith';
import 'rxjs/add/observable/merge';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/debounceTime';
import 'rxjs/add/operator/distinctUntilChanged';
import 'rxjs/add/observable/fromEvent';
import { InvitationService } from "../services/invitation.service";

@Component ( {
  selector: 'app-sandbox',
  templateUrl: './sandbox.component.html',
  styleUrls: [ './sandbox.component.css' ]
} )
export class SandboxComponent implements OnInit {

  dataSource: MyDataSource | null;

  displayedColumns = ['firstName'];

  @ViewChild('table')
  table: CdkTable<any>;

  constructor(private invitationService: InvitationService) {

  }

  ngOnInit() {
    this.dataSource = new MyDataSource();
    //this.dataSource.connect();
  }

}

export class MyDataSource extends DataSource<any[]> {

  data = [
    { firstName: 'Mike' },
    { firstName: 'Amy' },
    { firstName: 'Jillian' },
    { firstName: 'Juliette' }
  ];

  constructor() {
    super();
  }

  connect (): Observable<any[]> {
    return Observable.of(this.data);
  }

  disconnect (  ): void {

  }

}

And the template:

<md-table
  #table
  [dataSource]="dataSource">
  
  <ng-container cdkColumnDef="firstName">
    <md-header-cell *cdkHeaderCellDef> First Name </md-header-cell>
    <md-cell *cdkCellDef="let row"> {{row.firstName}} </md-cell>
  </ng-container>
  
  <md-header-row *cdkHeaderRowDef="displayedColumns"></md-header-row>
  <md-row *cdkRowDef="let row; columns: displayedColumns;"></md-row>
  
</md-table>

@etovian
Copy link

etovian commented Aug 1, 2017

@willshowell Oddly, this is working exactly as expected:

import { Component, OnInit } from '@angular/core';
import { DataSource } from '@angular/cdk';
import {Observable} from 'rxjs/Observable';
import 'rxjs/add/operator/startWith';
import 'rxjs/add/observable/merge';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/debounceTime';
import 'rxjs/add/operator/distinctUntilChanged';
import 'rxjs/add/observable/fromEvent';
import { InvitationService } from "../services/invitation.service";
import { BehaviorSubject } from "rxjs/BehaviorSubject";

@Component ( {
  selector: 'app-sandbox',
  templateUrl: './sandbox.component.html',
  styleUrls: [ './sandbox.component.css' ]
} )
export class SandboxComponent implements OnInit {

  dataSource: MyDataSource | null;

  dataSubject = new BehaviorSubject<any[]>([]);

  displayedColumns = ['firstName'];

  constructor(private invitationService: InvitationService) {
    this.invitationService.getInvitations().subscribe({
      next: value => this.dataSubject.next(value)
    });
  }

  ngOnInit() {
    this.dataSource = new MyDataSource(this.dataSubject);
  }

}

export class MyDataSource extends DataSource<any[]> {

  constructor(private subject: BehaviorSubject<any[]>) {
    super ();
  }

  connect (): Observable<any[]> {
    return this.subject.asObservable();
  }

  disconnect (  ): void {

  }

}

Since this much better represents how I'd do things in production, I'm not necessarily concerned with why the code from my previous post isn't working. Thanks again for your help! You definitely saved me some continued hair-pulling.

I can certainly get by with this, but I think that the option to work directly with an array would help folks get up and running much more quickly, even if the DataSource is a more performant solution.

Thanks to all the devs for their hard work!

@willshowell
Copy link
Contributor

@etovian your first issue looks like #6199 which is resolved and will be a part of the next release.

Also an array-based data source that is plug-and-play is in the works #6182, though it's not clear which direction that PR will take

@ctilley83
Copy link

@willshowell What does your getInvitations() method look like in your InvitationService service?

@etovian
Copy link

etovian commented Sep 1, 2017

constructor ( private http: Http ) { }

getInvitations (): Observable<Invitation[]> {
    return this.http.get ( this.invitationUrl ).map ( response => response.json () );
  }

@munjalpandya
Copy link

I am not getting any error but the data from web API is not rendering in datatable. Only columns are displayed.

Below is my code:

`
import { Component, OnInit, ViewChild } from '@angular/core';
import { UserService } from '../Service/user.service';
import { IUser } from '../Model/user';
import { DBOperation } from '../Shared/enum';
import { Observable } from 'rxjs/Rx';
import { Global } from '../Shared/global';
import { ManageUser } from './manageuser.component';
import { MdDialog, MdDialogRef } from '@angular/material';
import { UserFilterPipe } from '../filter/user.pipe';
import { DataSource } from "@angular/cdk/collections";

@component({
templateUrl: 'app/Components/userlist.component.html'
})

export class UserListComponent implements OnInit {

users: IUser[];
user: IUser;
dataSource: userDataSource;
displayedColumns = ['FirstName', 'LastName'];

constructor(private _userService: UserService) { }

ngOnInit(): void {
    this.LoadUsers();
}
LoadUsers(): void {
    this._userService.get(Global.BASE_USER_ENDPOINT)
        .subscribe(users => { this.users = users }
        );
    this.dataSource = new userDataSource(this.users);
}

}

export class userDataSource extends DataSource{

constructor(private _users: IUser[]) {
    super();
}

connect(): Observable<IUser[]> {
    return Observable.of(this._users);
}

disconnect() { }

}
`

Below is my html

`

User List using Table
First Name {{element.FirstName}}
        <ng-container mdColumnDef="LastName">
            <md-header-cell *mdHeaderCellDef> Last Name </md-header-cell>
            <md-cell *mdCellDef="let element"> {{element.LastName}} </md-cell>
        </ng-container>

        <md-header-row *mdHeaderRowDef="displayedColumns"></md-header-row>
        <md-row *mdRowDef="let row; columns: displayedColumns;"></md-row>
    </md-table>
</div>
`

Not getting where exactly the issue is.

@theunreal
Copy link

@etovian Can you provide an example of filtering such table?

@leocaseiro
Copy link
Contributor

@shrralis
Copy link

shrralis commented Oct 16, 2017

EXPLAIN ME please, HOW does it work? When I add new item to data source's array – table doesn't show it immediately. It shows no changes until I click on header for sorting or paginator and only after that table will re-render.

@shalugin
Copy link

@shrralis
Copy link

@shalugin for my sorry unfortunately no. I already have something like that:

public addManufacturer(manufacturer: Manufacturer): void {
    let newManufacturers: Manufacturer[] = [];

    this.data.subscribe((manufacturers: Manufacturer[]) => newManufacturers = manufacturers);
    newManufacturers.push(manufacturer);
    this.manufacturerDataSubject.next(newManufacturers);
  }

I know the code looks strange but this is because of my experiments on this to make it work. I can make plnkr with my code.

@shalugin
Copy link

Hmmm..

It shows no changes until I click on header for sorting or paginator and only after that table will re-render.

Maybe this indicating a problem. Try insert ChangeDetectorRef into contructor and trigger changes manually.

  constructor(private _cdr: ChangeDetectorRef) {
  }

.....
this.manufacturerDataSubject.next(newManufacturers);
this._cdr.detectChanges();
.....

@shrralis
Copy link

shrralis commented Oct 18, 2017

Try insert ChangeDetectorRef into contructor and trigger changes manually.

I'm trying with ApplicationRef (tick() method) because of I'm using a service. But there is no effect.

public addManufacturer(manufacturer: Manufacturer): void {
    let newManufacturers: Manufacturer[] = [];

    this.data.subscribe((manufacturers: Manufacturer[]) => newManufacturers = manufacturers);
    newManufacturers.push(manufacturer);
    this.manufacturerDataSubject.next(newManufacturers);
    this._ar.tick();
  }

  public getManufacturers(): Observable<Manufacturer[] | Error> {
    console.log('getManufacturers started!');
    return this.lookupService.getAllManufacturers();
  }

  public submitManufacturers(manufacturers: Observable<Manufacturer[]>): void {
    manufacturers.forEach((m: Manufacturer[]) => {
      console.log('submitManufacturers started! manufacturers: ' + JSON.stringify(m));
    });
    manufacturers.subscribe((m: Manufacturer[]) => this.manufacturerDataSubject.next(m));
    this._ar.tick();
    console.log('submitManufacturers finished!');
  }

@shalugin
Copy link

@shrralis Can you create a plunker or stackblitz with your code?

@shrralis
Copy link

@reemyrail
Copy link

Your plunker code is not really working would you please check on your dependencies @irossimoline

@irossimoline
Copy link

@rima-smart , I have updated the dependencies.

@tg8888
Copy link

tg8888 commented Jan 3, 2018

Here is what worked for me.
Stackblitz url :https://stackblitz.com/edit/angular-5zu7px
Below is the code to add new element to array without using behaviorSubject.

addRow() {
    alert('adding row');
    this.dataSource.data.push({
      position: 1,
      name: "Hydrogen",
      weight: 1.0079,
      symbol: "H"
    });
    this.dataSource.filter = "";
  }

@ORESoftware
Copy link
Contributor

ORESoftware commented Jan 8, 2018

Is there something less hacky than using

  this.dataSource.filter = "";

after pushing data, to get a re-render?

Also, I add new data, and new rows are added, but the cells in the new row are empty...even though the new data is good.

If I open "Dev Tools" and click around and inspect, then the table will render the cells. So I know something is wrong with this lib.

@armansujoyan
Copy link

You can use the method called _updateChangeSubscription(). This method will update subscription to changes that should trigger an update to the table's rendered rows. When the changes occur it processes the current state of the filter, sort, and pagination along with the base data and sends it to the table for rendering.

@marinantonio
Copy link

Took me some time but I finally got everything working. So, here's my CRUD implementation if anyone gets in trouble with this:
https://github.com/marinantonio/angular-mat-table-crud

Screenshot:
Alt Text

Or you can test it on gist:
https://marinantonio.github.io/angular-mat-table-crud/

@ORESoftware
Copy link
Contributor

@marinantonio cool, how did you create that GIF?

@ORESoftware
Copy link
Contributor

ORESoftware commented Jan 10, 2018

@armansujoyan

I tried this:

      self.dataSource.data.push(m as any);
      self.dataSource._updateChangeSubscription();

unfortunately didn't work...my table renders new rows, but the rows are empty - the cells don't get rendered. This is all very unfortunate. What kind of library is this? I can't add rows to a table? FML.

@marinantonio
Copy link

marinantonio commented Jan 11, 2018

@ORESoftware
For recording used free software ScreenToGif, and after that uploaded .gif to Imgur.

@paigudepooja
Copy link

@andrewseguin I want to create mat table with dynamic columns and I have array of object in which each object has fields which i want to show it vertically.
untitled

In above image I have object where all column fields are in one object and I want to show columns of every object in array.
If anyone could provide some guidance i would truly appreciate it.

@chethan1095
Copy link

@marinantonio how to post it permanently you are just pushing it temporary how to post it using post method in angular2

@marinantonio
Copy link

@chethan1095 What do you mean? You push post method from DataService.ts and then depending on result (successful or not) you push front-end update to a mat-table.

@sukumar89
Copy link

I need some help, in rendering the value in the data table when the datasource has multiple json array. below is the service json response
{
"_index": "com_reprocess",
"_type": "reprocessor",
"_id": "com-payment-intake-62fc632e-5ac3-40d8-a35a-0yment-intake-paypalclosure-q2",
"_score": 1,
"_source": {
"applicationName": "com-payment-intake-paypalclosure-q2",
"topic": "COMPayment-PaypalClosure-q2",
"retryInterval": 0,
"maxRetryCount": 3,
"isAutoRetryable": "false",
"cassandraTTL": 3000000,
"reprocessId": "aafde69c-7379-4bca-ac6e-c2e30bd4be11",
"sequenceNo": "1521827002607",
"key": "com-payment-intake-62fc632e-5ac3-40d8-a35a-0b2cbd9facf1_WA11121773",
"errorCode": "-104",
"createTimeStamp": "2018-03-23T17:43:22",
"currentRetryCount": 0,
"createProgId": "com-payment-intake-paypalclosure-q2_CONSUMER1_fcf59940-37c3-4478-becc-3798e0e0c4a2",
"state": "initial",
"groupforSameKey": false,
"nextRetryTime": "2018-03-23T17:44:22",
"clientHeaders": [
{"key": "com-acf1_WA11121773",
"value" :"FAILED"}
],
"hostIP": "10.255.220.35",
"hostName": "dbb6f7b4-4909-479c-79c3-1e95",
"isESRecordLocked": false,
"modifyTimeStamp": "2018-03-23T17:43:22"
}
},

where i'm trying to iterate the response and bind them to material datatable. It is all good but i can't access the attributes inside the clientHeaders array.

here is my material table html statements and it is not returning any value.

  <ng-container matColumnDef="value" style="column-width:500px">
    <mat-header-cell *matHeaderCellDef mat-sort-header [ngClass]="'value'"> fail_rsn </mat-header-cell>
    <mat-cell *matCellDef="let element" [ngClass]="'value'"> {{element._source.clientHeaders.key}} </mat-cell>
  </ng-container> Can any one help on this ? stuck with 2 days on this 

@Oyedele-Sg
Copy link

@andrewseguin Great job man! Can you please share a link to the repository for the updates you made that includes adding/removing/editing rows and if possible, editing individual cells. Thank you.

@rain01
Copy link

rain01 commented Jun 29, 2018

Are the examples still coming to the documentation?

@homj
Copy link

homj commented Jun 29, 2018 via email

@TalLoss
Copy link

TalLoss commented Jul 3, 2018

@andrewseguin This is really important! Is an example coming out soon?

@AdPik
Copy link

AdPik commented Jul 23, 2018

Hello, I have problem with my code. Any ideas? Thank you ! https://stackoverflow.com/questions/51478019/binding-the-data-into-mat-table-angular

@furqan-shakir
Copy link

@irossimoline I liked your solution, Can I use it with reactive forms and formArray?

@avatsaev
Copy link

avatsaev commented Aug 30, 2018

I created a library to solve that (seamless http paginating/sorting/filtering with MatTable), the docs are not ready yet, but you have a complete functional usage example in src/app: https://github.com/avatsaev/ngx-mat-table-extensions

If any adventurous folks want to try it out and let me know if I need to add anything else before official release?

online demo is here: https://stackblitz.com/github/avatsaev/ngx-mat-table-extensions

@edenchen123
Copy link

this.dataSource.data = [...this.dataSource.data, {}];

1 similar comment
@edenchen123
Copy link

this.dataSource.data = [...this.dataSource.data, {}];

@jayDesai89
Copy link

Is there any update or new info available on this issue?

I have created below the table and I have a pretty much same CRUD operation requirement on this table.
I am also using mat-dialog for pop up.
Parent-child(main HTML - popup HTML) communication between component is working but at the same time want to update view - backend data in real time
At this stage I am using locally created json mock data.
Below is the image of the table
image

@jayDesai89
Copy link

Any updates on this issue?

@angular-automatic-lock-bot
Copy link

This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.

Read more about our automatic conversation locking policy.

This action has been performed automatically by a bot.

@angular-automatic-lock-bot angular-automatic-lock-bot bot locked and limited conversation to collaborators Jun 26, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area: material/table docs This issue is related to documentation P4 A relatively minor issue that is not relevant to core functions
Projects
No open projects
Table
  
Docs
Development

Successfully merging a pull request may close this issue.