In a nutshell
Communities
Social Developer
Recognitions
Principal Product Manager @Microsoft
GDE,MCT and MVP from SL
Top stackoverflow contributor
Top mentor @ adplist
@sajeetharan
@kokkisajee
@sajeetharan
@sajeetharan
"Given the current AI trends, many sessions focus on AI, but this one is purely about Angular. Any expectation of AI content is your own.
The characters in this session are fictional, and while AI adoption is at 20%, there's still a long way to go. 80% of apps are still legacy, so let’s dive back into the fundamentals."
This is a hands-on session, so please make sure your laptops are open and connected to Wi-Fi.
(DevFest Podujana Peramuna)
Angular Module
Chamod Rajapaksha
Suresh Rajapaksha
Kubernetes
Podujana
Peramuna
Google Cloud
Podujana
Peramuna
Then
Now
2-way databinding
2009
Component based view library
2013
Native Mobile Apps using React
2014
2016
Future ready
$scope
ng-if
ng-app
ng-model
mobile
oriented
better
performance
x11
Angular 1.x vs Angular
2017
2020
2024
A framework for web front-end app
A platform for integrated development
An ecosystem for developers
World
1-1
x
1
CLI. What is it?
ngular
Scaffold
Preview
Local build
Local Testing
Unit tests
E2E tests
building blocks
ngular
Mathews Casually Pulls, Dhananjaya Misses, Shanaka Drops!
Module
Component
Pipes
Directives
Metadata
Services
Dependency Injection
Component
import { Component } from '@angular/core';
@Component({
selector: 'world', // Name of the html tag
providers: [], // Services
styleUrls: [], // Url of the styles
templateUrl: './world.component.html'
})
export class WorldComponent {
}
ngular
Basic building block of an UI in an Angular application
Module
Angular has at least one module, either root or functionality, it's a class with a decorator @ngModule
Highest level of abstraction
ngular
How are the components organized?
ngular
Module
Directive
Structure
Attribute
<li *ngFor="let enemy of enemies">
</li>
<enemy *ngIf="hasLife">
</enemy>
*ngFor
*ngIf
<div [ngClass]="['bold-text', 'green']"></div>
<div [ngClass]="'italic-text blue'"></div>
<div [ngClass]="{'small-text': true, 'red': true}"></div>
ngClass
ngStyle
You can create your own!
No more ng-show, ng-hide, ng-click but functionality is still available
...
...
ngular
ngular
Services
There is a single object of each service in the application (singleton)
That is, all the components share "the same" service
In this way the services maintain the status of the application
ngular
Services
You can make a service not shared among all the components of the application (not singleton)
You can create an exclusive service for a component and its children
Instead of declaring the service in @NgModule providers it is declared in the @Component
It can be shared by the parent component and by its children components (included in it)
import { Injectable } from '@angular/core';
@Injectable()
export class BlockService {
getCoins() {
return 100;
}
}
ngular
Services
ngular
import { Pipe } from '@angular/core';
import { PipeTransform } from '@angular/core';
@Pipe({name: 'filterReviewByStatus'})
export class FilterReviewByStatusPipe implements PipeTransform {
}
Pipes
Exclusive service for a component
import { Component } from '@angular/core';
import { BlockCoins } from './blockCoins.service';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
providers: 'BlockCoins'
})
export class AppComponent {
constructor (private blockCoins: BlockCoins) {
}
}
ngular
Services
Exclusive service for a component
export class AppComponent {
constructor (private blockCoins: BlockCoins) {
}
}
ngular
DI
Dependency Injection is a design pattern that passes an object as dependencies in different components across the application.
It creates a new instance of class along with its required dependencies
Events
<button (click)="jump()"></button>
ngular
Data Binding
DOM
Component
{{ value }}
[property] = *value*
(event) = *handler*
[(event)] = *handler*
ngular
Comunication
ngular
Configuration of Properties (Father -> Son)
<world [name]="worldName"></world>
export class AppComponent {
worldName = 'World 1-1';
}
import {Component, Input} from '@angular/core';
export class WorldComponent {
@Input()
private name: string;
}
<h1>{{name}}</h1>
app.component.ts
app.component.html
world.component.ts
world.component.html
Sending Events ( Son -> Father)
<world [name]="worldName" (changeName)='changeWorld($event)'></world>
export class AppComponent {
worldName:string = 'World 1-1';
changeWorld(name: string) {
this.worldName = name;
}
}
import {Component, Input, Output, EventEmitter} from '@angular/core';
export class WorldComponent {
@Input()
private name: string;
@Output()
changeName = new EventEmitter<string>();
click(level) {
this.changeName.next(level);
}
}
<h1>{{name}}</h1>
<button (click)='click("World 1-2")'>Next level</button>
app.component.ts
app.component.html
world.component.ts
world.component.html
Comunication
ngular
Router
ngular
<router-outlet></router-outlet>
<a [routerLink]="/world1"></a>
Set the Route
This is the container where the component or module indicated by the router will be loaded
World
2-1
x
2
Architecture
ngular
Our application is divided in two parts. -Application itself & Application bootstrap
Our modules, routes, components & services live here.
We bootstrap our app depending on what platform were developing for, and add any required polyfills & vendors.
Typescript config, npm packages, scripts, webpack config, express server, and so on...
World
3-1
x
3
Download
Install
Run!
https://nodejs.org/en/download/
npm install -g @angular/cli@latest
ng new devfest2024 --routing --style=scss
cd devfest2024
npm start
We are going to create these elements in Angular
Let's make something awesome!
Chamod Rajapaksha
Suresh
Rajapaksha
Safnaj Kumara
Kaveesha
Premadasa
Sri lanka
Sri Lnaka
Home
Logo
Home
Game
What's the target?
Welcome to DevFest 2024
The first step to address in this application is to identity patterns, what things are reused and what is not
Service
Character
Enemies
Sri Lanka
Why Standalone Components?
No More Dependency on NgModules
Simplified Application Structure
Faster Learning Curve
Enhanced lazy loading
Improved Migration
ngular (Do Not do This)
We will start by creating a new Module
AngularHero inside the folder app/modules/
ng g m AngularHero
Before Angular 17
ngular
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
@NgModule({
declarations: [],
imports: [
CommonModule
]
})
export class AngularHeroModule {
}
app/modules/angular-Hero/angularHero.module.ts
ngular
Lets start the first component
srilanka.component.ts
ng g c srilanka
Component SriLanka
import { Component } from '@angular/core';
@Component({
selector: 'app-srilanka',
imports: [],
templateUrl: './srilanka.component.html',
styleUrl: './srilanka.component.scss'
})
export class SrilankaComponent {
}
ngular
Component Home
ngular
What we get:
ng g c home
ngular
Let's add some style
Open index.html and add the following css link in the head section:
<link rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.98.1/css/materialize.min.css">
Component Safnaj
ngular
Now it's your turn, create a Safnaj component. Safnaj is an enemy of the SriLanka Mario game
ng g c Safnaj
Add a Safnaj to the Srilanka module
ngular
<h1>Sri Lanka</h1>
<app-safnaj></app-safnaj>
Add the component to the module AngularHeroModule.module.ts in the declarations section
Hope things go smoothly...
ngular
Exercise 1
<div class="card-panel blue lighten-2">
<div class="row">
<app-safnaj [id]="item" (removeGoomba)="removeGoomba($event)" *ngFor="let item of enemies">
</app-safnaj>
</div>
</div
Add all the components to Sri lanka Component and create 3 enemies ( Safnaj Kumara)
import { Component } from '@angular/core';
@Component({
selector: 'app-srilanka',
imports: [],
templateUrl: './srilanka.component.html',
styleUrl: './srilanka.component.scss'
})
export class SrilankaComponent {
enemies: Array<string> = ['0', '1', '2', '3'];
constructor() {
}
ngOnInit() {
}
removeGoomba(id: any) {
const i = this.enemies.indexOf(id);
if (i !== -1) {
this.enemies.splice(i, 1);
}
}
}
srilanka.component.html
srilanka.component.ts
Create the character component
Exercise 2
*Note that Chamod and Suresh are 2 equally playable characters, so you'll have to make your component flexible to define the character's name
ng g c character
Add Chamod and Suresh to SriLanka
Exercise 3
<app-character name="chamod" (removeGoomba)="removeGoomba($event)" name="chamod" style="background-color:lightblue;display:block"></app-character>
<app-character name="suresh" (removeGoomba)="removeGoomba($event)" name="suresh" style="background-color:lightgreen;display:block"></app-character>
Add all elements to the App Component
<nav>
<div class="nav-wrapper">
<a href="#" class="brand-logo">Angular - Standalone</a>
<ul id="nav-mobile" class="right hide-on-med-and-down">
<li><a [routerLink]="['/home']">Home</a></li>
<li><a [routerLink]="['/srilanka']">Lets Play</a></li>
</ul>
</div>
</nav>
<div class="container">
<router-outlet></router-outlet>
</div>
app.component.html
Exercise 4
Set Routing
Tell Angular which component to load when navigating
Open app.routes.ts
import { Routes } from '@angular/router';
import { HomeComponent } from './home/home.component';
import { SrilankaComponent } from './srilanka/srilanka.component';
export const routes: Routes =[
{ path: 'home', component: HomeComponent },
{ path: 'srilanka', component: SrilankaComponent }
];
Tell to load HomeComponent when navigating in the browser domain.com/home
Suresh and Chamodcan jumb. Create a button when clicking click on the console that Chamod or Suresh has jumped
Exercise 5
We are going to get some coins.Lets create a service that will supply Suresh and Chamod with some Votes when they are hit. Make it to show the total votes on each character.
Exercise 6
Exercise 7
Create the Vote Service
ng g s vote
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root'
})
export class VoteService {
votes = 0;
constructor() { }
getVotes() {
return this.votes;
}
setVoters(vote : number){
this.votes = +1;
}
}
We're going to crush those Safnaj Kumaras. Create a button that allows the safnaj to be crushed with a click and as a consequence, the safnaj dissapears from Sri lanka.
Exercise 7
button
Character component
<ul class="collection">
<li class="collection-item avatar">
<img *ngIf="name == 'suresh'" src="assets/mario.png" alt="" class="circle">
<img *ngIf="name == 'chamod'" src="assets/luigi.png" alt="" class="circle">
<span class="title">it is me! {{name}}, votes: {{votes}}</span>
</li>
</ul>
<input type="number" #goombaId/>
<button class="btn waves-effect waves-light green accent-3" (click)="crushGoomba(goombaId.value)">Jump over</button>
import { Component, EventEmitter, Input, Output } from '@angular/core';
import { CommonModule } from '@angular/common';
import { VoteService } from '../vote.service';
@Component({
selector: 'app-character',
imports: [CommonModule],
templateUrl: './character.component.html',
styleUrl: './character.component.scss'
})
export class CharacterComponent {
votes = 0;
@Input() name!: string;
@Output()
removeGoomba = new EventEmitter<string>();
constructor(private voteService: VoteService) { }
ngOnInit() {
}
jumpForCoins() {
this.votes = this.voteService.getVotes();
}
jump() {
alert(this.name + ' has jumped');
}
crushGoomba(goombaId: any) {
this.removeGoomba.next(goombaId);
this.votes = +1;
}
}
character.component.html
character.component.ts
Safnaj Component
<ul class="collection">
<li class="collection-item avatar">
<img src="assets/goomba.png" alt="" class="circle">
<span class="title">it is me! Safnaj Kumara {{id}} </span>
</li>
</ul>
<button class="btn waves-effect waves-light light-blue accent-2" (click)="crushGoomba()">Kill me </button>
import { Component, EventEmitter, Input, Output } from '@angular/core';
@Component({
selector: 'app-safnaj',
imports: [],
templateUrl: './safnaj.component.html',
styleUrl: './safnaj.component.scss'
})
export class SafnajComponent {
@Input() id!: string;
@Output()
removeGoomba = new EventEmitter<string>();
constructor() { }
ngOnInit() {
}
crushGoomba() {
this.removeGoomba.emit(this.id);
console.log("Goomba id", this.id)
}
}
safnaj.component.html
safnaj.component.ts
Let's make the character be the one who crushed the Safnaj. As a benefit, each Safnaj that you crush will recieve 1 more vote
Exercise 8
crushSafnaj(goombaId: any) {
this.removeSafnaj.next(goombaId);
this.votes = +1;
}
ngular
All are solved
Otherwise
https://github.com/sajeetharan/ng-standalone-devfest-2024
Q&A
Where to go from here?
- Build Applications
- Send your queries @kokkisajee
- If you want to hear more , attend my talk tomorrow