Component architecture
Angular follows the MV* pattern, a hybrid of the MVC and MVVM patterns. Previously, we went over the MVC pattern. At a high level, the architecture of both patterns is relatively similar, as shown in the diagram that follows:
Figure 1.9: MV* architecture
The new concept here is the ViewModel, which represents the glue code that connects your view to your model or service. In Angular, this glue is known as binding. Whereas MVC frameworks like Backbone or React must call a render
method to process their HTML templates, in Angular, this process is seamless and transparent for the developer. Binding is what differentiates an MVC application from an MVVM one.
The most basic unit of an Angular app is a component. A component combines a JavaScript class, written in TypeScript, and an Angular template, written in HTML, CSS, and TypeScript, as one element. The class and the template fit together like a jigsaw puzzle through bindings so that they can communicate with each other, as shown in the diagram that follows:
Figure 1.10: Anatomy of a component
Classes are an Object-Oriented Programming (OOP) construct. If you invest the time to dig deeper into the OOP paradigm, you will vastly improve your understanding of how Angular works. The OOP paradigm allows for the Dependency Injection (DI) of dependent services in your components, so you can make HTTP calls or trigger a toast message to be displayed to the user without pulling that logic into your component or duplicating your code. DI makes it very easy for developers to use many interdependent services without worrying about the order of the instantiation, initialization, or destruction of such objects from memory.
Angular templates allow similar code reuse via directives, pipes, user controls, and other components. These are pieces of code that encapsulate highly interactive end-user code. This kind of interactivity code is often complicated and convoluted and must be kept isolated from business logic or presentation logic to keep your code maintainable.
Angular 17 introduces a new control flow syntax (in preview), which replaces directives like *ngIf
with @if
, *ngFor
with @for
, and *ngSwitch
with @switch
and introduces @empty
, @defer
, contextual variables, and conditional statements. The new syntax makes templates easier to read and avoids importing legacy directives to every component in a standalone project. This book will exclusively use the control flow syntax.
You can run npx ng generate @angular/core:control-flow
to convert your existing template to the new syntax.
Angular apps can be created in two different ways:
- An NgModule project
- A standalone project
As of Angular 17, the default way is to bootstrap your app as a standalone project. This approach has many benefits, as further explained in The Angular Router section below. There is a lot of new terminology to learn, but modules as a concept aren’t going away. It’s just that they’re no longer required.
Whether your app starts with bootstrapApplication
or bootstrapModule
, at the root level of your application, Angular components, services, directives, pipes, and user controls are provided to the bootstrapApplication
function or organized under modules. The root level configuration renders your first component, injects any services, and prepares any dependencies it may require. In a standalone app, you can lazily load individual components.
You may also introduce feature modules to lazy load groups of services and components. All these features help the initial app load up very quickly, improving First Contentful Paint times because the framework doesn’t have to download and load all web application components in the browser simultaneously. For instance, sending code for the admin dashboard to a user without admin privileges is useless.
Being able to create standalone components allows us to ditch contrived modules. Previously, you were forced to place shared components in a shared module, leading to inefficiencies in reducing app size because developers wouldn’t necessarily want to create a module per shared component. For example, the LocalCast Weather app is a simple app that doesn’t benefit from the concept of a module, but the LemonMart app naturally reflects a modular architecture by implementing separate business functions in different modules. More on this later in the chapter in the Modular architecture section.
Standalone components shouldn’t be confused with Angular elements, an implementation of the web standard, custom elements, also known as Web Components. Implementing components in this manner would require the Angular framework to be reduced to only a few KB in size, as opposed to the current framework of around 150 KB. If this is successful, you will be able to use an Angular component you develop in any web application. Exciting stuff but also a tall order. You can read more about Angular elements at https://angular.dev/guide/elements.
Angular heavily uses the RxJS library, which introduces reactive development patterns to Angular instead of more traditional imperative development patterns.