لوحة المتصدرين
المحتوى الأكثر حصولًا على سمعة جيدة
المحتوى الأعلى تقييمًا في 07/16/21 في كل الموقع
-
يمكنك أن تقوم بملئ الفراغات في المصفوفة من خلال دالة zip_longest من مكتبة itertools كالتالي: import itertools np.array(list(itertools.zip_longest(*v, fillvalue=0))).T """ Output: array([[1, 0], [1, 2]]) """ بهذه الطريقة يمكنك أن تقوم بمليء المصفوفة بقيمة إفتراضية (صفر في هذه الحالة) في الأجزاء الناقصة. ملاحظة: إن كنت تستعمل Python الإصدار 2 فسيكون اسم الدالة هو izip_longest وليس zip_longest1 نقطة
-
1 نقطة
-
لا يوجد اي مشكلة في الكود, وهو يعمل بشكل جيد, فقط قم باستخدام الدالة وسوف تعمل, ربما البرنامج يقوم بعرض هذا الخط بسبب أمر ما, ولكن لا تقلق الكود يعمل ولا مشكلة فيه1 نقطة
-
إليك بعض القواعد عند كتابة كود Javascript داخل صفحة HTML 1. كود الجافاسكربت يجب أن يكون بين وسم script كما في الكود <script> // Javascript here </script> 2. أسماء الدوال، المتغيرات، وأيضاً الكلاسات يجب أن لا تحتوي على مسافات أو - هذه الأكواد غير صحيحة function say Hello() { // اسم غير صحيح يحتوي على مسافة console.log('Hello') } function say-Hello() { // اسم غير صحيح يحتوي على - console.log('Hello') } 3. أيضاً أسماء الدوال، المتغيرات، وأيضاً الكلاسات يجب أن لا يبدأوا بأرقام function 1Hello() { // اسم غير صحيح يبدأ برقم console.log('Hello') } هذه الأكواد غير صحيحة الآن حاول اتباع القواعد السابقة وسيشتغل الكود، انتظر ردك إذا كان هناك أي مشاكل أخرى1 نقطة
-
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name="description"content="welcome to my website"> <title>table</title> <link rel="stylesheet" href="style.css"> <script> function sayHello() { alert("Hello Html"); } </script> <style> </style> </head> <body> <form name="register" <LabeL>username</LabeL> <input type="text" name="username" maxlength="10" > <br> <LabeL>password</LabeL> <input type="password" name="password"> <br> <label>upload your cv</label> <input type="file"> <br> <input type="checkbox"> <Label>remmber name</Label> <br> <input type="button" name="Say Hello" value="Say Hello"> <input type="submit" value="Sign Up"> <input type="reset" value="restor default"> </form> </body> </html>1 نقطة
-
1 نقطة
-
<script> function sayHello() { alert("Hello Html"); } </script>1 نقطة
-
لقد أرفقت الجواب في التعليق السابق, لقد وضعت كود جافاسكربت بداخل العنصر style ولذلك يظهر الخطأ, ارجو منك نقله بداخل العنصر script1 نقطة
-
</script> <style> function say Hello() { alert("Hello HTML"); } </style> اسف على هدا الخطا1 نقطة
-
لاحظ أنك تضع الدالة بداخل العنصر style وهذا خاطئ, ارجو منك نقلها بداخل العنصر script1 نقطة
-
هذا بسبب أنك استخدمت الشرطة الخاطئة, هناك فرق بين الشرطة السفلية "_" والشرطة العلوية "-" يمكنك استخدام الشرطة السفلية باستخدام نفس الزر الخاص بالشرطة العلوية ولكن مع الضغط على زر shift1 نقطة
-
1 نقطة
-
يبدو أن هناك مسافة بين كلمتي say و hello وهذا خاطئ ومنافٍ لقواعد كتابة كود جافاسكربت, حيث يمكنك كتابة الدالة كالتالي function say_hello(){ ^^^^^^^^^ alert('hello html'); } كما رأيت يمكنك وضع شرطة سفلية بين الكلمتين "_", او يمكنك وصلهما مع بعضهما مع كتباة الحرف الأول من الكلمثة الثانية بشكل كبير كما يأتي function sayHello(){ ^^^^^^^^ alert('hello html'); }1 نقطة
-
السلام عليكم كنت ارفع مشروع يوميا بعد اجراء التعديل عليه على git لكن اليوم الملفات لا ترفع و يظهر الخطئ التالي ! [rejected] master -> master (fetch first) error: failed to push some refs to 'https://github.com/HASHAFAI/Tour-company-website.git' hint: Updates were rejected because the remote contains work that you do hint: not have locally. This is usually caused by another repository pushing hint: to the same ref. You may want to first integrate the remote changes hint: (e.g., 'git pull ...') before pushing again. hint: See the 'Note about fast-forwards' in 'git push --help' for details. ما السبب1 نقطة
-
إذا كان المشروع الخاص بك يحتوي على أكثر من branch ستحتاج لتحديد اسم ال branch الذي تجلب البيانات منه مثلًا git pull origin develop( branch اسم ال ) // git pull origin ( branch اسم ال ) إذا لم تنجح يمكنك استخدام الأمر التالي: git branch --set-upstream-to=origin/develop( branchاسم ال ) // git branch --set-upstream-to=origin/( branchاسم ال ) وبعدها التجربة مرة أخرى1 نقطة
-
رسالة الخطأ تخبرك أن المستودع البعيد الخاص بالمشروع يحتوي على تعديلات ليست موجودة لديك محليًا لذلك ستحتاج لجلب هذه التعديلات من المستودع باستخدام الأمر git pull و بعدها يمكنك رفع التعديلات الجديدة التي قمت بها.1 نقطة
-
<style> table,td,th{border:1px solid #676767} table {width:700px} tfoot td {font-weight: bold;text-align:center} thead td {font-weight:ext-align:center;color:#00F} </style>1 نقطة
-
لاحظ في التنسيق التالي أنك أخطأت في كتابته thead td {font-weight:ext-align:center;color:#00F} فيجب أن تعطي قيمة لخاصية font-weight , و ايضا يجب كتابة اسم الخاصية بشكل صحيح في text-align فيكون التنسيق بهذا الشكل thead td { font-weight:bold; text-align:center; color:#00F; } ثم لاحظ جيداً أنه لا يوجد وسم thead, tfoot في جدولك لذلك يجب عليك وضعها لتعمل التنسيقات أو إزالتها لتكون التنسيقات بهذا الشكل table td { font-weight: bold; text-align:center; } table td { font-weight:bold; text-align:center; color:#00F; } أو بإضافة tfoot, thead تكون التنسيقات tfoot td { font-weight: bold; text-align:center; } thead td { font-weight:bold; text-align:center; color:#00F; } فيكون كامل الكود الخاص بالجدول <table> <caption>This is Our Hosting Plan</caption> <thead> <tr> <td font-weight: bold;text-align:center;color:#00F>plan0</td> <td>plan1</td> <td>plan2</td> <td>plan3</td> <td>plan4</td> </tr> </thead> <tfoot> <tr> <td>HradDisk20GB</td> <td>HradDisk40GB</td> <td>HradDisk60GB</td> <td>HradDisk80GB</td> <td>HradDisk90GB</td> </tr> <tr> <td>5Databass</td> <td>6Databass</td> <td>4Databass</td> <td>3Databass</td> <td>2Databass</td> </tr> <tr> <td>3Ftp</td> <td>4Ftp</td> <td>5Ftp</td> <td>6Ftp</td> <td>7Ftp</td> </tr> <tr> <td>50$</td> <td>60$</td> <td>70$</td> <td>80$</td> <td>90$</td> </tr> </tfoot> </table>1 نقطة
-
انا احاول ان انشى جدول وقمت بانشاء الجدول بنجاح ولكن لا يمكننى تصمييم الجدول بشكل افضل لان الاكواد فى ملف cc لا يتم تفعيلها مع ان كتابة الاكواد صحيحة1 نقطة
-
كيف استطيع انفذ برنامج محاسبي وانا لا اعرف في المحاسبة نهائياً والدورة المستندية فكيف ممكن اقوم بذلك هل يوجد خريطة توضح لي ذلك اتبعها في التنفيذ او طريقه تقترحونها1 نقطة
-
السلام عليكم ورحمة الله وبركاته اتمنى احد يفيدني بسرح مختصر عن ماهو URI في جافا، ماهي استخداماته باختصار؟ وشكرا لكم1 نقطة
-
URI هو اختصار لـ: معرف المورد الموحد (Uniform Resource Identifier) وهو عبارة عن عنوان فريد يستخدم لتحديد المحتوى على الشبكة مثل مقطع فيديو، صفحة ويب،.. إلخ. ومن أهم الاختلافات بينه وبين URL (Uniform Resource Locator) باختصار هو أن URL بالإضافة لذلك يقوم بتحديد أين يوجد هذا المحتوى بالتحديد وكيفية الوصول إليه ومواصفات بروتوكول الاتصال. فإذاً وبشكل عام كل URL هو جزء من URI. أما بالنسبة لجافا أو أي لغة برمجة أخرى، قد تحتاج للتعامل مع محتويات موجودة على الشبكة، ويسهّل لك الصف URI الموجود في جافا من خلال java.net.URI تحقيق ذلك. وقد ترى الاختلاف من خلال مثال بسيط في جافا باستخدام URL أو URI كالتالي: كما يتيح لك الصف URI استخدام العديد من الخصائص مثل scheme، اسم المستخدم وكلمة المرور، authority التحقق وأيضاً إضافة استعلامات من خلال هذا الرابط نفسه، بحيث يمكنك جمعها كلها في نفس الرابط أو تقسيمها إلى متغيرات منفصلة عن بعضها، مثال: public void createURI() throws Exception { URI example1 = new URI( "scheme://user:password@authority:80" + "/path?query"); URI example2 = new URI( "scheme", "user:password", "authority", 80, "/path", "query"); assertEquals(example1.getScheme(), example2.getScheme()); assertEquals(example1.getPath(), example2.getPath()); } كما يمكنك استخدام بعض التوابع مثل getScheme و getPath وغيرها من التوابع التي يوفرها الصف URI في جافا. أما استخدام URL ضمن جافا فهو مختلف بعض الشيء، مثال: public void createURL() throws Exception { URL ex = new URL( "http://anywebsitehere.com"); URL ex2 = new URL("http", "somehost", 80, "/path/to/file"); assertEquals(ex.getHost(), ex2.getHost()); assertEquals(ex.getPath(), ex2.getPath()); } وأيضاً يمكنك بأي وقت التحويل من URI إلى URL وبالعكس باستخدام التوابع التالية: URL toURL = uri.toURL(); URI toURI = url.toURI();1 نقطة
-
يمكنك القيام بإعادة كتابة الاكواد مرة اخرى ولكن لوحدك ومن غير أي مساعدة, فمثلا يمكنك بناء form واستخدام جميع أنواع ال input كتدريب لك, يمكنك تخيل موقع ما من غير تنسيق ومن دون استخدام css في مخيلتك وبناءه فقط باستخدام عناصر html, فمثلا يمكنك استخدام عناصر مثل nav, header لبناء رأس الصفحة ومن ثم يمكنك استخدام عناصر مثل section , div , p لبناء جسم الصفحة واخيرا عنصر footer لبناء ذيل الصفحة, يمكنك أيضا البحث عن أسئلة بنظام quiz ل html وان تختبر معلوماتك فيها وسوف تجد الكثير عن الامر, بطبيعة الحال لن تجد تطبيقات بشكل فردي عن html لأنه لا بد من استخدام css معها لانتاج موقع جميل ومنسق, واذا كنت قلقا من الانتقال الى css لكي لا تنسى html فلا تقلق لانك سوف تستخدم html أكثر من السابق وبالتالي لن تنساها1 نقطة
-
أولاً أحب أن ألفت الانتباه إلى أن الأمر غير مرتبط ب pandas فحتى مع المصفوفات العادية سيسلك نفس السلوك، صحيح أن الأمر قد يربك قليلاً كون أي شخص سيتوقع للمرة الأولى أن axis=1 يجب أن تحسب المتوسط للعمود وليس للسطر كما يحدث، لكن الأمر هنا مختلف ف axis= 0 تشير إلى التجميع على طول الصفوف أما 1 تشير إلى التجميع على طول الأعمدة بشكل أبسط يمكنك القول: axis:يحدد المحور الذي يتم من خلاله حساب المتوسط. المحور 0: سيعمل على جميع الصفوف في كل عمود. أما المحور 1: يعمل على جميع الأعمدة في كل صف. وبشكل افتراضي يحسب المتوسط على كل المصفوفة (عند ضبطه على NONE أي الحالة الافتراضية). كما في المثال: import numpy as np v = [[1,5], [1, 2],[1, 2]] v=np.array(v) """ array([[1, 5], [1, 2], [1, 2]]) """ v.mean(axis=None) # 2.0 v.mean(axis=1) # array([3. , 1.5, 1.5]) v.mean(axis=0) # array([1., 3.])1 نقطة
-
تعتبر التطبيقات الخاصة بال html فقط التي يمكنك أن تجدها قليلة حيث معظم التطبيقات أو المواقع ستجدها تم استخدام ال css فيها لذلك يفضل الإنتقال لدراسة اساسيات ال css ثم التطبيق على ال html و ال css معاً وستجد الكثير من التطبيقات والمواقع والتصميمات التي يمكنك التدرب عليها وإذا كنت قلقاً من نسيان ال html عند دراسة ال css فلا تقلق فعند دراسة ال css ستنفذ بعض الأكواد بواسطة html لذلك يفضل الإنتقال لأساسيا ال css مباشرة1 نقطة
-
يمكن شرح ال axis سواء كان 1 أو 0 ببساطة كالتالي في حالة أن ال Axis = 0 وفي حالة الدالة mean سيقوم البرنامج بالعمل على أو حساب المتوسط من خلال العناصر الموجودة في جميع الصفوف في كل عمود بينما في حالة أن ال Axis = 1 (وبالطبع في الدالة mean) سيقوم البرنامج بحساب المتوسط من خلال العناصر الموجودة في جميع الأعمدة في كل صف والصورة التالية توضع الشرح السابق1 نقطة
-
الآن وقد انتهينا من إعداد بيئة التطوير، سنتعرف على أساسيات React Native وسنبدأ بتطوير تطبيقنا. سنتعلم في هذا الفصل كيف سنبني واجهة مستخدم بالاستعانة بمكوّنات React Native البنيوية، وكيف سننسق مظهر هذه المكونات البنيوية، وكيف سننتقل من واجهة عرض إلى أخرى، كما سنتعلم كيف سندير حالة النماذج بفعالية. المكونات البنيوية تعلمنا في الأقسام السابقة طريقة استخدام React في تعريف المكوّنات كدوال تتلقى الخصائص كوسطاء وتعيد شجرة من عناصر React. تُمثَّل هذه الشجرة عادةً باستخدام شيفرة JSX. كما رأينا كيفية استخدام المكتبة ReactDOM في بيئة المتصفح من أجل تحويل المكوّنات إلى شجرة DOM قابلة للتصيير ضمن المتصفح. لاحظ الشيفرة التالية التي تمثل مكوّنًا بسيطًا: import React from 'react'; const HelloWorld = props => { return <div>Hello world!</div>; }; يعيد المكوّن HelloWorld عنصر <div> أُنشئ باستخدام عبارة JSX. وتذكر أن عبارة JSX ستُصرّف إلى استدعاءات للتابع React.createElement كالتالي: React.createElement('div', null, 'Hello world!'); سيُنشئ سطر الشيفرة السابق عنصر <div> دون أية خصائص props وله عنصر ابن وحيد هو النص "Hello World". وعندما يُصيَّر هذا المكوّن إلى عنصر DOM جذري بتطبيق التابع ReactDOM.render، سيُصيَّر العنصر <div> إلى عنصر DOM المقابل. وهكذا نرى أن React لا ترتبط ببيئة محددة كبيئة المتصفح، لكن وبالاستعانة بمكتبات مثل ReactDOM ستتمكن من تصيير مجموعة من المكوّنات المعرّفة مسبقًا كعناصر DOM في بيئة محددة. ندعو هذه المكوّنات المعرّفة مسبقًا في React Native بالمكوّنات البنيوية core components. فالمكوّنات البنيوية هي مجموعة مكوّنات تؤمنها React Native قادرة على استخدام المكونات الأصلية لمنصة خلف الستار. لننفذ المثال السابق باستخدام React Native: import React from 'react'; import { Text } from 'react-native'; const HelloWorld = props => { return <Text>Hello world!</Text>;}; لاحظ كيف أدرجنا المكوّن Text من React Native، واستبدلنا العنصر<div> بالعنصر <text>. ستجد الكثير من عناصر DOM المقابلة لعناصر React Native، وسنستعرض بعض الأمثلة التي استقيناها من توثيق المكونات البنيوية: المكوّن Text: ويعتبر المكون الوحيد من مكونات React Native والذي يمتلك أبناء على شكل قيم نصية، ويشابه العناصر <h1> و<strong>. المكوّن View: ويمثل وحدات البناء الرئيسية لواجهة المستخدم، ويشابه العنصر <div>. المكوّن TextInput: وهو مكوّن لحقل نصي، ويشابه العنصر <input>. المكوّن TouchableWithoutFeedback: وغيره من المكونات القابلة للمس Touchable ويستخدم لالتقاط مختلف حوادث الضغط والنقر، ويشابه العنصر <button>. هنالك بعض الاختلافات الواضحة بين المكوّنات البنيوية وعناصر DOM. يظهر الاختلاف الأول بأن المكوّن Text هو المكوّن الوحيد الذي يمتلك أبناء على شكل قيم نصية في React Native. وبمعنًى آخر، لن تستطيع استبدال هذا المكوّن بالمكوّن View مثلًا في المثال الذي أوردناه سابقًا. يتعلق الاختلاف الثاني بمعالجات الأحداث. فقد اعتدنا عند العمل مع عناصر DOM على إضافة معالج أحداث مثل onClick إلى أية عناصر مثل <div> و<btton>. لكن عليك قراءة توثيق الواجهة البرمجية في React Native لمعرفة أية معالجات وأية خصائص أخرى سيقبلها المكوّن الذي تستخدمه. إذ تؤمن -على سبيل المثال- عائلة مكوّنات اللمس Touchable components القدرة على التقاط الحركات التي تُرسم على الشاشة، كما يمكنها إظهار استجابات محددة عند تمييز هذه الحركات. ومن هذه المكوّنات أيضًا TouchableWithoutFeedback الذي يقبل الخاصية onPress: import React from 'react'; import { Text, TouchableWithoutFeedback, Alert } from 'react-native'; const TouchableText = props => { return ( <TouchableWithoutFeedback onPress={() => Alert.alert('You pressed the text!')} > <Text>You can press me</Text> </TouchableWithoutFeedback> ); }; سنبدأ الآن في هيكلة مشروعنا بعد أن اطلعنا على أساسيات المكونات البنيوية. لننشئ مجلدًا باسم "src" في المجلد الجذري للمشروع، ولننشئ ضمنه مجلدًا آخر باسم "components". أنشئ ملفًا باسم "Main.js" وضعه في المجلد "components"، على أن يحتوي هذا الملف الشيفرة التالية: import React from 'react'; import Constants from 'expo-constants'; import { Text, StyleSheet, View } from 'react-native'; const styles = StyleSheet.create({ container: { marginTop: Constants.statusBarHeight, flexGrow: 1, flexShrink: 1, }, }); const Main = () => { return ( <View style={styles.container}> <Text>Rate Repository Application</Text> </View> ); }; export default Main; لنستخدم الآن المكوّن Main ضمن المكوّن App الموجود في الملف "App.js" والذي يقع بدوره في المجلد الجذري للمشروع. استبدل محتويات الملف السابق بالشيفرة التالية: import React from 'react'; import Main from './src/components/Main'; const App = () => { return <Main />; }; export default App; إعادة تحميل التطبيق يدويا رأينا كيف يعيد Expo تحميل التطبيق تلقائيًا عندما نجري تغييرات على الشيفرة. لكن قد تفشل عملية إعادة التحميل تلقائيًا في حالات معينة، وسنضطر إلى إعادة تحميل التطبيق يدويًا. تُنجز هذه العملية من خلال قائمة المطوّر التي يمكن الوصول إليها بهزّ جهازك أو باختيار الرابط "Shake Gesture" ضمن قائمة العتاد الصلب في محاكي iOS. كما يمكن أن تستخدم أيضًا الاختصار "D⌘" عندما يعمل تطبيقك على محاكي iOS، أو الاختصار "M⌘" عندما يعمل التطبيق على مقلّد Android في نظام macOS، والاختصار "Ctrl+M" لمقلد Android في نظامي Linux وWindows. اضغط على "Reload" عندما تفتح قائمة المطوّر لإعادة تحميل التطبيق. لن تحتاج بعد ذلك إلى إعادة تحميل التطبيق، بل ستحدث العملية تلقائيًا. التمرين 10.3 قائمة المستودعات المقيمة سننجز في هذه التمارين النسخة الأولى من تطبيق قائمة المستودعات المُقيَّمة. ينبغي أن تتضمن القائمة الاسم الكامل للمستودع ووصفه ولغته وعدد التشعبات وعدد النجمات ومعدل التقييم وعدد التقييمات. ولحسن الحظ، تؤمن مكوّنًا مفيدًا لعرض قائمة من البيانات وهو FlatList. انجز المكوّنين RepositoryList وRepositoryItem في الملفين "RepositoryList.jsx" و"RepositoryItem.jsx" وضعهما في المجلد "componenets". ينبغي أن يصيّر المكوّن RepositoryList المكوّن البنيوي FlatList وأن يصيّر المكوّن RepositoryItem عنصرًا واحدًا من القائمة استخدم ذلك كأساس للملف "RepositoryItem.jsx": import React from 'react'; import { FlatList, View, StyleSheet } from 'react-native'; const styles = StyleSheet.create({ separator: { height: 10, }, }); const repositories = [ { id: 'jaredpalmer.formik', fullName: 'jaredpalmer/formik', description: 'Build forms in React, without the tears', language: 'TypeScript', forksCount: 1589, stargazersCount: 21553, ratingAverage: 88, reviewCount: 4, ownerAvatarUrl: 'https://avatars2.githubusercontent.com/u/4060187?v=4', }, { id: 'rails.rails', fullName: 'rails/rails', description: 'Ruby on Rails', language: 'Ruby', forksCount: 18349, stargazersCount: 45377, ratingAverage: 100, reviewCount: 2, ownerAvatarUrl: 'https://avatars1.githubusercontent.com/u/4223?v=4', }, { id: 'django.django', fullName: 'django/django', description: 'The Web framework for perfectionists with deadlines.', language: 'Python', forksCount: 21015, stargazersCount: 48496, ratingAverage: 73, reviewCount: 5, ownerAvatarUrl: 'https://avatars2.githubusercontent.com/u/27804?v=4', }, { id: 'reduxjs.redux', fullName: 'reduxjs/redux', description: 'Predictable state container for JavaScript apps', language: 'TypeScript', forksCount: 13902, stargazersCount: 52869, ratingAverage: 0, reviewCount: 0, ownerAvatarUrl: 'https://avatars3.githubusercontent.com/u/13142323?v=4', }, ]; const ItemSeparator = () => <View style={styles.separator} />; const RepositoryList = () => { return ( <FlatList data={repositories} ItemSeparatorComponent={ItemSeparator} // other props /> ); }; export default RepositoryList; لا تبدّل محتويات المتغيّر repositories، فمن المفترض أن يحتوي على كل ما تحتاج إليه لإكمال التمرين. صيّر المكوّن ReositoryList ضمن المكوّن Main الذي أضفناه سابقّا إلى الملف "Main.jsx". ستبدو قائمة الكتب المُقَّيمة مشابهةً للتالي: تنسيق التطبيق بعد أن تعرفنا على آلية عمل المكوّنات، وكيفية استخدامها في بناء واجهة مستخدم بسيطة، لا بد من الالتفات إلى طرق تنسيق مظهر هذه التطبيقات. لقد رأينا في القسم 2 أنه من الممكن في بيئة المتصفح تعريف خصائص لتنسيق المكوّنات باستخدام CSS. كما رأينا أن هناك طريقتان لإنجاز ذلك: إما التنسيق ضمن السياق inline باستخدام الخاصية style، أو عن طريق محددات التنسيق المناسبة selectors المكتوبة في ملف CSS منفصل. ستجد تشابهًا واضحًا في الطريقة التي ترتبط بها خصائص التنسيق بمكونات React Native البنيوية والطريقة التي ترتبط بها هذه الخصائص بعناصر DOM. فمعظم مكوّنات React Native البنيوية تقبل الخاصية style، وتقبل هذه الخاصية بدورها كائنًا يحمل خصائص تنسيقٍ مع قيمها. وتتطابق في أغلب الأحيان خصائص التنسيق هذه مع مقابلاتها في CSS، مع اختلاف بسيط وهو كتابة أسماء هذه الصفات بطريقة سنام الجمل camelCase. أي ستكتب خصائص مثل "padding-top" على الشكل "paddingTop". وسيوضح المثال التالي طريقة استخدام الخاصية style: import React from 'react'; import { Text, View } from 'react-native'; const BigBlueText = () => { return ( <View style={{ padding: 20 }}> <Text style={{ color: 'blue', fontSize: 24, fontWeight: '700' }}> Big blue text </Text> </View> ); }; بالإضافة إلى أسماء الخصائص، ستلاحظ اختلافًا آخر في المثال السابق. إذ ترتبط بخصائص CSS التي تقبل قيمًا عددية واحدات مثل px أو % أو em أو rem. بينما لا تمتلك الخصائص التي تتعلق بالأبعاد مثل width وheight وpadding وكذلك font-size في React Native أية واحدات. تمثل هذه القيم العددية بيكسلات مستقلة عن الكثافة density-independent pixels. ولو تساءلت عن خصائص التنسيق المتاحة لمكوّن بنيوي محدد، ستجد الجواب في أوراق التنسيق الزائفة Styling Cheat Sheet لإطار عمل React Native. وبشكل عام، لا يعتبر تنسيق المكوًنات باستخدام الخاصية style مباشرة فكرة جيدة، لأنها ستجعل شيفرة المكونات غير واضحة. ويفضّل بدلًا عن ذلك، تعريف التنسيق خارج دالة المكوّن باستخدام التابع StyleSheet.create. يقبل هذا التابع وسيطًا واحدًا على شكل كائن يتألف بحد ذاته من كائنات تنسيق مسماة ومحددة القيم، كما ينشئ مرجعًا للمكوّن البنيوي StyleSheet من هذا الكائن. سنستعرض فيما يلي طريقة إعادة كتابة المثال السابق باستخدام التابع StyleSheet.create: import React from 'react'; import { Text, View, StyleSheet } from 'react-native'; const styles = StyleSheet.create({ container: { padding: 20, }, text: { color: 'blue', fontSize: 24, fontWeight: '700', },}); const BigBlueText = () => { return ( <View style={styles.container}> <Text style={styles.text}> Big blue text <Text> </View> ); }; أنشأنا في الشيفرة السابقة كائني تنسيق مسمّيين هما: styles.container وstyles.text. يمكننا الوصول ضمن مكوّن إلى كائن تنسيق محدد بنفس الطريقة التي نصل بها إلى كائن صرف. تقبل الخاصية style مصفوفة من الكائنات بالإضافة إلى الكائنات المفردة. وتتالى كائنات التنسيق في المصفوفة من اليسار إلى اليمين، وبالتالي سيكون للتنسيقات التي تظهر لاحقًا أولوية التنفيذ. كما يمكن تطبيق ذلك بشكل عودي recursive، إذ يمكن على سبيل المثال، أن تحتوي مصفوفة على مصفوفة أخرى لكائنات التنسيق وهكذا. إن كانت إحدى قيم المصفوفة غير صالحة أو كانت "null" أو "undefined"، سيتم تجاهل هذه القيم. وهكذا يصبح من السهل تعريف تنسيقات شرطية كتلك التي تُبنى على أساس قيمة خاصية مثلًا، وإليكم مثالًا عن ذلك: import React from 'react'; import { Text, View, StyleSheet } from 'react-native'; const styles = StyleSheet.create({ text: { color: 'grey', fontSize: 14, }, blueText: { color: 'blue', }, bigText: { fontSize: 24, fontWeight: '700', }, }); const FancyText = ({ isBlue, isBig, children }) => { const textStyles = [ styles.text, isBlue && styles.blueText, isBig && styles.bigText, ]; return <Text style={textStyles}>{children}</Text>; }; const Main = () => { return ( <> <FancyText>Simple text</FancyText> <FancyText isBlue>Blue text</FancyText> <FancyText isBig>Big text</FancyText> <FancyText isBig isBlue> Big blue text </FancyText> </> ); }; استخدمنا في الشيفرة السابقة العامل && في العبارة condition && exprIfTrue. سيعطي تنفيذ هذه العبارة قيمة "experIfTrue" إن كانت قيمة "condition" هي "true"، وإلا سيعطي القيمة "condition" والتي ستأخذ في هذه الحالة القيمة "false". هذه الطريقة المختصرة شائعة جدًا ومفيدة جدًا. كما يمكنك استخدام العامل الشرطي الثلاثي ":?" كالتالي: condition ? exprIfTrue : exprIfFalse سمات بواجهة مستخدم متناسقة لنبقى في مجال تنسيق التطبيقات لكن من منظورٍ أوسع قليلًا. لقد استخدم معظمنا تطبيقات متنوعة، وربما سنتفق أن ما بجعل واجهة المستخدم جيدة هو تناسق العناصر. فمظهر مكوّنات واجهة المستخدم كحجم الخط وعائلته ولونه ستتبع أسلوبًا متناسقًا. ولإنجاز ذلك لابد من ربط قيم خصائص التنسيق المختلفة بمعاملات، وهذا ما يعرف ببناء السمات theming. ربما يكون مصطلح "بناء السمات" مألوفًا لمستخدمي مكتبات بناء واجهات المستخدم المشهورة مثل Bootstrap وMaterial UI. وعلى الرغم من اختلاف طرق بناء السمات، إلا أنّ الفكرة الرئيسية ستبقى دائمًا استخدام المتغيرات مثل colors.primary بدلًا من الأرقام السحرية مثل 0366dd# عند تعريف التنسيق، وهذا ما سيقود إلى زيادة في الاتساق والمرونة. دعونا نلقي نظرة على طريقة التنفيذ العملي لبناء السمات في تطبيقنا. سنستخدم في عملنا العديد من النصوص وبتنسيقات مختلفة مثل حجم الخط ولونه. وطالما أنّ React Native لا تدعم التنسيق العام، لا بد من إنشاء مكوّنات Text خاصة بنا لنبقي المحتوى النصي متسقًا. لنبدأ إذًا بإضافة شيفرة كائن "تهيئة التنسيق" التالية إلى الملف "theme.js" الموجود في المجلد "src": const theme = { colors: { textPrimary: '#24292e', textSecondary: '#586069', primary: '#0366d6', }, fontSizes: { body: 14, subheading: 16, }, fonts: { main: 'System', }, fontWeights: { normal: '400', bold: '700', }, }; export default theme; علينا الآن إنشاء الكائن Text الفعلي الذي يستخدم قواعد تهيئة التنسيق. لننشئ إذًا الملف "Text.jsx" في المجلد "components" الذي يحتوي ملفات بقية المكوّنات، ولنضع فيه الشيفرة التالية: import React from 'react'; import { Text as NativeText, StyleSheet } from 'react-native'; import theme from '../theme'; const styles = StyleSheet.create({ text: { color: theme.colors.textPrimary, fontSize: theme.fontSizes.body, fontFamily: theme.fonts.main, fontWeight: theme.fontWeights.normal, }, colorTextSecondary: { color: theme.colors.textSecondary, }, colorPrimary: { color: theme.colors.primary, }, fontSizeSubheading: { fontSize: theme.fontSizes.subheading, }, fontWeightBold: { fontWeight: theme.fontWeights.bold, }, }); const Text = ({ color, fontSize, fontWeight, style, ...props }) => { const textStyle = [ styles.text, color === 'textSecondary' && styles.colorTextSecondary, color === 'primary' && styles.colorPrimary, fontSize === 'subheading' && styles.fontSizeSubheading, fontWeight === 'bold' && styles.fontWeightBold, style, ]; return <NativeText style={textStyle} {...props} />; }; export default Text; وهكذا نكون قد أنجزنا مكوّن النص الخاص بنا من حيث اللون وحجم الخط وكثافته لنستخدمه في أي مكان من تطبيقنا. يمكننا الحصول على تغييرات مختلفة في هذا المكوّن باستخدام خصائص مختلفة كالتالي: import React from 'react'; import Text from './Text'; const Main = () => { return ( <> <Text>Simple text</Text> <Text style={{ paddingBottom: 10 }}>Text with custom style</Text> <Text fontWeight="bold" fontSize="subheading"> Bold subheading </Text> <Text color="textSecondary">Text with secondary color</Text> </> ); }; export default Main; لك كامل الحرية في توسيع أو تعديل هذا المكوّن إن أردت. وقد يكون إنشاء مكوّنات نصية قابلة لإعادة الاستخدام تستخدم المكوّن Text مثل Subheading فكرة جيدة. حاول أيضًا أن توسع أو تعدل قواعد تهيئة السمة كلما تقدمنا في بناء تطبيقنا. استخدام flexbox في تصميم واجهة التطبيق سنعرّج أخيرًا في عرضنا لمواضيع تنسيق التطبيقات على تصميم الواجهات باستخدام flexbox. يعرف المطورون الذين ألفوا اللغة CSS أنّ flexbox لا يتعلق فقط بإطار العمل React Native، فقد يستعمل في حالات عدة لتطوير صفحات الويب أيضًا. وعمليًا لن يتعلم المطورون الذين يعرفون آلية عمل flexbox الكثير من هذه الفقرة، لكن دعونا على الأقل نراجع أو نتعلم أساسيات flexbox. flexbox هو كيان لتصميم الواجهات يتألف من مكونين منفصلين هما: حاوية flex وضمنها مجموعة من عناصر flex. تمتلك حاوية flex مجموعة من الخصائص التي تتحكم بتوزيع العناصر. ولجعل مكوّن ما حاوية flex، لا بد من أن يمتلك خاصية التنسيق display وقد أسندت لها القيمة "flex" وهي القيمة الافتراضية لهذه الخاصية. تظهر الشيفرة التالية طريقة تعريف حاوية flex: import React from 'react'; import { View, StyleSheet } from 'react-native'; const styles = StyleSheet.create({ flexContainer: { flexDirection: 'row', }, }); const FlexboxExample = () => { return <View style={styles.flexContainer}>{/* ... */}</View>; }; ربما تكون الخصائص التالية أهم خصائص حاوية flex: الخاصية flexDirection: وتتحكم بالاتجاه الذي ستأخذه عناصر flex عند ترتيبها ضمن الحاوية. وتأخذ هذه الخاصية القيم row وrow-reverse وcoulmn وcolumn-reverse. سيرتب row عناصر flex من اليسار إلى اليمين، بينما سيرتبها column من الأعلى للأسفل (القيمة الافتراضية). وستعكس القيم ذات اللاحقة reverse- اتجاه ترتيب العناصر. الخاصية justifyContent: وتتحكم بمحاذاة عناصر flex على طول المحور الرئيسي للترتيب الذي تحدده الخاصية السابقة. تأخذ هذه الخاصية القيم flex-start (القيمة الافتراضية) وflex-end وcenter وspace-between وspace-around وspace-evenly. الخاصية alignItems: وتقوم بوظيفة الخاصية السابقة لكن على المحور الآخر لترتيب العناصر. تأخذ هذه الخاصية القيم flex-start وflex-end وcenter وbaseline و stretch (القيمة الافتراضية). لننتقل الآن إلى عناصر flex والتي أشرنا سابقًا على أنها محتواة ضمن حاوية flex. تمتلك هذه العناصر خصائص تتحكم بتنسيق وسلوك هذه العناصر بالنسبة لبعضها في نفس الحاوية. ولكي يكون المكوّن عنصر flex يكفي أن نجعله ابنا مباشرًا لحاوية flex: import React from 'react'; import { View, Text, StyleSheet } from 'react-native'; const styles = StyleSheet.create({ flexContainer: { display: 'flex', }, flexItemA: { flexGrow: 0, backgroundColor: 'green', }, flexItemB: { flexGrow: 1, backgroundColor: 'blue', }, }); const FlexboxExample = () => { return ( <View style={styles.flexContainer}> <View style={styles.flexItemA}> <Text>Flex item A</Text> </View> <View style={styles.flexItemB}> <Text>Flex item B</Text> </View> </View> ); }; إنّ أكثر خصائص العناصر استخدامًا هي flexGrow. حيث تقبل هذه الخاصية قيمة تحدد قدرة العنصر على النمو إن دعت الحاجة. فلو كانت قيمة هذه الخاصية لكل عناصر flex في حاوية هي 1، ستتقاسم هذه العناصر المساحة المتاحة للحاوية بالتساوي. بينما لو كانت قيمتها 0 لعنصر ما، فسيأخذ المساحة التي يتطلبها محتواه فقط، وسيترك المساحة الباقية لبقية العناصر. ننصحك بدراسة المثال النموذجي Flexbox example، والذي يشكّل مثالًا رئيسًا أكثر تفاعلية لاستخدام flexbox في تنفيذ بطاقة بسيطة لها حاشية علوية وجسم وحاشية سفلية. إقرأ تاليًا المقالة تعرف على CSS Flexbox وأساسيات استعماله لهيكلة صفحات الويب التي تحتوي على أمثلة مصوّرة شاملة عن استخدام flex. ومن الجيد أيضًا أن تجرب خصائص flexbox ضمن أرضية العمل Flexbox Playground لترى كيفية تأثير الخصائص على تصميم الواجهة. وتذكر أنّ أسماء الخصائص في React Native هي نفسها في CSS ما عدا نقطة التسمية بأسلوب سنام الجمل، إلا أن قيم هذه الخاصيات مثل flex-start وflex-end ستبقى نفسها. التمارين 10.4 - 10.5 10.4 شريط تنقل للتطبيق سنحتاج قريبًا إلى التنقل بين الواجهات المختلفة للتطبيق، لذلك سنحتاج إلى شريط تنقل بين هذه الواجهات. أنشئ الملف "AppBar.jsx" وضعه في المجلد "components" ثم ضع الشيفرة التالية ضمنه: import React from 'react'; import { View, StyleSheet } from 'react-native'; import Constants from 'expo-constants'; const styles = StyleSheet.create({ container: { paddingTop: Constants.statusBarHeight, // ... }, // ... }); const AppBar = () => { return <View style={styles.container}>{/* ... */}</View>; }; export default AppBar; وبما أنّ المكوّن AppBar سيمنع شريط الحالة من تغطية المحتوى، يمكن إزالة خاصية التنسيق marginTop التي وضعناها للمكوّنMain في الملف "Main.jsx". ينبغي أن يضم المكوّن AppBar نافذة عنوانها "Repositories". اجعل النافذة مستجيبة للمس touchable باستخدام المكوّن TouchableWithoutFeedback، لكن لا حاجة الآن لتعالج الحدث onPress. أضف المكوّن AppBar إلى المكوّن Main ، بحيث يبقى المكوّن الأعلى في الواجهة. من المفترض أن يبدو المكوّن AppBar شبيهًا للتالي: إن لون خلفية التطبيق كما تظهره الصورة السابقة هو 24292e#، لكن يمكنك استخدام اللون الذي تشاء. يمكنك أيضًا تهيئة لون خلفية التطبيق ضمن قواعد تهيئة السمة، وسيصبح تغيير اللون أمرًا سهلًا إن احتجت لذلك. ومن الجيد أيضًا فصل شيفرة النافذة الموجودة في المكوّن AppBar لتصبح ضمن مكوّن مستقل يمكن أن تسميه AppBarTab، سيسهّل ذلك إضافة نوافذ أخرى مستقبلًا. 10.5 قائمة محسنة المظهر للمستودعات المقيمة تبدو النسخة الحالية من تطبيق قائمة المستودعات المقيَّمة كئيبة المظهر. عدّل المكوّن RepositoryListItem ليعرض أيضًا الصورة التمثيلية لمؤلف المستودع. ويمكنك إنجاز ذلك باستخدام المكوّن Image. ينبغي إظهار القيم العددية كعدد النجمات وعدد التشعبات التي تزيد عن 1000 برقم مرتبة الآلاف وبدقة فاصلة عشرية واحدة فقط تليها اللاحقة "k". فلو كان مثلًا عدد التشعبات هو 8439، ستُعرض على الشاشة القيمة "8.4k". حسِّن أيضًا المظهر الكلي للمكوّن بحيث تبدو قائمة المستودعات مشابهة للتالي: إنّ لون الخلفية للمكوّن Main في الصورة السابقة هو e1e4e8# بينما ستظهر خلفية للمكوّن RepositoryListItem باللون الأبيض. كما يأخذ لون خلفية محدد اللغة القيمة 0366d6 #، وهي نفسها قيمة المتغيّر colors.primary في إعدادات تهيئة السمة. لا تنس الاستفادة من المكوّن Text الذي أنجزناه سابقًا. افصل المكوّن RepositoryListItem -إن دعت الحاجة- إلى مكوّنات أصغر. مسارات التنقل في React Native سرعان ما سنحتاج إلى آلية للانتقال بين مختلف واجهات العرض في التطبيق كواجهة المستودعات وواجهة تسجيل الدخول، وذلك عندما نبدأ بتوسيعه. وكنا قد تعلمنا في القسم 7 استخدام المكتبة React router لتنفيذ مهمة التنقل والتوجه في تطبيقات الويب. تختلف آلية التنقل في React Native قليلًا عن التنقل في تطبيقات الويب. ويظهر الاختلاف الرئيسي في عدم القدرة على الإشارة المرجعية للصفحات من خلال العناوين "URL" التي نكتبها في شريط تنقل المتصفح، ولا يمكننا كذلك التنقل بين الصفحات التالية والسابقة اعتمادًا على الواجهة البرمجية لتاريخ تنقلات المستخدم history API في المتصفح. لكن تبقى هذه الإشكالية مسألة تتعلق بواجهة التنقل التي نستخدمها. يمكننا في ReactNative استخدام متحكمات المسار البنيوية React Router's core بالكامل كالخطافات والمكوّنات. لكن علينا استبدال BrowserRouter الذي يستخدم في بيئة المتصفح بمقابله NativeRouter الملائم لإطار عمل React Native والذي تؤمنه المكتبة react-router-native. لنثبّت إذًا هذه المكتبة: npm install react-router-native سيؤدي استخدام المكتبة react-router-native إلى توقف عرض التطبيق ضمن متصفح ويب المنصة Expo، بينما ستعمل بقية طرق عرض التطبيق بشكل جيد. يمكن إصلاح هذه المشكلة بتوسيع إعدادات تهيئة المجمع webpack للمنصة Expo بحيث ينقل transpile الشيفرة المصدرية للمكتبة باستخدام Babel. ولتوسيع إعدادات التهيئة، لابدّ من تثبيت المكتبة expo/webpack-config@: npm install @expo/webpack-config --save-dev سننشئ بعد ذلك الملف "webpack.config.js" في المجلد الجذري للمشروع وضمنه الشيفرة التالية: const path = require('path'); const createExpoWebpackConfigAsync = require('@expo/webpack-config'); module.exports = async function(env, argv) { const config = await createExpoWebpackConfigAsync(env, argv); config.module.rules.push({ test: /\.js$/, loader: 'babel-loader', include: [path.join(__dirname, 'node_modules/react-router-native')], }); return config; }; أعد تشغيل أدوات تطوير لكي تُطبق إعدادات تهيئة webpack الجديدة، وستجد أن المشكلة قد حُلّت. افتح الآن الملف "App.js" وأضف المكوّن NativeRouter إلى المكوّن App: import React from 'react'; import { NativeRouter } from 'react-router-native'; import Main from './src/components/Main'; const App = () => { return ( <NativeRouter> <Main /> </NativeRouter> ); }; export default App; بعد أن أنشأنا متحكمًا بالمسار، سنضيف أول مسار تنقل (وجهة- Route) في المكوّن Main، وذلك ضمن الملف "Main.jsx": import React from 'react'; import { StyleSheet, View } from 'react-native'; import { Route, Switch, Redirect } from 'react-router-native'; import RepositoryList from './RepositoryList'; import AppBar from './AppBar'; import theme from '../theme'; const styles = StyleSheet.create({ container: { backgroundColor: theme.colors.mainBackground, flexGrow: 1, flexShrink: 1, }, }); const Main = () => { return ( <View style={styles.container}> <AppBar /> <Switch> <Route path="/" exact> <RepositoryList /> </Route> <Redirect to="/" /> </Switch> </View> ); }; export default Main; التمرينان 10.6 - 10.7 10.6 واجهة عرض تسجيل الدخول سننفذ قريبًا نموذجًا لكي يصبح المستخدم قادرًا على تسجيل دخوله إلى التطبيق. لكن علينا قبل ذلك أن ننشئ واجهة عرض يمكن الوصول إليها من شريط التطبيق. أنشئ الملف "Signin.jsx" في المجلد "components"، ثم ضع الشيفرة التالية ضمنه: import React from 'react'; import Text from './Text'; const SignIn = () => { return <Text>The sign in view</Text>; }; export default SignIn; هيئ مسارًا وجهته المكوّن Signin وضعه في المكوًن Main. أضف بعد ذلك نافذة عنوانها النص "Sign in" في أعلى شريط التطبيق إلى جوار النافذة "Repositories". ينبغي أن يكون المستخدم قادرًا على التنقل بين النافذتين السابقتين بالضغط على عنوان كل نافذة 10.7 شريط تطبيق قابل للتمرير scrollable طالما أننا سنضيف نوافذ جديدة إلى شريط التطبيق، فمن الجيد أن يكون قابلًا للتمرير أفقيًا عندما لا تتسع الواجهة لكل النوافذ. سنستخدم في ذلك المكوّن الأنسب لأداء المهمة وهو ScrollView. ضع النوافذ الموجودة في المكوّن AppBar ضمن المكوّن ScrollView: const AppBar = () => { return ( <View style={styles.container}> <ScrollView horizontal>{/* ... */}</ScrollView> </View> ); }; عندما تُسند القيمة "true" إلى الخاصية horizontal العائدة للمكون ScrollView، سيجعله قابلًا للتمرير أفقيًا عندما لا تتسع الشاشة للنوافذ التي يحتويها شريط التطبيق. وانتبه إلى ضرورة إضافة خصائص تنسيق ملائمة للمكوّن ScrollView، بحيث تُرتَّب النوافذ على شكل صف "row" ضمن حاوية flex. للتأكد من إنجاز المطلوب أضف نوافذ جديدة إلى الشريط حتى لا تتسع الشاشة لها جميعًا، ولا تنس إزالة هذه النوافذ التجريبية بعد التحقق من إنجاز المهمة. إدارة حالة النموذج بعد أن حصلنا على حاضنة مناسبة لواجهة عرض تسجيل الدخول، ستكون المهمة التالية إنجاز نموذج لتسجيل الدخول. لكن قبل الشروع في ذلك، سنتحدث قليلًا عن النماذج وبمنظور أوسع. يعتمد تنفيذ النماذج بشدة على إدارة الحالة. فقد يقدم لنا الخطاف useState حلًا جيدًا للنماذج الصغيرة، لكن سيكون التعامل مع الحالة بنفس الأسلوب مرهقًا عندما يغدو النموذج أكثر تعقيدًا. ولحسن الحظ سنجد مكتبات تتعايش مع بيئة React لتسهيل عملية إدارة حالة النماذج ومنها المكتبة Formik. تعتمد المكتبة Formik على مفهومي سياق العمل context والحقل field. يؤمن سياق العمل المكوّن Formik الذي يحتوي على حالة النموذج. وتتكون الحالة من معلومات عن حقول النموذج، وتتضمن هذه المعلومات مثلًا قيم الحقول والأخطاء الناتجة عن تقييم كلٍ منها. يمكن الإشارة إلى حالة الحقول باستخدام اسم الحقل عن طريق الخطاف useField أو المكوّن Field. لنرى كيف سيعمل الأمر من خلال إنشاء نموذج لحساب "مؤشر كتلة الجسم" عند البشر: import React from 'react'; import { Text, TextInput, TouchableWithoutFeedback, View } from 'react-native'; import { Formik, useField } from 'formik'; const initialValues = { mass: '', height: '', }; const getBodyMassIndex = (mass, height) => { return Math.round(mass / Math.pow(height, 2)); }; const BodyMassIndexForm = ({ onSubmit }) => { const [massField, massMeta, massHelpers] = useField('mass'); const [heightField, heightMeta, heightHelpers] = useField('height'); return ( <View> <TextInput placeholder="Weight (kg)" value={massField.value} onChangeText={text => massHelpers.setValue(text)} /> <TextInput placeholder="Height (m)" value={heightField.value} onChangeText={text => heightHelpers.setValue(text)} /> <TouchableWithoutFeedback onPress={onSubmit}> <Text>Calculate</Text> </TouchableWithoutFeedback> </View> ); }; const BodyMassIndexCalculator = () => { const onSubmit = values => { const mass = parseFloat(values.mass); const height = parseFloat(values.height); if (!isNaN(mass) && !isNaN(height) && height !== 0) { console.log(`Your body mass index is: ${getBodyMassIndex(mass, height)}`); } }; return ( <Formik initialValues={initialValues} onSubmit={onSubmit}> {({ handleSubmit }) => <BodyMassIndexForm onSubmit={handleSubmit} />} </Formik> ); }; لا يعتبر هذا المثال جزءًا من تطبيقنا، لذا لا حاجة لإضافة شيفرته إلى التطبيق. لكن يمكنك تجريب الشيفرة في Expo Snack مثلًا، وهو محرر لشيفرة React Native على شبكة الإنترنت على غرار JSFiddle وCodePen، ويمثل منصة مفيدة لتجريب الشيفرات بسرعة. كما يمكنك مشاركة Expo Snacks مع الآخرين بتحديد رابط إلى عملك أو تضمين الشيفرة على شكل مشغّل Snack ضمن أي صفحة ويب. ولربما قد صادفت مسبقًا مشغل Snack في مادة منهاجنا أو أثناء اطلاعك على توثيق React Native. عرّفنا في المثال السابق سياق عمل Formik ضمن المكوّن BodyMassIndexCalculator، وزودناه بالقيم الأولية وباستدعاء لإرسال محتويات النموذج submit callback. كما استخدمنا الخاصية initialValues لتزويد السياق بالقيم الأولية على شكل كائنات مفاتيحها أسماء الحقول وقيمها هي القيم الأولية المقابلة. كما تزودنا الصفة onSubmit باستدعاء إرسال محتويات النموذج، حيث ينفَّذ هذا الاستدعاء عندما تُستدعى الدالة handleSubmit شريطة أن لا تظهر أية أخطاء تقييم لمحتويات الحقول. تُستدعى الدالة التي تمثل ابنا مباشرًا للمكوّن Formik من خلال الخصائص props التي تحتوي معلومات متعلقة بالحالة، كما تحتوي أفعالًا كالدالة handleSubmit. يحتوي المكوّن BodyMassIndexForm ارتباطات الحالة بين السياق وعناصر الإدخال النصيّة. ويُستخدم الخطاف useField للحصول على قيمة حقل أو تعديل قيمته. يقبل useField وسيطًا واحدًا هو اسم الحقل ويعيد مصفوفة من ثلاث كائنات هي [الحقل field, البيانات الوصفية meta، المُساعدات helpers]. يحتوي الكائن field على قيمة الحقل، بينما يحتوي الكائن meta على المعلومات الوصفية للحقل كرسائل الخطأ التي قد يرميها. أما الكائن الأخير helpers، فيحتوي على الأفعال التي تُستخدم لتغيير حالة الحقل كالدالة setValue. وتجدر الإشارة إلى أنّ المكوّن الذي يستخدم الخطاف لابدّ أن يكون ضمن سياق Formik. أي يجب أن يكون هذا المكوّن من أبناء المكوّن Formik. يمكنك الاطلاع على نسخة تفاعلية عن المثال السابق باسم Formik example على موقع Expo Snack. لقد سبب استخدام الخطاف useField مع المكوّن TextInput شيفرة مكررة. لنعزل هذه الشيفرة ونضعها في مكوّن جديد باسم FormikTextInput، ولننشئ مكوّنًا مخصصًا باسم TextInput لإضافة مظهر مرئي أفضل لعنصر الإدخال النصي. لنثبّت أولًا Formik: npm install formik سننشئ الآن الملف "TextInput.jsx" في المجلد "components" ونضع فيه الشيفرة التالية: import React from 'react'; import { TextInput as NativeTextInput, StyleSheet } from 'react-native'; const styles = StyleSheet.create({}); const TextInput = ({ style, error, ...props }) => { const textInputStyle = [style]; return <NativeTextInput style={textInputStyle} {...props} />; }; export default TextInput; لننتقل إلى المكوّن FormikTextInput الذي ينشئ رابطًا لحالة Formik بالمكوّن TextInput. سننشئ الآن الملف "FormikTextInput.jsx" في المجلد "components" ونضع فيه الشيفرة التالية: import React from 'react'; import { StyleSheet } from 'react-native'; import { useField } from 'formik'; import TextInput from './TextInput'; import Text from './Text'; const styles = StyleSheet.create({ errorText: { marginTop: 5, }, }); const FormikTextInput = ({ name, ...props }) => { const [field, meta, helpers] = useField(name); const showError = meta.touched && meta.error; return ( <> <TextInput onChangeText={value => helpers.setValue(value)} onBlur={() => helpers.setTouched(true)} value={field.value} error={showError} {...props} /> {showError && <Text style={styles.errorText}>{meta.error}</Text>} </> ); }; export default FormikTextInput; يمكننا بعد استخدام المكوّن FormikTextInput أن نعيد كتابة المكوّن BodyMassIndexForm في المثال السابق كالتالي: const BodyMassIndexForm = ({ onSubmit }) => { return ( <View> <FormikTextInput name="mass" placeholder="Weight (kg)" /> <FormikTextInput name="height" placeholder="Height (m)" /> <TouchableWithoutFeedback onPress={onSubmit}> <Text>Calculate</Text> </TouchableWithoutFeedback> </View> ); }; وكما نرى سيقلص إنشاء المكوّن FormikTextInput الذي يعالج ارتباطات حالة Formik بالمكوّن TextInput الشيفرة اللازمة. ومن الجيد أن تكرر نفس العملية إن استخدَمَت نماذج Formik التي لديك مكونات إدخال بيانات. التمرين 10.8 10.8 نموذج تسجيل الدخول أنجز نموذجًا لتسجيل الدخوّل يرتبط بالمكوّن SignIn الذي أضفناه سابقًا في الملف"SignIn.jsx". يجب أن يحتوي النموذج مربعي إدخال نصيين، أحدهما لاسم المستخدم والآخر لكلمة السر. كما يجب أن يحتوي زرًا لتسليم بيانات النموذج. لا حاجة لكتابة دالة الاستدعاء onSubmit، بل يكفي إظهار قيم الحقول باستخدام الأمر console.log عند تسليم البيانات: const onSubmit = (values) => { console.log(values); }; تذكّر أن تستخدم المكوّن FormikTextInput الذي أنشأناه سابقّا. كما يمكنك استخدام الخاصية secureTextEntry في المكوّن TextInput لحجب كلمة السر عند كتابتها. سيبدو نموذج تسجيل الدخول مشابهًا للتالي: تقييم النموذج تقدم Formik مقاربتين لتقييم النماذج: دالة تقييم أو تخطيط تقييم. فدالة التقييم: هي دالة تُمرر للمكون Formik كقيمة للخاصية validate. حيث تستقبل الدالة قيم حقول النموذج كوسطاء وتعيد كائنًا يحتوى الأخطاء المحتملة الخاصة بكل حقل. أما تخطيط التقييم فيمرر إلى المكوّن Formik كقيمة للخاصية validationSchema. ويمكن إنشاء هذا التخطيط باستخدام مكتبة تقييم تُدعى Yup. لنثبّت هذه المكتبة إذًا: npm install yup كمثال على ما أوردنا، سننشئ تاليًا تخطيط تقييم لنموذج "مؤشر كتلة الجسم" الذي أنجزناه سابقًا. نريد أن نتحقق أنّ قيمتي كتلة الجسم والطول موجودتان، وأنهما قيم عددية. كذلك نريد أن نتحقق أنّ كتلة الجسم أكبر أو تساوي1، وأنّ الطول أكبر أو يساوي 0.5. تمثل الشيفرة التالية طريقة تنفيذ التخطيط: import React from 'react'; import * as yup from 'yup'; // ... const validationSchema = yup.object().shape({ mass: yup .number() .min(1, 'Weight must be greater or equal to 1') .required('Weight is required'), height: yup .number() .min(0.5, 'Height must be greater or equal to 0.5') .required('Height is required'),}); const BodyMassIndexCalculator = () => { // ... return ( <Formik initialValues={initialValues} onSubmit={onSubmit} validationSchema={validationSchema} > {({ handleSubmit }) => <BodyMassIndexForm onSubmit={handleSubmit} />} </Formik> ); }; تجري عملية التحقق افتراضيًا عند حدوث أي تغيير في قيم حقول النموذج، وكذلك عند استدعاء الدالة handleSubmit. فإن أخفق التحقق، لن تُستدعى الدالة التي تمرر إلى الكائن Formik عبر الخاصية onSubmit يعرض المكوًن FormikTextInput الذي أنشأناه سابقًا رسائل الخطأ المتعلقة بالحقل إن وقع الخطأ الموافق لها ولُمس الحقل. أي في الحالة التي يتلقى فيها الحقل تركيز الدخل ثم يفقده: const FormikTextInput = ({ name, ...props }) => { const [field, meta, helpers] = useField(name); // Check if the field is touched and the error message is present const showError = meta.touched && meta.error; return ( <> <TextInput onChangeText={(value) => helpers.setValue(value)} onBlur={() => helpers.setTouched(true)} value={field.value} error={showError} {...props} /> {/* Show the error message if the value of showError variable is true */} {showError && <Text style={styles.errorText}>{meta.error}</Text>} </> ); }; التمرين 10.9 10.9 تقييم نموذج تسجيل الدخول تحقق أنّ حقلي كلمة السر واسم المستخدم في نموذج تسجيل الدخول إجباريين. وتذكر أن لا تستدعي الدالة onSubmit التي أنجزناها في التمرين السابق إن أخفق تقييم النموذج. سيعرض المكوّن FormikTextInput حاليًا رسالة خطأ إن حدث خطأ في الحقل الذي تم لمسه، عزز مظهر هذه الرسالة بجعل نصها أحمر اللون. بالإضافة إلى رسالة الخطأ، إجعل إطار حقل الإدخال أحمر اللون ليشير ذلك إلى وقوع خطأ ضمنه. وتذكر أن المكوّن FormikTextInput سيعطي القيمة "true" للخاصية error العائدة للمكوّن TextInput عند حدوث أية أخطاء ضمن الحقل. يمكنك استعمال قيمة هذه الخاصية لإضافة تنسيق شرطي للمكوّن TextInput. سيبدو شكل نموذج تسجيل الدخول عند حدوث خطأ في أحد الحقول قريبًا من الشكل التالي: الشيفرة الخاصة بمنصة من أعظم ميزات استخدام React Native هي أننا لن نهتم بالمنصة التي سيعمل عليها التطبيق سواء Android أو iOS. لكن، قد تصادفنا حالات نضطر فيها إلى تنفيذ شيفرة خاصة بمنصة محددة. من هذه الحالات مثلًا، كتابة مكوّنات بطرق مختلفة لتلائم منصات مختلفة. يمكن الوصول إلى المنصة التي يستخدمها المستخدم من خلال الثابت Platform.OS: import { React } from 'react'; import { Platform, Text, StyleSheet } from 'react-native'; const styles = StyleSheet.create({ text: { color: Platform.OS === 'android' ? 'green' : 'blue', }, }); const WhatIsMyPlatform = () => { return <Text style={styles.text}>Your platform is: {Platform.OS}</Text>; }; يأخذ هذا الثابت إحدى القيمتين: android أو ios. يمكن كتابة شيفرة مخصصة لمنصة أيضًا مستفيدين من التابع Platform.select. إذ يمرر للتابع كائن قد تأخذ مفاتيحه إحدى القيم التالية: ios أو android أو native أو default، ويعيد القيمة الأكثر ملائمة للمنصة التي يعمل عليها المستخدم. يمكننا إعادة كتابة المتغير styles في المثال السابق باستعمال التابع Platform.select كالتالي: const styles = StyleSheet.create({ text: { color: Platform.select({ android: 'green', ios: 'blue', default: 'black', }), }, }); وبالإمكان أيضًا استخدام التابع Platform.select لطلب مكوّن خاص بمنصة محددة: const MyComponent = Platform.select({ ios: () => require('./MyIOSComponent'), android: () => require('./MyAndroidComponent'), })(); <MyComponent />; إنّ الطريقة الأكثر تقدّمًا لكتابة وإدراج مكوّنات خاصة بمنصة محددة أو أية أجزاء من الشيفرة، هي استخدام ملفات لها إحدى اللاحقتين "ios.jsx." أو "android.jsx.". وانتبه إلى إمكانية استخدام أية لاحقة يمكن للمجمع أن يميزها مثل "js.". إذ يمكن مثلًا أن ننشئ ملفات باسم "Button.ios.jsx" أو باسم "Button.android.jsx" بحيث نستطيع إدراجها في شيفرتنا كالتالي: import React from 'react'; import Button from './Button'; const PlatformSpecificButton = () => { return <Button />; }; وهكذا ستضم حزمة Android للتطبيق المكوّن المعرّف في الملف "Button.android.jsx"، بينما تضم حزمة iOS المكوّن المعرّف في الملف "Button.ios.jsx". التمرين 10.10 10.10 خط خاص بمنصة محددة حُددت عائلة الخط الذي نستخدمه حاليًا في تطبيقنا بأنها "System" ضمن إعدادات تهيئة السمة الموجودة في الملف "theme.js". استخدم بدلًا من الخط "system" خطًا من خطوط العائلة Sans-serif ليكون خطًا خاصًا بكل منصة. استخدم الخط "Roboto" في منصة Android والخط Arial في منصة iOS. يمكن أن تُبقي "System" مثل خط افتراضي. هذا هو التمرين الأخير في هذا الفصل، وقد حان الوقت لتسليم التمارين إلى GitHub والإشارة إلى أنك أكملتها في منظومة تسليم التمارين. انتبه إلى وضع الحلول في القسم 2 ضمن المنظومة. ترجمة -وبتصرف- للفصل React Native basics من سلسلة Deep Dive Into Modern Web Development. اقرأ أيضًا المقال السابق: مدخل إلى React Native مدخل إلى التحريك في React Native1 نقطة