How to use BehaviorSubject in Angular

Maybe this is not the best example, but I used BehaviorSubject() in angular to two things on the project Angular + Drupal

  • Connecting two components to the same function. If that function change, the data change in both.  
  • Send a variable that I get from one component to another.

This is pretty unprofessional maybe, and I'm sure that there is a better approach to the same result. I have asked several questions in StackOverflow like for example: 

So, let's go on it: Connecting two components to the same function 

The problem: 

I have a navigation menu, with a list of tasks links, all of that comes from a web service. A simple GET Method. Now, when you click on that, it shows me a page ready to update the content from the task. A simple GET Method and a PATCH Method. So, we have two components, the navigation component with the menu, and the view-task component with a formBoth of them, are connected to the task.service.ts, which will call the HTTP request. 

After the update of a task, the content change, but,  it is not reflected in the menu. The reasons are simple, after an update of the task on the view-task.component.ts it doesn't affect the navigation component. Because the only way to update the menu task list is calling the getTask() function. Let's see some code: 

This is the HTML from the menu to print the tasks:


   <li *ngFor="let task of tasks" class="d-inline-block col-md-12">         <a routerLink="/task/{{task.id}}" > {{task.title}}</a>         <!-- <span class="close big"></span> -->         <button class="close big" title="delete task"         (click)="delete(task)">x</button>     </li>

And this is the getTask() function from the menu: 


ngOnInit() {     this.getTasks();     } getTasks(): void {     this.taskService.getTasks()     .subscribe(Tasks => this.tasks = Tasks);    }

And, here in the service, i do the HTTP request. 


  getTasks(): Observable<Task[]> {          const url = `${this.mainUrl}/tasks`;         return this.http.get<Task[]>(url)         .pipe(         tap(tasks => this.log(`fetched tasks`)),               catchError(this.handleError('getTasks', []))         );   

And, from the side of the view task component, we have a form that sends the data to the updateTask()  to the task.service with the save() function. 


    this.taskService.updateTask(task, id)           .subscribe();

And the updateTask from the service: 


    updateTask (task: Task, id): Observable<any>{       const url = `${this.mainUrl}/node/${id}`;               return this.http.patch(url, task, httpHaljson)         .pipe(         tap(_ => this.log(`updated task id=${id}`)),         catchError(this.handleError<any>('updateTask'))         );     }

So, this is not connected with the updateTask() function from the view-task component. Now, I need to subscribe my navigation component to the function getTask from the tas.service.ts, and this one, have to change everytime that we call the save()  function in the view-task component.

There is where be BehaviorSubject's comes;

Import it in the task.service.ts

	import { BehaviorSubject } from 'rxjs/BehaviorSubject';

Declate the task object, as a BehaviorSubject, 


  private tasks = new BehaviorSubject([]);   private taskList: Task[];

Now, the getTasks() function, is not longer the same as before, now will check if our array is empty or not: 


  getTasks() {       if (!this.taskList || this.taskList.length === 0) {           this.initializeTasks();       }       return this.tasks.asObservable();   }

And, something else important, we said that our task is an Observable. And we call the initializeTask() functionb. 


      initializeTasks(){       const url = `${this.mainUrl}/tasks`;         return this.http.get<Task[]>(url)             .subscribe (tasks => {                                   this.tasks.next(tasks);               });}

Basically is filling the task with the data from the service. Now we call all this from our navigation component. But don't worry, ngOnInit() and getTask() function are the same. 

What happened to the updateTask function? this has the responsibility to send the "order" to change the task array. 


    updateTask (task: Task, id)/*: Observable<any> */{       const url = `${this.mainUrl}/node/${id}`;               return this.http.patch(url, task, httpHaljson)           .subscribe(resp => {             this.initializeTasks()           });}

And look here, we call the initializeTasks() function, to fill the data!. And magically it shows in our menu component. 

Why is commented the observable?. 

You are defining your return type as Observable<Task[]>, but you are subscribing to the HTTP request. If you want to return an Observable, you cannot subscribe to it. If you just want to get the data and assign it to some attribute, you should subscribe to it but cannot define your return type as Observable. You should do one thing or the other.


I'm sure that there are a better approach to the same solution, but for practical purpose the app is doing what it have to do . Remember to add comments if you want, and to see How I delete and element, and if you are watching that elemnt it will send you to the /home page, click here