Jordi
Jordi

Reputation: 23187

Angular: Pipe doesn't work

I've created a pipe that doesn't works fine. The code seems to be straightforward and clear, but I don't quite figure out what's wrong.

Any ideas?

My app.module:

import { HeroModule } from "./hero/hero.module";

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule, HeroModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

My hero.module:

import { HeroComponent } from './hero.component';
import { FlyersPipe } from '../pipes/flyers.pipe';

@NgModule({
  imports: [
    CommonModule
  ],
  declarations: [HeroComponent, FlyersPipe],
  exports: [HeroComponent, FlyersPipe]
})
export class HeroModule { }

My flyers.pipe.ts:

import { Pipe, PipeTransform } from '@angular/core';
import { Hero } from '../model/hero';

@Pipe({
  name: 'appflyers'
})
export class FlyersPipe implements PipeTransform {

  transform(heroes: Array<Hero>, args?: any): any {
    console.info(heroes);
    return heroes.filter(hero => hero.canFly);
  }

}

Lastly, my hero.component.html:

<div *ngFor="let hero of heroes | appflyers">
    {{hero | json}}
</div>

EDIT

I'm adding heroes into heroes HeroComponent property using this code:

<input type="text" #box
          (keyup.enter)="addHero(box.value); box.value=''"
          placeholder="hero name">
  <input type="checkbox" [checked]="canFly" (change)="canFly = !canFly"/>

hero.component.ts:

import { Component, OnInit } from '@angular/core';
import { Hero } from '../model/hero';

@Component({
  selector: 'app-hero',
  templateUrl: './hero.component.html',
  styleUrls: ['./hero.component.css']
})
export class HeroComponent implements OnInit {

  private heroes: Array<Hero> = new Array<Hero>();
  private canFly: boolean = true;

  constructor() { }

  ngOnInit() {
  }

  private addHero(name: string) {
    this.heroes.push(new Hero(name, null, this.canFly));
  }

  private reset() { this.heroes = this.heroes.slice(); }

}

Upvotes: 2

Views: 5819

Answers (1)

David
David

Reputation: 34425

I think your scenario is exactly what the pipe documentation describes

Notice the odd behavior in the live example / download example: when you add flying heroes, none of them are displayed under "Heroes who fly."

Pipes are pure by default, meaning that they won't get executed on change detection if the input does not change.

Angular executes a pure pipe only when it detects a pure change to the input value. A pure change is either a change to a primitive input value (String, Number, Boolean, Symbol) or a changed object reference (Date, Array, Function, Object).

Angular ignores changes within (composite) objects. It won't call a pure pipe if you change an input month, add to an input array, or update an input object property

To make it work, you can turn your pipe into an impure pipe, meaning that it'll get executed during each change detection cycly. That's OK if the operation performed by the pipe is fast.

@Pipe({
  name: 'appflyers',
  pure: false
})

Another solution is to keep your pipe pure and change the reference to the array (e.g. using the spread operator) when you add a new hero

private addHero(name: string) 
{
    this.heroes = [...this.heroes, new Hero(name, null, this.canFly)];
}

Upvotes: 2

Related Questions