Introduction
Today, we’re discussing the building blocks of Angular. Basically, Angular is written in Typescript itself. So it is a prerequisite to know Typescript before starting Angular. And obviously, as Angular is written in Typescript, if we want to write our own custom code, then surely we’ll write in Typescript. Here is the Roadmap of Angular Series:
Table of Contents
In this article, we'll cover a lot of things:
Building Blocks of Angular
Components
At the heart of every Angular App, we’ve one or more components. And in fact, in the real world, we develop complex applications with tens of components inside them. A Component encapsulates the data, HTML markup and the logic for a view behind the view. Angular embraces component based architecture which allows us to work on smaller and more maintainable pieces that can also be reused in different places.
Every application must have one component which we called appcomponent or root component. A real world angular app is essentially a tree of component starting from the appcomponent.
Modules
A module is a container for a group of related components. Every angular app has at least one module which we call app module. As our application grows, we may want to break our modules into smaller, more maintainable modules. And as the application grows, we need to divide our app module into sub smaller modules and each module is responsible for a specific section. It has related components inside them.
Components
Let’s get started with some practical.
Actually, we need to follow 3 steps:
- Create the Component.
- Register the component in module.
- Add the element in HTML Markup.
Open Visual Studio Code and build the project.
PS > ng serve
Open the URL in browser (http://localhost:4200/).
Now let’s create the component.
Create Component
Open the File Panel in Visual Studio code and in the project directory, open the ‘src’ folder > ‘app’ folder
Here, we want to add the component which displays the courses
. So, we create the file and name it as ‘courses.component.ts’. This is the convention we use when we’re working with Angular applications. And if the component has multiple names like ‘course form
’, then we’ll separate it using hyphens ‘course-form.component.ts’.
Here, we start with creating plain typescript class in ‘courses.component.ts’.
class CoursesComponent {
}
So in order for angular to see this class, we need to export it.
export class CoursesComponent {
}
So far, we’ve this plain typescript class. It is not a component. In order to convert this into component, we need to add some metadata to it that Angular understands. We use the decorator by achieving this. In Angular, we’ve got a decorator called component that we can attach to a class to make that class a component. So we need to import this decorator on the top.
As we can see, this @Component()
decorator function needs 1 argument. Here, we’ll create the object. And in this object, we’ll create 1 or more properties to tell how this component works. For example, one property we use quite often is selector
and we select this as CSS selector. If we want to reference an element like:
Element Tag | Selector |
<courses> | “courses” |
<div class=”courses”> | “.courses” |
<div id=”courses”> | “#courses” |
So here, we want to reference an element called <courses>
because with components, we can extend HTML vocabulary. So we can define new elements like courses
and inside that, we’ll have the list of courses or in the future, we can define a custom element, custom HTML element called <rating>
. So finally, our selector of this component is courses
. And the template is the markup which we render on the web page on calling this template.
import { Component } from '@angular/core';
@Component({
selector: 'courses',
template: '<h2>Angular</h2>'
})
export class CoursesComponent {
}
So this is the basic component in Angular.
Register Component in Module
Now the 2nd step is to register the component in the module. Currently, we’ve just one module called ‘appmodule
’.
Here, we’ve 3 import
statements and 1 export
statement at the bottom. And note that this class is decorated with another decorator function called @NgModule
. Now don’t worry about the properties used in decorator. We’ll discuss them later on. Here, we just focus on declarations, this is where we add all the components that are part of this module. So by default, when we generate an application, we’ve 1 component called appcomponent
and we can see this in app module. And here, we need to add our custom component in declarations and if you’re using VS Code, then we have an Extension (Auto import). It imports the header reference statement automatically where you create any class object and when you provide the reference name like we do here in declarations.
import { CoursesComponent } from './courses.component';
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
@NgModule({
declarations: [
AppComponent,
CoursesComponent
],
imports: [
BrowserModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
Add the Element in HTML Markup
Now, it is time to use the component in HTML file. Open app.component.html file from src > app > app.component.html.
This HTML file is actually for rendering the homepage when we open the localhost:4200 in the browser. Now just comment this HTML code and let’s try our component in HTML.
<h1>My First App With</h1>
<courses></courses>
When Angular sees this custom element, it renders the template of our courses
component. Now just save the file and webpack automatically executes. And you can open the web page and see the difference.
Look, this is how it works. And if we inspect our elements in the browser, then you’ll know the structure of the Angular application and how it works under the hood.
Now you might be thinking if you’re watching this above pic with focus, then you’ll see:
<h1 _ngcontent-c0>My First App With</h1>
Where ngcontent
is coming here. So, now open the index.html page from your src here, you’ll see the <app-root> </app-root>
custom HTML element and it is using our appcomponent
, you can verify it. Just open app.component.ts and here, you’ll see:
selector: ‘app-root’
So whenever Angular sees the element like that, it's going to render the template for this component inside that element.
Point
You might be thinking about the views, here we have different HTML pages in the application. We have 1 index.html page in src folder and we’ve custom component HTML files as well. And here, we’ve @Component({ template: ‘’ })
decorator as well to write HTML in it. Actually, when we run the application so our main view is actually index.html where we’re consuming <app-root></app-root>
app component. And if you app.component.ts here, you’re defining templateUrl
of app.component.html and in app.component.html, we’re consuming CoursesComponent
selector <courses></courses>
.
And we’ve already coded our CoursesComponent
and define its selector above which is courses
.
Generating Components Using Angular CLI
Now there are 2 problems with the approach we’ve already discussed to make the custom component.
- This approach is a little bit tedious. There are so many steps we need to keep in mind.
- And if we forget the 2nd step (register the component into
appmodule
), then our application will break.
We can take an experiment with appmodule
- just remove the CoursesComponent
from declaration in app.module.ts.
Now save the file and open the url (localhost:4200/) and you’ll see the blank white browser screen. And now, open the browser console.
Here, you’ll see the error.
Now let’s move on more quicker and in a reliable way to create the Angular components. Here, we use Angular CLI to generate the component. Open the VS Code Terminal.
Just like we create the new application with ng new
command, we can also generate the component with ng
:
Statement Syntax: ng g c nameofcomponent
g
for generate, c
for component and then the name of component. Let’s create the component called course
.
PS > ng g c course
Look how Angular CLI has created the directory called course and then it created the 4 files inside the directory. (.css) file for style of component, (.html) for html, (.spec.ts) for unit testing of component, (.ts) which is the actual component file. Another important thing is, when we created the component, it automatically updated our app module as well and registered our new component here. Let’s verify the automatic update in our app module.
Now open the app module.
Look it is automatically updated. And you can see, it has reduced a lot of effort. And if you open the course component:
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-course',
templateUrl: './course.component.html',
styleUrls: ['./course.component.css']
})
export class CourseComponent implements OnInit {
constructor() { }
ngOnInit() {
}
}
Here is all the boilerplate code. This is how Angular CLI saves us a lot of time and effort.
Templates
Component can encapsulate data, logic, HTML markup for a view. And if we open our coursescomponent
here, we’ve just HTML markup but we’ve not any data or any logic. So let’s extend the example.
import { Component } from '@angular/core';
@Component({
selector: 'courses',
template: '<h2>Angular</h2>'
})
export class CoursesComponent {
}
Now what we want is to encapsulate the data in the container and show in the template markup dynamically. In template, we’ve double curly braces syntax to encapsulate the data and when the data changes here at runtime, Angular automatically updates it in the browser view as well.
import { Component } from '@angular/core';
@Component({
selector: 'courses',
template: '<h2>{{ name }}</h2>'
})
export class CoursesComponent {
name = "My Name Is Usama";
}
This is what we call Data Binding.
We can’t just place the variables in HTML template, but we can also write the simple JavaScript expressions and we can also call the method here. Let’s take an example.
In JavaScript, we can add the string
and concatenate the string
s in template.
import { Component } from '@angular/core';
@Component({
selector: 'courses',
template: '<h2>{{ "My Name Is: " + name }}</h2>'
})
export class CoursesComponent {
name = "Usama";
}
We can also call the functions in template as well.
import { Component } from '@angular/core';
@Component({
selector: 'courses',
template: '<h2>{{ "My Name Is: " + myName() }}</h2>'
})
export class CoursesComponent {
name = "Usama";
myName(){
return this.name;
}
}
This is what we’re working in template markup is called string
interpolation.
Directives
Now let’s display the list of courses
.
export class CoursesComponent {
name = "Usama";
courses = ["BIO", "MTH", "CHE", "PHY"];
}
To display this array of courses in template, we need to perform some changes.
- Change the single quote (
‘
) with backtick (`
) in template.
Now the benefit of using backtick is we can break our template into multiple lines and make it more readable.
Now let’s print the name of courses in ul
in template:
@Component({
selector: 'courses',
template: `
<h2>{{ name }}</h2>
<ul>
<li></li>
</ul>
`
})
Here, we need to loop on our courses
array to print them in, in the individual li
. Here, we use directive.
Directives are used to manipulate the DOM. We can use them to add a DOM element or remove an existing DOM element or change the class of the DOM element or its style and so on.
Here, we use directive ngFor
:
import { Component } from '@angular/core';
@Component({
selector: 'courses',
template: `
<h2>{{ "List of Courses" }}</h2>
<ul>
<li *ngFor="let course of courses">
{{ course }}
</li>
</ul>
`
})
export class CoursesComponent {
courses = ["BIO", "MTH", "CHE", "PHY"];
}
This is the special syntax of Angular where we’re iterating over the courses array just like we do in JavaScript or C# foreach
loop. And then, we show the data in string
interpolation.
Here, we’ve displayed the data on the screen. But keep in mind, it is showing on the screen because I’m using courses component selector in my app.component.html.
<courses></courses>
Service
In our real world application, obviously data comes from the server. Here is the service
in Angular to understand how to consume the data in Angular coming from the server.
Here, we have 2 options:
- Write logic for calling an HTTP service in component
But there are couple of problems with this approach. The first problem is that this logic is tightly coupled from this component to that http endpoint. And in future, when we write the unit test in this class, we don’t want to be dependent upon live http end point because it is going to make it harder to execute those unit test.
So we need to make fake http implementation of http service.
The 2nd issue is maybe somewhere else in the application, we need to display the list of courses like in dashboard or admin or home page. With this implementation, we need to consume our http services at multiple places.
And the 3rd issue with this implementation is, the component should not include any logic other than the presentation logic. Details should be delegated somewhere else in the application.
- So the solution is, make a separate service class where we write the logic to retrieve the data and then we reuse this class in multiple places in the application.
Add the new file in app folder courses.service.ts:
And here, once again, we export the typescript class. But we don't have any decorator for service classes. These are just plain Angular classes.
export class CoursesService {
getCourses() {
return ["BIO", "MTH", "CHE", "PHY"];
}
}
Now back in the component, here we are not going to consume our http service and this allows us to unit test without dependency upon that http endpoint. So while unit testing in this class, we can provide the fake implementation of that service. But it is quite complicated stuff, we’ll see it later on.
Dependency Injection
Now, we have the service to get the list of courses from the server. We need to use this service in CoursesComponent
. So first of all, we need to add the constructor here in component class. With the help of constructor, we initialize an object. So, here, we need to create an instance of service in constructor. And if you’re not using auto import plugin of VS Code, you need to manually import the service file in CoursesComponent
.
And then, we need to initialize our courses
in CoursesComponent
with service
method in the constructor.
import { Component } from '@angular/core';
import { CoursesService } from './courses.service';
@Component({
selector: 'courses',
template: `
<h2>{{ "List of Courses" }}</h2>
<ul>
<li *ngFor="let course of courses">
{{ course }}
</li>
</ul>
`
})
export class CoursesComponent {
courses;
constructor(){
let service = new CoursesService();
this.courses = service.getCourses();
}
}
Now let’s test the application and see what’s happening there. And yes, it is working fine.
However, there is a problem with this implementation, the first problem is CoursesComponent
is tightly coupled on CoursesService
and if they’re tightly coupled on each other. We can’t unit test this class. The major problem is we’re creating the CoursesService()
object in constructor. And the 2nd issue is if in the future, we decide to add the parameter to the constructor of CoursesService
, we’ve to come back here and anywhere in the application where we’ve use this CoursesService
, we need to add a new argument there. So everytime we add a new parameter in CoursesServices
, we automatically need to add the other changes as well in the complete application.
What Should We Do?
Instead of recreating an instance of CoursesService
, we can ask Angular to do that for us. So delete the object creation line and add the changes in this way.
export class CoursesComponent {
courses;
constructor(service: CoursesService){
this.courses = service.getCourses();
}
}
With this, Angular is going to create an instance of CoursesComponent
, it looks at this constructor and sees that this constructor has a dependency of type CoursesService
. So first, it automatically creates an instance of CoursesService
and passes to this constructor. Now if you make any kind of changes to CoursesService
constructor, we don’t need to modify all the changes in the complete application. The 2nd benefit of this implementation is that when we’re going to unit test this CoursesComponent
instead of supplying an actual course to this constructor, we can create the fake implementation of Service
that doesn’t use http service at the backend. In other words, we’ve decoupled our courses
component from courses
service.
So the lesson is, if we’re creating an instance of the class in any function or in another class, we’re tightly coupled this class to that class. We can’t change this at runtime but when you add that dependency as a parameter of constructor, we’ve decoupled that class from that dependency.
Now it is not even done yet, we need to explicitly instruct the Angular to create an instance of CoursesService
and passes to our CoursesComponent
. This concept is called Dependency Injection. So we should instruct Angular to inject the dependency of this component into its constructor.
A lot of people think dependency injection is so complicated but it is really a 25$ dollar term for a 5 cent concept. So Dependency Injection means injecting or providing the dependencies of the class into its constructor.
Angular as a DI (Dependency Injection) framework built in to it. So when we create an instance of a component, it can inject the dependency but in order for that to work, we need to register the dependency.
(service: CoursesService)
Somewhere in our module. Now open the app.module.ts look at this NgModule
declarator, here we’ve got a property called providers which is set to an empty array. In this array, we need to register all the dependencies that components in this module are dependent upon, i.e., CoursesComponent
is dependent upon CoursesService
, so we need to register CoursesService
as a provider in this module.
And if you’re using auto import, then it automatically adds the reference of CoursesService
class in this file.
And if you forget this step to add the reference in providers array, then it isn't going to work. You can do the experiment yourself by commenting out or removing this item from providers array and run the URL in browser and when you open the console, you’ll see the errors.
Actually, what happens under the hood?
When you register the dependency provider in the module, Angular creates a single instance of that class for that entire module. So imagine in this module, we’ve 100 components and half of them need CoursesService
. In the memory, we’ve only a single instance of CoursesService
and Angular will pass the same instance to all these components. This is what we call Singleton Pattern.
So a single instance of a given object exists in the memory.
Generating Services using Angular CLI
Now let me tell you the quick way to generate the service using Angular CLI. Open the Terminal window in Visual Studio Code.
Statement: ng g s NameOfService
It generates 2 files for us, 1 is the actual service file and other one (.spec.ts) has the boilerplate code for writing unit test for that service.
Here, we’ve something new which we’ve not seen before @Injectable
declarator function. We would only need this decorator function if the service had the dependencies in this constructor, i.e.:
We’ve the dependency of logService
in the constructor.
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root'
})
export class EmailService {
constructor(log: LogService) { }
}
Now in this case, we need to apply this Injectable()
function on this class and this tells Angular at this class, an injectable class which means Angular should be able to inject dependencies of this class into its constructor. Now we didn’t use this decorator when defining the components because when we use the component decorator, that decorator internally includes the Injectable decorator. And the property...
providedIn: ‘root’
...means that this service should be created by the root application injector.
What We Have Learned
Let’s be a little more practical and make something new. Here, we’ll explore this result by using CLI generated component and service:
So first of all, let’s create the component and service through Angular CLI:
PM > ng g c author
PM > ng g s author
Now first of, let’s create the code for author service.
@Injectable({
providedIn: 'root'
})
export class AuthorService {
constructor() { }
getAuthors(){
return ["Bob", "Adam", "Joff", "Scott"];
}
}
Components are automatically registered in the app.module.ts but if we want to register the authorservice
, then we need to manually register the service here.
@NgModule({
declarations: [
AppComponent,
CoursesComponent,
CourseComponent,
AuthorComponent
],
imports: [
BrowserModule
],
providers: [CoursesService, AuthorService],
bootstrap: [AppComponent]
})
export class AppModule { }
Now it is time to consume author
service in our component.
@Component({
selector: 'app-author',
templateUrl: './author.component.html',
styleUrls: ['./author.component.css']
})
export class AuthorComponent implements OnInit {
authors;
constructor(author: AuthorService) {
this.authors = author.getAuthors();
}
ngOnInit() {
}
}
And here the Component
decorator has templateUrl
author.component.html, now come on in the author.component.html.
<h2> {{ authors.length }} Authors</h2>
<ul>
<li *ngFor="let author of authors">
{{ author }}
</li>
</ul>
.length
property is a JavaScript property through which we can get the total number of arrays. And in <li>
, we’re using ngFor
decorator, as it is changing our DOM so it is prefixed with Asterik (*).
Now as we know, our base component is app
. We’re using our subcomponents in base components. So come on app.component.html and place your author
component selector to use it.
<app-author></app-author>
Conclusion
Here, we’ve discussed the building blocks of Angular. Components are where we write the logic, define the selector and HTML markup, services are nothing but where we get the data and consume it into the component. Here, we discuss how we can remove the tight coupling between classes and inject the dependency among them. We’ve learned how we can create the component and services through Angular CLI to make our code less buggy and make things ready in seconds. This is how we work in Angular.