6

I have the below code and it works fine, what it does is ngFor repeat to create a column for each column in the column object.

Currently it shows every card from the cards object in every column.

What i want it to do is show only cards in the column WHERE column.id = card.column

How can I modify my code to do this?

  import {Input, Output, EventEmitter, Component, Injectable} from 'angular2/core'
  import {NgFor} from 'angular2/common'
  import {Observable} from 'rxjs/Observable';

  interface Card {
    id?: number;
    title?: string;
    column?: number;
  }

  @Injectable()
  export class DataService {
    cards$: Observable<Array<Card>>;
    private _cardsObserver: any;
    private _dataStore: Array<Card>;

    constructor(){
      this._dataStore = [
        {id: 1, title: 'Card A', column: 1},
        {id: 2, title: 'Card B', column: 2},
        {id: 3, title: 'Card C', column: 2}
      ];

      this.cards$ = new Observable(observer =>
        this._cardsObserver = observer).share();
    }

    loadCards() {
      this._cardsObserver.next(this._dataStore);
    }

    addCard(){
      this._dataStore.push({id: 4, title: 'Card D', column: 3});
      this._cardsObserver.next(this._dataStore);
    }

    getColumns(){

      var columns = [
        {id: 1, title: 'Column One'},
        {id: 2, title: 'Column Two'},
        {id: 3, title: 'Column Three'}
      ];

      return columns;

    }

  }


  @Component({
    selector: 'app',
    providers: [DataService],
    directives: [],
    template: `
    <button (click)="dataService.addCard()">Add Card</button>
    <div class="columns" *ngFor="#c of columns">
      {{c.title}}
      <div class="card" *ngFor="#card of cards">{{card.title}}</div>
    </div>
    `
  })
  export class App {

    private cards: Array<Card>;
    private columns: any;

    constructor(public dataService: DataService) {
      dataService.cards$.subscribe(updatedCards => this.cards = updatedCards);
      dataService.loadCards();
      this.columns = dataService.getColumns();

    }
  }
B Hull
  • 3,153
  • 6
  • 22
  • 24
  • product.column? did you mean card? is this what you are looking for? : https://angular.io/docs/js/latest/api/common/NgIf-directive.html – Langley Jan 12 '16 at 05:44
  • that's right, card.column. – B Hull Jan 12 '16 at 05:46
  • 1
    without modifying your example too much you can add something like `*ngIf="card.colum == column.id"` or use a pipe to filter them in the ngFor https://angular.io/docs/ts/latest/api/common/SlicePipe-class.html but you might want to reformat your data structure to do it in a more performant way so you don't have to iterate all of them for each column – Langley Jan 12 '16 at 05:54
  • you can use `.filter(card => card.id === card.column)` on Observable, in your service before `.share()` or in component before `.subscribe()` – Sasxa Jan 12 '16 at 07:21
  • Don't keep the cards in your service, either return them or return an observable or a promise of them and set them as a property in your Controller. Your logic is too convoluted right now, hard to understand, troubleshoot and maintain, If you change it like that and upload a repo or a codepen/plunkr with your example, I'm sure you'll get help quicker. – Langley Jan 16 '16 at 19:21

1 Answers1

5

Like @Langley said in his comment, I would create a custom pipe for this:

import {Injectable, Pipe} from 'angular2/core';

@Pipe({
  name: 'filter'
})
@Injectable()
export class CardFilterPipe implements PipeTransform {
  transform(items: any[], args: any[]): any {
    return items.filter(item => item.column === args[0]);
  }
}

And use it like that:

<div class="columns" *ngFor="#c of columns">
  {{c.title}}
  <div class="card" *ngFor="#card of cards | filter:c">{{card.title}}</div>
</div>

If you want to take into account update, you need to set the pure attribute to false:

@Pipe({
  name: 'filter',
  pure: false
})
@Injectable()
export class CardFilterPipe implements PipeTransform {
  (...)
}

Hope it helps you, Thierry

Thierry Templier
  • 198,364
  • 44
  • 396
  • 360
  • Hi Thierry, this solution produces an error: EXCEPTION: Expression 'cards | filter:c in App@4:29' has changed after it was checked. Previous value: ''. Current value: '' in [cards | filter:c in App@4:29] – B Hull Jan 12 '16 at 22:33
  • I think that you need to enable the production mode not to have this message. Simply add this line right before bootstrapping your application: `import {enableProdMode} from 'angular2/core'; enableProdMode();`. See this answer for details about the production mode: http://stackoverflow.com/questions/34868810/what-is-diff-between-production-and-development-mode-in-angular2. Hope it helps you. – Thierry Templier Jan 21 '16 at 09:37
  • 1
    @ThierryTemplier, there are (better) alternatives to using production mode because of this "problem" with stateful pipes in dev mode: see http://stackoverflow.com/a/34497504/215945. In production mode, we lose that valuable second change detection cycle that we get in dev mode, which could reveal problems with our code. (However in this case, the error thrown is rather bogus/misleading, as explained in that link.) – Mark Rajcok Jan 22 '16 at 23:09
  • Yes, you're right, Mark! Always great comments ;-) Thanks very much for pointing this out and for the link! The latter i's very useful. – Thierry Templier Jan 23 '16 at 07:21