4
votes

I have two components.

  1. Item List -- for displaying a list of items (parent)
  2. Item -- wrapper for a single item (child)

In the Item List component I basically create an observable using a service. The function getItems() returns an observable of items:

items$: Observable<Item[]>;

ngOnInit() {
  this.items$ = this.service.getItems();
}

In the template I do ngFor with async pipe to display a list of items:

<div *ngFor="let item of items$ | async">
    <card-item [item]="item"></card-item>
</div>

Now, in the Item component I have an option to remove that particular item from a firestore database:

@Input() item: Item;

removeItem() {
    this.service.removeItem(this.item.id);
}

This operation takes some time so I wanted to remove it "locally" from the observable in the parent component.

I probably have to filter the observable in the parent component and somehow return a new filtered observable, but I don't know how can I manage this from the child component?

removeItem() {
    // remove from firestore, but this can take some time

    this.service.removeItem(this.item.id);

    // meanwhile... remove it from the observable on parent

    source.pipe(filter(item => item.id !== this.item.id)); // here is the problem I have
}

As you can see above I don't know how to "connect" to the observable and filter it.

The reason for that is when I want to animate the remove operation (like fade out), the item flashes. It's because the animation finishes earlier than the record is removed from the firestore.

1

1 Answers

1
votes

I suggest you keep your card-item as a Presentation/Dumb component. Means you should remove the service dependency from it since its main job is to display the item.

Then, a humble solution would be adding an Output to the card-item that emits removal-events telling its parent component "hey! you should delete me".

So, basically in card-item.component.ts your code would look as below:

@Input() item: Item;
@Output() removeClick = new EventEmitter<Item>();

removeItem() {
    this.removeClick.emit(this.item);
}

Now, we come to the "remove" from observable part. I would prefer using the term filter since observables are streams and you just observe values at a certain point of time. Instead you can remove items from the array of items. In this case, you need to keep a local copy of your current items you display. Means remove the async pipe and subscribe to your observable in your component code.

And in your item-list.component.ts

ngOnInit() {
    this.items$ = this.service.getItems();
    this.items$.subscribe(items => {
        this.localItems = items;
    }
    )
}

onRemoveClick($event: Item) {
    // Removal logic here
    // Something like
    const index = this.localItems.indexOf($event);
    if (index > -1) {
        this.localItems.splice(index, 1);
    }
}

In your item-list.component.html :

<div *ngFor="let item of localItems">
    <card-item [item]="item" (removeClick)="onRemoveClick($event)"> </card-item>
</div>