# بناء تطبيق مصرفي الجزء 3: طرق جلب واستخدام البيانات ## اختبار ما قبل المحاضرة [اختبار ما قبل المحاضرة](https://ff-quizzes.netlify.app/web/quiz/45) ### المقدمة في قلب كل تطبيق ويب توجد *البيانات*. يمكن أن تأخذ البيانات أشكالًا متعددة، ولكن الغرض الرئيسي منها دائمًا هو عرض المعلومات للمستخدم. مع تزايد تفاعلية وتعقيد تطبيقات الويب، أصبح كيفية وصول المستخدم إلى المعلومات والتفاعل معها جزءًا أساسيًا من تطوير الويب. في هذه الدرس، سنرى كيفية جلب البيانات من الخادم بشكل غير متزامن، واستخدام هذه البيانات لعرض المعلومات على صفحة ويب دون إعادة تحميل HTML. ### المتطلبات الأساسية يجب أن تكون قد أنشأت [نموذج تسجيل الدخول والتسجيل](../2-forms/README.md) كجزء من تطبيق الويب لهذا الدرس. كما تحتاج إلى تثبيت [Node.js](https://nodejs.org) وتشغيل [API الخادم](../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 الحالية باستخدام JavaScript باستخدام واجهة برمجة التطبيقات [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)، التي تستخدم الوعود وتناسب بشكل أفضل التعامل مع بيانات 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