| محور المقارنة التقنية | الاستهداف المباشر لعناصر DOM (ElementRef) | استهداف المكونات الفرعية (Component Reference) |
|---|---|---|
| الهدف الأساسي من الاستخدام | التفاعل البرمجي البحت مع عناصر HTML الأصلية (مثل التركيز أو القياسات) | الوصول إلى الخصائص والدوال التنفيذية داخل صنف المكون الفرعي |
| الأداة البرمجية المستخدمة | مزخرف @ViewChild أو دالة viewChild مع فئة ElementRef | مزخرف @ViewChild أو دالة viewChild مع صنف المكون (Component Class) |
| طبيعة التأثير الناتج | تغيير مظهر أو سلوك العنصر في المتصفح مباشرة متجاوزاً القوالب | تنفيذ مهام داخلية وتحديث حالة الابن البرمجية مباشرة من المكون الأب |
| مستوى الاقتران المعماري (Coupling) | اقتران منخفض ومقيد بالواجهة والخصائص البصرية للعنصر | اقتران أعلى يتطلب معرفة مسبقة ببنية دوال المكون الفرعي المستهدف |
نصل في هذا الدرس السابع من الفصل السابع إلى تقنية متقدمة وحاسمة في عمارة أنجولر المقدمة، ألا وهي الوصول لعناصر DOM والمكونات الفرعية (ViewChild). عندما تفرض الحاجة المعمارية على المكون أن يتفاعل بشكل مباشر مع عناصر واجهة المستخدم الأصلية (DOM Elements) أو يتحكم في دوال وخصائص داخل المكون الفرعي دون الاعتماد حصرياً على المدخلات والمخرجات التقليدية، فإن استخدام مُزخرف ViewChild (أو دالة viewChild الحديثة) يوفر جسراً مباشراً يربط صنف المكون بالعنصر أو الابن بكفاءة عالية.
عند الحاجة للتدخل البرمجي المباشر في عنصر HTML (مثل وضع المؤشر داخل حقل إدخال)، يتم استخدام ViewChild لالتقاط العنصر عبر متغير مرجعي (#referenceName) والوصول إلى خاصية nativeElement لتنفيذ الأمر بأمان.
يسمح التقاط المكون الفرعي عبر نوع الصنف الخاص بفتح قناة اتصال تنفيذية مباشرة، بحيث يستطيع المكون الأب تشغيل دوال الابن الداخلية دون الحاجة لمرور البيانات عبر شجرة المدخلات والمخرجات العادية.
| 1 | import { Component, signal } from '@angular/core'; |
| 2 | |
| 3 | @Component({ |
| 4 | selector: 'app-sub-status-box', |
| 5 | standalone: true, |
| 6 | template: ` |
| 7 | <div class="status-box"> |
| 8 | <p>{{ boxMessage() }}</p> |
| 9 | </div> |
| 10 | `, |
| 11 | styles: [` |
| 12 | .status-box { |
| 13 | background: #1e293b; |
| 14 | color: #38bdf8; |
| 15 | padding: 14px; |
| 16 | border-radius: 8px; |
| 17 | border: 1px solid #475569; |
| 18 | margin-top: 12px; |
| 19 | } |
| 20 | `] |
| 21 | }) |
| 22 | export class SubStatusBoxComponent { |
| 23 | boxMessage = signal<string>('الحالة الأولية: في انتظار التنشيط'); |
| 24 | |
| 25 | activateBox() { |
| 26 | this.boxMessage.set('تم تنشيط الصندوق بنجاح عبر ViewChild!'); |
| 27 | } |
| 28 | } |
| 1 | import { Component, ViewChild, ElementRef, AfterViewInit } from '@angular/core'; |
| 2 | import { SubStatusBoxComponent } from './sub-status-box.component'; |
| 3 | |
| 4 | @Component({ |
| 5 | selector: 'app-parent-dom-controller', |
| 6 | standalone: true, |
| 7 | imports: [SubStatusBoxComponent], |
| 8 | template: ` |
| 9 | <div class="parent-container"> |
| 10 | <h3>المكون الأب - تحكم الـ DOM و ViewChild</h3> |
| 11 | |
| 12 | <!-- عنصر DOM مستهدف للتركيز المباشر --> |
| 13 | <input #targetInput type="text" placeholder="اكتب هنا لتفعيل التركيز التلقائي..." /> |
| 14 | <br><br> |
| 15 | |
| 16 | <button (click)="executeDOMAndComponentActions()">تنفيذ العمليات البرمجية</button> |
| 17 | |
| 18 | <!-- المكون الفرعي المستهدف عبر ViewChild --> |
| 19 | <app-sub-status-box></app-sub-status-box> |
| 20 | </div> |
| 21 | `, |
| 22 | styles: [` |
| 23 | .parent-container { background: #0f172a; color: #f8fafc; padding: 20px; border-radius: 10px; } |
| 24 | input { padding: 8px; width: 70%; background: #1e293b; color: #fff; border: 1px solid #64748b; border-radius: 4px; } |
| 25 | button { padding: 8px 16px; background: #0ea5e9; color: white; border: none; border-radius: 4px; cursor: pointer; font-weight: bold; margin-top: 10px; display: block; } |
| 26 | `] |
| 27 | }) |
| 28 | export class ParentDomControllerComponent implements AfterViewInit { |
| 29 | // التقاط عنصر DOM أصلي |
| 30 | @ViewChild('targetInput') searchInput!: ElementRef<HTMLInputElement>; |
| 31 | |
| 32 | // التقاط المكون الفرعي بالكامل |
| 33 | @ViewChild(SubStatusBoxComponent) subBoxInstance!: SubStatusBoxComponent; |
| 34 | |
| 35 | ngAfterViewInit() { |
| 36 | // الواجهة أصبحت جاهزة للتفاعل المباشر |
| 37 | } |
| 38 | |
| 39 | executeDOMAndComponentActions() { |
| 40 | // 1. التفاعل المباشر مع عنصر الـ DOM (إعطاء التركيز وتغيير الإطار) |
| 41 | if (this.searchInput) { |
| 42 | this.searchInput.nativeElement.focus(); |
| 43 | this.searchInput.nativeElement.style.border = '2px solid #10b981'; |
| 44 | } |
| 45 | |
| 46 | // 2. استدعاء دالة المكون الفرعي مباشرة عبر مرجعه |
| 47 | if (this.subBoxInstance) { |
| 48 | this.subBoxInstance.activateBox(); |
| 49 | } |
| 50 | } |
| 51 | } |