skip to Main Content

We are trying to use an angular pipe to filter a list of sub items, (if no items then parent item is removed)
HTML :-

   <div class="row border-bottom item" *ngFor="let item of pageContent | filter: searchText; let i = index">
      <div class="col-6">{{item.name}}</div>
      <div class="col-6">
        <ul>
          <li *ngFor="let link of item.content">
            {{link.content}}
          </li>
        </ul>
      </div>
    </div>

our pipe is like this :-

export class FilterPipe implements PipeTransform {
transform(items: AllContent[], searchText: string): AllContent[] {

if (!items) return [];
if (!searchText) return items;


return items.filter(item => {
  item.content.filter(c => c.content.toLowerCase().includes(searchText.toLowerCase()))
})

we cannot get this filter to work and it literally not filtering the the child items out.

we have also tried

return items.foreach(item => {
item.content.filter(c => c.content.toLowerCase().includes(searchText.toLowerCase()))
})

is there somthing that we are missing?

2

Answers


  1. You’re missing a return statement in the filter method and you need to add .length:

    return items.filter(item => {
      // add the return below and append .length at the end
      return item.content.filter(c => c.content.toLowerCase().includes(searchText.toLowerCase())).length
    })
    

    When it’s a one-liner, you don’t need the return, but when breaking it to multiple lines using the {...}, then you need to return the items. Same with array.map().

    What @Adam Jenkins mentions in the comment is important to note. The condition in an array.filter() method needs to return either true or false (or a truthy or falsey value). The nested .filter() (on the inside) however returns an array, which is always truthy. This means the outside .filter() method, and therefore the pipe, will always return all of the items. To get around this you could add .length at the end of the nested filter() method. This will return a number based on how many items are included in the array returned by the nested .filter(). If it has 1 or more items, the condition will be truthy and the pipe will return the pageItems that has content that includes the searched text. If the nested filter() returns 0 items in its array, the condition will be falsey, in which case the pipe will not return that pageItem.

    Another solution could be to use the .some() method, which returns true or false. This array stops when it hits true, in which case it won’t have to continue iterating over the rest of the items. This is better for performance.

    If you want to filter both the pageItem and content of the pageItem then you could do the following (assuming the pipe resets the items when filtering. Not sure if it does.):

    const filteredItems = items.filter(item => {
        item.content = item.content.filter(c => c.content.includes(searchText))
        return item.content.length ? true : false
    })
    return filteredItems
    

    Also, the way it’s written it looks like an item has a property of content which has a property of content inside it. Not sure if this is what you intended, but this code is assuming that that is correct.

    Login or Signup to reply.
  2. The problem lies in how you’re using the filter function. The filter function creates a new array with all elements that pass the test implemented by the provided function. However, in your current implementation, you’re using filter on the parent item, but not returning the result of this operation.

    Here’s the corrected version of your custom pipe:

    import { Pipe, PipeTransform } from '@angular/core';
    @Pipe({
        name: 'filter'
    })
    export class FilterPipe implements PipeTransform {
        transform(items: AllContent[], searchText: string): AllContent[] {
            if (!items) return [];
            if (!searchText) return items;
            return items.filter(item => {
                item.content = item.content.filter(c => c.content.toLowerCase().includes(searchText.toLowerCase()));
                return item.content.length > 0; // Filter out parent items with no matching sub-items
            });
        }
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search