Search icon CANCEL
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Conferences
Free Learning
Arrow right icon
Arrow up icon
GO TO TOP
Architecting Angular Applications with Redux, RxJS, and NgRx

You're reading from   Architecting Angular Applications with Redux, RxJS, and NgRx Learn to build Redux style high-performing applications with Angular 6

Arrow left icon
Product type Paperback
Published in Mar 2018
Publisher Packt
ISBN-13 9781787122406
Length 364 pages
Edition 1st Edition
Languages
Tools
Arrow right icon
Author (1):
Arrow left icon
Christoffer Noring Christoffer Noring
Author Profile Icon Christoffer Noring
Christoffer Noring
Arrow right icon
View More author details
Toc

Table of Contents (12) Chapters Close

Preface Quick Look Back at Data Services for Simple Apps 1.21 Gigawatt – Flux Pattern Explained FREE CHAPTER Asynchronous Programming Functional Reactive Programming RxJS Basics Manipulating Streams and Their Values RxJS Advanced Redux NgRx – Reduxing that Angular App NgRx – In Depth Other Books You May Enjoy

An MVC flow in Angular

Let's look at the following problems and how we solve them in Angular:

  • Creating and rendering model data to the screen
  • Learning how the MVC pattern maps to the Angular framework
  • Learning how we can structure an Angular application in different building blocks
  • Fetching data/persisting data

The model

The model in Angular is a plain class, as we are using TypeScript. It can look like the following code:

// mvc/MvcExample/src/app/product.model.ts

export class Product {
constructor(
private id: number,
private title: string,
private description: string,
private created: Date
) {}

method() {}

anotherMethod() {}
}

It is a plain TypeScript file, or rather an ES2015 module, not to be confused with an Angular module. We will discuss in the next main section what an Angular module is, in terms of setup and how it is consumed. For now, remember the model is a simple thing.

The component – a controller and a building block

In the context of MVC, the component is the V and C, the view and the controller. The component allows you to define either a separate template file or an inline template. The template is the view part.

The controller in this context is a component class file that handles user interactions and also fetches the necessary data for the template to display.

Components have come to be a central concept for a lot of frameworks that are popular today, such as React, Vue.js, and Polymer. A component can take inputs, which are either data or methods. It consists of a piece of code and an HTML template, which render interesting data, living on the component. A component in Angular consists of three major parts:

  • A decorator function
  • A class
  • A template

A component consists of a controller class and a template. It can play two different roles in an Angular application: either it can be the responder to the route or it can serve as a building block. In the first case, Angular will instantiate it when a new route happens and respond with that component. In the latter case, the component is created directly by existing as a child component within another component.

We will explain next what we meant by the previous paragraph.

First responder to a route

As mentioned, a component can be used as a responder to a route. So let's say the application routes to the /products route as a result of a user interaction, or programmatically. Angular's way of dealing with this is to associate the /products route with a component. With the help of a component's class and HTML markup, we are able to produce a piece of HTML containing our markup and data rendered together. Pointing out a component as a responder to a route, is done when defining the so-called route map, like so:

// example of what routing might look like

export const appRoutes: Routes = [
{
path: '',
component: HomeComponent
},
{
path: 'payments',
component: ProductsComponent,
data: { title: 'Products' }
}
]

Essentially, a route is defined as an object with path properties, pointing out our route, and a component property pointing to the responding component. We can attach other properties to the route, such as data, to give the responding components some initial data to render.

Used as a building block

Using a component as a building block means it will be part of another component's template. Essentially, it will be seen as that component's child. This line of thinking is quite natural and means that we can think of our application as a hierarchical tree of components. A component in Angular consists of a controller class and a template as we have mentioned previously. A typical component looks like so:

// an example component

@Component({
selector: 'example-component'
})
export class ExampleComponent {}

The @Component decorator function adds metadata to the class. This instructs Angular on how to create the component so that Angular can place the component in the DOM. This enables you to use it as a responder to a route or as your own custom element. The property selector is what decides what your component should be called, if used as a custom element. Example usage looks like the following:

// an example container component
@Component({
selector: `
{{ title }}
<example-component>
`
})
export class ContainerComponent {
title ="container component";
}

The fact that components can be used this way makes it easy to think about an app as consisting of a hierarchical tree of components. A Todo application could therefore look like the following:

AppComponent
TodoList
TodoItem
TodoItem
TodoItem
...

Let's start to create this app, starting with the AppComponent. As this is the topmost component, it is also referred to as the root component. The AppComponent should render the TodoListComponent in its own template, like so:

// mvc/MvcExample/src/app/app.component.ts

import { Component } from "@angular/core";

@Component({
selector: "app-root",
template: `
<todo-list></todo-list>
`,
styleUrls: ["./app.component.css"]
})
export class AppComponent {
title = "app";
}

The next step is defining the TodoListComponent and knowing that it should be able to render a number of TodoItemComponent instances within its template. The size of a list is usually unknown. This is exactly what the structural directive *ngFor is for. So that is what we will utilize in the following code as we define the TodoListComponent:

// mvc/MvcExample/src/app/todo-list.component.ts

import { Component } from "@angular/core";

@Component({
selector: "todo-list",
template: `
<h1>{{title}}</h1> <custom></custom>
<div *ngFor="let todo of todos">
<todo-item [todo]="todo" ></todo-item>
</div
>
` . // the view
})
export class TodoListComponent { // the controller class
title: string;
todos = [{
title: "todo1"
},{
title: "todo1"
}]
}

Here, we can see that we render out a list of todo items by looping out the todos array in the template, like so:

<div *ngFor="let todo of todos">
<todo-item [todo]="todo" ></todo-item>
</div>

We can see in the preceding code that we are rendering out the todo-item selector, which points to a TodoItemComponent that we are yet to define. Worth noting is how we pass it a todo object and assign it to an input property on the TodoItemComponent. The definition for said component is as follows:

// mvc/MvcExample/src/app/todo-item.component.ts

import { Component, Input } from "@angular/core";
@Component({
selector: "todo-item",
template: `<h1>{{todo.title}}</h1>`
})
export class TodoItemComponent {
@Input() todo;
}

Reasoning about which components should exist as part of which other components is something you are going to dedicate a lot of time to.

Components from an architectural standpoint

You are encouraged to create a lot of components in your Angular application. With the former section's example of creating a todo list application it was tempting to create an application that just consisted of one component, the AppComponent. This would have meant that one component would have been responsible for a ton of things, such as displaying todo items, saving said items, removing them and so on. Components are meant to be used to solve one thing well. That's why we created a TodoItemComponent which only job in life was to display a todo item. Same thing goes for the TodoListComponent. It should only care about displaying a list, nothing else. The more you split down your applications into small and focused areas the better.

NgModule – our new facade (and some other bits)

So far, we have talked about components in terms of them being dedicated to solving one task well. However, there are other constructs that can be used in Angular, such as pipes, directives, and services. A lot of our components will find themselves belonging to a common theme, such as products or user management and so on. When we realize what constructs belong to the same theme, we also realize that some of these constructs are constructs we want to use elsewhere in the application. Conversely, some constructs are only meant to be used in the context of the mentioned theme. To protect the latter constructs from unintended use, we would like to group them in a facade-like way and put a protective layer between the constructs and the rest of the application. The way to do that in pure ES2015 modules is to create a facade file, in which public constructs are exported and others are not, like so:

// an old facade file, index.ts

import { MyComponent } from 'my.component';
import { MyService } from 'my.service';

export MyComponent;
export MyService;

Imagine we have a directory consisting of the following files:

/my
MyComponent.ts
MyService.ts
MyOtherService.ts
index.ts

The intent of creating a facade file here is to ensure there is only one place from where you import all the constructs you need. In this case that would be the index.ts file. A consumer of the preceding directory would do the following:

// consumer.ts

import * as my from './my';
let component = new my.MyComponent();
let service = new MyService();

MyOtherService is not being exposed by the index.ts file though, so attempting to access it like we do in consumer.ts would lead to an error. You could theoretically specify the full path to the construct but you are supposed to be using the barrel. Barrels are usually meant to be used to easily access your constructs without having to write import statements that are five miles long, like so:

// index.ts
import { Service } from '../../../path-to-service';
import { AnotherService } from '../../path-to-other-service';
export Service;
export AnotherService;

// consumer.ts

// the long and tedious way
import { Service } from '../../../path-to-service';
import { AnotherService } from '../../path-to-other-service';

// the easier way using a barrel
import * as barrel from './index';
let service = new barrel.Service();
let anotherService = new barrel.AnotherService();

As you can see that barrel, index.ts is the one that is responsible for knowing where all your constructs are located. This also means that were you to move files around, changing directories for certain constructs, the barrel file is the only one where updating the paths to these constructs is needed.

The Angular way of dealing with this is to use Angular modules. An Angular module looks like the following:

// mvc/MvcExample/src/app/my/my.module.ts

import { NgModule } from "@angular/core";
import { MyComponent } from "./my.component";
import { MyPipe } from "./my.pipe";

@NgModule({
imports: [],
exports: [MyComponent],
declarations: [MyComponent, MyPipe],
providers: []
})
export class MyModule {}

The effect of putting MyComponent and MyPipe into the declarations property of the module is so that these components can be freely used within MyModule. For example, you can use MyPipe within the MyComponent template. However, if you want to use MyComponent outside of this module, in a component belonging to another module, you will need to export it. We do that by placing it in the array belonging to the exports property:

exports: [MyComponent]

Angular takes the concept of a module way beyond grouping. Some instructions in our NgModule are meant for the compiler so that it knows how to assemble the components. Some other instructions we give it are meant for the Dependency Injection tree. Think of the Angular module as a configuration point, but also as the place where you logically divide up your application in to cohesive blocks of code.

On the object sent to the @NgModule decorator, there are properties you can set that have different meanings. The most important properties are:

  • The declarations property is an array that states what belongs to our module
  • The imports property is an array that states what other Angular modules we are dependent on; it could be basic Angular directives or common functionality that we want to use inside of our module
  • The exports property is an array stating what should be made available for any module importing this module; MyComponent is made public whereas MyPipe would become private for this module only
  • The providers property is an array stating what services should be injectable into constructs belonging to this module, that is, to constructs that are listed in the declarations array
lock icon The rest of the chapter is locked
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at $19.99/month. Cancel anytime