valor-software / ng2-table

Simple table extension with sorting, filtering, paging... for Angular2 apps
http://valor-software.github.io/ng2-table/
MIT License
553 stars 336 forks source link

Dynamic columns issue #162

Open petarblazevski opened 8 years ago

petarblazevski commented 8 years ago

Hi guys,

I've been trying to dynamically change the columns and the rows on a table. When i create the new array of columns, instead of re-creating the table with the new columns, the table is persisting the old columns, and appending the new once.

We found out that the "issue" is located in ng-table.component.ts starting from line 37.

Can you please give me an info if this is an issue, or if it was intentional made it like this? And if this is not an issue, is there a workaround?

alfakappa commented 8 years ago

@petarblazevski how do you manage to re-create the table anyway onChanges? I need to update the data within my table and for some reason it is just not working...

//shortened
@Component({
    selector: 'section.news-letter-articles',
    template: `

                    Sortable table:
                    <ng-table [config]="config.sorting"
                               (tableChanged)="onChangeTable(config)"
                               [rows]="rows" [columns]="columns">
                    </ng-table>
                </div>   
              `,
    directives: [NG_TABLE_DIRECTIVES]
})

export class NewsLetterArticles {

    /**
     * @description onChange in tree of components (filter) getNewsLetter with new selected newsletterID
     * @method public
     * @return function with parameters
     * */
    ngOnChanges() {
        this.getNewsLetter(this.newsletterID, this.editionID);
    }
    /**
     * @description getNewsLetter data
     * @method private
     * @param newsletterID <string> id of Newsletter to fetch
     * @param editionID <string> if of Edition to fetch
     * @return function with parameter
     * */
    private getNewsLetter(newsletterID, editionID) {
var url = working
        this._newsLetterService.getNewsLetter(url)
            .subscribe(
                newsletter => this.newsletter = newsletter,
                error =>  this.errorMessage = <any>error,
                () => this.createArticleData(this.newsletter)
            );
    }

    //Table input

    TableData: Array<any> = [
        {
            'name': 'Victoria Cantrell',
            'position': 'Integer Corporation',
            'office': 'Croatia',
            'ext': '0839',
            'startDate': '2015/08/19',
            'salary': 208.178
        }];

    public rows:Array<any> = [];
    public columns:Array<any> = [
        {title: 'Name', name: 'name', sort:'asc'},
        {title: 'Position', name: 'position', sort: false},
        // {title: 'Office', name: 'office', sort: 'asc'},
        // {title: 'Extn.', name: 'ext', sort: ''},
        // {title: 'Start date', name: 'startDate'},
        // {title: 'Salary ($)', name: 'salary'}
    ];
    public page:number = 1;
    public itemsPerPage:number = 10;
    public maxSize:number = 5;
    public numPages:number = 1;
    public length:number = 0;

    public config:any = {
        paging: false,
        sorting: {columns: this.columns},
        filtering: {filterString: '', columnName: 'position'}
    };
    private createArticleData(newsletter){
        if(newsletter.length > 0 && newsletter[0].articles.length > 0){
            this.rows = newsletter[0].articles;
            this.length = this.data.length;
            this.onChangeTable(this.config);
        } else {
            console.log("no articles");
        }
    }
    private articleData:Array<any> = [{
        'name': "No Article available",
        'position': ''
    }];
    private data:Array<any> = this.articleData;

    public ngOnInit() {
        this.onChangeTable(this.config);
    }

    public changePage(page:any, data:Array<any> = this.data):Array<any> {
        console.log(page);
        let start = (page.page - 1) * page.itemsPerPage;
        let end = page.itemsPerPage > -1 ? (start + page.itemsPerPage) : data.length;
        return data.slice(start, end);
    }

    public changeSort(data:any, config:any):any {
        if (!config.sorting) {
            return data;
        }

        let columns = this.config.sorting.columns || [];
        let columnName:string = void 0;
        let sort:string = void 0;

        for (let i = 0; i < columns.length; i++) {
            if (columns[i].sort !== '') {
                columnName = columns[i].name;
                sort = columns[i].sort;
            }
        }

        if (!columnName) {
            return data;
        }

        // simple sorting
        return data.sort((previous:any, current:any) => {
            if (previous[columnName] > current[columnName]) {
                return sort === 'desc' ? -1 : 1;
            } else if (previous[columnName] < current[columnName]) {
                return sort === 'asc' ? -1 : 1;
            }
            return 0;
        });
    }

    public changeFilter(data:any, config:any):any {
        if (!config.filtering) {
            return data;
        }

        let filteredData:Array<any> = data.filter((item:any) =>
            item[config.filtering.columnName].match(this.config.filtering.filterString));

        return filteredData;
    }

    public onChangeTable(config:any):any {
        if (config.filtering) {
            Object.assign(this.config.filtering, config.filtering);
        }
        if (config.sorting) {
            Object.assign(this.config.sorting, config.sorting);
        }
        let filteredData = this.changeFilter(this.data, this.config);
        let sortedData = this.changeSort(filteredData, this.config);
        this.length = sortedData.length;
    }

}
petarblazevski commented 8 years ago

@alfakappa not really re-creating the table. In my case, i have 3 "radio buttons". So when you switch between them, i am re-defining the columns property

alfakappa commented 8 years ago

Ok I have a slightly different issue here.

when I update rows, I do get the new info in my table. However, when I'm trying to sort this, it gives me this error: TypeError: item[config.filtering.columnName].match is not a function

really no clue what to do.

kidtronnix commented 8 years ago

+1 @petarblazevski On this issue. I am trying to display a table that changes it's columns when a different report is selected. Can't seem to change the columns dynamically.

HenricusL commented 7 years ago

+1 I have the same problem. Doesn't seem to be a way to clear the columns before setting new columns

SylTi commented 7 years ago

I have the same problem, I can't remove a column after it's set. Anyone found a workaround ?

alfakappa commented 7 years ago

I solved it by updating the data parsed to the table on ngChanges on the above component. Then in the table component onChanges I made a "createData function" and init the table again. It maybe a bit "bloathing" but works fine for me.

Hasnain-Bukhari commented 7 years ago

when ever you made a change to columns then make sure you have call this function given below.

this.onChangeTable(this.config);

SylTi commented 7 years ago

@alfakappa could you please show a code example ? Trying to do something similar but so far no luck :

if (quaterly) { if (this.columns.find((col) => col.name === 'quaterly')) { console.log('splice'); this.columns.splice(10, 1); } } this.config = { sorting: {columns: this.columns}, className: ['table-striped', 'table-bordered', 'text-xs-center'] }; this.onChangeTable(this.config);

The splice get called successfully but the columns don't update

alfakappa commented 7 years ago

Hmm.. that is weird actually.. I have it this way

ngOnChanges():void {
        this.createData(this.articles);
        this.onChangeTable(this.config);
    }

private createData(articles:Array<any>){
        let articlesArray:any = articles;

        if (articlesArray) {

            if(articlesArray.length > 0) {
                this.rows = articlesArray;
                this.tableData = articlesArray;

            } else {
                let firstCellValue = 'No articles',
                    firstCellName = 'Titel',
                    emptyMessage:any = {};
                emptyMessage[firstCellName] = firstCellValue;
                this.rows = [emptyMessage]
            }
        }
    }

I think the table will only update itself if you update this.rows and this.data (original code, I renamed it to tableData)

So I think you might need to update dat within your if(quaterly)-statement as well. Make a new array of rows and then update this.rows if that makes sense to you.

SylTi commented 7 years ago

I'm not sure that's the problem since i'm already updating this.rows inside of the onChangeTable :

public onChangeTable(config:any):any {
    if (config.sorting) {
      Object.assign(this.config.sorting, config.sorting);
    }
    this.opportunitiesService.getList()
      .subscribe(
        (data) => {

          this.rows = data;
});
ervbsankar commented 7 years ago

does anyone figured it out. how to dynamically change colums

HenricusL commented 7 years ago

I have changed locally ng-table.component.ts (I'm using sinopia so i can easily handle local adjustments).

just added this method: public clearColumns () : void { this._columns = []; }

I've also created a pull request.

shamgang commented 7 years ago

+1

Is this pull request merged? If not can you link to it please and/or give us a status? Thanks.

HenricusL commented 7 years ago

Sorry for the slow reaction. https://github.com/valor-software/ng2-table/pull/433

RamiAtieyeh commented 6 years ago

Hi How would I call this method clearColumns() from the parent component? Also, isn't it better to clear the columns array when the property setter gets called?

HenricusL commented 6 years ago

@RamiAtieyeh Not sure what you mean. I call this method when I want to replace the dataset completely. I agree this would be better to call in the setter.. but that really would cahnge the setter functionality and possibly break something.

itsnotme01 commented 6 years ago

Hello everyone,

I chose to fix the problem directly in the setter by refreshing all columns as you can see:

@Input()
  public set columns(values:Array<any>) {
    // Column reset added, now we always repopulate de columns instead of recycling, preventing columns refresh bugs
    this._columns = [];
    values.forEach((value:any) => {
      if (value.filtering) {
        this.showFilterRow = true;
      }
      if (value.className && value.className instanceof Array) {
        value.className = value.className.join(' ');
      }

      this._columns.push(value);
    });
  }

And here the component part with the configuration refresh:


//Add LocalIT column for SHOW ALL mode
        this.columns = [
          {title: 'Local IT', name: 'LocalIT', filtering: {filterString: '', placeholder: 'LocalIT'}, sort:'', className: 'groupidColumnWidth'},    
          {title: 'Product Name', name: 'ProductName', filtering: {filterString: '', placeholder: 'Product Filter'}, sort:'', className: 'productColumnWidth'},    
          {title: 'Target', name: 'Target', filtering: {filterString: '', placeholder: 'GID Filter'}, sort: '', className: 'groupidColumnWidth'},    
          {title: 'Status', name: 'Status', filtering: {filterString: '', placeholder: 'Status Filter'}, sort: '', className: 'statusColumnWidth'},    
          {title: 'Creation Date', name: 'CreatedOn', sort: '', className: 'dateColumnWidth'},
          {title: 'Modification Date', name: 'ModifiedOn', sort:'', className: 'dateColumnWidth'},
          {title: 'Requestor', name: 'Requestor', filtering: {filterString: '', placeholder: 'GID Filter'}, sort:'', className: 'groupidColumnWidth'}
        ];

        //Refresh the sorting onfiguration with the ne column and update de table with the new config
        if(this.config){
          this.config.sorting = {columns: this.columns};
        }

        this.onChangeTable(this.config);

For now i've no downside with this fix. I hope it can help someone,

Have a nice day