# بناء تطبيق مصرفي الجزء 3: طرق جلب واستخدام البيانات ## اختبار ما قبل المحاضرة [اختبار ما قبل المحاضرة](https://ff-quizzes.netlify.app/web/quiz/45) ### المقدمة في جوهر كل تطبيق ويب توجد *البيانات*. يمكن أن تأخذ البيانات أشكالًا متعددة، ولكن الغرض الرئيسي منها دائمًا هو عرض المعلومات للمستخدم. مع تزايد تفاعلية وتعقيد تطبيقات الويب، أصبحت كيفية وصول المستخدم إلى المعلومات والتفاعل معها جزءًا أساسيًا من تطوير الويب. في هذا الدرس، سنتعلم كيفية جلب البيانات من الخادم بشكل غير متزامن، واستخدام هذه البيانات لعرض المعلومات على صفحة ويب دون إعادة تحميل HTML. ### المتطلبات الأساسية يجب أن تكون قد أنشأت [نموذج تسجيل الدخول والتسجيل](../2-forms/README.md) كجزء من تطبيق الويب لهذا الدرس. كما تحتاج إلى تثبيت [Node.js](https://nodejs.org) وتشغيل [واجهة برمجة التطبيقات للخادم](../api/README.md) محليًا للحصول على بيانات الحساب. يمكنك اختبار ما إذا كان الخادم يعمل بشكل صحيح عن طريق تنفيذ هذا الأمر في الطرفية: ```sh curl http://localhost:5000/api # -> should return "Bank API v1.0.0" as a result ``` --- ## AJAX وجلب البيانات تقوم مواقع الويب التقليدية بتحديث المحتوى المعروض عندما يختار المستخدم رابطًا أو يرسل بيانات باستخدام نموذج، عن طريق إعادة تحميل صفحة HTML بالكامل. في كل مرة تحتاج إلى تحميل بيانات جديدة، يقوم خادم الويب بإرجاع صفحة HTML جديدة تمامًا تحتاج إلى معالجتها بواسطة المتصفح، مما يقطع الإجراء الحالي للمستخدم ويحد من التفاعلات أثناء إعادة التحميل. يُطلق على هذا التدفق أيضًا اسم *تطبيق متعدد الصفحات* أو *MPA*.  عندما بدأت تطبيقات الويب تصبح أكثر تعقيدًا وتفاعلية، ظهرت تقنية جديدة تُسمى [AJAX (JavaScript غير المتزامن وXML)](https://en.wikipedia.org/wiki/Ajax_(programming)). تتيح هذه التقنية لتطبيقات الويب إرسال واسترجاع البيانات من الخادم بشكل غير متزامن باستخدام JavaScript، دون الحاجة إلى إعادة تحميل صفحة HTML، مما يؤدي إلى تحديثات أسرع وتفاعلات أكثر سلاسة. عند استلام بيانات جديدة من الخادم، يمكن أيضًا تحديث صفحة HTML الحالية باستخدام واجهة برمجة التطبيقات [DOM](https://developer.mozilla.org/docs/Web/API/Document_Object_Model). مع مرور الوقت، تطورت هذه الطريقة إلى ما يُعرف الآن باسم [*تطبيق الصفحة الواحدة* أو *SPA*](https://en.wikipedia.org/wiki/Single-page_application).  عندما تم تقديم AJAX لأول مرة، كانت واجهة برمجة التطبيقات الوحيدة المتاحة لجلب البيانات بشكل غير متزامن هي [`XMLHttpRequest`](https://developer.mozilla.org/docs/Web/API/XMLHttpRequest/Using_XMLHttpRequest). ولكن المتصفحات الحديثة تدعم الآن واجهة برمجة التطبيقات الأكثر ملاءمة وقوة [`Fetch API`](https://developer.mozilla.org/docs/Web/API/Fetch_API)، التي تستخدم الوعود (Promises) وتناسب بشكل أفضل التعامل مع بيانات JSON. > على الرغم من أن جميع المتصفحات الحديثة تدعم `Fetch API`، إذا كنت تريد أن يعمل تطبيق الويب الخاص بك على المتصفحات القديمة، فمن الجيد دائمًا التحقق من [جدول التوافق على caniuse.com](https://caniuse.com/fetch) أولاً. ### المهمة في [الدرس السابق](../2-forms/README.md) قمنا بتنفيذ نموذج التسجيل لإنشاء حساب. الآن سنضيف كودًا لتسجيل الدخول باستخدام حساب موجود، وجلب بياناته. افتح ملف `app.js` وأضف وظيفة `login` جديدة: ```js async function login() { const loginForm = document.getElementById('loginForm') const user = loginForm.user.value; } ``` نبدأ هنا باسترجاع عنصر النموذج باستخدام `getElementById()`، ثم نحصل على اسم المستخدم من الإدخال باستخدام `loginForm.user.value`. يمكن الوصول إلى كل عنصر تحكم في النموذج من خلال اسمه (المحدد في HTML باستخدام خاصية `name`) كخاصية للنموذج. على غرار ما فعلناه للتسجيل، سننشئ وظيفة أخرى لإجراء طلب إلى الخادم، ولكن هذه المرة لاسترجاع بيانات الحساب: ```js 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` لجلب البيانات بشكل غير متزامن من الخادم، ولكن هذه المرة لا نحتاج إلى أي معلمات إضافية بخلاف عنوان URL، حيث إننا نستعلم فقط عن البيانات. بشكل افتراضي، ينشئ `fetch` طلب HTTP من النوع [`GET`](https://developer.mozilla.org/docs/Web/HTTP/Methods/GET)، وهو ما نحتاجه هنا. ✅ `encodeURIComponent()` هي وظيفة تقوم بترميز الأحرف الخاصة لعناوين URL. ما هي المشكلات التي قد نواجهها إذا لم نستدعِ هذه الوظيفة واستخدمنا قيمة `user` مباشرة في عنوان URL؟ الآن لنقم بتحديث وظيفة `login` لاستخدام `getAccount`: ```js 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'); } ``` أولاً، بما أن `getAccount` هي وظيفة غير متزامنة، نحتاج إلى استخدام الكلمة المفتاحية `await` لانتظار نتيجة الخادم. كما هو الحال مع أي طلب إلى الخادم، علينا أيضًا التعامل مع حالات الخطأ. في الوقت الحالي، سنضيف فقط رسالة سجل لعرض الخطأ، وسنعود إليها لاحقًا. ثم علينا تخزين البيانات في مكان ما حتى نتمكن من استخدامها لاحقًا لعرض معلومات لوحة التحكم. بما أن المتغير `account` غير موجود بعد، سننشئ متغيرًا عامًا له في أعلى ملفنا: ```js let account = null; ``` بعد حفظ بيانات المستخدم في متغير، يمكننا الانتقال من صفحة *تسجيل الدخول* إلى *لوحة التحكم* باستخدام وظيفة `navigate()` التي لدينا بالفعل. أخيرًا، نحتاج إلى استدعاء وظيفة `login` عند إرسال نموذج تسجيل الدخول، عن طريق تعديل HTML: ```html