Smart and Dumb Components in Angular

Today we’ll see how to organize our components so they are small, reusable, performant and only contain the necessary amount of logic. Let’s go through these bullet points.

Small components

Generally we want to solve a problem with the least amount of code that still can be understood. We don’t want to use hacks and tricks that are hard to figure out, but we want to make it as terse as possible. That’s the whole point of dividing our application to smaller components.

We’ll write our components with the Single Responsibility Principle in mind – a component will only have one responsibility, and one reason to change.

The Single Responsibility Principle (SRP) is a part of the SOLID principles. These principles are a set of best practices writing object-oriented applications. The SRP states that a class should encapsulate one and only one responsibility of the application, and that responsibility should be defined entirely in the class.

Reusable

We touched this subject when working with the SharedModule. A set of our components will eventually be reused in multiple places of our application. We need to make these components as dumb as possible – by removing any business logic from them, and by having a well-defined interface.

Performant

Our application needs to be responsive and fast. We’ll minimize the need for change detection and will have clear cases when it should be run on our components.

Smart components

There are two kinds of components. In React, they are called container and component. Angular has the same concept, but here they are called smart and dumb component, respectively.

A container or smart component is concerned with the how. It inject services, fetches data and passes it to its host of dumb components. Here’s the code and template of a smart component:

Notice the following:

  1. Our component injects a service.
  2. It also defines an Observable. Dumb components generally don’t define Observables. They deal with eagerly-available data.
  3. It has an ngOnInit hook, where we do some data fetching.
  4. Its template instantiates our dumb component (app-repo).

Dumb components

A dumb component’s task is to display data. It does not inject services. It only contains logic needed for displaying items (e.g. toggling an accordion, maintaining the currently selected item). A dumb component only communicates with its parent component, by using @Input and @Output properties.

They are commonly defined in the SharedModule. We do this to maximize reusability. It’s a great practice to start early on. I ran into this issue countless times when wanted to reuse a component in another module, only to find out that I’m causing circular dependencies.

Let’s see the code and template of a dumb component:

Key points:

  1. A dumb component is dumb. It only has an @Input property to accept data from outside.
  2. The input parameter is not an Observable. You’d open up yourself to serious subscription-management nightmares if you pass Observables to dumb components.
  3. I prefer using strongly-typed parameters, the Repo class represents a GitHub repository. It’s a good practice to get into, since you’ll get compile-time warnings and errors.
  4. The main task of the dumb component is to display data.
  5. We marked it with OnPush change detection. We do the same with smart components. We get into change detection a bit later.

Best practices

  • Write your smart components to fetch data and handle user communication (form submission, button clicks, etc.).
  • Place your smart components in the feature module where they are needed.
  • Write dumb components to display data. Do not put business logic into them.
  • Place dumb components in the SharedModule to avoid circular dependencies.
  • Dumb components can be promoted and smart components demoted. When this happens, move the component to its new place.
  • Litmus test: if you have a constructor your component is probably smart.

Source code

I’m maintaining and developing a repo on GitHub. It contains Angular7 best practices. Follow this link to the relevant part of the code.

Angular CoreModule

In this article we’ll continue reviewing Angular module best practices. We’ve already covered how the SharedModule helps us eliminating 3rd party module listings and circular dependencies. We also reviewed the benefits of having a separate RoutingModule. Now we’ll check how to make true singleton services and organize them using the CoreModule.

As usual, there’s no CoreModule class shipping with Angular, we’re talking about best practices and conventions here.

What’s in a CoreModule?

The idea behind a CoreModule is that we put all global services in one place and import them to where they are required. The following services would be global:

  • Your HttpClient wrapper service: it’s a great idea to create a wrapper around it. You’d shield yourself from obsoletions and breaking changes. Consider that HttpClient is the second Angular Http service implementation. No guarantee there wouldn’t be a third.
  • Your Authentication service, if there is one. No point having multiple instances for multiple modules.
  • Your logging service.
  • A wrapper around the ngRx store – we’ll cover this much later.

Note that contrary to SharedModule, CoreModule does contain logic. You don’t need to keep it stupid, actual business rules can and should be written here.

Global singletons in Angular

Angular services are application-wide singletons, right? Well, the correct answer is that it depends. As long as you don’t use lazy-loading then technically they are. As soon as you start to lazy-load your modules it gets trickier.

If you have a theoretical BuzzService and inject it into your eagerly loaded AppComponent then you have one instance. It’ll be a singleton the same instance in all your other eagerly loaded components as well.

Let’s assume you have a lazy-loaded Settings and Dashboard module, both having components injecting BuzzService. When you load your SettingsModule you’ll get a second instance of BuzzService. The same happens when you load the DashboardModule – boom, a third instance. Depending on your use case this might or might not cause problems. Let’s see how to solve this and make our CoreModule services true singletons.

forRoot and providing services

Let’s see the code for a complete CoreModule and dissect it:

Until line 8 there’s nothing really new or noteworthy. On line 9, however we are defining a static method. Its name is forRoot, a convention you should follow too. This forRoot method returns an implementor of the ModuleWithProviders interface. This interface has two members, the type of the module called ngModule, and an array of providers.

As you can see in the example we use the same type as defined in the module. We list the providers here instead of the NgModule decorator. The last thing we need to do is import our CoreModule using the forRoot method in our AppModule. Note that you only use forRoot in AppModule and nowhere else. Here’s an example:

Notice that on line 10 we used CoreModule.forRoot instead of the “normal” way. This ensures that all our services provided by CoreModule (at least those which are listed in the forRoot method) are true global singletons, regardless of how they are loaded.

Source code

I’m maintaining and developing a repo on GitHub. It contains Angular7 best practices. Follow this link to the relevant part of the code.

RoutingModule best practices in Angular7

ezgif.com-webp-to-jpg

In this post we’ll explore further Angular module best practices. Although we’ll talk about routing, the main focus of the article is the concept of a RoutingModule.

A custom RoutingModule

This best practice is “official”, the default Angular project template includes a file called app.routing.module.ts. Let’s see this file and figure out the benefits it offers:

Not much happens here – an empty route array is created. It is passed to the RouterModule.forRoot call, and RouterModule itself is exported.

Let’s focus on the latter first – in the previous article about the SharedModule we saw the benefits of importing and re-exporting common module dependencies. The same goes on here, our own RoutingModule imports the stock Angular RouterModule, works with it, then re-exports it. Our app module will only have to import the custom RoutingModule, no need to import RouterModule as well.

The point of a custom RoutingModule is to handle complicated routing. Sure, we could set up everything in our app module, but it tends to become rather bulky after a certain number of dependencies. So we create separate modules (Shared, Routing, Core and Features) to offload some initializing code, and make the process easier to follow.

Let’s see a real-world RoutingModule:

Notice that it’s not super complicated either. In fact, there’s only two extra lines of code, two Route objects representing two lazily-loaded feature modules. Don’t worry about lazy-loading yet, we’ll cover it in a later post. Point is, RoutingModules only focus on setting up your route hierarchy.

If you have a lazy-loaded app, or a smaller one using eager-loading this file will not get too long. If you’ve more complex needs (multiple layers of parent-child routes, a huge app) then you can separate your routing into smaller files. Either way, routing is stored in one place and will be simple to reason about.

Source code

I’m maintaining and developing a repo on GitHub. It contains Angular7 best practices. Follow this link to the relevant part of the code.

 

Book read: The Productive Programmer

There was a long time since I read anything „classic” in software literature. Nowadays I entertain myself with more practical stuff and less methodology or best practices. So came the thought that it’d be nice to read something that wouldn’t become obsolete in five years.

I checked the recommended readings thread at stackoverflow but I already read a lot of them so I thought Amazon would be able to help me out. I wanted to read something like The Pragmatic Programmer so I checked which books were bought together with it. After some fiddling I found the book in the title, The Productive Programmer. I thought why not I’m in desperate need of productivity. I tend to set up The Ultimate Productivity Blog on every workstation I get to work with. As its name suggests, it’s one of the single best resources of productivity materials. Be sure to check it out.

Let’s talk about the book a little. It’s a short read, about two hundred pages. It is split into two parts, the first hundred pages is full with practical advices (called mechanics in the book), and the second half is more theoretical. I’d recommend quickly skimming trough the whole first part; since it’s full of tools pretty outdated by now (the book was published in 2008). The tools which stood the test of time are mostly for Java, so they weren’t very useful to me also, but if you’re a Java guy then they could come handy. The reason for not to skip it altogether is that there are some useful advices on how to eliminate distractions which are less specific to tools.

Cover art of The Productive ProgrammerThe second part is much more interesting and it contains new and fresh advice after a lot of books in the mentioned stackoverflow reading list. The topics include unit testing and test-driven development, best practices for designing classes and organizing code, and YAGNI has a whole chapter dedicated to it.

I particularly liked the chapter titled “Question Authority” there was a nice parallel on the behavior of monkeys and humans regarding authority. The gist of the experiment with monkeys was that the experimenters punished a certain natural behavior. Then they started replacing the conditioned monkeys with new ones, and watched the old ones punish the new ones to prevent the behavior in question. When all of the originally conditioned monkeys were replaced the monkeys still punished newcomers showing the bad behavior, although they themselves didn’t know the original cause. That’s a nice example of “that’s the way it’s done” attitude. And that’s an attitude which certainly prevents positive changes in any organization.

There’s a chapter about reading classic books on software which contains useful advices, and another which tries to introduce the concepts of the classical philosophers, although the examples weren’t that good there.

As a summary I’d definitely recommend the second half of this book. It’s an easy read and contains lots of useful advices. For the first part, I wouldn’t be so sure but it definitely won’t hurt to read that either.