About Smart and Presentational Components, Architecture Overview & Concepts: Condensed Angular Experiences – Part 3

Modern web technologies enable us to write huge business applications that are performant and easy to use. But with time comes complexity to our projects naturally. That added complexity sums up and makes further app development slow and cumbersome. This article discusses how a simple architectural concept can make every app more maintainable, faster to develop, and extendable in the long run.

In diesem Artikel:

MS-rund
Max Schulte ist Consultant bei Thinktecture mit dem Schwerpunkt auf Angular und 2D/3D Visualisierung.

This is the third part of the series „Condensed Angular experiences“. We will explore concepts in Angular that seem simple but are very powerful and critical for the application architecture.

  1. Understanding Angular’s Async pipe
  2. What is the hype with Angular’s OnPush change detection?
  3. Interlude: About smart and presentational components
  4. Smart and presentational components with Angular
  5. Different approaches to complex and advanced forms in Angular (coming soon)

You may have heard or read about this topic at some point. It is known under multiple names, but they describe the same or a similar architecture concept in the end.

Those different names include:

  • Smart & Presentational Components (the naming we use in this article)
  • Smart & Dumb Components
  • Stateful & Stateless Components
  • Stateful & Pure Components
  • Container & Presentational Components

Since everything revolves around components in this article, let’s take a deeper look at what a component is and what we should keep in mind when writing such.

Understanding Angular’s Async pipe What is the hype with Angular’s OnPush change detection? Interlude: About smart and presentational components Presentational and Smart Components with Angular Different approaches to complex and advanced forms in Angular

This is the first article explaining this architectural concept in a set of two. This part discusses the conceptual principles and tries to give an overview of the topic. The other part goes into detail on implementing this architecture with Angular.

What is a component?

Let’s start by really taking a look at what defines a component. Taking code-of-some-kind and creating reusable components of it is done for as long as there are frameworks. Starting with jQuery and such, continuing with Single Page Application Frameworks such as Angular to the latest pinnacle framework less web-native Web-Components. As seen by the recently emerging Web Components, a Component can be very atomic in its definition, scope, and usage. Therefore, let’s define components as encapsulated and reusable elements, with a clear use-case and nice API. Of course, a component should also be testable.

Now that we know what a component should be, we also must look at principles that make it a good component compared to others.

Important principles for good components

Why do developers care to write components? Why not just put everything in one place and be done with it? Because we must make sure that it outlives the development cost, it must be maintainable, testable, and extensible.

One core principle that enables this is DRY „Don’t repeat yourself“, reuse code if you can, or even better scope classes, services, and components to their needs. Those scopes should create a component with straightforward usage and responsibility.

That is also the second principle: Extract and specify responsibilities and scopes. Try to write components that solve one problem, not many.

The third principle is „Open/Close“ – „Open for extension and closed for modification“. Let’s say you got a dropdown component, extensible means that you can provide another component as a toggle button or even use that dropdown as display content for a combo box. A modification would mean that you can use the simple dropdown as the combo-box itself, containing all components itself (the activator Button, the content items, and the dropdown pane), switching between use cases by toggling variables. That would also violate the second rule: solving problems out of scope.

The fourth, final, and maybe most important concept is „Inversion of control“. It is quite a large topic and requires an article by itself, but we try to find a brief explanation for this article. Components should be „servants“ to the components that come before or to the outer context. For example, the outer component or context can create that relationship if it provides a service and overrides the standard provided service. With that, the outer component can control what the inner does without explicitly interacting with it. Other examples are Angular’s content projection with ng-content component or HTML template slots. The outer component defines what the inner component displays – I could easily find hundreds of more examples in serval languages and Frameworks. It is a vast topic.

However, Inversion of Control plays nicely with the other principles. It enables us to keep the scopes clean.

Enough of those boring concepts and definitions; let’s get to the core. There are two different component types in this architecture, the presentational components displaying stuff and the smart components handling data, application logic, and more. What makes a component an excellent presentational component?

What is a presentational component?

Presentational components are standard UI components. Their purpose is to display information and interact with the user. They should have few to no dependencies on services or other contexts. Of course, one component can be split-down further to increase maintainability and the ease of testing. Presentational components should only use a local state and logic where necessary, like controlling a checkbox or managing the opening and closing of a dropdown. They are somewhat trimmed down to the core of their functionality, displaying data and interacting with a user. However, they can receive data from other components and output data to other components, e.g., surrounding smart components or other UI components.

Let’s head over to smart components, providing global data, logic, and connecting to other contexts.

What is a Smart Component?

While presentational components handle the UI and user interaction, smart components care about the app state and general logic. They connect different parts and combine multiple presentational components into a bigger picture. In general smart components should only contain presentational components, as long as they represent a single „view“ in your application. They include services and data sources of all kinds, connecting different app parts and app states.

That’s all; it isn’t that much, right? It is a pretty broad and loose topic. So let’s recap the most important aspects.

TL;DR

  • Try to be use-case Agnostic
  • Write small components
  • Use clear naming and scopes
  • Define clean responsibility and scoped components
  • Move shared logic between components into services
  • Separate UI and Logic

Possible drawbacks

As with all architectures, this one is not free of flaws either. As we are optimizing, cutting components, and scoping contexts, we tend to optimize the application eagerly. We tend to build features that are not necessary. This comes in naturally because this architecture is made to build features and to create reusable, extendable components. The biggest drawback may be the upfront development time. It takes time to discuss the feature and how to create components properly. At best, it is done in pair with another person. Using this pattern becomes easier and speeds up over time and experience – discussing it with another person will always help.

Knowing what to be aware of, we can take a sophisticated look at the advantages.

Advantages and profits

Having small components with clear scoped responsibilities brings multiple advantages. For once, the onboarding process can become more accessible. It is now possible to create smaller tasks with a clear agenda. That way, the things a person has to know become fewer. A new colleague could start exploring the application on its own. The same reason enables us to write good tests with ease. There are less side effects to be aware of and fewer dependencies to mock. A clean API also provides the necessary parts to make the next development steps easier. We trade some up-front development time for faster, safer, and less complex development in a later state. We know that with time and effort, an app will become more complex and hard to develop further. With this architectural approach, we can reduce that complexity in the future. That way, we can easily add new features later and react fast to changing requirements.

Kostenloser
Newsletter

Aktuelle Artikel, Screencasts, Webinare und Interviews unserer Experten für Sie

Verpassen Sie keine Inhalte zu Angular, .NET Core, Blazor, Azure und Kubernetes und melden Sie sich zu unserem kostenlosen monatlichen Dev-Newsletter an.

Newsletter Anmeldung
Diese Artikel könnten Sie interessieren
Angular
SL-rund

View Transition API Integration in Angular—a brave new world (Part 1)

If you previously wanted to integrate view transitions into your Angular application, this was only possible in a very cumbersome way that needed a lot of detailed knowledge about Angular internals. Now, Angular 17 introduced a feature to integrate the View Transition API with the router. In this two-part series, we will look at how to leverage the feature for route transitions and how we could use it for single-page animations.
15.04.2024
Low-angle photography of metal structure
AI
cl-neu

AI-Funktionen zu Angular-Apps hinzufügen: lokal und offlinefähig

Künstliche Intelligenz (KI) ist spätestens seit der Veröffentlichung von ChatGPT in aller Munde. Wit WebLLM können Sie einen KI-Chatbot in Ihre eigenen Angular-Anwendungen integrieren. Wie das funktioniert und welche Vor- und Nachteile WebLLM hat, lesen Sie hier.
26.02.2024
Angular
SL-rund

Konfiguration von Lazy Loaded Angular Modulen

Die Konfigurierbarkeit unserer Angular-Module ist für den Aufbau einer wiederverwendbaren Architektur unerlässlich. Aber in der jüngsten Vergangenheit hat uns Angular seine neue modullose Zukunft präsentiert. Wie sieht das Ganze jetzt aus? Wie konfigurieren wir jetzt unsere Lazy-Komponenten? Lasst uns gemeinsam einen Blick darauf werfen.
03.08.2023
Angular
yb

Using EntityAdapter with ComponentStore: @ngrx/entity Series – Part 3

As someone who enjoys the ComponentStore on an average level, I have written simple reactive CRUD logic several times. While storing a vast number of entities in the component state might not be a frequent use case, I will briefly illustrate the usage of the EntityAdapter with the @ngrx/component-store.
14.02.2023
Angular
yb

Multiple Entity Collections in the Same Feature State: @ngrx/entity-Series – Part 2

After introducing the @ngrx/entity package, I am often asked how to manage multiple entity types in the same feature state. While I hope that the previous part of this article series has made this more apparent, I will further focus on this question in the following.
07.02.2023
Angular
yb

Managing Your Collections With the EntityAdapter: @ngrx/entity-Series – Part 1

This three-part series of blogposts is targeted at developers who have already gained experience with NgRx but still manage their collections themselves. In the first part I introduce the Entity Adapter, in the second part I show you how to connect it to NgRx and in the third part how to do it with the Component Store as well.
31.01.2023