DEV Community

Stephen Chiang
Stephen Chiang

Posted on

Generating Angular Container Components, the Fast and Easy Way

Some of you might be familiar with the concept of container components. If not, they are components meant to 'wrap' around your presentation components as a data layer to separate concerns and improve the speed of testing.

Containers

There's also a lot of other benefits to this structure such as easier debugging and reasoning about. What I also like about this structure is you can leverage it with NgRx and really squeeze out some efficiencies by switching to OnPush detection strategy for your presentation components as all their data at this point should be coming in through @Input or as immutable new objects from the Store.

Here's a great article by Lars Gyrup Brink Nielsen (@layzeedk) that I helped review on this very subject: Container Components with Angular

In the article, Lars takes you on a well-written journey of refactoring the Tour of Heroes example project.

If you're looking for really great info on advanced Angular topics, I suggest giving this fellow a follow:

[deleted user] image

[Deleted User]

I can't explain the subject better than Lars, but I thought I would it would be fun to add another practical example from another perspective since his involves refactoring an existing project.

So what if you're starting from new project and no refactoring is needed? This is the way I would quickly generate container components in a brand new project to set the structure off on a good foot.

So let's say I have a feature module called auth.module.ts and I want to generate a component called login.

Generating the Container Component

First I want to generate the container component, and register it to the auth module. Because it is a container component, it's most likely I won't need a separate HTML file, nor will I need styles. Just the *.ts and *.spec.test files.

So to do that in one fell swoop, we leverage the Angular CLI like so:



> ng g c auth/login -t -s -m=auth.module


Enter fullscreen mode Exit fullscreen mode

Let's dissect that a little. Most of you are familiar with the ng part.

  • g is the short alias for generate.
  • c is the short alias for component.
  • next we specify the path as to where we want the component files generated.
  • -t is the short alias for --inline-template; the optional flag that says to skip the HTML file so we can go with inline template (more on that in a sec).
  • -s is the short alias for --inline-style; the optional flag that says to skip the style file so we can go with inline styles (also more on that in a sec).
  • -m is the short alias for --module and we assign this component to the auth module

If we wrote that command out without the short aliases it would look like:



> ng generate component auth/login --inline-template --inline-style --module=auth.module


Enter fullscreen mode Exit fullscreen mode

That will produce a file structure like so:



auth\
   login\
    -login.component.spec.ts
    -login.component.ts


Enter fullscreen mode Exit fullscreen mode

A difference between what I have here and the Lars' article, is that the file is still *.component.ts instead of *.container.ts. It really doesn't matter as long as you pick a convention and stick with it. Since the presentation component I am about to generate will have UI in the file name and selector, I think keeping this a *.component.ts is ok.

Generating the Presentation Component

So when generating the login presentation component, we have two options, generating the component in it's own sub-directory or in the same directory level as the container component. Personally, I like generating them in sub-directories because it will be easier to mentally reason about when looking at the file structure if you have a container component with multiple presentation components. For example, I could refactor the login presentation component to a login-form sub-component, or a forgot-password component, etc.



> ng g c auth/login/login-ui


Enter fullscreen mode Exit fullscreen mode

This results in the following file structure:



auth\
   login\
    -login.component.spec.ts
    -login.component.ts
    login-ui\
      -login-ui.component.html
      -login-ui.component.scss
      -login-ui.component.spec.ts
      -login-ui.component.ts


Enter fullscreen mode Exit fullscreen mode

In the container component, we write the login-ui component selector in the inline template, then it's wired up, just specify inputs and outputs where needed.



import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-login',
  template: `<app-login-ui [inputs]="$inputs | async"></app-login-ui>`,
  styleUrls: []
})
export class LoginComponent implements OnInit {
  inputs$: Observable<boolean>;

  constructor(private store: Store) { }

  ngOnInit() {
        this.inputs$ = this.store.pipe(select(inputs));
  }
}


Enter fullscreen mode Exit fullscreen mode

It might seem like extra work, but this pattern really makes things easier in the long run on complex apps.

Let me know what you guys think of the pattern in the comments!

Cheers!

Top comments (4)

Collapse
 
rierjarv profile image
Riku Järvinen

Great article thanks, I'll Be looking into using container components from now on. By the way, you can probably use store.select directly, thus making pipe unnecessary?

Collapse
 
chiangs profile image
Stephen Chiang • Edited

Hi thanks! You can indeed go directly with store.select, but the benefit of piping in custom selectors and using the createSelector from NgRx Store is that you get memoized selectors.

Memoized selectors cache the data and provide efficient re-rendering if needed, because a call doesn't have to be made all the way back to the resource if the data hasn't changed and another component needs it.

Collapse
 
rierjarv profile image
Riku Järvinen

Ok, good to know :) thanks!

Collapse
 
layzee profile image
Lars Gyrup Brink Nielsen

I only now discovered your article, Stephen. Thanks for mentioning my work and for helping me write it 😊 Great article. To the point.