المشكلة التي يعرفها كل مطور Angular
\n\nهل سبق لك أن حاولت عرض محتوى كائن JavaScript عادي في قالب Angular باستخدام *ngFor وفجأة أدركت: "كيف يمكنني الحصول على كل من المفتاح والقيمة؟" ففي الواقع، *ngFor يعمل افتراضيًا فقط مع المصفوفات. والكائن ليس مصفوفة.
هذا السؤال هو واحد من أكثر الأسئلة شيوعًا على Stack Overflow (آلاف المشاهدات وعشرات الإجابات). يبحث المطورون ليس فقط عن حل، بل عن الحل الصحيح - الحل الذي لا يقتل أداء التطبيق، خاصة عندما يكون الكائن كبيرًا أو يتم تحديثه بشكل متكرر.
\n\nفي هذه المقالة، لن نقدم إجابة فقط. سنقوم بتحليل جميع الأساليب الرئيسية، من البسيطة إلى المتقدمة، وسنوضح كيفية كتابة كود يعمل بسرعة واستقرار. هيا بنا!
\n\nلماذا لا يعمل *ngFor مع الكائنات "خارج الصندوق"؟
\n\nالتوجيه *ngFor في Angular مصمم لـ الهياكل القابلة للتكرار (Iterable). المصفوفة هي هيكل قابل للتكرار. الكائن ليس كذلك. عندما تكتب:
<div *ngFor="let item of myObject">{{ item }}</div>\n\nسيصدر Angular خطأ: "Cannot find a differ supporting object '...'". Angular ببساطة لا يعرف كيفية تكرار الكائن.
\n\nولكن في الممارسة العملية، نحتاج غالبًا إلى عرض بيانات من كائن: الإعدادات، القواميس، استجابات API، حيث يكون للمفاتيح معنى (على سبيل المثال، { name: 'آنا', age: 25, city: 'موسكو' }).
أبسط طريقة: Object.keys() و Object.entries()
\n\nالإجابة الأكثر شيوعًا ووضوحًا على Stack Overflow هي تحويل الكائن إلى مصفوفة مباشرة في القالب أو في المكون.
\n\nالخيار 1: Object.keys() - الحصول على المفاتيح فقط
\n\n// في المكون (TypeScript)\nexport class MyComponent {\n myObject = { name: 'آنا', age: 25, city: 'موسكو' };\n}\n\n<!-- في القالب -->\n<div *ngFor="let key of objectKeys(myObject)">\n المفتاح: {{ key }}, القيمة: {{ myObject[key] }}\n</div>\n\nولكن هناك فارق بسيط: يجب أن تكون objectKeys متاحة في القالب. إما أن نجعلها طريقة في المكون، أو نضعها في خاصية منفصلة:
// المكون\nexport class MyComponent {\n myObject = { name: 'آنا', age: 25, city: 'موسكو' };\n objectKeys = Object.keys; // مجرد مرجع للدالة الأصلية\n}\n\nهذا يعمل، ولكنه يستدعي الدالة في كل مرة يتم فيها التحقق من التغييرات (change detection). إذا كان الكائن كبيرًا، فهذا يؤثر على الأداء.
\n\nالخيار 2: Object.entries() - المفاتيح والقيم معًا
\n\n// المكون\nexport class MyComponent {\n myObject = { name: 'آنا', age: 25, city: 'موسكو' };\n get entries() {\n return Object.entries(this.myObject);\n }\n}\n\n<!-- القالب -->\n<div *ngFor="let entry of entries">\n المفتاح: {{ entry[0] }}, القيمة: {{ entry[1] }}\n</div>\n\nأو مع التفكيك (أكثر قابلية للقراءة):
\n\n<div *ngFor="let [key, value] of entries">\n المفتاح: {{ key }}, القيمة: {{ value }}\n</div>\n\nالإيجابيات: بسيط، مفهوم، واضح.
\nالسلبيات: سيتم استدعاء طريقة entries في كل دورة كشف تغييرات. بالنسبة للكائنات الثابتة - لا بأس، أما للكائنات الديناميكية - فقد يصبح مشكلة.
الأداء: لماذا هذا مهم؟
\n\nتخيل أن الكائن الخاص بك يحتوي على 10,000 سجل. أو أنه يتم تحديثه كل 100 مللي ثانية (على سبيل المثال، بيانات من خادم في الوقت الفعلي). كل استدعاء لـ Object.entries() ينشئ مصفوفة جديدة. Angular في كل دورة كشف تغييرات سيرى مصفوفة "جديدة" وسيقوم بإعادة رسم القائمة بأكملها. هذا يؤدي إلى ارتعاش الواجهة، وفقدان التركيز على حقول الإدخال، والتباطؤ.
لذلك، بالنسبة للمشاريع الجادة، يجب استخدام التحسين