هالكورس Crash Course لـ Angular موجّه للمطورين اللي عندهم خلفية بـ React أو Vue أو Solid أو أي JavaScript framework. رح نغطي المفاهيم الأساسية اللي بتحتاجها لتبدأ تبني تطبيقات Angular فعلية.
المواضيع اللي رح نمر فيها: CLI، components، data bindings، event listeners، routing، services، directives، وأكثر.
روحوا على angular.dev وتأكدوا إنه في عندكم Angular CLI. في أكثر من طريقة للتنزيل - NPM أو PNPM أو YARN. بعد ما تنزلوا، تحققوا من الإصدار:
ng version
ng new angular-demo
بيسألكم بعض الأسئلة:
NCSSNبعد ما يخلص، افتحوا الـ project:
cd angular-demo
code .
yarn start
بتشوفوا الـ app على localhost:4200.
| الملف/الفولدر | الوظيفة |
|---|---|
src/app/ | محل كتابة كل الكود |
src/app/app.component.ts | أعلى component بالـ app |
src/main.ts | نقطة الدخول للـ application |
src/styles.css | الـ global styles |
package.json | الـ dependencies والـ scripts |
src/main.ts هو أعلى نقطة بالـ application. الـ app.component هو أعلى component، وكل شيء ثاني بيعيش جواته. الـ styles.css للـ global styles اللي بتنطبق على كل الـ components.
اشترك في النشرة البريدية
دروس جديدة، مقالات، وأدوات مباشرة لبريدك.
أهم الـ scripts:
yarn start # تشغيل development server
yarn build # بناء الـ production build
yarn test # تشغيل الـ testsكل component بيتكون من أربع ملفات:
.css - الـ styles الخاصة بهالـ component.html - الـ template.spec.ts - ملف الـ tests.ts - الـ component نفسهالـ .ts file بيتكون من قسمين - decorator وclass:
@Component({
selector: 'app-root',
imports: [],
templateUrl: './app.component.html',
styleUrl: './app.component.css'
})
export class AppComponent {
title = 'angular-demo';
}الـ decorator فيه عدة properties مهمة:
| Property | الوظيفة |
|---|---|
selector | اسم الـ HTML tag لاستخدام الـ component |
imports | الـ components واللي بنستخدمها جوات هالـ component |
templateUrl | وين موجود الـ HTML |
styleUrl | وين موجودة الـ styles |
الـ selector هو اسم الـ tag. مثلاً app-root بيصير <app-root> بالـ HTML.
فيكن كمان تكتبوا الـ template مباشرة بدل ملف خارجي:
@Component({
selector: 'app-root',
template: `<h1>Hello from template</h1>`,
})
export class AppComponent {}ng generate component titleهيك بينشئ أربع ملفات جوات app/title/. لاستخدام الـ component بـ component ثاني:
app.component.tsimports array:@Component({
selector: 'app-root',
imports: [TitleComponent],
templateUrl: './app.component.html',
})
export class AppComponent {}<app-title></app-title>بالـ Angular الجديدة (v19)، الطريقة المنصوحة لإنشاء reactive variables هي Signals:
import { signal } from '@angular/core';
export class TitleComponent {
title = signal('Tracker Application');
}لاستخدام الـ signal بالـ HTML، بنعمل invoke لها:
<h2>{{ title() }}</h2>الفرق عن الطريقة القديمة إنك لازم تستدعيها كـ function. هيكي Angular تعرف إنها signal وبتشتغل reactive.
لتمرير data من component أب لـ component ابن، نستخدم input:
import { input } from '@angular/core';
export class TitleComponent {
title = input.required<string>();
}الـ input.required معناها هالـ property مطلوبة ولازم تنعمل pass لها قيمة. فيكن كمان تستخدموا input<string>() إذا بدكم تكون optional.
بالـ component الأب، تمرروا القيمة هيك:
<!-- قيمة hard-coded -->
<app-title title="My App"></app-title>
<!-- قيمة dynamic من variable -->
<app-title [title]="appTitle"></app-title>الـ square brackets بتخبروا Angular إنك عم تمرر قيمة dynamic.
لإضافة event listener على input مثلاً:
<input type="text" (keyup)="keyUpHandler($event)" />وبالـ component:
export class AppComponent {
keyUpHandler(event: KeyboardEvent) {
console.log('Key pressed:', event.key);
}
}الـ parentheses على الـ event name هي طريقة Angular للـ event binding. $event بيحمل الـ event object.
خلينا نبني component فيه steps counter مع increment وdecrement.
أول شيء، ننشئ الـ component:
ng generate component trackerبالـ tracker.component.ts:
import { signal } from '@angular/core';
export class TrackerComponent {
steps = signal(0);
increment() {
this.steps.update(previous => previous + 1);
}
decrement() {
if (this.steps() > 0) {
this.steps.update(previous => previous - 1);
}
}
reset() {
this.steps.set(0);
}
}الفرق بين set وupdate:
set - لما بتعرف القيمة الجديدة مباشرة (مثل reset)update - لما القيمة الجديدة معتمدة على القيمة السابقةبالـ HTML:
<div class="tracker">
<button (click)="decrement()">Decrement</button>
<span>{{ steps() }} steps</span>
<button (click)="increment()">Increment</button>
<button (click)="reset()">Reset</button>
</div>لإضافة أكثر من صفحة بالـ app، نستخدم Angular Router.
أول شيء، ننشئ صفحتين:
ng generate component home
ng generate component todosبعدين بالـ app.routes.ts نضيف الـ routes:
import { Routes } from '@angular/router';
export const routes: Routes = [
{
path: '',
pathMatch: 'full',
loadComponent: () =>
import('./home/home.component').then(c => c.HomeComponent),
},
{
path: 'todos',
loadComponent: () =>
import('./todos/todos.component').then(c => c.TodosComponent),
},
];الـ loadComponent بتعمل lazy loading للـ component - ما بيتحمل الا لما المستخدم يروح على هالـ route.
بالـ app.component.html نضيف <router-outlet>:
<nav>
<a routerLink="/">Home</a>
<a routerLink="/todos">Todos</a>
</nav>
<router-outlet></router-outlet>ولاستخدام routerLink، لازم تعمل import لـ RouterLink وRouterOutlet بالـ component:
import { RouterOutlet, RouterLink } from '@angular/router';
@Component({
imports: [RouterOutlet, RouterLink],
})
export class AppComponent {}RouterLink بتمنع الـ full page refresh عكس الـ href العادي.
Services بالـ Angular بتستخدم لعزل الـ logic عن الـ UI. كل شيء ما إله علاقة بالـ rendering ينحط بـ service.
لإنشاء service:
ng generate service services/todosimport { Injectable, signal } from '@angular/core';
import { Todo } from '../types/todo';
@Injectable({ providedIn: 'root' })
export class TodosService {
todoItems = signal<Todo[]>([
{ id: 1, userId: 1, title: 'Task 1', completed: false },
{ id: 2, userId: 1, title: 'Task 2', completed: true },
]);
}الـ { providedIn: 'root' } معناها الـ service متاحة لكل الـ app. لاستخدام الـ service بـ component:
import { inject } from '@angular/core';
import { TodosService } from '../services/todos.service';
export class TodosComponent {
todoService = inject(TodosService);
todos = computed(() => this.todoService.todoItems());
}ngOnInit بتشتغل مجرد ما الـ component ينعمل render:
import { OnInit } from '@angular/core';
export class TodosComponent implements OnInit {
ngOnInit() {
console.log('Component mounted!');
}
}لجلب data من API، أول شيء نفعّل HttpClient بالـ app.config.ts:
import { provideHttpClient, withFetch } from '@angular/common/http';
export const appConfig: ApplicationConfig = {
providers: [
provideHttpClient(withFetch()),
],
};الـ withFetch() بتخلي Angular تستخدم الـ browser's Fetch API بدل XMLHttpRequest.
بالـ service:
import { inject } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { catchError, throwError } from 'rxjs';
@Injectable({ providedIn: 'root' })
export class TodosService {
private http = inject(HttpClient);
fetchTodos() {
return this.http.get<Todo[]>('https://jsonplaceholder.typicode.com/todos').pipe(
catchError(error => {
console.error(error);
return throwError(() => error);
})
);
}
}وبالـ component نستدعي الـ service:
export class TodosComponent implements OnInit {
todos = signal<Todo[]>([]);
todoService = inject(TodosService);
ngOnInit() {
this.todoService.fetchTodos().subscribe(data => {
this.todos.set(data);
});
}
}لعرض قائمة بالـ template، نستخدم @for:
<ul>
@for (todo of todos(); track todo.id) {
<li>{{ todo.title }}</li>
}
</ul>الـ track نفس الـ key بالـ React - بيساعد Angular تميّز كل element عن الثاني وتحسّن الـ performance.
Directives بتخلينا نضيف behaviors على HTML elements. مثلاً، directive تعمل line-through على الـ todos المكتملة:
ng generate directive directives/highlight-completed-todoimport { Directive, ElementRef, effect, input } from '@angular/core';
@Directive({ selector: '[appHighlightCompletedTodo]' })
export class HighlightCompletedTodoDirective {
isCompleted = input(false);
private el = inject(ElementRef);
constructor() {
effect(() => {
this.el.nativeElement.style.textDecoration =
this.isCompleted() ? 'line-through' : 'none';
this.el.nativeElement.style.color =
this.isCompleted() ? 'green' : 'white';
});
}
}الـ effect بيشتغل كل ما الـ signal اللي بداخله تتغير.
لاستخدام الـ directive:
@for (todo of todos(); track todo.id) {
<li appHighlightCompletedTodo [isCompleted]="todo.completed">
<input type="checkbox" [checked]="todo.completed" />
<label>{{ todo.title }}</label>
</li>
}Pipes بتحوّل values بالـ template. Angular فيها built-in pipes جاهزة:
import { TitleCasePipe } from '@angular/common';
@Component({
imports: [TitleCasePipe],
})<!-- "task one" تصير "Task One" -->
{{ todo.title | titlecase }}فيكن كمان تبنوا custom pipe. مثلاً pipe لفلترة الـ todos:
ng generate pipe pipes/filter-todoimport { Pipe, PipeTransform } from '@angular/core';
import { Todo } from '../types/todo';
@Pipe({ name: 'filterTodo' })
export class FilterTodoPipe implements PipeTransform {
transform(todos: Todo[], searchBy: string): Todo[] {
return todos.filter(todo =>
todo.title.toLowerCase().includes(searchBy.toLowerCase())
);
}
}@for (todo of todos() | filterTodo: searchBy(); track todo.id) {
<li>{{ todo.title }}</li>
}لاستخدام two-way binding على input، نستخدم NgModel:
import { FormsModule } from '@angular/forms';
@Component({
imports: [FormsModule],
})<input type="text" [(ngModel)]="searchBy" placeholder="Search todos..." />هيكي أي تغيير على الـ input بيعمل update للـ variable، والعكس صحيح.
Angular framework قوي وكامل من البداية - فيه testing وHTTP وforms وrouting كلهم مضمونين فيه بدون ما تحتاج تنزّل packages إضافية.