9

I'm trying to create custom pipe on asynchronous pipe, I tried many solutions, but still not working. Here is the snippet of code.

product.sort.ts - custom pipe

import { PipeTransform, Pipe } from '@angular/core';
import { Observable } from 'rxjs/Observable';

@Pipe({
    name: 'sortByName'
})
export class ProductPipe implements PipeTransform{
    /*transform(values: Array<any>, term:string){
        return values.filter(obj => obj.pname.startsWith(term))
    }*/

    //CODE NOT WORKING >>>>>
    transform($value: Observable<Array<any>>, term:string){
        if($value){
            $value.subscribe(
                (obj) => {
                    return obj.filter(obj => obj.pname.startsWith(term))
                }
            )
        }
    }
}

products.component.ts - main component

import { Component, OnInit } from '@angular/core';
import { Store } from '@ngrx/store';
import { Observable } from 'rxjs/Observable';
import { AppService } from '../app.service/app.service';
import { ProductPipe } from '../products.sort/products.sort';

@Component({
    selector: 'products-pg',
    template: `
        Products List:
        <ul>
            <li *ngFor = 'let product of $productList | async | sortByName:"A"'>{{product.pname}}</li>
        </ul>
    `
})
export class ProductsComponent implements OnInit{
    private $productList:Observable<Array<any>>;

    constructor(private _service: AppService, private _store: Store<Array<any>>){}

    ngOnInit(){
        this._service.setProductList();
        this.$productList = this._store.select('products');
    }
}

Here, I'm using store for state management, I'm trying to sort by name, so passing "A" as first letter. Since $productList is observable, how to write pipe which handles asynchronous behavior like this, plase help me to solve this.

12
  • async resolves the observable, so sortByName just gets the resulting array. Did your commented-out implementation not work? What precisely is going wrong? Commented Jan 9, 2017 at 12:28
  • its showing error link "$value.subscribe is not a function" Commented Jan 9, 2017 at 12:31
  • Presumably because (due to the fact that you're already using the AsyncPipe), by the time it gets to your pipe it's just an array, and arrays don't have a subscribe function. The minimal reproducible example is [].subscribe(). Commented Jan 9, 2017 at 12:32
  • yap, im trying to use Observable but not able to figure out the proper way of implementing it in 'transform()' method. Commented Jan 9, 2017 at 12:41
  • 1
    Your pipe is not getting an observable. That's the whole point of putting | async before calling your own pipe. It resolves the observable and returns the result, an array. Why have you commented out the code that takes an array? What was the initial problem that led to your trying to rewrite the pipe? Commented Jan 9, 2017 at 12:42

3 Answers 3

8

I had the same issue as well the way I fixed it, was like this:

<li class="list-group-item" *ngFor='let alert of _alerts$ | ipwrAlertFilter:seeAll | async'>

look how I had my custom filter before the async pipe, this way my custom pipe gets an observable and then in my pipe I have this:

return value.map(data => data.filter(x => x.originalHasBeenSeen === false));

this way my custom pipe still returns something that I can still apply async pipe on. and so with each new item in stream I still get a hit on my custom pipe and my view gets updated. hope this helps.

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks, also for the last line in my case I had to use the map operator return value.pipe(map(el => el.value));
5

The best way to go about it is to still stick with your async and after the async call your custom pipes , like the one in the question, just that your pipe code will now change to not act on the to be loaded array since the record is still loading, so we tell our custom pipe not to do anything again or return empty array. e.g

transform(items: any[], field: string, format?: string) { 
    if (items == null) //since the async is still working
      return [];
//do our normal pipe logic or function
    return items.sort((a: any, b: any) => {
      let value1 = a[field];
      let value2 = b[field];

      if (value1 > value2) {
        return 1;
      } else if (value1 < value2) {
        return -1;
      } else {
        return 0;
      }
    });

then your template still retains

*ngFor = 'let product of $productList | async | sortByName:"A"'

Comments

3

In case this isn't clear for everyone, it's as easy as...

<div *ngFor="let product of (products$ | async | searchFilter: (query$ | async) )">

Notice the brackets (query$ | async) already in an async pipe operation.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.