How to Write Better Components in Angular

Good practices for building angular components

Chidume Nnamdi 🔥💻🎵🎮
Bits and Pieces

--

Components are the simplest building blocks of Angular applications. An Angular app is a tree of components, it starts with the root component, going up spreading into multiple branches.

Before we start — When building NG apps, it’s better to share your components in a reusable collection, so you won’t have to rewrite them.

Using tools like Bit (GitHub) you can instantly share components (automatically isolated with dependencies) into a shared hub, use and develop them anywhere, sync changes and build multiple apps faster. Try it.

The Components renders other components, which recursively renders its children components. The Components are composable, meaning we can build large Components composed of simpler Components.

Whenever we want to build an app. we first draw a mockup design either in wireframe or Dreamweaver . Let’s say we want to build a Movies app, we will draw the mockup like this:

Pouring the above into a single html file is not ideal, as the app grows we will have too many codes in the file to handle, we might lose tracks of so many functionalities. Using SPAs like Angular, we will break the design into components:

  • HeaderComponent
  • PageNameComponent
  • MoviesComponent

HeaderComponent

This Component holds the html view for the header. See this Component have links for the pages for this app; New Movies, Trending Movies and Highest-Grossing Movies.

The code will be:

@Component({
selector: 'header-cmp',
template: `
<div class="header">
<ul class="role">
<li><a routerLink="/movies">New Movies</a></li>
<li><a routerLink="/trending-movies">Trending Movies</a></li>
<li><a routerLink="/hg-movies">Highest-Grossing Movies</a></li>
</ul>
</div>
`
})
export class HeaderComponent{...}

PageNameComponent

This component will display the current route navigation

@Component({
selector: 'pageName-cmp',
template: `
<div class="pageName">
<hr />
{{pageLocation}}
<hr />
</div>
`
})
export class PageNameComponent {...}

MoviesComponent

This holds and renders the list of movies.

@Component({
selector: 'movies',
template: `
<div>
<div *ngFor="let movie of movies">
<div>
{{movies.name}}
Year: {{movie.year}}
Gross: {{movie.gross}}
</div>
</div>
</div>
`
})
export class Movies {
private movies: Array<any>
constructor(private movieService: MovieService) {}
ngOnInit() {
this.movieService.getMovies().subscribe(data => this.movies = data)
}
}

The app actually stems from the root component AppComponent. Now we broke the app into components, and in each component, we decide the view of the app it will handle: the HeaderComponent handles the display of the header, the PageNameComponent deals with the display of the current page position. MoviesComponent handles the fetching and display of the movies.

One thing we have to note is that the Components have a single responsibility, it deals with a particular thing. Also, Components are built to be re-used. With a single responsibility comes re-usability. If you see your self re-writing a similar Components know that its time to isolate the Component so you can plug it anywhere you want its functionality, in that way we write better components.

Looking at the above Components we would think that they are all re-usable, but if we look closer we will see they have different concerns especially the MoviesComponent.

To write better components in Angular, we should follow the below steps:

  • Identify Mixed concerns
  • Divide Components into Container and Presentational
  • Try as much as possible to reduce your Container Components

Identify Mixed concerns

Mixed concerns are when your components have different responsibilities. A Component may be responsible for:

  • State management
  • UI presentation
  • Business logic
  • Data fetching
  • User interaction

Those are different concerns, Components should be responsible for one of the above. Looking at the MoviesComponent:

@Component({
selector: 'movies',
template: `
<div>
<div *ngFor="let movie of movies">
<div>
{{movies.name}}
Year: {{movie.year}}
Gross: {{movie.gross}}
</div>
</div>
</div>
`
})
export class MoviesComponent {
private movies: Array<any>
constructor(private movieService: MovieService) {}
ngOnInit() {
this.movieService.getMovies().subscribe(data => this.movies = data.movies)
}
}

This Component is responsible for these:

  • UI presentation: It displays the array of movies
  • Business logic: It is tied to the business logic of the application, the app has its logic on how to get movies.
  • Data fetching: It fetches its data from a Service.
  • State management: It injects a Service MoviesService and queries the movies array from server and slices the data from the returned Observable.

See we have identified different concerns in our Component.

Mixed Components

What are Mixed Components? Mixed Components are Components that span multiple concerns. They contain at least two concerns listed above.

The MoviesComponent is a mixed component, it has multiple concerns: It presents the UI of the fetched movies, it injects the MovieService, closely tied to the business logic. Whenever a mixed component gets large we have to divide the component into Container components and Presentational Component.

Divide Components into Container and Presentational

It’s always a good practice to divide a component into Container component and Presentational component. Why should we divide a component into Container and Presentational? It is majorly because of reusability, maintainability, and optimization.

Reusability: This is the reason why SPAs are componentized, to make reusing of a group of views possible. Imagine re-writing certain views regularly. It will be ideal to pack the view in a component and call it wherever you need it.

Millions of libraries are made up of reusable components. Look at the Angular Material library, it is made up:

  • Buttons
  • Tabs
  • Tables
  • Labels
  • Modals
  • etc

All those are components made to be re-usable, we often need button in different places in our app. We know that button have different styles:

The Button component is crafted like this:

@Component({
slector: 'button',
template: `
<button class=""></button>
`
})
export class Button {
@Input()
}

Look the Button component does not inject any Service, does not side-effect, it receives data to render via the input bindings and content projection ng-content.

These re-usable components are what is shared with the outside world. These components are shared via NPM or Bit.

Maintainability: Even as the system contains many parts, each of the parts addresses a single concern each of them is easy to maintain. There will be no breaking changes if one part decides to change.

Optimization: If a component becomes predictable, we can guess the values and optimize based on that info. Presentational components are much more like pure functions. They work based on the inputs they are given without affecting state outside of their scope, so with this, we know when it would re-render. It would re-render when the inputs changes and since its display is dependent on an outside data, we can safely memoize the component to only re-render when its inputs changes. Having as much as Presentational in our app is good, it will greatly boost the performance rate of our app.

Container Components

These components are also known as “Smart” components. Smart? as in smart? Yes, they are smart enough to produce their own data they render or pass down to child components. They are self-contained, they need not be supplied data via the input/output bindings. They are tied to the business logic of the application, not meant to be reused or shared.

Their template is majorly made up of child components and input/output bindings. Container components generally:

  • Pass data to Presentational components or react to events from them presentational components.
  • Inject Services or the Store service if using State management.
  • Perform the business logic either via Services or Effects when using the Store.

Presentational Components

Like the name implies it displays the UI. They are also called “Dumb” components, aww :(, why dumb? They don’t produce the data they display. They are dependent on a parent component to pass them its data via its input bindings.

They do not know of any Service or inject them, their only responsibility are to present UI element and to delegate user interaction up to the container elements via its event bindings. Presentational components are very good to be re-usable: Buttons, Tabs, etc because they are predictable and doesn’t side-effect.

Try as much as possible to reduce your Container Components

What I am trying to say here? If you have 25 components in your app and Container components are 20, try to reduce it like to 10 or 5. Why? Because they will impact the performance of your app. Container component cannot be optimized because of its unpredictable nature but Presentational components are inputs and outputs dependent so they are predictable in that case we can optimize them using the OnPush Cd strategy.

When making your components as Dumb as possible try

Not to mutate its inputs: Mutation would lead to data inconsistency. If a component mutates the parent data, because Angular uses === equality check to know when to re-render a child component, the child component would not re-render because there is no reference change which is caused by the mutation so there will be an incorrect display of data.

Not to side-effect: Side-effecting adds un-predictability to Presentational components making them hard to optimize. If a component needs to communicate data it should emit it via its output bindings.

Not to inject Service: Injecting any Service makes the component have a more than one concern: Ui presentation and Business logic. Presentational components do not get their own data. If a Presentational component needs data it should get it via its input bindings.

Making our Movies App Components Better

Now, we know how to write better components, let’s refactor our Movies app components. Looking at the MoviesComponent

@Component({
selector: 'movies',
template: `
<div>
<div *ngFor="let movie of movies">
<div>
{{movies.name}}
Year: {{movie.year}}
Gross: {{movie.gross}}
</div>
</div>
</div>
`
})
export class Movies {
private movies: Array<any>
constructor(private movieService: MovieService) {}
ngOnInit() {
this.movieService.getMovies().subscribe(data => this.movies = data)
}
}

We have to split it into Container and Presentational components.

@Component({
selector: 'movies',
template: `
<div>
<movies-list [movies]="movies"></movies-list>
</div>
`
})
export class Movies {
private movies: Array<any>
constructor(private movieService: MovieService) {}
ngOnInit() {
this.movieService.getMovies().subscribe(data => this.movies = data)
}
}
@Component({
selector: 'movies-list',
template: `
<div *ngFor="let movie of movies">
<div>
{{movies.name}}
Year: {{movie.year}}
Gross: {{movie.gross}}
</div>
</div>
</div>
`,
changeDetection: ChangeDectionStrategy.OnPush
})
export class MoviesList {
@Input() movies
}

We have split the MoviesComponent, the MoviesList component now handles the UI presentation of the movies array. See the Movies component is no longer concerned about how the movies are rendered, it just passes it to the MoviesList component.

The MoviesList only concern is UI presentation, it receives the movies array from the @Input() movies binding, and uses the *ngFor to loop through the movies array and render them. We optimized the MoviesList component using the OnPush strategy if the movies array changes referentially Angular would re-render the MoviesList component if not it would not re-render the component.

We can further refactor the Movies component to use Effects. Service-based components use Services to get data, these Services may depend on other services, this causes components to have multiple concerns. Using Effects decreases the responsibilities, this is important because it will make our component less stateful. Now, we will put the movie data to Store, then inject Store in our Movies components:

@Component({
selector: 'movies',
template: `
<div>
<movies-list [movies]="movies"></movies-list>
</div>
`
})
export class Movies {
private movies: Observable<Array<any>> = this.store.select(state => state.movies)
constructor(private store: Store) {}
ngOnInit() {
this.store.dispatch({type: 'Load New Movies'})
}
}

Now, the Movies component is no longer concerned about how the movies are gotten, it just tells the Store that it wants to load new movies and selects the movies slice from the state state => state.movies. The responsibility of the loading of movies is up to the Effects.

class MoviesEffects {
loadMovies$ = this.actions.pipe(
ofType('Load New Movies'),
switchMap(action =>
this.moviesService.getMovies()
.map(res => ({ type: 'Load New Movies Success',payload: res }))
.catch(err => Observable.of({ type: 'Load New Movies Failure', payload: err }))
);
)
constructor(private moviesService: MoviesService, private actions: Actions) {}
}

Our app has three routes new movies, highest-grossing movies, and trending movies.

new movies lists the newest movies highest-grossing movies list highest grossing movies trending movies lists trending movies

These have one thing in common they list movies, their components would use the MoviesList component to display the movies.

@Component({
selector: 'movies',
template: `
<div>
<movies-list [movies]="movies"></movies-list>
</div>
`
})
export class Movies {
private movies: Array<any>
constructor(private movieService: MovieService) {}
ngOnInit() {
this.movieService.getMovies().subscribe(data => this.movies = data)
}
}
@Component({
selector: 'movies-grossing',
template: `
<div>
<movies-list [movies]="movies"></movies-list>
</div>
`
})
export class MoviesHighestGrossing {
private movies: Array<any>
constructor(private movieService: MovieService) {}
ngOnInit() {
this.movieService.getHighestGrossingMovies().subscribe(data => this.movies = data)
}
}
@Component({
selector: 'movies-trending',
template: `
<div>
<movies-list [movies]="movies"></movies-list>
</div>
`
})
export class MoviesTrending {
private movies: Array<any>
constructor(private movieService: MovieService) {}
ngOnInit() {
this.movieService.getTrendingMovies().subscribe(data => this.movies = data)
}
}

See we re-used the Presentational component MoviesList components in all the components. There is no need to re-write a display list for each component.

Conclusion

Anything worth doing is worth doing well, in fact very well. Writing better components help us a lot. Whenever we want to test, scale, refactor or optimize, it will be so easy because the components have been prepared for such actions because of how they were structured. Writing better components is like preparing for the worst in the future, we won’t see ourself editing large components.

If you have any question regarding this or anything I should add, correct or remove, feel free to comment, email or DM me.

Thanks !!!

Learn More

--

--

JS | Blockchain dev | Author of “Understanding JavaScript” and “Array Methods in JavaScript” - https://app.gumroad.com/chidumennamdi 📕