Introduction
I started developing in Angular four years ago with my team. There are several concepts I wish I knew at the beginning. And this one is probably the main subject. I hope this can be helpful to anybody that would start working with angular.
What is it and why use Behaviour Subject
Behaviour subject is a component from the RxJx library. You can check the documentation here :
RxJs Behaviour Subject Documentation
A behaviour subject is basically an object that emits the current value to new subscribers.
So If you have a data structure, object or variable you want to share all the way through your application, this will be very useful.
Our Application example
So I created a new Angular project, and i added three components to create our main page.
It is a very simple application. So our page will have a header with a save button, a metadata part with an input text and a content part with a textarea. Of course this could be way more complex but the goal here is to show the principle of the behaviour subject.
What we would like to do is to disable the save button in the header when the metadata field and the content field are not filled.
All the source code can be found at this repository :
Source code in GitHub
Scafolding the components
For our page, we create a main component create-article, and inside this component we create our three components : header, metadata and content.
I created a common folder to put in the services and models and everything that could be common or shared between the different components.
We put a model Article and a service for the state of the article.
The Article and Article state
Our article interface, with the data from the metadata and content.
export interface Article {
/**
* the medata Infos
*/
metadata?: string;
/**
* content field
*/
content?: string;
}
And our article state class will look like this :
import { Injectable } from '@angular/core';
import { BehaviorSubject } from "rxjs";
import { Article } from "../model/Article";
@Injectable({
providedIn: 'root'
})
export class ArticleStateService {
public readonly selectedArticleState$ = new BehaviorSubject<Article>([] as Article);
private selectedArticleState: Article = {} as Article;
public constructor() {
}
/**
* set the current article
* @param value
*/
public setSelectedArticleState(value: Article): void {
this.selectedArticleState = value;
this.selectedArticleState$.next(this.selectedArticleState);
}
/**
* Get the current article
*/
public getSelectedArticleState(): Article {
return <Article>this.selectedArticleState;
}
}
By convention, we suffix the behaviour Subject with a $. This behaviour subject is used to send the data to its subscribers. We use the selectedArticleState for storing the current Article value inside the class.
We also have a setter and a getter.
Let use our article state
So in the main component create-article, in the create-article-component.html, we use our 3 sub-components :
<app-header></app-header>
<app-metadata></app-metadata>
<app-content></app-content>
Our header component
import {Component, OnInit} from '@angular/core';
import {ArticleStateService} from "../../common/service/article-state.service";
import {Article} from "../../common/model/Article";
@Component({
selector: 'app-header',
templateUrl: './header.component.html',
styleUrls: ['./header.component.scss']
})
export class HeaderComponent implements OnInit {
public disabled: boolean;
public constructor(public readonly articleStateService: ArticleStateService) {
}
public ngOnInit(): void {
this.disabled = true;
this.articleStateService.selectedArticleState$.subscribe((article: Article) => {
if (article.content && article.metadata) {
this.disabled = !(article.content.length > 0 && article.metadata.length > 0);
} else {
this.disabled = true;
}
});
}
}
When the component is initialized, we subscribe to the behaviour subject, so anytime the article change, we will get here the current article.
For our application, we can set the disabled variable to the right value, depending on the content and the metadata values.
The setting of the article state
For example, in the metadata component we create a formGroup for the metadata field and when the value changes we set the current article from the state with its value.
import {Component, OnInit} from '@angular/core';
import {FormControl, FormGroup, Validators} from "@angular/forms";
import {Article} from "../../common/model/Article";
import {ArticleStateService} from "../../common/service/article-state.service";
@Component({
selector: 'app-metadata',
templateUrl: './metadata.component.html',
styleUrls: ['./metadata.component.scss']
})
export class MetadataComponent implements OnInit {
public metadataForm: FormGroup;
constructor(public articleStateService: ArticleStateService) {
// We create a formGroup for the metadata field
this.metadataForm = new FormGroup({
metadata: new FormControl(
'', [Validators.required])
});
}
public ngOnInit(): void {
this.metadataForm.valueChanges.subscribe(formGroup => {
// We get the current article with the article state service
const article: Article = this.articleStateService.getSelectedArticleState();
// We set the metadata in the current article to the formGroup metadata value
article.metadata = formGroup.metadata;
// We set it back in the state service as the current article
this.articleStateService.setSelectedArticleState(article);
});
}
}
The end
I hope this post can be useful to anybody, beginner or not in Angular. You can find the unit tests for the components and service in the github repository.
Please let me know what you think.
Do you use the behaviour subjects that way in your applications?
Do you have other ways of doing it ?
Top comments (0)