31

Hi There I have been trying to filter an array with some success using ngIF and ngFor.

<button *ngFor="let item of items"> <div *ngIf="(item.data.type == 1)"> {{item.data.name}} </div> </button>

This code does only show buttons with name in it for data that has type=1 but it also create empty buttons for each data entry that doesn't have type=1, I can't figure out how to get rid of empty buttons. Any help is much appreciated.

Kim Kern
  • 54,283
  • 17
  • 197
  • 195
DN0300
  • 864
  • 2
  • 12
  • 27

3 Answers3

89

I would flip-flop your button and div:

<div *ngFor="let item of items">
    <button *ngIf="(item.data.type == 1)">{{item.data.name}}</button>
</div>

This way only buttons get created for valid items.

If <div>'s aren't desirable use <ng-container> instead.

Though not advisable due to performance reasons, you can also use a function in your component:

<button *ngFor="let item of filterItemsOfType('1')">{{item.data.name}}</button>

Where your component has a function:

filterItemsOfType(type){
    return this.items.filter(x => x.data.type == type);
}

While this works, it is not recommended and should be avoided where possible.

Community
  • 1
  • 1
Tyler Jennings
  • 8,761
  • 2
  • 44
  • 39
  • 1
    Flip-flopping the div worked really well :) Thank you! – DN0300 Mar 21 '17 at 15:50
  • Hi, I'm trying to use *ngFor for creating table headers. This is my code {{m}}{{m}}. When *ngIf doesn't satisfies either of the condition mentioned in span, still a is getting creating(logically that is correct), which is empty but takes some width in the UI. I don't want it to create a th when condition doesn't satisfies. – Vasanth Nov 22 '18 at 08:08
  • 2
    @Vasanth try putting the ngFor on a parent `` element. That way a `th` isn't rendered for every item. And change your `span`s to be the `th`s. – Tyler Jennings Nov 26 '18 at 16:13
  • solution 2 i.e. making a function works well for option list of dropdown... thanks :) – Anshul Gupta Sep 07 '20 at 14:52
  • Flip-Flopping would still create empty divs. Using the method is so far the best in my opinion – arunwithasmile Sep 17 '21 at 01:09
  • 2
    there is a lot of articles about using function in templates and how bad is this pattern bad. Please avoid them. [ref](https://medium.com/showpad-engineering/why-you-should-never-use-function-calls-in-angular-template-expressions-e1a50f9c0496) – Filip Kováč Oct 09 '21 at 20:02
33

You can create your own pipe. That is better solution.

@Pipe({
    name: 'somepipe',
})
export class SomePipe {

    transform(objects: any[]): any[] {
        if(objects) {
            return objects.filter(object => {
                return object.data.type === 1;
            });
        }
    }

}

and use it in html in this way:

<button *ngFor="let item of items | somepipe"> <div> {{item.data.name}} </div> </button>
Jaroslaw K.
  • 5,224
  • 2
  • 39
  • 65
7

<button *ngFor="let item of getItems()">...</button>

getItems() {
  return this.items.filter((item) => item.data.type === 1);
}

where this.items is your array of items somewhere above this function.

Check this out

elzoy
  • 5,237
  • 11
  • 40
  • 65
  • 2
    I'm not sure, if using function returned list is good solution. That method will be called every time when html be rendering – Jaroslaw K. Mar 21 '17 at 15:46
  • @JaroslawK. even worse, function (also getter) is called so often that you can have troubles with performance. [ref](https://medium.com/showpad-engineering/why-you-should-never-use-function-calls-in-angular-template-expressions-e1a50f9c0496) – Filip Kováč Oct 09 '21 at 20:03