Observables In ANGULAR 2+-
Observables In ANGULAR 2+
جدول المصطلحات:
مقدمة
المراقبات (Observables) في Angular هي حجر الأساس للتعامل مع البيانات غير المتزامنة والتفاعلات في التطبيقات الحديثة. تعتمد Angular بشكل كامل على مكتبة RxJS (Reactive Extensions for JavaScript) لتنفيذ هذا النمط التفاعلي.
ما هي المراقبات (Observables)؟
المراقبات هي تدفقات بيانات غير متزامنة تسمح بالاشتراك فيها لاستقبال قيم متعددة بمرور الوقت. تختلف عن الوعود (Promises) في قدرتها على إرجاع قيم متعددة بدلاً من قيمة واحدة.
إنشاء Observable بسيط
typescript
import { Observable } from 'rxjs';
// إنشاء Observable يدوياً
const customObservable = new Observable(subscriber => {
let count = 0;
const interval = setInterval(() => {
subscriber.next(count); // إرسال قيمة
count++;
if (count === 3) {
subscriber.error('حدث خطأ عند الرقم 3'); // إرسال خطأ
}
if (count === 5) {
subscriber.complete(); // إعلام بالاكتمال
clearInterval(interval);
}
}, 1000);
});
// الاشتراك في Observable
const subscription = customObservable.subscribe({
next: (value) => console.log('القيمة المستلمة:', value),
error: (err) => console.error('حدث خطأ:', err),
complete: () => console.log('اكتمل تدفق البيانات!')
});
// إلغاء الاشتراك (مهم لمنع تسرب الذاكرة)
setTimeout(() => {
subscription.unsubscribe();
console.log('تم إلغاء الاشتراك');
}, 7000);
أنواع الإشعارات في Observable
المراقب يرسل ثلاثة أنواع من الإشعارات:
1. Next (قادم)
الوظيفة: إرسال قيمة جديدة
التكرار: يمكن استدعاؤه صفر، مرة، أو عدة مرات
مثال: subscriber.next('مرحباً')
2. Error (خطأ)
الوظيفة: إرسال خطأ يوقف التنفيذ
التأثير: يؤدي إلى إنهاء الاشتراك تلقائياً
مثال: subscriber.error(new Error('حدث خطأ'))
3. Complete (مكتمل)
الوظيفة: إشعار بانتهاء تدفق البيانات
التأثير: لا يمكن إرسال قيم بعدها
مثال: subscriber.complete()
ملاحظة مهمة: بعد إرسال error أو complete، لا يمكن إرسال المزيد من القيم.
أمثلة عملية من المقال الأصلي
مثال 1: استخدام interval المدمج
typescript
import { Component, OnInit, OnDestroy } from '@angular/core';
import { Subscription, interval } from 'rxjs';
@Component({
selector: 'app-timer',
template: `<p>العداد: {{counter}}</p>`
})
export class TimerComponent implements OnInit, OnDestroy {
counter = 0;
private subscription: Subscription;
ngOnInit() {
this.subscription = interval(1000).subscribe({
next: (value) => {
this.counter = value;
console.log('القيمة:', value);
}
});
}
ngOnDestroy() {
// إلغاء الاشتراك لمنع تسرب الذاكرة
this.subscription.unsubscribe();
}
}
مثال 2: بناء Observable مخصص
typescript
// بناء Observable يدوي كما في الصورة الأصلية
const customIntervalObservable = new Observable(subscriber => {
let count = 0;
const intervalId = setInterval(() => {
subscriber.next(count);
count++;
if (count > 10) {
subscriber.complete();
clearInterval(intervalId);
}
}, 1000);
// دالة التنظيف عند إلغاء الاشتراك
return () => {
console.log('تم تنظيف الموارد');
clearInterval(intervalId);
};
});
استخدام Observable مع Angular Services
مثال مع HttpClient:
typescript
import { HttpClient } from '@angular/common/http';
import { Component, OnInit } from '@angular/core';
import { Observable } from 'rxjs';
interface User {
id: number;
name: string;
email: string;
}
@Component({
selector: 'app-user-list',
template: `
<ul>
<li *ngFor="let user of users$ | async">
{{user.name}} - {{user.email}}
</li>
</ul>
`
})
export class UserListComponent implements OnInit {
users$: Observable<User[]>;
constructor(private http: HttpClient) {}
ngOnInit() {
// HttpClient يعيد Observable
this.users$ = this.http.get<User[]>('https://api.example.com/users');
// الاشتراك التقليدي
this.http.get<User[]>('https://api.example.com/users').subscribe({
next: (users) => console.log('المستخدمون:', users),
error: (err) => console.error('خطأ:', err),
complete: () => console.log('اكتمل جلب البيانات')
});
}
}
الموضوعات (Subjects) - نوع خاص من Observables
typescript
import { Subject } from 'rxjs';
// إنشاء Subject
const messageBus = new Subject<string>();
// اشتراك متعدد
messageBus.subscribe(msg => console.log('المستخدم 1:', msg));
messageBus.subscribe(msg => console.log('المستخدم 2:', msg));
// إرسال قيمة (ستصل للمشتركين جميعاً)
messageBus.next('مرحباً بالجميع!');
messageBus.next('كيف الحال؟');
// إغلاق Subject
messageBus.complete();
// Subject خاص يرسل آخر قيمة للمشتركين الجدد
import { BehaviorSubject } from 'rxjs';
const userStatus = new BehaviorSubject<string>('غير متصل');
userStatus.subscribe(status => console.log('الحالة:', status));
userStatus.next('متصل'); // سيتم استلامها من المشترك الحالي
userStatus.subscribe(status => console.log('مشترك جديد:', status)); // سيستقبل 'متصل' فوراً
المشغلات (Operators) - تحويل وتصفية البيانات
typescript
import { of } from 'rxjs';
import { map, filter, tap, take } from 'rxjs/operators';
const numbers$ = of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
numbers$.pipe(
filter(num => num % 2 === 0), // أرقام زوجية فقط
map(num => num * 10), // ضرب في 10
tap(num => console.log('القيمة:', num)), // تنفيذ تأثير جانبي
take(3) // أخذ أول 3 قيم فقط
).subscribe(result => {
console.log('النتيجة النهائية:', result);
});
// الناتج:
// القيمة: 20
// النتيجة النهائية: 20
// القيمة: 40
// النتيجة النهائية: 40
// القيمة: 60
// النتيجة النهائية: 60
أفضل الممارسات في Angular
1. استخدام Async Pipe في القوالب
typescript
@Component({
selector: 'app-example',
template: `
<div *ngIf="user$ | async as user">
<h2>{{user.name}}</h2>
<p>{{user.email}}</p>
</div>
<ul>
<li *ngFor="let item of items$ | async">
{{item.name}}
</li>
</ul>
`
})
export class ExampleComponent {
user$ = this.http.get<User>('/api/user/1');
items$ = this.http.get<Item[]>('/api/items');
constructor(private http: HttpClient) {}
}
2. إلغاء الاشتراك تلقائياً
typescript
import { Component, OnDestroy } from '@angular/core';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
@Component({
selector: 'app-safe',
template: `...`
})
export class SafeComponent implements OnDestroy {
private destroy$ = new Subject<void>();
constructor() {
interval(1000)
.pipe(takeUntil(this.destroy$))
.subscribe(value => console.log(value));
}
ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
}
}
3. معالجة الأخطاء بشكل صحيح
typescript
import { Component, OnDestroy } from '@angular/core';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
@Component({
selector: 'app-safe',
template: `...`
})
export class SafeComponent implements OnDestroy {
private destroy$ = new Subject<void>();
constructor() {
interval(1000)
.pipe(takeUntil(this.destroy$))
.subscribe(value => console.log(value));
}
ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
}
}
مثال متكامل من الحياة الواقعية
typescript
import { Component, OnInit, OnDestroy } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Subject, interval, merge } from 'rxjs';
import {
takeUntil,
switchMap,
catchError,
startWith,
map
} from 'rxjs/operators';
interface StockPrice {
symbol: string;
price: number;
timestamp: Date;
}
@Component({
selector: 'app-stock-tracker',
template: `
<h2>تتبع أسعار الأسهم</h2>
<div *ngFor="let stock of stocks">
<p>{{stock.symbol}}: {{stock.price | currency}}</p>
<small>{{stock.timestamp | date:'medium'}}</small>
</div>
<button (click)="stopTracking()">إيقاف التتبع</button>
`
})
export class StockTrackerComponent implements OnInit, OnDestroy {
stocks: StockPrice[] = [];
private destroy$ = new Subject<void>();
constructor(private http: HttpClient) {}
ngOnInit() {
// تحديث الأسهم كل 5 ثوانٍ
interval(5000).pipe(
startWith(0), // البدء فوراً
switchMap(() => this.fetchStockPrices()),
takeUntil(this.destroy$)
).subscribe({
next: (prices) => this.stocks = prices,
error: (err) => console.error('خطأ في تتبع الأسهم:', err)
});
}
private fetchStockPrices() {
return this.http.get<StockPrice[]>('/api/stocks').pipe(
catchError(error => {
console.error('فشل جلب البيانات:', error);
return of([]); // إرجاع مصفوفة فارغة عند الخطأ
})
);
}
stopTracking() {
this.destroy$.next();
}
ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
}
}
المشاكل الشائعة والحلول:
المصادر
الخاتمة
المراقبات (Observables) هي أداة قوية في Angular تمكنك من التعامل مع البيانات غير المتزامنة والأحداث بشكل فعال. إتقانها يتطلب فهم:
الإنشاء و الاشتراك الأساسي
المشغلات (Operators) للتحويل والتصفية
الموضوعات (Subjects) للتواصل بين المكونات
أفضل الممارسات لمنع المشاكل الشائعة
باستخدام هذه المفاهيم، يمكنك بناء تطبيقات Angular أكثر كفاءة وسهولة في الصيانة.
ليست هناك تعليقات: