How to make component in angular to re-render on route change?

Viewed 2751

I have an Angular 7 page-component that contains another component. When selecting a product from select box on the little component, it navigates to another page with the same page-component (ProductDetails), but with different parameters in the url (different productId).

The navigate on the little component when selecting a product:

onProductSelected(args) {
    const selectedOption = args.target.value;
    this.router.navigate(['/products', selectedOption]);
  }

page-component ProductDetails.ts:

@Component({
    selector: 'product-details',
    templateUrl: './product-details.component.html',
    styleUrls: ['./product-details.component.scss']
})
export class ProductDetailsComponent implements OnInit, OnDestroy {
    currentProductId: string;
    product: ProductModel;
    subscriber;

    constructor(private productService: ProductService,
                private route: ActivatedRoute) {}

    ngOnInit() {
        this.currentProductId = _.get(this.route, 'snapshot.params.productId');
        this.subscriber = this.productService.getProdactById(this.currentProductId)
            .subscribe(res => {
                this.product = res;
            });
    }

    ngOnDestroy() {
        if (this.subscriber) {
            this.subscriber.unsubscribe();
        }
    }
}

I need the ngOnInit of the page-component to notice that the url changed and re-create itself. (in order to get the new productId parameter and render the product details). How can I do that?

Thanks!

1 Answers

Angular router params is an observable. This can be used to subscribe to changes in parameters values which can later be passed, using map or other RxJs operators, to a service for manipulation.

@Component({
    selector: 'product-details',
    templateUrl: './product-details.component.html',
    styleUrls: ['./product-details.component.scss']
})
export class ProductDetailsComponent implements OnInit {
    product$: Observable<ProductModel>;

    constructor(private productService: ProductService,
                private route: ActivatedRoute) {}

    ngOnInit() {
        this.product$ = route.params.pipe(
            map(params => params.productId),
            map(productId => this.productService.getProdactById(productId))
        );
    }
}

Now you can use the product observable values in your template using async pipe and there is no need to handle the subscription:

<div *ngIf="product$ | async as product">
  <p>Product name {{product.name}}</p>
</div>

See example in this stackblitz

Related