This repository is notes of my angular 4 journey. I couldn't switched to ng2. Now I decided to give a shot and getting course about ng4. I will write everthing that I learn, Maybe this repository guide you too.
Thanks Maximilian Schwarzmuller for great guide.
Farewell my friend.
- Installation
- Creating project
- Serving project
- Investigating created project
- Creating a new component
- Create component with cli
- Including bootstrap css to project
- Databinding
- Directives
- Input
- Output
- View Encapsulation
- Local Reference
- ng-content
- Life cycle of components
- Creating a new directive
- Services and dependency injection
- Router
To develop some applications we need to install node.js first. If you didn't know node.js, please check it before ng4. We will use angular cli. Angular cli will help us creating stuff. Its very useful tool.
npm i @angular/cli -g
We should install @angular/cli
with global parameter. This parameter will use global node_modules folder rather than local one.
Now we can use ng
command in terminal.
To create project we will use ng
command. Try to type ng
in terminal if you get error then you failed Installation
step. Please go there and make sure you did this steps right.
ng command has some more arguments too. We will come to that but rightnow we just have to create a project so we should use new
argument.
This argument will create a new project structure in current working directory. But we should give some project name to proceed. I will name the project as "my-first-app"
ng new my-first-app
After using this command in terminal some files will generated by ng
. It will install necessary packages via npm. Wait until installation is done.
We just create a new project. Proceed to my-first-app
folder. Use serve
command of ng
. It will do packaging stuff and create a http server.
cd my-first-app
ng serve
In terminal output, there should be a url address. If you can't figure the address then with --port xxx
parameter, you can change port number to whatever you want.
ng serve --port 8080
Typescript compiled and Webpack packed your application.
Now try to access to http://localhost:8080
Before going deeper make sure you opened the project with an IDE software. I recommend Visual Studio Code or WebStorm..
In project folder we can see e2e
folder, src
folder, and some files. e2e
folder contains end to end testing stuff. We will check it after. All our project source stuff are in src
folder. Other files contains information about configurations, required packages etc. We will check them when we need.
Inside of src
folder there are multiple files. The most important file in this folder is index.html
file. This file is the highest point of our project. If you open it you will see something like this.
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>MyFirstApp</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico">
</head>
<body>
<app-root>Loading...</app-root>
</body>
</html>
In side of body element we have special tag which aren't in html standard. Its app-root
element.
If you check app
folder. Go dive into app.component.ts
file.
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
}
As you can see there is some property selector
is setted to app-root
. In angular components will bind to element that given as selector
. Selector is kinda like css element selector. If you use name
then you will select tag
, if you use .name
then you will select class
, if you use [name]
you will select property
.
templateUrl
property routes component's template location. You can use template
property too if you don't want to create html file.
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
template: `
<b>hi</b>
`,
styleUrls: ['./app.component.css']
})
export class AppComponent {
}
styleUrls
property shows us component's css file location. You can use styles
too if you don't want to create css file. Difference between template
and styles
, styles is expecting an array of string, not directly string.
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
template: `
<b>hi</b>
`,
styles: [
`b {
color: red;
}`
]
})
export class AppComponent {
}
app.component.spec.ts
file contains information about tests. We will ignore it just for now.
In angular projects works under modules. These modules look like java packages
or c# namespaces
. Your components defined in a module. app.module.ts
file contains our main module.
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { HttpModule } from '@angular/http';
import { AppComponent } from './app.component';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
FormsModule,
HttpModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
All components that using in this project should declared in this file. If we don't declare it, angular couldn't find component that we want to use.
Rightnow I won't explain other files that we didn't check. We will come to them too.
Before going deeper I recommend to learn typescript. If you don't have a clue about it please check it.
To create new component; first create a folder inside of ./src/app/
folder. We name it server
.
Inside of this folder create a file that called server.component.ts
. Also you may create server.component.html
too.
In server.component.ts;
export class ServerComponent {
}
We created a class that named ServerComponent
. This class should exported out. We can't use if we didn't export it. we still have to do some stuff. We have to create a decorator
that decorates this class is a Component
. So lets do it?
@Component({
})
export class ServerComponent {
}
But it won't compile. We have to import something.. Component
decorator is defined in @angular/core
package. We can import it from this package by like this.
import { Component } from '@angular/core';
@Component({
})
export class ServerComponent {
}
Now we created a component that angular could use. But still it is invalid. Because it's like untitled subject. It just there but no one could call it. So we have to declare some selector
.
import { Component } from '@angular/core';
@Component({
selector: 'app-server'
})
export class ServerComponent {
}
selector
is similiar to css selector. If you just put something in there like this app-server
; it would look for <app-server></app-server>
. If you put there .app-server
then you get <div class="app-server"></div>
. You can even use [app-server]
to look for properties. (like <div app-server></div>
).
We have to bind component's html file to component itself. We can use templateUrl
. It is same as app.component
.
import {Component} from '@angular/core';
@Component({
selector: 'app-server',
templateUrl: './server.component.html'
})
export class ServerComponent {
}
To use this component we have to add it to app.module.ts
.
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { HttpModule } from '@angular/http';
import { AppComponent } from './app.component';
import { ServerComponent } from './server/server.component' // <<-- import first
@NgModule({
declarations: [
AppComponent,
ServerComponent // <<-- we have to add component here
],
imports: [
BrowserModule,
FormsModule,
HttpModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
I will change server.component.html
<b> server component </b>
Now we can use this component. Create a new element in app.component.html
.
<b> app component </b>
<app-server></app-server>
Check out http://localhost:8080 now.
Sometimes we don't want to create basic structures over and over again. So @angular/cli
has a solution. Using generate function of ng
we can create components without any labor.
ng generate component <name>
or
ng g c <name>
for example, If we want to create servers component;
ng generate component servers
Will generate servers
folder in app
folder and It will create ts
, html
, css
and .spec.ts
files automaticly. Also it will add declaration to app.module.ts
. Basically it makes component ready to use.
We may need bootstrap for our project. How we import it? First we have to download the package.
npm install bootstrap --save
Then open the .angular-cli.json
file. Modify it like this;
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"project": {
"version": "1.0.0-beta.32.3",
"name": "new-cli"
},
"apps": [
{
"root": "src",
"outDir": "dist",
"assets": [
"assets",
"favicon.ico"
],
"index": "index.html",
"main": "main.ts",
"polyfills": "polyfills.ts",
"test": "test.ts",
"tsconfig": "tsconfig.json",
"prefix": "app",
"styles": [
"../node_modules/bootstrap/dist/css/bootstrap.min.css", // <<-- we add this line
"styles.css"
],
"scripts": [],
"environmentSource": "environments/environment.ts",
"environments": {
"dev": "environments/environment.ts",
"prod": "environments/environment.prod.ts"
}
}
],
"e2e": {
"protractor": {
"config": "./protractor.conf.js"
}
},
"lint": [
{
"files": "src/**/*.ts",
"project": "src/tsconfig.json"
},
{
"files": "e2e/**/*.ts",
"project": "e2e/tsconfig.json"
}
],
"test": {
"karma": {
"config": "./karma.conf.js"
}
},
"defaults": {
"styleExt": "css",
"component": {}
}
}
Now you can use bootstrap.
Note: There is a library for bootstrap. It basically provides components too.
Databinding is basically binding datas between template and class.
- OUT String Interpolation: Bind a variable to template. Syntax
{{ data }}
- OUT Property Binding: Bind a variable to template's property. Syntax
[property]="data"
- IN Event Binding: Bind a event to class. Syntax
(event)="expression"
Lets a create basic component that writes a name. Name should provided with String Interpolation from a variable that we define in class.
import { Component } from '@angular/core';
@Component({
selector: 'app-name',
template: `
<p> My name is {{name}} </p>
`
})
export class NameComponent {
name: string = "Doğan";
}
Output:
This time lets add a 1 second timeout. After 1 second we will change the name to "Göksel"
import { Component } from '@angular/core';
@Component({
selector: 'app-name',
template: `
<p> My name is {{name}} </p>
`
})
export class NameComponent {
name: string = "Doğan";
constructor() {
setTimeout(() => {
this.name = "Göksel";
}, 1000);
}
}
In the beginning it will show you My name is Doğan
but after 1 second you will get My name is Göksel
. Databinding makes template render again. So you don't care rendering mechanism.
This time we will use other binding types too.
import { Component } from '@angular/core';
@Component({
selector: 'app-name',
template: `
<button [disabled]="isDisabled" (click)="someAction()">Regular button</button>
<button (click)="changeDisabled()"> {{isDisabled}} </button>
`
})
export class NameComponent {
isDisabled = true;
someAction() {
alert("hello");
}
changeDisabled() {
this.isDisabled = !this.isDisabled; // reverse the value
}
}
At the beginning regular button isn't clickable. But when we click second button it will be enabled and you get alert "hello" when you click it.
There is one more databinding type of angular. Its called Two way databinding
. This time events, properties and classes binded.
import { Component } from '@angular/core';
@Component({
selector: 'app-name',
template: `
<input [(ngModel)]="name">
<p> My name is {{name}} </p>
`
})
export class NameComponent {
name: string = "Doğan";
}
You will see that when input change class's name will automaticly change and then My name is ____
will automaticly render back. If you change name from class then input's value will change too.
Note:
ngModel
should be imported inapp.module.ts
file. Required module isFormsModule
.
There are 3 kinds of directives.
- Components
- Structural Directives (you will see this as star
*
character) - Attribute Directives
You already know the components. Lets dive into structural directives.
These directives are control full of dom. You may ask why?
Lets check an example of *ngIf
import { Component } from '@angular/core';
@Component({
selector: 'app-name',
template: `
<button (click)="visible = !visible">{{visible}}</button>
<p *ngIf="visible">
I'm visible now
</p>
`
})
export class NameComponent {
visible: boolean = false;
}
In this example when you click the button, some text appears. But interesting part is when visible is false, p element won't exist. It will be created when visible is true. Structural directives modify current dom and remove.
You can use else syntax (Angular 4) too.
import { Component } from '@angular/core';
@Component({
selector: 'app-name',
template: `
<button (click)="visible = !visible">{{visible}}</button>
<p *ngIf="visible; else hidden">
I'm visible now
</p>
<ng-template #hidden>
<p>
I'm hidden
</p>
</ng-template>
`
})
export class NameComponent {
visible: boolean = false;
}
Please try it before continue.
ngFor is a structural directive too. It will modify and clone itself as given array. For example;
import { Component } from '@angular/core';
@Component({
selector: 'app-name',
template: `
<ul>
<li *ngFor="let car of cars">{{car}}</li>
</ul>
`
})
export class NameComponent {
cars = [
'Toyota',
'Honda',
'Ford'
]
}
ngFor also has a index syntax. If you use a ;
character then define a variable that equals the index; you can use the index in the scope.
import { Component } from '@angular/core';
@Component({
selector: 'app-name',
template: `
<ul>
<li *ngFor="let car of cars; let i = index">({{i}}) {{car}}</li>
</ul>
`
})
export class NameComponent {
cars = [
'Toyota',
'Honda',
'Ford'
]
}
ngStyle is an attribute directive. It doesn't like structural directives.
import { Component } from '@angular/core';
@Component({
selector: 'app-name',
template: `
<ul>
<li *ngFor="let car of cars" [ngStyle]="{backgroundColor: car.total > 0 ? 'green' : 'red'}">{{car.name}}</li>
</ul>
`
})
export class NameComponent {
cars = [
{
name: 'Toyota',
total: 1
},
{
name: 'Ford',
total: 0
}
]
}
You will see that Toyota item will green but Ford item will red.
ngClass is an attribute directive too.
import { Component } from '@angular/core';
@Component({
selector: 'app-name',
template: `
<ul>
<li *ngFor="let car of cars" [ngClass]="{notInStock: car.total == 0}">{{car.name}}</li>
</ul>
`,
styles: [
`.notInStock {
background-color: red
}`
]
})
export class NameComponent {
cars = [
{
name: 'Toyota',
total: 1
},
{
name: 'Ford',
total: 0
},
]
}
You will see that Toyota item will looking normal but Ford item will be red.
ngSwitch is for switching between multiple cases. Its very usefull built-in directive.
import { Component } from '@angular/core';
@Component({
selector: 'app-name',
template: `
<div [ngSwitch]="count">
<p *ngSwitchCase="5"> Count is 5 </p>
<p *ngSwitchCase="10"> Count is 10 </p>
<p *ngSwitchDefault> Count is Default </p>
</div>
`
})
export class NameComponent {
count: number = 5;
}
In this chapter our goal is making some property to accessible from outside. You may ask why we need this? We creating components that has own scope. For example we create "create new user" component and "user list" component. So one component must effect to another one.
First lets do what we wanted. We will create some components.
ng new my-second-app
cd my-second-app
ng g c users --spec false #-- spec false blocks spec file generation
ng g c users/user-list --spec false # users/list syntax will create a component in users folder.
ng g c users/user-item --spec false
ng g c users/user-create --spec false
Edit app.component.html as this
<app-users></app-users>
Edit users.component.html as this
<app-user-create></app-user-create>
<hr>
<app-user-list></app-user-list>
Add an array to user-list component file.
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-user-list',
templateUrl: './user-list.component.html',
styleUrls: ['./user-list.component.css']
})
export class UserListComponent implements OnInit {
users = ['Jack', 'George', 'Another common name']; // << this line
constructor() { }
ngOnInit() {
}
}
Edit user-list.component.html as this
<app-user-item *ngFor="let user of users"></app-user-item>
Edit user-item.component.html and user-item.component.ts as like this.
<p>
User name: {{name}}
<button>Edit</button>
<button>Delete</button>
</p>
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-user-item',
templateUrl: './user-item.component.html',
styleUrls: ['./user-item.component.css']
})
export class UserItemComponent implements OnInit {
name: string;
constructor() { }
ngOnInit() {
}
}
So now we ready to process. If you get this point you probably see this screen.
User fields are created but name seems doesn't work at all. We have to do something don't we. UserItemComponent element's name property cannot be accessed by other component because it works in own closure. We have to add a decorator to access. Its called @Input
We now editing user.component.ts
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-user-item',
templateUrl: './user-item.component.html',
styleUrls: ['./user-item.component.css']
})
export class UserItemComponent implements OnInit {
@Input() name: string;
constructor() { }
ngOnInit() {
}
}
@Input
decorator is actually decorator generating function. So we have to call it like @Input()
. You can use the first parameter as alias. I will give you an example for it too.
Now we test our application but result is same. Nothing changed 😟
We forgot to set name because we never set it or access it from outside of user-item component.
Edit the user-list.component.html
<app-user-item *ngFor="let user of users" [name]="user"></app-user-item>
As you can see, we used a property binding. Basically @Input
working as property. We set the value as user, because we declared a variable as user
in ngFor directive.
Now give a shot.
Lets check example of the alias parameter @Input()
.
app-user-item.ts
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-user-item',
templateUrl: './user-item.component.html',
styleUrls: ['./user-item.component.css']
})
export class UserItemComponent implements OnInit {
@Input('user') name: string; // << as you can see we give some parameter
constructor() { }
ngOnInit() {
}
}
Now component looking for user
property.
Edit the user-list.component.html
<app-user-item *ngFor="let user of users" [user]="user"></app-user-item>
Last chapter we dive into Input. In our example we created edit and delete buttons. Also we must have a working user-create component too. Lets check it.
user-create.component.html
name: <input type="text" [(ngModel)]="name">
<button (click)="onUserCreate()">create</button>
user-create.component.ts
import { Component, OnInit, Output, EventEmitter } from '@angular/core';
@Component({
selector: 'app-user-create',
templateUrl: './user-create.component.html',
styleUrls: ['./user-create.component.css']
})
export class UserCreateComponent implements OnInit {
constructor() { }
ngOnInit() {
}
name: string; // two-way-binding property
@Output()
onUserCreated = new EventEmitter<string>(); // this is the our event that can be binded out of this component
// note: EventEmitter should be imported from @angular/core
onUserCreate() { // this function get trigger when user click button
this.onUserCreated.emit(this.name); // we send data to eventemitter
}
}
Note: I will move users to upper component.
users.component.html
<app-user-create (onUserCreated)="onUserCreated($event)"></app-user-create>
<hr>
<app-user-list [users]="users"></app-user-list>
users.component.ts
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-users',
templateUrl: './users.component.html',
styleUrls: ['./users.component.css']
})
export class UsersComponent implements OnInit {
users = ['Jack', 'George', 'Another common name'];
constructor() { }
ngOnInit() {
}
onUserCreated(name) {
this.users.push(name);
}
}
user-list.component.ts
import { Component, OnInit, Input } from '@angular/core';
@Component({
selector: 'app-user-list',
templateUrl: './user-list.component.html',
styleUrls: ['./user-list.component.css']
})
export class UserListComponent implements OnInit {
@Input() // added this
users;
constructor() { }
ngOnInit() {
}
}
Lets check our application. We type some name to input. Then click the create button. 😎
Component's css files are specific to component. For example in css file;
b {
color: red;
}
This css compiled with some property selector so this way other components b element won't get any effect but you may don't want to do that. Simply changing encapsulation can modify this feature.
import { Component, ViewEncapsulation } from '@angular/core';
@Component({
selector: 'app-some',
templateUrl: './some.component.html',
styleUrls: ['./some.component.css'],
encapsulation: ViewEncapsulation.None
})
export class SomeComponent {
}
Local reference makes a marking for DOM elements. We use that in *ngIf
structure directive section. There is one more thing that I should write and thats named @ViewChild
decorator.
This decorator allows to access DOM element from code. If you know what are you going to do then you can use this decorator, otherwise please avoid using this feature.
import { Component, ViewChild, ElementRef } from '@angular/core';
@Component({
selector: 'app-some',
template: `
<div #localReference>
</div>
`
})
export class SomeComponent {
@ViewChild('localReference')
localReferenceDiv: ElementRef;
}
ng-content is a special directive that provide element's content. Normally angular will override the content of components. <app-root>Loading...</app-root>
is good example for this consept. When angular handle app-root
then Loading
text will disapear. But what if we want to show it.
Let me show you an example.
import { Component } from '@angular/core';
@Component({
selector: 'app-bold',
template: `
<b>
<ng-content></ng-content> <!-- all content will goes to this -->
</b>
`
})
export class BoldComponent {
}
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
template: `
<app-bold> this text will be bold </app-bold>
`
})
export class AppComponent {
}
Note: Last chapter we learnt ViewChild. If you want to use ViewChild in a content it won't work. You have to use
@ContentChild
Components have a standard life cycle. They all have these things. We can hook them.
- ngOnChanges: Called after a bound input proerty changes
- ngOnInit: Called once the component initalized
- ngDoCheck: Called during every change detection run
- ngAfterContentInit: Called after content (ng-content) has been projected into view
- ngAfterContentChecked: Called every time the projected content has been checked
- ngAfterViewInit: Called after the component's view (and child views) has been initalized.
- ngAfterViewChecked: Called every time the view (and child views) has been checked.
- ngOnDestroy: Called once the components is about the be destroyed.
import { Component, OnInit, OnChanges, SimpleChanges, DoCheck, AfterContentInit, AfterContentChecked, AfterViewInit, AfterViewChecked, OnDestroy } from '@angular/core';
@Component({
selector: 'app-some',
template: ` `
})
export class SomeComponent implements OnInit, OnChanges, DoCheck, AfterContentInit, AfterContentChecked, AfterViewInit, AfterViewChecked, OnDestroy {
ngOnChanges(changes: SimpleChanges): void {
console.log('ngOnChanges', changes);
}
ngOnInit(): void {
console.log('ngOnInit');
}
ngDoCheck(): void {
console.log('ngDoCheck');
}
ngAfterContentInit(): void {
console.log('ngAfterContentInit');
}
ngAfterContentChecked(): void {
console.log('ngAfterContentChecked');
}
ngAfterViewChecked(): void {
console.log('ngAfterViewChecked');
}
ngAfterViewInit(): void {
console.log('ngAfterViewInit');
}
ngOnDestroy(): void {
console.log('ngOnDestroy');
}
}
We saw some already defined directives. But how we can defire new? This chapter we will dive into that.
Directives are defining just like components. You have to add them to app.module.ts. We can create manual but I will use @angular/cli.
ng generate directive <name>
or
ng g d <name>
I will create a green directive that makes elements green on hover.
ng g d green
I remove spec.ts file because we don't care tests just now. There should be green.directive.ts file. Directive files are created as <name>.directive.ts
syntax.
import { Directive } from '@angular/core';
@Directive({
selector: '[appGreen]'
})
export class GreenDirective {
constructor() { }
}
This directive will handle the appGreen
property. If you use somewhere else this then directive will bound to element.
We trying to make green element whenever mouse hover's the element so we have to catch the events.
import { Directive, HostListener } from '@angular/core';
@Directive({
selector: '[appGreen]'
})
export class GreenDirective {
constructor() { }
@HostListener('mouseenter')
mouseenter() {
// mouse enters
}
@HostListener('mouseleave')
mouseleave() {
// mouse leaves
}
}
How about to change colors? Now we use @HostBinding
.
import { Directive, HostBinding, HostListener } from '@angular/core';
@Directive({
selector: '[appGreen]'
})
export class GreenDirective {
@HostBinding('style.backgroundColor') backgroundColor: string = 'transparent';
@HostListener('mouseenter')
mouseenter() {
this.backgroundColor = 'green';
}
@HostListener('mouseleave')
mouseleave() {
this.backgroundColor = 'transparent';
}
}
Services useful to carry data between components. We do not require any decorator to create service. Lets create an user service.
I recommend to create file as <name>.service.ts
syntax.
So lets create a new users.service.ts file on app
folder.
export class UserService {
users = [
{id: 1, name: 'co3moz'},
{id: 2, name: 'goxel'}
{id: 3, name: 'Ilrkhoaktul'}
]
getUsers() {
return this.users;
}
addUser(user: {id: number, name: string}) {
this.users.push(user);
}
removeUser(id: number) {
this.users.splice(id, 1);
}
}
We basically create an users service class with some property and functions.
Lets use it from a component.
import { UserService } from 'user.service';
import { Component } from '@angular/core';
@Component({
selector: 'app-user-list',
template: `
<p *ngFor="let user of users"> {{ user.name }} </p>
`,
providers: [
UserService
]
})
export class UserListComponent implements OnInit {
users;
constructor(private userService: UserService) {} // userService's type must be declared. Otherwise it won't work.
ngOnInit() {
this.users = this.userService.getUsers(); // pass reference of array to local variable.
}
}
Important Note: Providers will provide a service to component but every creation of userlistComponent will make own UserService. Because we said to component when you initializing create a new service that called UserService. But what if we just want to application wide? We may need to use this datas from outside of this component. We have to use app.module.ts file for this job. Inside of app.module.ts there is providers section that we can put our service.
Important Note 2: Children of userListComponent can access same service if they didn't declared a new provider of UserService. So Providers section of component should be used only for creating new provider.
You can use @angular/cli
to generate service
ng generate service <name>
or
ng g s <name>
We may need a service inside of another service. For example we may have a logging service and this service may required to other places. So how we use another service in our service?
There you go, some example of @Injectable
import { LoggingService } from 'logging.service';
import { Injectable } from '@angular/core';
@Injectable()
export class UserService {
users = [
{id: 1, name: 'co3moz'},
{id: 2, name: 'goxel'}
{id: 3, name: 'Ilrkhoaktul'}
];
constructor(private loggingService: LoggingService) {
}
getUsers() {
return this.users;
}
addUser(user: {id: number, name: string}) {
this.users.push(user);
this.loggingService.log('user added');
}
removeUser(id: number) {
this.users.splice(id, 1);
this.loggingService.log('user removed');
}
}
We may need a event that emits some information.
import { LoggingService } from 'logging.service';
import { Injectable, EventEmitter } from '@angular/core';
@Injectable()
export class UserService {
users = [
{id: 1, name: 'co3moz'},
{id: 2, name: 'goxel'}
{id: 3, name: 'Ilrkhoaktul'}
];
constructor(private loggingService: LoggingService) {
}
userCreated = new EventEmitter<{id: number, name: string}>();
getUsers() {
return this.users;
}
addUser(user: {id: number, name: string}) {
this.users.push(user);
this.userCreated.emit(user);
this.loggingService.log('user added');
}
removeUser(id: number) {
this.users.splice(id, 1);
this.loggingService.log('user removed');
}
}
In component we can just subscribe the event. We will learn better feature so this feature is just for knowledge.
import { UserService } from 'user.service';
import { Component } from '@angular/core';
@Component({
selector: 'app-user-list',
template: `
<p *ngFor="let user of users"> {{ user.name }} </p>
`,
providers: [
UserService
]
})
export class UserListComponent implements OnInit {
users;
constructor(private userService: UserService) {
this.userService.userCreated.subscribe((user) => {
console.log('new user just created');
});
} // userService's type must be declared. Otherwise it won't work.
ngOnInit() {
this.users = this.userService.getUsers(); // pass reference of array to local variable.
}
}
Important Note: When you subscribing manually don't forget to unsubscribe with
ngOnDestroy
.
Router is routes components as pages. To you router first go app.module.ts
Import the Routes from @angular/router
import { Routes, RouterModule } from '@angular/router';
Then create a array that contains following objects.
const appRoutes: Routes = [
{ path: 'users', component: UsersComponent}
];
path
declares a route in browser. for example users
makes routes for /users
. If path given as empty string then it routes for
so this means we can make a home page.
const appRoutes: Routes = [
{ path: '', component: HomeComponent},
{ path: 'users', component: UsersComponent}
];
Also you must add RouterModule.forRoot(appRoutes)
to imports section of AppModule
.
Note You may create a
app.route.ts
orapp.routing.ts
file. Its your decision.
Now we have to declare where will content go? we will use <router-outlet></router-outlet>
.
I will add this to app.component.html.
In this sub-chapter we will learn router links.
Normally to make link we simple use href
.
<a href="/home">Home</a>
But for angular its invalid because every click causes full page reload. Our application must be stable as possible as can.
We will use routerLink
directive.
<a routerLink="/home">Home</a>
You may use []
syntax for expressions.
<a [routerLink]="['/user', id]">Home</a>
We may need to style active page or etc. To archive this we will use routerLinkActive
.
<div routerLinkActive="active">
<a routerLink="/">Home</a>
</div>
routerLinkActive directive has a another directive that named routerLinkActiveOptions
. With routerLinkActiveOptions we can give options to routerLinkActive. For example for full path check;
<div routerLinkActive="active" [routerLinkActiveOptions]="{exact: true}">
<a routerLink="/">Home</a>
</div>
Accesing the router from a component might be useful sometimes.
import { Component } from '@angular/core';
import { Router } from '@angular/router';
@Component({
selector: 'app-user-list',
template: ``
})
export class UserListComponent {
constructor(private router: Router) {
}
navigateSomewhereElse() {
this.router.navigate(['/home']);
}
}
Examples can show everything.
const appRoutes: Routes = [
{ path: '', component: HomeComponent},
{ path: 'users/:id', component: UsersComponent}
];
import { Component } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
@Component({
selector: 'app-user-list',
template: ``
})
export class UsersComponent {
constructor(private route: ActivatedRoute) {
}
getIdParameter() {
return this.route.snapshot.params['id'];
}
}
Nested routes are useful when we need show multiple components at same time.
const appRoutes: Routes = [
{ path: '', component: HomeComponent },
{ path: 'users', component: UsersComponent, children: [
{ path: ':id', component: UserComponent },
{ path: ':id/edit', component: UserEditComponent }
]}
];
Don't forget to add <router-outlet></router-outlet>
to parent component.
We may need a redirecting route. I achive that we simple add a route which doesn't have component property. It needs only redirectTo
parameter.
const appRoutes: Routes = [
{ path: '', component: HomeComponent },
{ path: 'users', component: UsersComponent },
{ path: 'people', redirectTo: '/users' }
];
With this feature and wildcard feature we can make 404 pages.
const appRoutes: Routes = [
{ path: '', component: HomeComponent },
{ path: 'users', component: UsersComponent },
{ path: 'not-found', component: NotFoundComponent },
{ path: '**', redirectTo: '/not-found' } // make sure wildcard is at the end.
];
Simply other routes will redirected to NotFoundComponent