1

Hi I have a ShopItemComponent. I want it to take a function as an input so that when a button in shop-item.component.html is clicked, the function gets called. inside the function in shop-item.component.html is an observable, the function subscribes to the function:

removeShopItem(shopItemId) {
    return this.removeShopItemFunction(shopItemId).subscribe();
  }

the observable is defined in shop-item-big.component:

  removeShopItem = (shopItemId: string): Observable<any> => {
    return this.shopService.remove(shopItemId);
  };

I heard that it's not ideal to call .subscribe() directly, a better way is to use async. I tried to modify the function in shop-item.component.ts to be like this

removeShopItem(shopItemId) {
    return this.removeShopItemFunction(shopItemId);

and modified the shop-item.component.html to be like this

    <button (click)="removeShopItem(shopItem.id)|async">

But the editor is complaining: Action expression cannot contain pipes

What's the right way to subscribe to an observable with an onclick? much help is appreciated! thanks!

shop-item.component.ts

@Component({
  selector: 'shop-item',
  templateUrl: './shop-item.component.html',
  styleUrls: ['./shop-item.component.scss']
})

export class ShopItemComponent {
  @Input() shopItemId;
  @Input() removeShopItemFunction: (shopItemId: string) => Observable<any>;

  removeShopItem(shopItemId) {
    return this.removeShopItemFunction(shopItemId).subscribe();
  }

}

shop-item.component.html

  <div>
    <ng-content select="[header]"></ng-content>
    <button (click)="removeShopItem(shopItem.id)">
    </button>
  </div>
  <div>
    <ng-content select="[body]"></ng-content>
  </div>

shop-item-big.component.ts

@Component({
  selector: 'shop-item-big',
  templateUrl: './shop-item-big.component.html',
  styleUrls: ['./shop-item-big.component.scss']
})
export class ShopItemBigComponent {
  @Input() shopItem: ShopItem;

  constructor(
    private readonly shopService: ShopService) {
  }

  removeShopItem = (shopItemId: string): Observable<any> => {
    return this.shopService.remove(shopItemId);
  };
}

shop-item-big.component.html

<shop-item [shopItemId]="shopItemId" [removeShopItemFunction]="removeShopItem ">
  <div header>
    <h2>Item Name</h2>
  </div>
    <div body>
      <p>details</p>
    </div>
</shop-item>
STH
  • 73
  • 6

1 Answers1

3

Do not use pipe here. Your subscribe method was the right way. You can actually use the pipe async when using it in an *ngFor or *ngIf for instance, so when your observable is providing a data to the HTML. Here, your click does not provide any data to the HTML, so it has no sense to use the pipe.

Also, providing a function to a child is bad practice. Your child could actually emit an Output. The parent listens to that output, and triggers his own method.

@Component({
  selector: 'shop-item',
  templateUrl: './shop-item.component.html',
  styleUrls: ['./shop-item.component.scss']
})

export class ShopItemComponent {
  @Input() shopItemId;
  @Output() removeShopItem: EventEmmiter<number> = new EventEmitter();

  removeShopItem(shopItemId) {
    return this.removeShopItem.emit(shopItemId);
  }

}

And in the parent:

<shop-item [shopItemId]="shopItemId" (removeShopItem)="removeShopItem($event)">
  <div header>
    <h2>Item Name</h2>
  </div>
    <div body>
      <p>details</p>
    </div>
</shop-item>
Random
  • 3,158
  • 1
  • 15
  • 25
  • thank you for your input. can you talk more one why providing a function to a child is bad practice, please? – STH Jun 20 '23 at 14:40
  • It breaks components isolation. A component should not execute code from an other component. Angular Change Detection Cycles may not detect the impact your child is making on the parent attributes, so the parent attribute may not propagate correctly. With Inputs/Outputs, Angular is well informed of data flows, and can then only update components that need to be. – Random Jun 20 '23 at 14:43
  • Addition to @Random's comment: Correct, but only when the *value or object reference* of an Input/Output changes. One thing I had learned the hard way is that changing properties, or array elements, is not enough. It has to be a *different* object or array. – JSmart523 Jun 22 '23 at 01:48