paint-brush
Angular Mastery: Reacting to Dynamic Input Parameter Changesby@mpotapov
503 reads
503 reads

Angular Mastery: Reacting to Dynamic Input Parameter Changes

by Mikhail PotapovJuly 14th, 2023
Read on Terminal Reader
Read this story w/o Javascript
tldt arrow

Too Long; Didn't Read

In this article, we will look at ways to solve this problem using the example of a "user-profile" component. The current approach is to load the data with the id we need into ngOnInit. This approach works if the id does not change during the lifecycle of the component. Another option is to use a subject that will update when the id value changes and then load new data.
featured image - Angular Mastery: Reacting to Dynamic Input Parameter Changes
Mikhail Potapov HackerNoon profile picture

In Angular, we often encounter a situation where a component needs to react to changes in input parameters. In this article, we will look at ways to solve this problem using the example of a "user-profile" component.

Problem Statement

Imagine we have a user profile component that accepts an input parameter @Input() id: string, representing a user ID. We need to request user data using this identifier and display it.

Initializing the data in OnInit

The first thing that comes to mind is to load the data with the id we need into ngOnInit(). This approach works if the id does not change during the life of the component. Also, it's important to remember to unsubscribe from the observable to avoid potential memory leaks.


@Component({
  selector: 'user-profile',
  templateUrl: './user-profile.component.html',
  styleUrls: ['./user-profile.component.css'],
})
export class UserProfileComponent implements OnInit, OnDestroy {
  @Input() id!: string;

  user?: User;

  private sub$ = Subscription.EMPTY;

  constructor(private readonly userService: UserService) {}

  ngOnInit() {
    this.sub$ = this.userService.getUser(this.id).subscribe(user => {
      this.user = user;
    })
  }

  ngOnDestroy() {
    this.sub$.unsubscribe();
  }
}


<ng-container *ngIf="user">
  ...
</ng-container>


But what if the id can change over time? With the current approach, we won't catch this change and the data won't be updated.

Using Subject to track changes

Another option is to use a Subject that we will update when the id value changes and then load new data. For this, we can use the ngOnChanges hook or set id as a setter @Input() set id(id: number) {...}.


@Component({
  selector: 'user-profile',
  templateUrl: './user-profile.component.html',
  styleUrls: ['./user-profile.component.css'],
})
export class UserProfileComponent {
  @Input() set id(id: string) {
    this.id$.next(id);
  }

  private id$ = new ReplaySubject<string>(1);

  readonly user$: Observable<User> = this.id$.pipe(
    switchMap(id => this.userService.getUser(id))
  );

  constructor(private readonly userService: UserService) {}
}


<ng-container *ngIf="user$ | async as user">
  ...
</ng-container>


With this approach, we can track id changes and update user data accordingly. It is convenient to implement this option by using the async pipe to subscribe to profile updates in the template. This will save us from having to manually unsubscribe.

Simplifying the code using setter and async pipe

There is a simplification option in the above approach. We can create a new pipe directly in the setter @Input() set id(id: string) {...} and subscribe to it in the template. This approach reduces code size, improves readability, and simplifies subscription management.


@Component({
  selector: 'user-profile',
  templateUrl: './user-profile.component.html',
  styleUrls: ['./user-profile.component.css'],
})
export class UserProfileComponent {
  @Input() set id(id: string) {
    this.user$ = this.userService.getUser(id);
  }

  user$?: Observable<User>;

  constructor(private readonly userService: UserService) {}
}


<ng-container *ngIf="user$ | async as user">
  ...
</ng-container>


With this approach, a new observable will be created each time the id is changed, and the async pipe will automatically unsubscribe from the old observable and subscribe to the new one.


This ensures the relevance of the user data even if the id changes dynamically during the component's life.


As a result, we get an efficient and manageable way to update data based on changes in the input parameters of the component.