# بناء تطبيق مصرفي الجزء 3: طرق جلب واستخدام البيانات تخيل حاسوب المؤسسة في مسلسل ستار تريك - عندما يسأل الكابتن بيكار عن حالة السفينة، تظهر المعلومات فورًا دون أن يتوقف النظام أو يعيد بناء نفسه بالكامل. هذا التدفق السلس للمعلومات هو بالضبط ما نسعى لبنائه هنا من خلال جلب البيانات الديناميكية. في الوقت الحالي، تطبيقك المصرفي يشبه الصحيفة المطبوعة - مفيد ولكنه ثابت. سنقوم بتحويله إلى شيء يشبه مركز التحكم في ناسا، حيث تتدفق البيانات باستمرار وتُحدث في الوقت الفعلي دون أن تقاطع سير عمل المستخدم. ستتعلم كيفية التواصل مع الخوادم بشكل غير متزامن، التعامل مع البيانات التي تصل في أوقات مختلفة، وتحويل المعلومات الخام إلى شيء ذو معنى لمستخدميك. هذا هو الفرق بين العرض التوضيحي والبرمجيات الجاهزة للإنتاج. ## اختبار ما قبل المحاضرة [اختبار ما قبل المحاضرة](https://ff-quizzes.netlify.app/web/quiz/45) ### المتطلبات الأساسية قبل الغوص في جلب البيانات، تأكد من أن لديك هذه المكونات جاهزة: - **الدرس السابق**: أكمل [نموذج تسجيل الدخول والتسجيل](../2-forms/README.md) - سنبني على هذا الأساس. - **الخادم المحلي**: قم بتثبيت [Node.js](https://nodejs.org) و[تشغيل API الخادم](../api/README.md) لتوفير بيانات الحساب. - **اتصال API**: اختبر اتصال الخادم الخاص بك باستخدام هذا الأمر: ```bash curl http://localhost:5000/api # Expected response: "Bank API v1.0.0" ``` هذا الاختبار السريع يضمن أن جميع المكونات تتواصل بشكل صحيح: - يتحقق من أن Node.js يعمل بشكل صحيح على نظامك. - يؤكد أن خادم API نشط ويستجيب. - يتحقق من أن تطبيقك يمكنه الوصول إلى الخادم (مثل التحقق من الاتصال اللاسلكي قبل المهمة). --- ## فهم جلب البيانات في تطبيقات الويب الحديثة لقد تطورت طريقة تعامل تطبيقات الويب مع البيانات بشكل كبير خلال العقدين الماضيين. فهم هذا التطور سيساعدك على تقدير قوة التقنيات الحديثة مثل AJAX وFetch API ولماذا أصبحت أدوات أساسية لمطوري الويب. دعونا نستكشف كيف كانت تعمل المواقع التقليدية مقارنة بالتطبيقات الديناميكية والاستجابية التي نبنيها اليوم. ### التطبيقات متعددة الصفحات التقليدية (MPA) في الأيام الأولى للويب، كان كل نقرة تشبه تغيير القنوات على تلفزيون قديم - الشاشة تصبح فارغة، ثم تبدأ ببطء في عرض المحتوى الجديد. هذه كانت حقيقة التطبيقات الويب الأولى، حيث كل تفاعل يعني إعادة بناء الصفحة بالكامل من الصفر. ```mermaid sequenceDiagram participant User participant Browser participant Server User->>Browser: Clicks link or submits form Browser->>Server: Requests new HTML page Note over Browser: Page goes blank Server->>Browser: Returns complete HTML page Browser->>User: Displays new page (flash/reload) ``` ![تدفق التحديث في تطبيق متعدد الصفحات](../../../../translated_images/mpa.7f7375a1a2d4aa779d3f928a2aaaf9ad76bcdeb05cfce2dc27ab126024050f51.ar.png) **لماذا كان هذا النهج يبدو غير عملي:** - كل نقرة تعني إعادة بناء الصفحة بالكامل من الصفر. - المستخدمون كانوا يتعرضون لانقطاع في التفكير بسبب تلك الومضات المزعجة للصفحة. - اتصال الإنترنت كان يعمل بجهد إضافي لتحميل نفس الرأس والتذييل مرارًا وتكرارًا. - التطبيقات كانت تبدو وكأنها تصفح ملفات بدلاً من استخدام البرمجيات. ### التطبيقات أحادية الصفحة الحديثة (SPA) غيرت AJAX (JavaScript وXML غير المتزامن) هذا النموذج تمامًا. مثل التصميم المعياري لمحطة الفضاء الدولية، حيث يمكن لرواد الفضاء استبدال مكونات فردية دون إعادة بناء الهيكل بالكامل، يسمح AJAX بتحديث أجزاء محددة من صفحة الويب دون إعادة تحميل كل شيء. على الرغم من أن الاسم يشير إلى XML، إلا أننا نستخدم JSON في الغالب اليوم، لكن المبدأ الأساسي يبقى كما هو: تحديث فقط ما يحتاج إلى التغيير. ```mermaid sequenceDiagram participant User participant Browser participant JavaScript participant Server User->>Browser: Interacts with page Browser->>JavaScript: Triggers event handler JavaScript->>Server: Fetches only needed data Server->>JavaScript: Returns JSON data JavaScript->>Browser: Updates specific page elements Browser->>User: Shows updated content (no reload) ``` ![تدفق التحديث في تطبيق أحادي الصفحة](../../../../translated_images/spa.268ec73b41f992c2a21ef9294235c6ae597b3c37e2c03f0494c2d8857325cc57.ar.png) **لماذا التطبيقات أحادية الصفحة تبدو أفضل بكثير:** - يتم تحديث فقط الأجزاء التي تغيرت بالفعل (ذكي، أليس كذلك؟). - لا مزيد من الانقطاعات المزعجة - يبقى المستخدمون في تدفقهم. - بيانات أقل تنتقل عبر الشبكة تعني تحميل أسرع. - كل شيء يبدو سريعًا واستجابياً، مثل التطبيقات على هاتفك. ### التطور إلى Fetch API الحديث توفر المتصفحات الحديثة [`Fetch` API](https://developer.mozilla.org/docs/Web/API/Fetch_API)، الذي يحل محل [`XMLHttpRequest`](https://developer.mozilla.org/docs/Web/API/XMLHttpRequest/Using_XMLHttpRequest) القديم. مثل الفرق بين تشغيل التلغراف واستخدام البريد الإلكتروني، يستخدم Fetch API الوعود لكتابة كود غير متزامن بشكل أنظف ويتعامل مع JSON بشكل طبيعي. | الميزة | XMLHttpRequest | Fetch API | |---------|----------------|----------| | **الصياغة** | معقدة تعتمد على الاستدعاءات | تعتمد على الوعود بشكل نظيف | | **التعامل مع JSON** | يتطلب التحليل اليدوي | طريقة `.json()` مدمجة | | **التعامل مع الأخطاء** | معلومات محدودة عن الأخطاء | تفاصيل شاملة عن الأخطاء | | **الدعم الحديث** | توافق مع الإصدارات القديمة | وعود ES6+ وasync/await | > 💡 **توافق المتصفح**: أخبار جيدة - يعمل Fetch API في جميع المتصفحات الحديثة! إذا كنت مهتمًا بالإصدارات المحددة، [caniuse.com](https://caniuse.com/fetch) يحتوي على القصة الكاملة للتوافق. > **الخلاصة:** - يعمل بشكل رائع في Chrome، Firefox، Safari، وEdge (بشكل أساسي في كل مكان يتواجد فيه المستخدمون). - فقط Internet Explorer يحتاج إلى مساعدة إضافية (وبصراحة، حان الوقت للتخلي عن IE). - يجهزك بشكل مثالي لأنماط async/await الأنيقة التي سنستخدمها لاحقًا. ### تنفيذ تسجيل الدخول واسترجاع البيانات الآن دعونا ننفذ نظام تسجيل الدخول الذي يحول تطبيقك المصرفي من عرض ثابت إلى تطبيق وظيفي. مثل بروتوكولات المصادقة المستخدمة في المنشآت العسكرية الآمنة، سنقوم بالتحقق من بيانات اعتماد المستخدم ثم توفير الوصول إلى بياناته المحددة. سنقوم ببناء هذا تدريجيًا، بدءًا من المصادقة الأساسية ثم إضافة قدرات جلب البيانات. #### الخطوة 1: إنشاء أساس وظيفة تسجيل الدخول افتح ملف `app.js` وأضف وظيفة `login` جديدة. هذه الوظيفة ستتعامل مع عملية مصادقة المستخدم: ```javascript async function login() { const loginForm = document.getElementById('loginForm'); const user = loginForm.user.value; } ``` **لنقم بتفصيل هذا:** - الكلمة المفتاحية `async`؟ تخبر JavaScript "مرحبًا، قد تحتاج هذه الوظيفة إلى الانتظار لبعض الوقت". - نحن نحصل على النموذج من الصفحة (ليس هناك شيء معقد، فقط العثور عليه بواسطة معرفه). - ثم نقوم بسحب ما كتبه المستخدم كاسم مستخدم. - إليك خدعة رائعة: يمكنك الوصول إلى أي إدخال نموذج بواسطة خاصية `name` - لا حاجة لاستدعاءات إضافية لـ getElementById! > 💡 **نمط الوصول إلى النموذج**: يمكن الوصول إلى كل عنصر تحكم في النموذج بواسطة اسمه (المحدد في HTML باستخدام خاصية `name`) كخاصية لعنصر النموذج. هذا يوفر طريقة نظيفة وسهلة للحصول على بيانات النموذج. #### الخطوة 2: إنشاء وظيفة جلب بيانات الحساب بعد ذلك، سنقوم بإنشاء وظيفة مخصصة لاسترجاع بيانات الحساب من الخادم. هذا يتبع نفس النمط مثل وظيفة التسجيل الخاصة بك ولكنه يركز على استرجاع البيانات: ```javascript async function getAccount(user) { try { const response = await fetch('//localhost:5000/api/accounts/' + encodeURIComponent(user)); return await response.json(); } catch (error) { return { error: error.message || 'Unknown error' }; } } ``` **ما يحققه هذا الكود:** - **يستخدم** Fetch API الحديث لطلب البيانات بشكل غير متزامن. - **يبني** عنوان طلب GET مع معلمة اسم المستخدم. - **يطبق** `encodeURIComponent()` للتعامل بأمان مع الأحرف الخاصة في عناوين URL. - **يحول** الاستجابة إلى صيغة JSON لتسهيل التعامل مع البيانات. - **يتعامل** مع الأخطاء بشكل جيد عن طريق إرجاع كائن خطأ بدلاً من التعطل. > ⚠️ **ملاحظة أمان**: وظيفة `encodeURIComponent()` تتعامل مع الأحرف الخاصة في عناوين URL. مثل أنظمة التشفير المستخدمة في الاتصالات البحرية، تضمن وصول رسالتك كما هو مقصود، مما يمنع الأحرف مثل "#" أو "&" من أن يتم تفسيرها بشكل خاطئ. > **لماذا هذا مهم:** - يمنع الأحرف الخاصة من كسر عناوين URL. - يحمي من هجمات التلاعب بعناوين URL. - يضمن أن الخادم يتلقى البيانات المقصودة. - يتبع ممارسات البرمجة الآمنة. #### فهم طلبات HTTP GET إليك شيء قد يفاجئك: عندما تستخدم `fetch` بدون أي خيارات إضافية، فإنه ينشئ تلقائيًا طلب [`GET`](https://developer.mozilla.org/docs/Web/HTTP/Methods/GET). هذا مثالي لما نقوم به - طلب من الخادم "مرحبًا، هل يمكنني رؤية بيانات حساب هذا المستخدم؟" فكر في طلبات GET كطلب استعارة كتاب من المكتبة - تطلب رؤية شيء موجود بالفعل. طلبات POST (التي استخدمناها للتسجيل) تشبه تقديم كتاب جديد ليتم إضافته إلى المجموعة. | طلب GET | طلب POST | |-------------|-------------| | **الغرض** | استرجاع البيانات الموجودة | إرسال بيانات جديدة إلى الخادم | | **المعلمات** | في مسار/سلسلة استعلام URL | في جسم الطلب | | **التخزين المؤقت** | يمكن تخزينه مؤقتًا بواسطة المتصفحات | عادةً لا يتم تخزينه مؤقتًا | | **الأمان** | مرئي في عنوان URL/السجلات | مخفي في جسم الطلب | #### الخطوة 3: جمع كل شيء معًا الآن الجزء الممتع - دعونا نربط وظيفة جلب الحساب بعملية تسجيل الدخول. هنا حيث كل شيء يتكامل: ```javascript async function login() { const loginForm = document.getElementById('loginForm'); const user = loginForm.user.value; const data = await getAccount(user); if (data.error) { return console.log('loginError', data.error); } account = data; navigate('/dashboard'); } ``` تتبع هذه الوظيفة تسلسلًا واضحًا: - استخراج اسم المستخدم من إدخال النموذج. - طلب بيانات حساب المستخدم من الخادم. - التعامل مع أي أخطاء تحدث أثناء العملية. - تخزين بيانات الحساب والانتقال إلى لوحة التحكم عند النجاح. > 🎯 **نمط Async/Await**: بما أن `getAccount` هي وظيفة غير متزامنة، نستخدم الكلمة المفتاحية `await` لإيقاف التنفيذ حتى يستجيب الخادم. هذا يمنع الكود من الاستمرار مع بيانات غير معرفة. #### الخطوة 4: إنشاء مكان لبياناتك تطبيقك يحتاج إلى مكان لتذكر معلومات الحساب بمجرد تحميلها. فكر في هذا كمكان ذاكرة قصيرة المدى لتطبيقك - مكان للحفاظ على بيانات المستخدم الحالي في متناول اليد. أضف هذا السطر في أعلى ملف `app.js`: ```javascript // This holds the current user's account data let account = null; ``` **لماذا نحتاج هذا:** - يحافظ على بيانات الحساب متاحة من أي مكان في تطبيقك. - البدء بـ `null` يعني "لا أحد مسجل الدخول بعد". - يتم تحديثه عندما يسجل أحدهم الدخول أو التسجيل بنجاح. - يعمل كمصدر واحد للحقيقة - لا يوجد ارتباك حول من سجل الدخول. #### الخطوة 5: ربط النموذج الخاص بك الآن دعونا نربط وظيفة تسجيل الدخول الجديدة الخاصة بك بنموذج HTML الخاص بك. قم بتحديث علامة النموذج الخاصة بك مثل هذا: ```html
``` **ما الذي يفعله هذا التغيير الصغير:** - يوقف النموذج من القيام بسلوكه الافتر للمحتوى الأكثر تعقيدًا، اجمع بين [`document.createElement()`](https://developer.mozilla.org/docs/Web/API/Document/createElement) وطريقة [`append()`](https://developer.mozilla.org/docs/Web/API/ParentNode/append): ```javascript // Safe way to create new elements const transactionItem = document.createElement('div'); transactionItem.className = 'transaction-item'; transactionItem.textContent = `${transaction.date}: ${transaction.description}`; container.append(transactionItem); ``` **فهم هذا النهج:** - **إنشاء** عناصر DOM جديدة برمجياً - **الحفاظ** على التحكم الكامل في خصائص العناصر ومحتواها - **السماح** بإنشاء هياكل عناصر متداخلة ومعقدة - **الحفاظ** على الأمان بفصل الهيكل عن المحتوى > ⚠️ **اعتبارات الأمان**: بينما يظهر [`innerHTML`](https://developer.mozilla.org/docs/Web/API/Element/innerHTML) في العديد من الدروس، يمكنه تنفيذ النصوص البرمجية المضمنة. مثل بروتوكولات الأمان في CERN التي تمنع تنفيذ الأكواد غير المصرح بها، فإن استخدام `textContent` و `createElement` يوفر بدائل أكثر أمانًا. > **مخاطر استخدام innerHTML:** - تنفيذ أي علامات `