npm uninstall -g @angular/cli npm i -g @angular/cli ng --version
ng new my-app cd my-app ng serve --open Open in the Browser http://localhost:4200/
cd my-app npm i bootstrap Optional: npm i jquery popper.js
ng add @fortawesome/angular-fontawesome Use down arrow and space to select all free options It updates automatically app.module.ts
import { faTimes } from '@fortawesome/free-solid-svg-icons'; export class TaskItemComponent implements OnInit { faTimes = faTimes; }
<div> <fa-icon [ngStyle]="{'color': 'red'}" [icon]="faTimes"></fa-icon> </div>
cd my-app npm i @fortawesome/fontawesome-free
In style.css
write
@import "@fortawesome/fontawesome-free/css/all.min.css"; @import "bootstrap/dist/css/bootstrap.min.css";
Update angular.json
file.
"styles": [ "src/styles.css", "./node_modules/@fortawesome/fontawesome-free/css/all.min.css", "./node_modules/bootstrap/dist/css/bootstrap.min.css" ], --- optional: --- "scripts": [ "./node_modules/jquery/dist/jquery.js", "./node_modules/popper.js/dist/umd/popper.js", "./node_modules/bootstrap/dist/js/bootstrap.js" ]
ng generate component components/layout/header ng generate component components/common/button
In older version: ng g c components/list --skipTests true ng g c components/list-item --skipTests true
src/styles.css
Put all global styles in this file.
<div *ngIf="tasks"> <app-task-item *ngFor="let task of tasks" [task]="task"> </app-task-item> </div>
@Input() task!: Task;
<div class="task"> {{ task.text }} </div>
<div *ngIf="users && users?.length > 0"> <div *ngFor="let user of users"> <app-user [user]="user"></app-user> </div> </div>
<ul> <li *ngFor="let user of users"> {{ user.firstName }} </li> </ul>
<li *ngFor="let page of pages; let i = index;"> <a>{{ i + 1 }}</a> </li>
<img [src]="post.imageUrl" alt="Post image">
import { Component, OnInit, Input } from '@angular/core'; import { User } from 'src/app/models/User'; @Component({ selector: 'app-user', templateUrl: './user.component.html', styleUrls: ['./user.component.css'] }) export class UserComponent implements OnInit { @Input() user: User; <---- Important! constructor() { } ngOnInit(): void { } }
<div> {{user.name}} </div>
ng g s services/users --skipTests=true
app.module.ts
import { HttpClientModule } from '@angular/common/http'; import { UsersService } from './services/users.service'; ... imports: [ ... HttpClientModule ], ... providers: [UsersService],
import { Injectable } from '@angular/core'; import { HttpClient, HttpHeaders } from '@angular/common/http'; import { Observable } from 'rxjs'; import { User } from '../models/User'; const httpOptions = { headers: new HttpHeaders({ 'Content-Type': 'application/json' }) } -- OR -- const httpOptions = { headers: new HttpHeaders({ 'Content-Type': 'application/json' }), withCredentials: true }; ----- @Injectable({ providedIn: 'root' }) export class UsersService { rootUrl:string = 'https://jsonplaceholder.typicode.com/users'; constructor(private http:HttpClient) { } getUsers():Observable<User[]> { return this.http.get<User[]>(this.rootUrl); } ////////////// addComment(comment: Comment): Observable{ return this.http.post (this.rootUrl, comment, httpOptions); } updateComment(comment: Comment): Observable { const currUrl: string = `${this.rootUrl}/${comment._id}`; return this.http.put (currUrl, comment, httpOptions); } deleteComment(_id: string):Observable { const currUrl: string = `${this.rootUrl}/${_id}`; return this.http.delete (currUrl); } }
import { Component, OnInit } from '@angular/core'; import { UsersService } from "../../../services/users.service"; import { User } from '../../../models/User'; @Component({ selector: 'app-users-list', templateUrl: './users-list.component.html', styleUrls: ['./users-list.component.css'] }) export class UsersListComponent implements OnInit { users: User[]; constructor(private usersService: UsersService) { } ngOnInit(): void { this.usersService.getUsers().subscribe(users => { this.users = users; }); } }
export interface Task { id?: number; text: string; day: string; reminder: boolean; }
import { Task } from './Task'; export const TASKS: Task[] = [ { id: 1, text: 'Food Shopping', day: 'May 5th at 2:30pm', reminder: true, }, { id: 2, text: 'Meeting at School', day: 'May 6th at 1:30pm', reminder: true, } ];
import { Injectable } from '@angular/core'; import { Task } from 'src/app/Task'; import {TASKS} from 'src/app/mock-tasks'; @Injectable({ providedIn: 'root' }) export class TasksService { constructor() { } getTasks():Task[] { return TASKS; } }
import { TasksService } from 'src/app/services/tasks.service'; import { Task } from 'src/app/Task'; @Component({ selector: 'app-tasks', templateUrl: './tasks.component.html', styleUrls: ['./tasks.component.css'], }) export class TasksComponent implements OnInit { tasks: Task[] = []; constructor(private taskService: TasksService) {} ngOnInit(): void { this.tasks = this.taskService.getTasks(); }
import { Injectable } from '@angular/core'; import {Observable, of} from 'rxjs'; import { Task } from 'src/app/Task'; import {TASKS} from 'src/app/mock-tasks'; @Injectable({ providedIn: 'root', }) export class TasksService { constructor() {} getTasks(): Observable<Task[]> { const tasks = of(TASKS); // of() turns it into observable return tasks; } }
import { Component, OnInit } from '@angular/core'; import { TasksService } from 'src/app/services/tasks.service'; import { Task } from 'src/app/Task'; @Component({ selector: 'app-tasks', templateUrl: './tasks.component.html', styleUrls: ['./tasks.component.css'], }) export class TasksComponent implements OnInit { tasks: Task[] = []; constructor(private taskService: TasksService) {} ngOnInit(): void { this.taskService.getTasks().subscribe(tasks => this.tasks = tasks) } }
<header> <app-button color="green" text="Add" (btnClick)="toggleAddTask()" ></app-button> </header>
toggleAddTask() { console.log('Button toggle clicked'); }
<button [ngStyle]="{ 'background-color': color }" class="btn" (click)="onClick()"> {{ text }} </button>
@Input() text: string = 'Default text'; @Input() color: string = 'red'; @Output() btnClick = new EventEmitter(); onClick(): void { this.btnClick.emit(); }
import { Injectable, EventEmitter } from '@angular/core'; commentSelected: EventEmitter<Comment> = new EventEmitter();
ngOnInit(): void { this.commentsService.commentSelected?.subscribe(comment => { this.isEdit = true; this.current = comment; }); }
constructor(private commentsService: CommentsService) { } ... editWasPressed(comment: Comment) { this.commentsService.commentSelected.emit(comment); }
<form (submit)="onSubmit($event)"> </form>
onSubmit(e) { e.preventDefault(); }
<form (ngSubmit)="onSubmit()"> <input type="text" name="title" [(ngModel)]="title"> ... </form>
import { FormsModule } from '@angular/forms'; ... imports: [ ... FormsModule ],
<h2>Add Post</h2> <div class="card card-body mb-3"> <form> <div class="form-group"> <label>Title</label> <input type="text" class="form-control" name="title" #title> </div> <div class="form-group"> <label>Body</label> <input type="text" class="form-control" name="body" #body> </div> <button (click)="addPost(title.value, body.value)" type="submit" class="btn btn-dark btn-block">Add Post</button> </form> </div>
import { Component, OnInit, Output, EventEmitter } from '@angular/core'; import { PostService } from '../../services/post.service'; import { Post } from '../../models/Post'; @Component({ selector: 'app-post-form', templateUrl: './post-form.component.html', styleUrls: ['./post-form.component.css'] }) export class PostFormComponent implements OnInit { @Output() newPost: EventEmitter<Post> = new EventEmitter(); constructor(private postService: PostService) { } ngOnInit(): void { } addPost(title, body) { if (!title || !body) { alert('Please add post'); } else { this.postService.savePost({title, body} as Post).subscribe(post => { this.newPost.emit(post); }); } } }
<app-post-form (newPost)="onNewPost($event)"></app-post-form> <h2>Posts</h2> ...
... onNewPost(post: Post) { this.posts.unshift(post); }
<form (ngSubmit)="onSubmit()"></form>
onSubmit() { this.addUser(); // e.preventDefault() is not required }
<form #userForm="ngForm" (ngSubmit)="onSubmit(userForm)"> <div class="form-group"> <label>First Name</label> <input type="text" [(ngModel)]="user.firstName" [ngClass]="{'is-invalid': userFirstName.errors && userFirstName.touched}" class="form-control" name="firstName" #userFirstName="ngModel" required minlength="2" > <div [hidden]="!userFirstName.errors?.required" class="invalid-feedback"> First name required </div> <div [hidden]="!userFirstName.errors?.minlength" class="invalid-feedback"> Must be at least 2 characters </div> </div> ... <button [disabled]="!userForm.form.valid" class="btn btn-block bg-light mb-3">Add New User</button> </form>
... onSubmit({value, valid}: {value: User, valid: boolean}) { }
<form> <div class="form-group"> <input type="text" name="text" [(ngModel)]="text" > </div> <input [disabled]="!text" type="submit" value="Add Log"> <button [hidden]="!text" type="button">Clear</button> </form>
See project #186 DevLogger
import { ReactiveFormsModule } from '@angular/forms'; ... imports: [ ReactiveFormsModule ],
import { FormGroup, FormControl, Validators } from '@angular/forms'; ... cardForm = new FormGroup({ name: new FormControl('', [ Validators.required, Validators.minLength(2), ]) });
<form [formGroup]="cardForm" (ngSubmit)="onSubmit()"> <input formControlName="name" /> <ng-container *ngIf="cardForm.controls.name.touched && cardForm.controls.name.errors"> <div *ngIf="cardForm.controls.name.errors.required"> Value is required. </div> <div *ngIf="cardForm.controls.name.errors.minlength"> Value should contain at least {{ cardForm.controls.name.errors.minlength.requiredLength }} characters. </div> </ng-container> <button [disabled]="cardForm.invalid" type="submit">Submit</button> </form>----
For Debugging:
<div>Form Contents {{ cardForm.value | json }} </div> <div>Form is valid: {{ cardForm.valid }} <---- default is true </div> <div>Errors around name: {{ cardForm.controls.name.errors | json }} </div> <div>Errors around name: {{ cardForm.get('name').errors | json }} </div>
import { FormGroup, FormControl, Validators } from '@angular/forms'; ... cardForm = new FormGroup({ name: new FormControl('', [ Validators.required, Validators.minLength(2), Validators.maxLength(40), // Validators.pattern(/\s/), ]) });
<form [formGroup]="cardForm" (ngSubmit)="onSubmit()"> <app-input label="Name" [control]="cardForm.get('name')"></app-input> <button [disabled]="cardForm.invalid" type="submit">Submit</button> </form>
import { FormControl } from '@angular/forms'; @Input() control: FormControl; @Input() label: string;
<label>{{ label }}</label> <input [formControl]="control"/> <ng-container *ngIf=" control.touched && control.errors" > <div *ngIf="control.errors.required"> Value is required. </div> <div *ngIf="control.errors.minlength"> Value should contain at least {{ control.errors.minlength.requiredLength }} characters. </div> <div *ngIf="control.errors.maxlength"> Value should contain maximum {{ control.errors.maxlength.requiredLength }} characters. </div> <div *ngIf="control.errors.pattern"> Invalid format </div> </ng-container>
<input (input)="onChangeLength($event.target.value)" />
<input (change)="onToggle(todo)" type="checkbox"> {{ todo.title }} <button (click)="onDelete(todo)" class="del">x</button>
onToggle(todo: Todo) { todo.completed = !todo.completed }
<input (keydown)="fireEvent($event)" type="text" name="firstName"> keyup keypress focus blur cut paste copy
fireEvent(e) { console.log(e.type); console.log(e.target.value); }
<div *ngFor="let user of users"> {{user.name}} </div>
<ul *ngIf="loaded && users?.length > 0"> <li> </li> </ul>
<ul *ngIf="loaded && users?.length > 0;else noUsers"> </ul> <ng-template #noUsers><h4>No Users Found</h4></ng-template>
Use when *ngFor and *ngIf are needed.
<ng-container *ngFor="let page of pages; let i = index;"> <li class="page-item" *ngIf="i < 5" > ... </li> </ng-container>
<div [ngSwitch]="currentPage" class="mt-3"> <div *ngSwitchCase="0"> <p>First Page</p> </div> <div *ngSwitchDefault> ... </div> </div>
<li ngNonBindable><pre>{{ 5 | number:"2.4" }}</pre></li> gives same text: {{ 5 | number:"2.4" }}
Set bg-light class if user.isActive is true
<div [ngClass]="setClasses()"></div>
// Set Dinamic classes setClasses(): object { let classes = { todo: true, 'is-complete': this.todo.completed } return classes; }-- OR --
setClasses() { let classes = []; if (something()) { classes.push('active'); } if (somethingElse()) { classes.push('something'); } return classes; }
users.component.html <button class="btn" [ngClass]="currentClasses"></button>
users.component.ts currentClasses = {} ... setCurrentClasses(); ... setCurrentClasses() { this.currentClasses = { 'btn-success': this.enableAdd, 'text-center': true } }
<button <span><i [ngClass]="user.hide ? 'fa fa-plus' : 'fa fa-minus'" ></i></span></button>
<li [ngClass]="{ active: currentPage === i }" <---- object *ngFor="let page of pages; let i = index;" > </li>
<li *ngFor="let log of logs" [class.list-group-item-secondary]="selectedLog === log" > ... </li>
See DevLogger - project #186
<div [style.height.rem]="2"><p>AAA</p></div>
<div [style.border-color]="user.isActive ? 'green' : ''"></div>
<p [ngStyle]="{'color': 'red'}">BBBB</p>
<button [ngStyle]="{ 'background-color': color }" class="btn" (click)="onClick()"> {{ text }} </button>
<h3 [ngStyle]="currentStyles">{{ user.name }}</h3>
currentStyles = {} ... setCurrentStyles(); ... setCurrentStyles() { this.currentStyles = { 'padding-top': this.showExtended ? '0' : '40px', 'font-size': this.showExtended ? '' : '40px' } }
<button (click)="fireEvent($event)">A button</button>...
fireEvent(e) { console.log(e.type) }
{{ name | uppercase }} {{ name | lowercase }}
{{ balance | currency }} {{ balance | currency:"ILS" }} {{ balance | currency:"GBP" }} {{ balance | currency:"GBP":"code" }} {{ balance | currency:"GBP":"symbol" }} same as {{ balance | currency:"GBP" }}
{{ registered | date }} {{ registered | date:"dd/MM/yyyy HH:mm:ss" }} {{ registered | date:"mm/dd/yyyy" }} {{ registered | date:"yyyy" }} {{ registered | date:"shortDate" }} {{ registered | date:"longDate" }} {{ registered | date:"fullDate" }} includes day of the week {{ registered | date:"shortTime" }} {{ registered | date:"longTime" }}
{{ 5 | number:"1.2"}} gives 5.00 {{ 5 | number:"2.4"}} gives 05.0000
{{ 1 | percent}} gives 100% {{ 0.5 | percent}} gives 50%
removePost(post: Post | number): Observable<Post> { const id = typeof post === 'number' ? post : post.id; ... }
ng g m app-routing
app.module.ts
import { AppRoutingModule } from './app-routing/app-routing.module'; imports: [ ... AppRoutingModule ],
app.component.html
<div class="container"> <router-outlet></router-outlet> </div>
app-routing.module.ts
import { NgModule } from '@angular/core'; import { Routes, RouterModule } from '@angular/router'; import { HomeComponent } from '../components/home/home.component'; import { UsersComponent } from '../components/users/users.component'; const routes: Routes = [ {path: '', component: HomeComponent}, {path: 'users', component: UsersComponent}, ]; @NgModule({ imports: [RouterModule.forRoot(routes)], exports: [RouterModule] }) export class AppRoutingModule { }
Use when Page Not Found does not work in Angular: imports: [RouterModule.forRoot(routes, {useHash: true})]
components/home.component.html
<button class="btn btn-secondary" routerLink="/users">Users Section</button>
import { Router } from '@angular/router'; constructor( private router: Router ) { } myFunc() { this.router.navigate(['/somewhere']); }
<nav class="navbar navbar-expand navbar-dark bg-success mb-4"> <div class="container"> <a routerLink="/" class="navbar-brand">Tasks App</a> <div> <ul class="navbar-nav ml-auto"> <li class="nav-item" [routerLinkActive]="['active']" [routerLinkActiveOptions]="{exact: true}"> <a routerLink="/" class="nav-link">Home</a> </li> <li class="nav-item" [routerLinkActive]="['active']" [routerLinkActiveOptions]="{exact: true}"> <a routerLink="/add-task" class="nav-link">Add Task</a> </li> </ul> </div> </div> </nav>
ng g m elements --routing <----- Make this module ready for navigation
elements-routing.module.ts
file
const routes: Routes = [ { path: 'elements', component: ElementsHomeComponent} ];
app.module.ts
file
imports: [ ... ElementsModule ]
app.component.html
file
<router-outlet></router-outlet>
ng g m elements --routing <----- Make this module ready for navigation
elements.module.ts
file
imports: [...], exports: [ElementsHomeComponent]
app.module.ts
file
imports: [ ... ElementsModule ]
app.component.html
file
<app-elements-home></app-elements-home> <router-outlet></router-outlet>
ng g m modules/Shared ng g c modules/shared/components/FormControls/Input --skipTests true
shared.module.ts
import { ReactiveFormsModule } from '@angular/forms'; import { InputComponent } from './components/FormControls/input/input.component'; @NgModule({ imports: [ ... ReactiveFormsModule ], exports: [InputComponent] })
auth.module.ts
import { SharedModule } from '../shared/shared.module'; ... imports: [ SharedModule ],
register.component.html
<app-input></app-input>
ng g m modules/Auth --routing ng g m modules/Shared ng g c modules/auth/components/Login --skipTests true ng g c modules/auth/components/Register --skipTests true ng g c modules/shared/components/FormControls/Input --skipTests true
auth-routing.module.ts
file
const routes: Routes = [ { path: 'register', component: RegisterComponent }, { path: '', component: LoginComponent } ];
app.module.ts
file
imports: [ AuthModule ],
app.component.html
file
<a routerLink="" routerLinkActive="active"> Login </a> <a routerLink="/register" routerLinkActive="active"> Register </a> <router-outlet></router-outlet>
See project #194
ng g m modules/Account --routing <----- Make this module ready for navigation ng g c modules/Account/components/AccountHome --skipTests true
account-routing.module.ts
file
import { AccountHomeComponent } from './components/account-home/account-home.component'; const routes: Routes = [ { path: '', component: AccountHomeComponent } ];
app-routing.module.ts
const routes: Routes = [ // --> Lazy Loading { path: 'account', loadChildren: () => import('./modules/account/account.module').then(m => m.AccountModule) } // <-- Lazy Loading ];
<a routerLink="/account">Account</a>
ng g m views --routing <----- Make this module ready for navigation
app.component.html
<a routerLink="/views" routerLinkActive="active"> Views </a>
views module
in app-routing.module.ts
const routes: Routes = [ // --> Lazy Loading ... { path: 'views', loadChildren: () => import('./views/views.module').then(m => m.ViewsModule) }, // <-- Lazy Loading { path: '', component: HomeComponent }, { path: '**', component: NotFoundComponent } ];
ViewsHome
component in the the views module
ng g c views/components/viewsHome --skipTests true
views-routing.module.ts
to show ViewsHome
component
const routes: Routes = [ { path: '', component: ViewsHomeComponent } ];
statistics
component in the the views module
ng g c views/components/statistics --skipTests true
statistics
component in ViewsHome
component: in views-home-component.html
<app-statistics></app-statistics>
Shared
module
views.module.ts
imports: [ SharedModule ]
views-home-component.html
Add in tsconfig.app.json
file:
"compilerOptions": { ... "resolveJsonModule": true, "esModuleInterop": true },
See project #149
.css
file
:host:not(:first-of-type) { <-- host component, but not the first one display: block; margin-top: 20px; }
.html
file
<app-devider>First Component</app-devider> <app-devider>Second Component</app-devider>
this.myService.getValues() <--- observable == sourse of event. Observable emits an event .pipe( <--- contains a chain of specific operators map(event => console.log(event)) <-- gets an event that is comming out of the observable ) .subscribe() --------------- this.myService.getValues() .pipe( map(event => event.someValue), <-- result of event.someValue is returned automatically map(value => parseInt(value)), <-- the "value" was return during the previous step map(value => { if (isNaN(value)) { throw new Error('Enter a number') } return value; }) ) .subscribe() --------------- this.myService.getValues() .pipe( map(event => event.someValue), <-- result of event.someValue is returned automatically map(value => parseInt(value)), <-- the "value" was return during the previous step map(value => { if (isNaN(value)) { throw new Error('Enter a number') } return value; }) ) .subscribe({ <--- observer is inside the subscribe method next(value) { console.log(`Your value is ${value}`); }, error(err) { // catches errors console.error(err.message); }, complete() { } })
pluck
operator
{ color: 'red', year: 2000 } --> pluck('year') --> 2000
this.myService.getValues() .pipe( pluck('target', 'value'), <-- if input is { target: { value: 'fvhg'}} map(value => parseInt(value)), <-- the "value" was return during ..... )
observable.subsribe - way 1
const observable = new Observable((subscriber) => { // Throw the value 1 into our pipeline subscriber.next(1); // Marks the observable as complete, no more values will come out subscriber.complete(); //Emit an error, no more values will come out. Since complete() was called above, error will not be emitted subscriber.error(new Error('hgfhkfvk')); // not throw new Error }).pipe( // the value 1 will come here ); observable.subscribe({ <--- observer next(value) { console.log('Got a value: ', value); }, complete() { // called at complete above, no argument here console.log('Observable is complete. Don't expect any values); }, error(err) { // called when error emits console.log(err.message); } });
observable.subsribe - way 2
observable.subsribe( (value) => console.log('Next value:', value), // next (err) => console.error(err.message), // error () => console.log('COMPLETE') // completion );
observable.subsribe - way 3
observable.subsribe( (value) => console.log('Next value:', value), // next );
const observable = new Observable((subscriber) => { // Throw the value 1 into our pipeline subscriber.next(1); subscriber.next(2); subscriber.next(3); // Marks the observable as complete, no more values will come out subscriber.complete(); //Emit an error, no more values will come out. Since complete() was called above, error will not be emitted subscriber.error(new Error('hgfhkfvk')); // not throw new Error }).pipe( // the value 1 will come here tap(value => console.log('From tap:', value)) // tap - Perform a side effect for every emission on the source Observable, but return an Observable that is identical to the source. );
observable.subsribe( (value) => console.log('Next value:', value), // next (err) => console.error(err.message), // error () => console.log('COMPLETE') // completion );
observable.subsribe((value) => { console.log('From second subscribe', value); });
ng build --prod --base-href .