| نوع الإشارة البرمجية | طريقة التعريف والاستخدام | الغرض البرمجي والخصائص الأساسية |
|---|---|---|
| الإشارة القابلة للكتابة | signal(value) | تخزين وتعديل حالة أساسية بشكل مباشر باستخدام دوال set أو update |
| الإشارة المحسوبة | computed(() => expression) | استنتاج وتوليد بيانات مشتقة تلقائياً من إشارات أخرى دون إمكانية التعديل المباشر عليها |
| الدوال العادية (Methods) | استدعاء دالة كـ function() في القالب | تعيد حساب قيمتها في كل مرة يتم فيها فحص المكون دون حفظ أو تخزين مؤقت للناتج |
نصل في هذا الدرس إلى مستوى متقدم ومهم للغاية في نظام الإشارات ضمن إصدار Angular 22.1, ألا وهو الإشارات المحسوبة (Computed Signals). عندما ترغب في بناء بيانات جديدة تعتمد كلياً على قيم إشارات أخرى وتتحدث تلقائياً كلما تغيرت تلك القيم، فإن الإشارات المحسوبة هي الحل الهندسي الأمثل. تمنحك هذه الخاصية كفاءة استثنائية في الأداء من خلال تقنية التخزين المؤقت (Memoization)، حيث لا يتم إعادة حساب القيمة إلا إذا تغيرت الإشارات الاعتمادية المرتبطة بها حقاً.
الإشارات المحسوبة لا تُنفذ كودها فور إنشائها، بل تنتظر حتى يتم طلب قيمتها. والأهم من ذلك، أنها تقوم بتخزين الناتج مؤقتاً (Cached)، فإذا تم استدعاؤها عدة مرات دون أن تتغير الإشارات الأصلية التي تعتمد عليها، فإنها تعيد الناتج المخزن مباشرة دون إعادة تنفيذ العمليات الحسابية بداخلها، مما يوفر أداءً جباراً.
بما أن الإشارات المحسوبة هي قيم مشتقة ومستنتاجة من إشارات أخرى، فهي غير قابلة للكتابة المباشرة (لا تحتوي على دوال set أو update)، وقيمتها تتغير حصراً وبشكل آلي استجابة لتغير الإشارات الاعتمادية التي تبنى عليها.
| 1 | import { Component, signal, computed } from '@angular/core'; |
| 2 | |
| 3 | @Component({ |
| 4 | selector: 'app-computed-signal-demo', |
| 5 | standalone: true, |
| 6 | template: ` |
| 7 | <div class="cart-box"> |
| 8 | <h3>حاسبة سلة المشتريات</h3> |
| 9 | <p>سعر الوحدة: <strong>{{ itemPrice() }} جنيه</strong></p> |
| 10 | <p>الكمية المطلوبة: <strong>{{ quantity() }}</strong></p> |
| 11 | |
| 12 | <!-- استدعاء الإشارة المحسوبة كدالة تماماً مثل الإشارات العادية --> |
| 13 | <hr> |
| 14 | <p>السعر الإجمالي النهائي: <strong>{{ totalPrice() }} جنيه</strong></p> |
| 15 | |
| 16 | <button (click)="increaseQuantity()">زيادة الكمية</button> |
| 17 | <button (click)="decreaseQuantity()">تقليل الكمية</button> |
| 18 | </div> |
| 19 | ` |
| 20 | }) |
| 21 | export class ComputedSignalDemoComponent { |
| 22 | // إشارات أساسية قابلة للكتابة |
| 23 | itemPrice = signal(150); |
| 24 | quantity = signal(3); |
| 25 | |
| 26 | // إشارة محسوبة تستنتج السعر الإجمالي تلقائياً عند تغير السعر أو الكمية |
| 27 | totalPrice = computed(() => { |
| 28 | return this.itemPrice() * this.quantity(); |
| 29 | }); |
| 30 | |
| 31 | increaseQuantity() { |
| 32 | this.quantity.update(q => q + 1); |
| 33 | } |
| 34 | |
| 35 | decreaseQuantity() { |
| 36 | this.quantity.update(q => (q > 1 ? q - 1 : 1)); |
| 37 | } |
| 38 | } |
| 1 | import { Component, signal, computed } from '@angular/core'; |
| 2 | import { CommonModule } from '@angular/common'; |
| 3 | import { FormsModule } from '@angular/forms'; |
| 4 | |
| 5 | @Component({ |
| 6 | selector: 'app-filtered-list-demo', |
| 7 | standalone: true, |
| 8 | imports: [CommonModule, FormsModule], |
| 9 | template: ` |
| 10 | <div class="filter-container"> |
| 11 | <h3>البحث في قائمة المنتجات</h3> |
| 12 | |
| 13 | <input type="text" [(ngModel)]="searchQuery" placeholder="ابحث عن منتج..."> |
| 14 | |
| 15 | <ul> |
| 16 | <!-- عرض النتائج المستنتجة من الإشارة المحسوبة --> |
| 17 | @for (product of filteredProducts(); track product) { |
| 18 | <li>{{ product }}</li> |
| 19 | } |
| 20 | </ul> |
| 21 | </div> |
| 22 | ` |
| 23 | }) |
| 24 | export class FilteredListDemoComponent { |
| 25 | // إشارة تحتوي على كافة المنتجات |
| 26 | allProducts = signal(['شاشة ذكية 4K', 'لوحة مفاتيح ميكانيكية', 'سماعات رأس لاسلكية', 'فأرة احترافية للألعاب', 'شاشة عرض محمولة']); |
| 27 | |
| 28 | // متغير البحث العادي أو إشارة البحث |
| 29 | searchQuery = ''; |
| 30 | |
| 31 | // إشارة محسوبة تقوم بفلترة القائمة لحظياً بناءً على ما يكتبه المستخدم |
| 32 | filteredProducts = computed(() => { |
| 33 | const query = this.searchQuery.toLowerCase(); |
| 34 | return this.allProducts().filter(product => product.toLowerCase().includes(query)); |
| 35 | }); |
| 36 | } |