الفرق بين المراجعتين ل"Rails/form helpers"

من موسوعة حسوب
اذهب إلى التنقل اذهب إلى البحث
ط
 
(5 مراجعات متوسطة بواسطة مستخدمين اثنين آخرين غير معروضة)
سطر 1: سطر 1:
مساعدو النموذج في وحدة العرض في ريلز
+
<noinclude>{{DISPLAYTITLE:مساعدو الاستمارة في وحدة العرض في ريلز}}</noinclude>
 +
[[تصنيف:Rails]]
 +
[[تصنيف:Rails View]]
 +
تعتبر [[HTML/form|الاستمارات]] (forms) في تطبيقات الويب واجهة أساسية للتفاعل مع المستخدم وجلب بيانات منه. ومع ذلك، يمكن أن تصبح عملية إنشاء استمارةٍ وصيانتها عمليةً مملةً بسبب الحاجة إلى التحكم بالتسميات وبخاصياتها الكثيرة.
  
تعتبر النماذج في تطبيقات الويب واجهة أساسية للتفاعل مع المستخدم والحصول على بيانات منه. ومع ذلك، يمكن أن تصبح عملية إنشاء نموذجٍ وصيانته عمليةً مملةً بسبب الحاجة إلى التحكم بالتسمية وبخاصياته الكثيرة.
+
تخلَّص ريلز من هذا التعقيد بتوفير مساعدين للعرض لإنشاء الاستمارات بسهولة. ومع ذلك، بما أن للمساعدين حالات استخدام مختلفة، يحتاج المطورون إلى معرفة الاختلافات بين توابع كل مساعد قبل استخدامها.
 
 
تخلَّص ريلز من هذا التعقيد بتوفير مساعدين للعرض لإنشاء النماذج بسهولة. ومع ذلك، بما أن للمساعدين حالات استخدام مختلفة، يحتاج المطورون إلى معرفة الاختلافات بين توابع كل مساعد قبل استخدامها.
 
  
 
بعد قراءة هذا الدليل، ستتعلم:
 
بعد قراءة هذا الدليل، ستتعلم:
* كيفية إنشاء نماذج البحث ونوع مماثل من النماذج العامة التي لا تمثل أي نموذج (model) محدد في تطبيقك.
+
* كيفية إنشاء استمارات للبحث ونوع مماثل من الاستمارات العامة التي لا تمثل أي نموذج (model) محدد في تطبيقك.
* كيفية عمل استمارات (نماذج) مركزية النموذج (model-centric forms) لإنشاء وتعديل سجلات محددة في قاعدة بيانات.
+
* كيفية عمل استمارات مركزية النموذج (model-centric forms) لإنشاء وتعديل سجلات محددة في قاعدة بيانات.
 
* كيفية إنشاء مربعات اختيار من أنواع متعددة من البيانات.
 
* كيفية إنشاء مربعات اختيار من أنواع متعددة من البيانات.
 
* مساعدو التاريخ والوقت الذين يوفرهم ريلز.
 
* مساعدو التاريخ والوقت الذين يوفرهم ريلز.
 
* ما الذي يجعل نموذج تحميل الملف مختلفًا.
 
* ما الذي يجعل نموذج تحميل الملف مختلفًا.
* كيفية نشر النماذج إلى الموارد الخارجية وتحديد إعداد authenticity_token.
+
* كيفية نشر الاستمارات إلى الموارد الخارجية وتحديد الإعداد <code>authenticity_token</code>.
* كيفية بناء نماذج معقدة.
+
* كيفية بناء استمارات معقدة.
'''ملاحظة''': ليس المقصود من هذا الدليل أن يكون توثيقًا كاملًا لمساعدي النماذج المتاحة وكل ما يتعلق بهم. يرجى زيارة توثيق [http://api.rubyonrails.org/v5.2.2/ الواجهة البرمجية لريلز] للاطلاع على المرجع الكامل.
+
'''ملاحظة''': ليس المقصود من هذا الدليل أن يكون توثيقًا كاملًا لمساعدي الاستمارات المتاحة وكل ما يتعلق بها. يرجى زيارة توثيق [http://api.rubyonrails.org/v5.2.2/ الواجهة البرمجية لريلز] للاطلاع على المرجع الكامل.
 +
 
 +
'''تنويه''': استعملنا المصطلح "استمارة" ترجمةً للكلمة form، والمصطلح "نموذج" ترجمةً للكلمة model لكي لا يحدث لبسٌ بينهما، إذ تترجم form في كثير من الأحيان في الموسوعة إلى "نموذج". انتبه إلى أنَّ هذه الترجمة قد لا تُطبَّق على بقية توثيق ريلز.
  
== التعامل مع النماذج الأساسية ==
+
== التعامل مع الاستمارات الأساسية ==
مساعد النموذج الأساسي هو <code>form_tag</code>.<syntaxhighlight lang="rails">
+
مساعد الاستمارة الأساسي هو <code>form_tag</code>.<syntaxhighlight lang="rails">
 
<%= form_tag do %>
 
<%= form_tag do %>
 
   Form contents
 
   Form contents
 
<% end %>
 
<% end %>
</syntaxhighlight>عندما يستدعى بدون معاملات كم سبق، فإنه ينشئ الوسم <code>[[HTML/form|<form>]]</code> والذي، عند إرساله، سيرسل النموذج عبر POST إلى الصفحة الحالية. على سبيل المثال، بافتراض أن الصفحة الحالية هي ‎/home/index، ستبدو شيفرة [[HTML]] المولدة هي (أضيفت إليها بعض فواصل الأسطر لقابلية القراءة):<syntaxhighlight lang="html">
+
</syntaxhighlight>عندما يستدعى بدون معاملات كما سبق، فإنه ينشئ الوسم <code>[[HTML/form|<form>]]</code> والذي، عند إرساله، سيرسل الاستمارة عبر POST إلى الصفحة الحالية. على سبيل المثال، بافتراض أن الصفحة الحالية هي ‎/home/index، ستبدو شيفرة [[HTML]] المولدة هي (أضيفت إليها بعض فواصل الأسطر لقابلية القراءة):<syntaxhighlight lang="html">
 
<form accept-charset="UTF-8" action="/" method="post">
 
<form accept-charset="UTF-8" action="/" method="post">
 
   <input name="utf8" type="hidden" value="&#x2713;" />
 
   <input name="utf8" type="hidden" value="&#x2713;" />
سطر 26: سطر 29:
 
   Form contents
 
   Form contents
 
</form>
 
</form>
</syntaxhighlight>ستلاحظ أن شيفرة [[HTML]] تحتوي على عنصر [[HTML/input|إدخال]] من النوع <code>hidden</code>. هذا العنصر مهم، لأنه لا يمكن إرسال النموذج بنجاح بدونه. يجبر عنصر الإدخال المخفي ذو الاسم utf8 المتصفحات أن تراعي ترميز محارف هذا النموذج وأنه قد أُنشِئ لجميع النماذج سواء كانت طريقة الإرسال عبر "GET" أو "POST".
+
</syntaxhighlight>ستلاحظ أن شيفرة [[HTML]] تحتوي على عنصر [[HTML/input|إدخال]] من النوع <code>hidden</code>. هذا العنصر مهم، لأنه لا يمكن إرسال الاستمارة بنجاح بدونه. يجبر عنصر الإدخال المخفي ذو الاسم utf8 المتصفحات أن تراعي ترميز محارف هذه الاستمارة وأنه قد أُنشِئ لجميع الاستمارات سواء كانت طريقة الإرسال عبر "GET" أو "POST".
  
عنصر [[HTML/input|الإدخال]] الثاني ذو الاسم <code>Authenticity_token</code> هو ميزة أمان في ريلز تسمى "حماية هجمات تزوير الطلب عبر الموقع" (cross-site request forgery protection)، يولده كل مساعد نموذج لكل نموذج لا يرسل عبر GET (بشرط تفعيل ميزة الأمان هذه). يمكنك قراءة المزيد حول هذا الأمر في [[Rails/security|دليل تأمين تطبيقات ريلز]].
+
عنصر [[HTML/input|الإدخال]] الثاني ذو الاسم <code>Authenticity_token</code> هو ميزة أمان في ريلز تسمى "حماية هجمات تزوير الطلب عبر الموقع" (cross-site request forgery protection)، يولده كل مساعد لكل استمارة لا ترسل عبر GET (بشرط تفعيل ميزة الأمان هذه). يمكنك قراءة المزيد حول هذا الأمر في [[Rails/security|دليل تأمين تطبيقات ريلز]].
  
=== نموذج بحث عام ===
+
=== استمارة بحث عامة ===
أحد النماذج الأساسية التي تراها على الويب هو نموذج البحث. يحتوي هذا النموذج على:
+
إحدى الاستمارات الأساسية التي تراها على الويب هي استمارة البحث. تحتوي هذه الاستمارة على:
* عنصر نموذج يرسل عبر الطريقة "GET".
+
* عنصر استمارة يرسل عبر الطريقة "GET".
 
* عنصر تسمية لحقل الإدخال.
 
* عنصر تسمية لحقل الإدخال.
 
* عنصر إدخال نصي.
 
* عنصر إدخال نصي.
 
* عنصر إرسال.
 
* عنصر إرسال.
لإنشاء هذا النموذج، ستستخدم المساعدين <code>form_tag</code> و <code>label_tag</code> و <code>text_field_tag</code> و <code>submit_tag</code> على التوالي كالتالي:<syntaxhighlight lang="html">
+
لإنشاء هذه الاستمارة، سيُستخدَم المساعدين <code>form_tag</code> و <code>label_tag</code> و <code>text_field_tag</code> و <code>submit_tag</code> على التوالي كالتالي:<syntaxhighlight lang="html">
 
<%= form_tag("/search", method: "get") do %>
 
<%= form_tag("/search", method: "get") do %>
 
   <%= label_tag(:q, "Search for:") %>
 
   <%= label_tag(:q, "Search for:") %>
سطر 49: سطر 52:
 
   <input name="commit" type="submit" value="Search" />
 
   <input name="commit" type="submit" value="Search" />
 
</form>
 
</form>
</syntaxhighlight>'''تنويه''': لكل عنصر إدخال للنموذج، تُنشَئ الخاصية <code>id</code> من اسمه ("q" في المثال أعلاه). يمكن أن تكون هذه المعرفات مفيدة جدًا لتنسيق العناصر عبر [[CSS]] أو معالجة عناصر تحكم النموذج باستخدام [[JavaScript|جافاسكربت]].
+
</syntaxhighlight>'''تنويه''': لكل عنصر إدخال للاستمارة، تُنشَئ الخاصية <code>id</code> من اسمه ("q" في المثال أعلاه). يمكن أن تكون هذه المعرفات مفيدة جدًا لتنسيق العناصر عبر [[CSS]] أو معالجة عناصر تحكم الاستمارة باستخدام [[JavaScript|جافاسكربت]].
  
 
بالإضافة إلى <code>text_field_tag</code> و <code>submit_tag</code>، يوجد مساعد مماثل لكل عنصر تحكم في [[HTML]].
 
بالإضافة إلى <code>text_field_tag</code> و <code>submit_tag</code>، يوجد مساعد مماثل لكل عنصر تحكم في [[HTML]].
  
'''تحذير''': استخدم دائمًا الطريقة "GET" لإرسال نماذج البحث. يتيح ذلك للمستخدمين وضع إشارة حفظ (bookmark) على بحث معين للعودة إليه لاحقًا. بشكل عام، يشجعك ريلز على استخدام طريقة HTTP المناسبة لأي إجراء.
+
'''تحذير''': استخدم دائمًا الطريقة "GET" لإرسال استمارات البحث. يتيح ذلك للمستخدمين وضع إشارة حفظ (bookmark) على بحث معين للعودة إليه لاحقًا. بشكل عام، يشجعك ريلز على استخدام طريقة HTTP المناسبة لأي إجراء.
  
=== استعمال جدول <code>[[Ruby/Hash|Hash]]</code> مع استدعائات مساعد النموذج ===
+
=== استعمال جدول <code>[[Ruby/Hash|Hash]]</code> مع استدعائات مساعد الاستمارة ===
يقبل المساعد <code>form_tag</code> تمرير وسيطين: مسار الإجراء وجدول <code>[[Ruby/Hash|Hash]]</code> يحوي الخيارات المتعلقة بالعملية. يحدد ذلك الجدول طريقة إرسال النموذج وخيارات [[HTML]] الأخرى مثل الصنف (الخاصية <code>class</code>).
+
يقبل المساعد <code>form_tag</code> تمرير وسيطين: مسار الإجراء وجدول <code>[[Ruby/Hash|Hash]]</code> يحوي الخيارات المتعلقة بالعملية. يحدد ذلك الجدول طريقة إرسال الاستمارة وخيارات [[HTML]] الأخرى مثل الصنف (الخاصية <code>class</code>).
  
 
كما هو الحال مع المساعد <code>link_to</code>، لا يتوجب أن يكون وسيط المسار سلسلة نصية فقط، بل يمكن أن يكون جدول <code>[[Ruby/Hash|Hash]]</code> من معاملات URL يمكن التعرف عليها من خلال [[Rails/routing|آلية التوجيه]] في ريلز، والتي بدورها تحول الجدول إلى عنوان URL صالح. ومع ذلك، نظرًا لأن كلا المعاملين الممررين إلى <code>form_tag</code> هما من النوع <code>[[Ruby/Hash|Hash]]</code>، يمكنك بسهولة أن تواجه مشكلة إذا كنت ترغب في استعمالهما مع بعضهما بعضًا. على سبيل المثال، دعنا نقول أنك كتبت الشيفرة التالية:<syntaxhighlight lang="rails">
 
كما هو الحال مع المساعد <code>link_to</code>، لا يتوجب أن يكون وسيط المسار سلسلة نصية فقط، بل يمكن أن يكون جدول <code>[[Ruby/Hash|Hash]]</code> من معاملات URL يمكن التعرف عليها من خلال [[Rails/routing|آلية التوجيه]] في ريلز، والتي بدورها تحول الجدول إلى عنوان URL صالح. ومع ذلك، نظرًا لأن كلا المعاملين الممررين إلى <code>form_tag</code> هما من النوع <code>[[Ruby/Hash|Hash]]</code>، يمكنك بسهولة أن تواجه مشكلة إذا كنت ترغب في استعمالهما مع بعضهما بعضًا. على سبيل المثال، دعنا نقول أنك كتبت الشيفرة التالية:<syntaxhighlight lang="rails">
سطر 62: سطر 65:
 
# => '<form accept-charset="UTF-8" action="/people/search?method=get&class=nifty_form" method="post">'
 
# => '<form accept-charset="UTF-8" action="/people/search?method=get&class=nifty_form" method="post">'
  
</syntaxhighlight>هنا، يضاف <code>method</code> و <code>class</code> إلى سلسلة الاستعلام الخاصة بعنوان URL المُنشَأ لأنه حتى لو كنت تقصد كتابة جدولين من النوع <code>[[ٌعلاغظHash|Hash]]</code>، فأنت بذلك حددت واحدًا فقط. بناءً على ما سبق، عليك أن تخبر روبي أي الجدولين هو للمعامل الأول وأيهما للثاني وذلك عن طريق تغليف الجدول الأولى (أو كليهما) بالأقواس المعقوصة. سيؤدي هذا إلى إنشاء شيفرة [[HTML]] التي تريدها بالضبط:<syntaxhighlight lang="rails">
+
</syntaxhighlight>هنا، يضاف <code>method</code> و <code>class</code> إلى سلسلة الاستعلام الخاصة بعنوان URL المُنشَأ لأنه حتى لو كنت تقصد كتابة جدولين من النوع <code>[[Ruby/Hash|Hash]]</code>، فأنت بذلك حددت واحدًا فقط. بناءً على ما سبق، عليك أن تخبر روبي أي الجدولين هو للمعامل الأول وأيهما للثاني وذلك عن طريق تغليف الجدول الأولى (أو كليهما) بالأقواس المعقوصة. سيؤدي هذا إلى إنشاء شيفرة [[HTML]] التي تريدها بالضبط:<syntaxhighlight lang="rails">
 
form_tag({controller: "people", action: "search"}, method: "get", class: "nifty_form")
 
form_tag({controller: "people", action: "search"}, method: "get", class: "nifty_form")
 
# => '<form accept-charset="UTF-8" action="/people/search" method="get" class="nifty_form">'
 
# => '<form accept-charset="UTF-8" action="/people/search" method="get" class="nifty_form">'
سطر 68: سطر 71:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
=== مساعدون لتوليد عناصر النموذج ===
+
=== مساعدون لتوليد عناصر الاستمارة ===
يوفر ريلز سلسلة من المساعدين لتوليد مختلف عناصر النموذج مثل [[HTML/input/checkbox|مربعات الاختيار]] و<nowiki/>[[HTML/input/text|حقول نصية]] و<nowiki/>[[HTML/input/radio|أزرار الانتقاء]]. ينشئ هؤلاء المساعدون الأساسيون، مع أسماء تنتهي بـ ‎<code>_tag</code> (مثل <code>text_field_tag</code> و <code>check_box_tag</code>)، عنصر <code>[[HTML/input|<input>]]</code> وحيد فقط. المعامل الأول المُمرَّر إليهم هو دائما اسم عنصر الإدخال. عند إرسال النموذج، سيُمرر الاسم مع بيانات النموذج، وسوف يشق طريقه إلى <code>params</code> في المتحكم مرفقًا بالقيمة التي أدخلها المستخدم لهذا الحقل. على سبيل المثال، إذا كان النموذج يحتوي على <code><‎%= text_field_tag(:query) %‎></code>، فستتمكن من الحصول على قيمة هذا الحقل في المتحكم باستخدام <code>params [: query]‎</code>.
+
يوفر ريلز سلسلة من المساعدين لتوليد مختلف عناصر الاستمارة مثل [[HTML/input/checkbox|مربعات الاختيار]] و<nowiki/>[[HTML/input/text|حقول نصية]] و<nowiki/>[[HTML/input/radio|أزرار الانتقاء]]. ينشئ هؤلاء المساعدون الأساسيون، مع أسماء تنتهي بـ ‎<code>_tag</code> (مثل <code>text_field_tag</code> و <code>check_box_tag</code>)، عنصر <code>[[HTML/input|<input>]]</code> وحيد فقط. المعامل الأول المُمرَّر إليهم هو دائما اسم عنصر الإدخال. عند إرسال الاستمارة، سيُمرر الاسم مع بيانات الاستمارة، وسوف يشق طريقه إلى <code>params</code> في المتحكم مرفقًا بالقيمة التي أدخلها المستخدم لهذا الحقل. على سبيل المثال، إذا كانت الاستمارة تحتوي على <code><‎%= text_field_tag(:query) %‎></code>، فستتمكن من الحصول على قيمة هذا الحقل في المتحكم باستخدام <code>params[:query]‎</code>.
  
 
عند تسمية المدخلات، يستخدم ريلز اصطلاحات معينة تجعل من الممكن إرسال المعاملات مع قيم غير أساسية (non-scalar values) مثل [[Ruby/Array|المصفوفات]] أو [[Ruby/Hash|جداول Hash]]، والتي يمكن الوصول إليها أيضًا في <code>params</code>. يمكنك قراءة المزيد عنها في الفصل السابع من هذا الدليل. للحصول على تفاصيل حول الاستخدام الدقيق لهؤلاء المساعدين، يرجى الرجوع إلى توثيق [http://api.rubyonrails.org/v5.2.2/classes/ActionView/Helpers/FormTagHelper.html الواجهة البرمجية الكامل].
 
عند تسمية المدخلات، يستخدم ريلز اصطلاحات معينة تجعل من الممكن إرسال المعاملات مع قيم غير أساسية (non-scalar values) مثل [[Ruby/Array|المصفوفات]] أو [[Ruby/Hash|جداول Hash]]، والتي يمكن الوصول إليها أيضًا في <code>params</code>. يمكنك قراءة المزيد عنها في الفصل السابع من هذا الدليل. للحصول على تفاصيل حول الاستخدام الدقيق لهؤلاء المساعدين، يرجى الرجوع إلى توثيق [http://api.rubyonrails.org/v5.2.2/classes/ActionView/Helpers/FormTagHelper.html الواجهة البرمجية الكامل].
  
 
==== مربعات الاختيار ====
 
==== مربعات الاختيار ====
[[HTML/input/checkbox|مربعات الاختيار]] هي عناصر تحكم في النموذج تمنح المستخدم مجموعة من الخيارات التي يمكن تمكينها أو تعطيلها:<syntaxhighlight lang="html">
+
[[HTML/input/checkbox|مربعات الاختيار]] هي عناصر تحكم في الاستمارة تمنح المستخدم مجموعة من الخيارات التي يمكن تمكينها أو تعطيلها:<syntaxhighlight lang="html">
 
<%= check_box_tag(:pet_dog) %>
 
<%= check_box_tag(:pet_dog) %>
 
<%= label_tag(:pet_dog, "I own a dog") %>
 
<%= label_tag(:pet_dog, "I own a dog") %>
سطر 85: سطر 88:
 
<label for="pet_cat">I own a cat</label>
 
<label for="pet_cat">I own a cat</label>
  
</syntaxhighlight>المعامل الأول المُمرَّر إلى <code>check_box_tag</code>، بالطبع، هو اسم عنصر الإدخال. المعامل الثاني، بطبيعة الحال، هو قيمة العنصر. ستُشمَل هذه القيمة في بيانات النموذج (وتكون موجودة في <code>params</code>) عند اختيار (check) مربع الاختيار.
+
</syntaxhighlight>المعامل الأول المُمرَّر إلى <code>check_box_tag</code>، بالطبع، هو اسم عنصر الإدخال. المعامل الثاني، بطبيعة الحال، هو قيمة العنصر. ستُشمَل هذه القيمة في بيانات الاستمارة (وتكون موجودة في <code>params</code>) عند اختيار (check) مربع الاختيار.
  
 
==== أزرار الانتقاء ====
 
==== أزرار الانتقاء ====
سطر 98: سطر 101:
 
<input id="age_adult" name="age" type="radio" value="adult" />
 
<input id="age_adult" name="age" type="radio" value="adult" />
 
<label for="age_adult">I'm over 21</label>
 
<label for="age_adult">I'm over 21</label>
</syntaxhighlight>كما هو الحال مع <code>check_box_tag</code> ، فإن المعامل الثاني في <code>radio_button_tag</code> هو قيمة عنصر الإدخال. نظرًا لأن هذين الزرين يشتركان في الاسم نفسه (age)، فسيتمكن المستخدم من تحديد أحدهما فقط، وستتضمن المعاملات [: age] إما "child" أو "adult".
+
</syntaxhighlight>كما هو الحال مع <code>check_box_tag</code> ، فإن المعامل الثاني في <code>radio_button_tag</code> هو قيمة عنصر الإدخال. نظرًا لأن هذين الزرين يشتركان في الاسم نفسه (age)، فسيتمكن المستخدم من تحديد أحدهما فقط، وستتضمن المعاملات <code>[:age]</code> إما "child" أو "adult".
  
 
'''ملاحظة''': استخدم دومًا [[HTML/label|لافتةً نصيةً]] مع مربع الاختيار وزر الانتقاء. فهي تربط نصًا بخيار محدد، وتوسع المنطقة القابلة للنقر، مما يسهل على المستخدمين النقر على المدخلات.
 
'''ملاحظة''': استخدم دومًا [[HTML/label|لافتةً نصيةً]] مع مربع الاختيار وزر الانتقاء. فهي تربط نصًا بخيار محدد، وتوسع المنطقة القابلة للنقر، مما يسهل على المستخدمين النقر على المدخلات.
  
=== مساعدون آخرون مهمون ===
+
=== مساعدون آخرون مهمّون ===
عناصر التحكم الأخرى بالنموذج الجديرة بالذكر هي [[HTML/textarea|مربع نصي متعدد الأسطر]]، و<nowiki/>[[HTML/input/password|حقول كلمة المرور]]، و<nowiki/>[[HTML/input/hidden|الحقول المخفية]]، و<nowiki/>[[HTML/input/search|حقول البحث]]، و<nowiki/>[[HTML/input/tel|حقول رقم الهاتف]]، و<nowiki/>[[HTML/input/date|حقول التاريخ]]، و<nowiki/>[[HTML/input/time|حقول الوقت]]، و<nowiki/>[[HTML/input/color|حقول اللون]]، و<nowiki/>[[HTML/input/datetime-local|حقول التوقيت المحلي]]، و<nowiki/>[[HTML/input/month|حقول الشهر]]، و<nowiki/>[[HTML/input/week|حقول الأسبوع]]، و<nowiki/>[[HTML/input/url|حقول عنوان URL]]، و<nowiki/>[[HTML/input/email|حقول البريد الإلكتروني]]، و<nowiki/>[[HTML/input/number|حقول الأرقام]] و<nowiki/>[[HTML/input/range|حقول المجالات]]:<syntaxhighlight lang="html">
+
عناصر التحكم الأخرى بالاستمارة الجديرة بالذكر هي [[HTML/textarea|مربع نصي متعدد الأسطر]]، و<nowiki/>[[HTML/input/password|حقول كلمة المرور]]، و<nowiki/>[[HTML/input/hidden|الحقول المخفية]]، و<nowiki/>[[HTML/input/search|حقول البحث]]، و<nowiki/>[[HTML/input/tel|حقول رقم الهاتف]]، و<nowiki/>[[HTML/input/date|حقول التاريخ]]، و<nowiki/>[[HTML/input/time|حقول الوقت]]، و<nowiki/>[[HTML/input/color|حقول اللون]]، و<nowiki/>[[HTML/input/datetime-local|حقول التوقيت المحلي]]، و<nowiki/>[[HTML/input/month|حقول الشهر]]، و<nowiki/>[[HTML/input/week|حقول الأسبوع]]، و<nowiki/>[[HTML/input/url|حقول عنوان URL]]، و<nowiki/>[[HTML/input/email|حقول البريد الإلكتروني]]، و<nowiki/>[[HTML/input/number|حقول الأرقام]] و<nowiki/>[[HTML/input/range|حقول المجالات]]:<syntaxhighlight lang="html">
 
<%= text_area_tag(:message, "Hi, nice site", size: "24x6") %>
 
<%= text_area_tag(:message, "Hi, nice site", size: "24x6") %>
 
<%= password_field_tag(:password) %>
 
<%= password_field_tag(:password) %>
سطر 138: سطر 141:
 
</syntaxhighlight>لا يتم عرض المدخلات المخفية للمستخدم ولكن بدلًا من ذلك يحتفظ بالبيانات مثل أي إدخال نصي. يمكن تغيير القيم الموجودة داخلها باستخدام جافاسكربت.
 
</syntaxhighlight>لا يتم عرض المدخلات المخفية للمستخدم ولكن بدلًا من ذلك يحتفظ بالبيانات مثل أي إدخال نصي. يمكن تغيير القيم الموجودة داخلها باستخدام جافاسكربت.
  
'''تحذير''': حقول [[HTML/input/search|البحث]]، و<nowiki/>[[HTML/input/tel|رقم الهاتف]]، و<nowiki/>[[HTML/input/date|التاريخ]]، و<nowiki/>[[HTML/input/time|الوقت]]، و<nowiki/>[[HTML/input/color|اللون]]، و<nowiki/>[[HTML/input/datetime-local|التاريخ والوقت المحلي]]، و<nowiki/>[[HTML/input/month|الشهر]]، و<nowiki/>[[HTML/input/week|الأسبوع]]، و<nowiki/>[[HTML/input/url|عنوان URL]]، و<nowiki/>[[HTML/input/email|البريد الإلكتروني]]، و<nowiki/>[[HTML/input/number|العدد]] و<nowiki/>[[HTML/input/range|نطاق المدخلات]]  هي عناصر تحكم أوجدت في HTML5. إذا كنت ترغب في أن يتوافق تطبيق مع المتصفحات القديمة، فستحتاج إلى استعمال تقنية "الترقيع المتعدد" (polyfill، توفرها [[CSS]] بمفردها أو مع [[JavaScript|جافاسكربت]]). من المؤكد أنه [https://github.com/Modernizr/Modernizr/wiki/HTML5-Cross-Browser-Polyfills لا يوجد نقص في الحلول لذلك]، على الرغم من أن الطريقة الشائعة في الوقت الحالي هي [https://modernizr.com/ Modernizr]، والتي توفر طريقة بسيطة لإضافة وظائف تعتمد على وجود ميزات HTML5 المكتشفة.
+
'''تحذير''': حقول [[HTML/input/search|البحث]]، و<nowiki/>[[HTML/input/tel|رقم الهاتف]]، و<nowiki/>[[HTML/input/date|التاريخ]]، و<nowiki/>[[HTML/input/time|الوقت]]، و<nowiki/>[[HTML/input/color|اللون]]، و<nowiki/>[[HTML/input/datetime-local|التاريخ والوقت المحلي]]، و<nowiki/>[[HTML/input/month|الشهر]]، و<nowiki/>[[HTML/input/week|الأسبوع]]، و<nowiki/>[[HTML/input/url|عنوان URL]]، و<nowiki/>[[HTML/input/email|البريد الإلكتروني]]، و<nowiki/>[[HTML/input/number|العدد]] و<nowiki/>[[HTML/input/range|نطاق المدخلات]] هي عناصر تحكم أوجدت في HTML5. إذا كنت ترغب في أن يتوافق تطبيق مع المتصفحات القديمة، فستحتاج إلى استعمال تقنية "الترقيع المتعدد" (polyfill، توفرها [[CSS]] بمفردها أو مع [[JavaScript|جافاسكربت]]). من المؤكد أنه [https://github.com/Modernizr/Modernizr/wiki/HTML5-Cross-Browser-Polyfills لا يوجد نقص في الحلول لذلك]، على الرغم من أن الطريقة الشائعة في الوقت الحالي هي [https://modernizr.com/ Modernizr]، والتي توفر طريقة بسيطة لإضافة وظائف تعتمد على وجود ميزات HTML5 المكتشفة.
  
 
'''تنويه''': إذا كنت تستخدم حقول إدخال كلمة المرور (لأي غرض)، فقد ترغب في ضبط تطبيقك لمنع قراءة وتسجيل مثل هذه المعاملات. يمكنك تعلم ذلك في دليل [[Rails/security|تأمين تطبيقات ريلز]].
 
'''تنويه''': إذا كنت تستخدم حقول إدخال كلمة المرور (لأي غرض)، فقد ترغب في ضبط تطبيقك لمنع قراءة وتسجيل مثل هذه المعاملات. يمكنك تعلم ذلك في دليل [[Rails/security|تأمين تطبيقات ريلز]].
  
== التعامل مع كائنات النموذج (Model Objects) ==
+
== التعامل مع كائنات النموذج ==
  
=== مساعدو كائن النموذج (Model Object) ===
+
=== مساعدو كائن النموذج ===
من المهام الشائعة الخاصة بالنموذج هو تعديل أو إنشاء كائن نموذج (model object، سيذكر المصطلح الأجنبي في هذا الدليل دومًا لمنع الالتباس مع المصطلح "نموذج" [form] الذي لا يمت بصلة للمصطلح "نموذج" [model]). على الرغم من إمكانية استخدام المساعدين <code>tag_*‎</code> لأداء هذه المهمة، إلا أنه حل طويل ومضجر بعض الشيء، إذ يجب لكل وسم التأكد من استخدام اسم المعامل الصحيح وتعيين القيمة الافتراضية لعنصر الإدخال بشكل مناسب. يوفر ريلز مساعدين مصممين خصيصًا لهذه المهمة. هؤلاء المساعدون يفتقرون إلى الاحقة <code>tag_</code>، مثل <code>text_field</code> و <code>text_area</code>.
+
من المهام الشائعة الخاصة بالاستمارة هو تعديل أو إنشاء كائن نموذج (Model Object). على الرغم من إمكانية استخدام المساعدين <code>tag_*‎</code> لأداء هذه المهمة، إلا أنه حل طويل ومضجر بعض الشيء، إذ يجب لكل وسم التأكد من استخدام اسم المعامل الصحيح وتعيين القيمة الافتراضية لعنصر الإدخال بشكل مناسب. يوفر ريلز مساعدين مصممين خصيصًا لهذه المهمة. هؤلاء المساعدون يفتقرون إلى اللاحقة <code>tag_</code>، مثل <code>text_field</code> و <code>text_area</code>.
  
 
بالنسبة إلى هؤلاء المساعدين، يكون المعامل الأول هو اسم متغير نسخة والثاني هو اسم تابع (عادةً خاصية) لاستدعائه مع ذلك الكائن. سيعين ريلز قيمة [[HTML/input|عنصر الإدخال]] إلى القيمة التي يعيدها ذلك التابع للكائن ثم سيعيِّن اسمًا مناسبًا للعنصر. إذا كان المتحكم الخاصة بك قد عرَّف ‎<code>@person</code> واسم ذلك الشخص هو "هنري" (Henry) مثلًا، فإن النموذج الذي يحتوي على:<syntaxhighlight lang="html">
 
بالنسبة إلى هؤلاء المساعدين، يكون المعامل الأول هو اسم متغير نسخة والثاني هو اسم تابع (عادةً خاصية) لاستدعائه مع ذلك الكائن. سيعين ريلز قيمة [[HTML/input|عنصر الإدخال]] إلى القيمة التي يعيدها ذلك التابع للكائن ثم سيعيِّن اسمًا مناسبًا للعنصر. إذا كان المتحكم الخاصة بك قد عرَّف ‎<code>@person</code> واسم ذلك الشخص هو "هنري" (Henry) مثلًا، فإن النموذج الذي يحتوي على:<syntaxhighlight lang="html">
سطر 151: سطر 154:
 
</syntaxhighlight>سيولد مخرجات شبيه بالمخرجات التالية:<syntaxhighlight lang="html">
 
</syntaxhighlight>سيولد مخرجات شبيه بالمخرجات التالية:<syntaxhighlight lang="html">
 
<input id="person_name" name="person[name]" type="text" value="Henry"/>
 
<input id="person_name" name="person[name]" type="text" value="Henry"/>
</syntaxhighlight>عند إرسال النموذج، ستُخَزَّن القيمة التي أدخلها المستخدم في <code>[params[:person][:name</code>. إنَّ <code>params[:person]‎</code> ذي النوع <code>[[Ruby/Hash|Hash]]</code> مناسبٌ لتمريره إلى <code>Person.new</code>، أو إلى ‎<code>@person.update</code> إذا كان <code>person@</code> نسخةً من <code>Person</code>. في حين أن اسم الخاصية يستعمل بشكل شائع مع المعامل الثاني المُمرَّر إلى هؤلاء المساعدين، فهذا ليس إلزاميًا. في المثال أعلاه، طالما أنَّ الكائنات <code>person</code> لها اسم (name) وتابع <code>name=‎</code>، فإن ريلز ستكون سعيدة.
+
</syntaxhighlight>عند إرسال الاستمارة، ستُخَزَّن القيمة التي أدخلها المستخدم في <code>[params[:person][:name</code>. إنَّ <code>params[:person]‎</code> ذي النوع <code>[[Ruby/Hash|Hash]]</code> مناسبٌ لتمريره إلى <code>Person.new</code>، أو إلى ‎<code>@person.update</code> إذا كان <code>person@</code> نسخةً من <code>Person</code>. في حين أن اسم الخاصية يستعمل بشكل شائع مع المعامل الثاني المُمرَّر إلى هؤلاء المساعدين، فهذا ليس إلزاميًا. في المثال أعلاه، طالما أنَّ الكائنات <code>person</code> لها اسم (name) وتابع <code>name=‎</code>، فإن ريلز ستكون سعيدة.
  
 
'''تحذير''': يجب عليك تمرير اسم متغير نسخة، أي <code>:person</code> أو <code>"person"</code>، وليس نسخةً فعلية لكائن النموذج (model object) الخاص بك.
 
'''تحذير''': يجب عليك تمرير اسم متغير نسخة، أي <code>:person</code> أو <code>"person"</code>، وليس نسخةً فعلية لكائن النموذج (model object) الخاص بك.
  
يوفر ريلز مساعدين لعرض أخطاء التحقق من الصحة المقترنة مع كائن نموذجٍ (model object). جرى الحديث عن ذلك بالتفصيل في دليل [[Rails/active record validations|عمليات التحقق من السجل الفعال]].
+
يوفر ريلز مساعدين لعرض أخطاء التحقق من الصحة المقترنة مع كائن نموذجٍ (model object). جرى الحديث عن ذلك بالتفصيل في دليل [[Rails/active record validations|عمليات التحقق من Active Record]].
  
 
=== ربط نموذج بكائن ===
 
=== ربط نموذج بكائن ===
صحيح أن هذا مريحٌ أكثر إلا أنه ليس السلوك المثالي. إذا كان لدى <code>Person</code> العديد من الخاصيات المراد تعديلها، فسنكرر حينئذٍ اسم الكائن المعدَّل عدة مرات. ما نريد القيام به هو ربط نموذج بكائن نموذج (model object)، وهو ما يفعله <code>form_for</code> بالضبط.
+
صحيح أن هذا مريحٌ أكثر إلا أنه ليس السلوك المثالي. إذا كان لدى <code>Person</code> العديد من الخاصيات المراد تعديلها، فسنكرر حينئذٍ اسم الكائن المعدَّل عدة مرات. ما نريد القيام به هو ربط استمارة بكائن نموذج، وهو ما يفعله <code>form_for</code> بالضبط.
  
 
افترض أنَّه لدينا متحكم للتعامل مع المقالات في الملف app/controllers/articles_controller.rb:<syntaxhighlight lang="html">
 
افترض أنَّه لدينا متحكم للتعامل مع المقالات في الملف app/controllers/articles_controller.rb:<syntaxhighlight lang="html">
سطر 172: سطر 175:
 
</syntaxhighlight>هناك بعض الأشياء التي يجب ملاحظتها هنا:
 
</syntaxhighlight>هناك بعض الأشياء التي يجب ملاحظتها هنا:
 
* ‎<code>@article</code> هو الكائن الفعلي الذي يجري تعديله.
 
* ‎<code>@article</code> هو الكائن الفعلي الذي يجري تعديله.
* هناك [[Ruby/Hash|جدول Hash]] واحد من الخيارات. تُمرر خيارات التوجيه في <code>:url</code> ذي النوع <code>[[Ruby/Hash|Hash]]</code>، وتُمرر خيارات [[HTML]] في <code>:html</code> ذي النوع <code>[[Ruby/Hash|Hash]]</code>. كما يمكنك توفير الخيار <code>:namespace</code> لنموذجك لضمان كون الخاصيات id فريدة في عناصر النموذج. ستضاف شرطة سفلية بادئة للخاصية <code>namespace</code> في الخاصيات <code>id</code> في شيفرة HTML المولدة.
+
* هناك [[Ruby/Hash|جدول Hash]] واحد من الخيارات. تُمرر خيارات التوجيه في <code>:url</code> ذي النوع <code>[[Ruby/Hash|Hash]]</code>، وتُمرر خيارات [[HTML]] في <code>:html</code> ذي النوع <code>[[Ruby/Hash|Hash]]</code>. كما يمكنك توفير الخيار <code>:namespace</code> لنموذجك لضمان كون الخاصيات id فريدة في عناصر الاستمارة. ستضاف شرطة سفلية بادئة للخاصية <code>namespace</code> في الخاصيات <code>id</code> في شيفرة HTML المولدة.
* تنتج الدالة <code>form_for</code> كائنًا بانيًا لنموذج (المتغير <code>f</code>).
+
* تنتج الدالة <code>form_for</code> كائنًا بانيًا للاستمارة (المتغير <code>f</code>).
* تُستدعَى توابع إنشاء عناصر التحكم بالنموذج في كائن باني النموذج <code>f</code>.
+
* تُستدعَى توابع إنشاء عناصر التحكم بالاستمارة في كائن باني النموذج <code>f</code>.
 
شيفرة HTML الناتجة هي:<syntaxhighlight lang="html">
 
شيفرة HTML الناتجة هي:<syntaxhighlight lang="html">
 
<form class="nifty_form" id="new_article" action="/articles" accept-charset="UTF-8" method="post">
 
<form class="nifty_form" id="new_article" action="/articles" accept-charset="UTF-8" method="post">
سطر 183: سطر 186:
 
   <input type="submit" name="commit" value="Create" data-disable-with="Create" />
 
   <input type="submit" name="commit" value="Create" data-disable-with="Create" />
 
</form>
 
</form>
</syntaxhighlight>الاسم الذي مُرِّر إلى <code>form_for</code> يتحكم في المفتاح المستخدم في <code>params</code> للوصول إلى قيم النموذج. الاسم في حالتنا هذه هو <code>article</code> وحتى كل عناصر الإدخال لها الأسماء <code>article[attribute_name]‎</code> الخاصة بالنموذج. وبناءً على ذلك، ستكون <code>params[:article]‎</code> في الإجراء <code>create</code> جدول <code>[[Ruby/Hash|Hash]]</code> يحوي المفتاح <code>title:</code> و <code>body:</code>. يمكنك قراءة المزيد حول أهمية أسماء عناصر الإدخال في القسم "[[Rails/form helpers#.D9.81.D9.87.D9.85 .D8.A7.D9.84.D8.B9.D8.B1.D9.81 .D8.A7.D9.84.D9.85.D8.AA.D8.A8.D8.B9 .D9.81.D9.8A .D8.AA.D8.B3.D9.85.D9.8A.D8.A9 .D8.A7.D9.84.D9.85.D8.B9.D8.A7.D9.85.D9.84.D8.A7.D8.AA|فهم العرف المتبع في تسمة المعاملات]]".
+
</syntaxhighlight>الاسم الذي مُرِّر إلى <code>form_for</code> يتحكم في المفتاح المستخدم في <code>params</code> للوصول إلى قيم الاستمارة. الاسم في حالتنا هذه هو <code>article</code> وحتى كل عناصر الإدخال لها الأسماء <code>article[attribute_name]‎</code> الخاصة بالنموذج. وبناءً على ذلك، ستكون <code>params[:article]‎</code> في الإجراء <code>create</code> جدول <code>[[Ruby/Hash|Hash]]</code> يحوي المفتاح <code>title:</code> و <code>body:</code>. يمكنك قراءة المزيد حول أهمية أسماء عناصر الإدخال في القسم "[[Rails/form helpers#.D9.81.D9.87.D9.85 .D8.A7.D9.84.D8.B9.D8.B1.D9.81 .D8.A7.D9.84.D9.85.D8.AA.D8.A8.D8.B9 .D9.81.D9.8A .D8.AA.D8.B3.D9.85.D9.8A.D8.A9 .D8.A7.D9.84.D9.85.D8.B9.D8.A7.D9.85.D9.84.D8.A7.D8.AA|فهم العرف المتبع في تسمة المعاملات]]".
  
توابع المساعد التي تستدعى مع باني النموذج تتشابه مع مساعدي كائن النموذج (model object) باستثناء أنه ليس من الضروري تحديد أي كائن يُعَدَّل لأن ذلك يُدار بالفعل بواسطة باني النموذج.
+
توابع المساعد التي تستدعى مع باني النموذج تتشابه مع مساعدي كائن النموذج (model object) باستثناء أنه ليس من الضروري تحديد أي كائن يُعَدَّل لأن ذلك يُدار بالفعل بواسطة باني الاستمارة.
  
يمكنك إنشاء ربط مشابه بدون إنشاء الوسوم <code>[[HTML/form|<form>]]</code> بالفعل مع المساعد <code>field_for</code>. هذا مفيد لتعديل كائنات نموذج إضافي (additional model objects) مع نفس النموذج. على سبيل المثال، إذا كان لديك النموذج <code>Person</code> (model)‎ مع النموذج <code>ContactDetail</code> (model)‎ المرتبط، فيمكنك إنشاء نموذج لإنشاء كليهما بالشكل التالي:<syntaxhighlight lang="html">
+
يمكنك إنشاء ربط مشابه بدون إنشاء الوسوم <code>[[HTML/form|<form>]]</code> بالفعل مع المساعد <code>field_for</code>. هذا مفيد لتعديل كائنات نموذج إضافي (additional model objects) مع نفس الاستمارة. على سبيل المثال، إذا كان لديك النموذج <code>Person</code>‎ مع النموذج <code>ContactDetail</code> المرتبط، فيمكنك إنشاء استمارة لإنشاء كليهما بالشكل التالي:<syntaxhighlight lang="html">
 
<%= form_for @person, url: {action: "create"} do |person_form| %>
 
<%= form_for @person, url: {action: "create"} do |person_form| %>
 
   <%= person_form.text_field :name %>
 
   <%= person_form.text_field :name %>
سطر 204: سطر 207:
  
 
=== الاعتماد على تعريف السجل ===
 
=== الاعتماد على تعريف السجل ===
النموذج <code>Article</code> (model)‎ متاح مباشرة لمستخدمي التطبيق، لذلك - ولاتباع أفضل ممارسات التطوير مع ريلز - يجب عليك أن تصرِّح على أنه مورد (resource):<syntaxhighlight lang="rails">
+
النموذج <code>Article</code>‎ متاح مباشرة لمستخدمي التطبيق، لذلك - ولاتباع أفضل ممارسات التطوير مع ريلز - يجب عليك أن تصرِّح على أنه مورد (resource):<syntaxhighlight lang="rails">
 
resources :articles
 
resources :articles
 
</syntaxhighlight>'''تنبيه''': التصريح عن مورد له عدد من الآثار الجانبية. اطلع على قسم [[Rails/routing#.D8.AA.D9.88.D8.AC.D9.8A.D9.87 .D8.A7.D9.84.D9.85.D9.88.D8.B1.D8.AF: .D8.A7.D9.84.D8.A7.D9.81.D8.AA.D8.B1.D8.A7.D8.B6.D9.8A .D9.81.D9.8A .D8.B1.D9.8A.D9.84.D8.B2|توجيه المورد في دليل التوجيه]] لمزيد من المعلومات حول إعداد الموارد واستخدامها.
 
</syntaxhighlight>'''تنبيه''': التصريح عن مورد له عدد من الآثار الجانبية. اطلع على قسم [[Rails/routing#.D8.AA.D9.88.D8.AC.D9.8A.D9.87 .D8.A7.D9.84.D9.85.D9.88.D8.B1.D8.AF: .D8.A7.D9.84.D8.A7.D9.81.D8.AA.D8.B1.D8.A7.D8.B6.D9.8A .D9.81.D9.8A .D8.B1.D9.8A.D9.84.D8.B2|توجيه المورد في دليل التوجيه]] لمزيد من المعلومات حول إعداد الموارد واستخدامها.
  
عند التعامل مع موارد RESTful، يمكن أن تصبح استدعاءات <code>form_for</code> أسهل بكثير إذا كنت تعتمد على تعريف السجل. باختصار، يمكنك فقط تمرير نسخة النموذج (model) وترك الباقي على ريلز مثل اكتشاف اسم النموذج (model):<syntaxhighlight lang="rails">
+
عند التعامل مع موارد RESTful، يمكن أن تصبح استدعاءات <code>form_for</code> أسهل بكثير إذا كنت تعتمد على تعريف السجل. باختصار، يمكنك فقط تمرير نسخة النموذج (model) وترك الباقي على ريلز مثل اكتشاف اسم النموذج:<syntaxhighlight lang="rails">
 
## إنشاء مقالة جديدة
 
## إنشاء مقالة جديدة
 
# نمط طويل
 
# نمط طويل
سطر 220: سطر 223:
 
# نمط قصير
 
# نمط قصير
 
form_for(@article)
 
form_for(@article)
</syntaxhighlight>لاحظ كيف أن النمط القصير لاستدعاء <code>form_for</code> يؤدي نفس الغرض الذي يفعله استدعاء التابع نفسه في النمط الطويل، بغض النظر عن كون السجل جديدًا أو موجودًا. تعريف السجل ذكي بما فيه الكفاية لمعرفة ما إذا كان السجل جديدًا عبر استدعاء <code>?record.new_record</code>. كما أنه يحدد المسار الصحيح لإرسال النموذج إليه والاسم استنادًا على صنف الكائن.
+
</syntaxhighlight>لاحظ كيف أن النمط القصير لاستدعاء <code>form_for</code> يؤدي نفس الغرض الذي يفعله استدعاء التابع نفسه في النمط الطويل، بغض النظر عن كون السجل جديدًا أو موجودًا. تعريف السجل ذكي بما فيه الكفاية لمعرفة ما إذا كان السجل جديدًا عبر استدعاء <code>?record.new_record</code>. كما أنه يحدد المسار الصحيح لإرسال الاستمارة إليه والاسم استنادًا على صنف الكائن.
  
سيعيّن ريلز أيضًا الصنف <code>class</code> والمعرف <code>id</code> للنموذج تلقائيًا بشكل مناسب: النموذج الذي ينشئ مقالًا سيكون له الصنف والمعرف <code>new_article</code>. إذا كنت تعدل مقالة ذات المعرف 23، فسيُعيَّن الصنف إلى   <code>edit_article</code> والمعرف إلى <code>edit_article_23</code>. سيتم حذف هاتين الخاصيتين للإختصار في بقية هذا الدليل.
+
سيعيّن ريلز أيضًا الصنف <code>class</code> والمعرف <code>id</code> للنموذج تلقائيًا بشكل مناسب: الاستمارة التي تنشئ مقالًا سيكون لها الصنف والمعرف <code>new_article</code>. إذا كنت تعدل مقالة ذات المعرف 23، فسيُعيَّن الصنف إلى <code>edit_article</code> والمعرف إلى <code>edit_article_23</code>. سيتم حذف هاتين الخاصيتين للإختصار في بقية هذا الدليل.
  
'''تحذير''': عندما تستخدم STI ([[Rails/association basics#.D9.88.D8.B1.D8.A7.D8.AB.D8.A9 .D8.A7.D9.84.D8.AC.D8.AF.D9.88.D9.84 .D8.A7.D9.84.D9.88.D8.AD.D9.8A.D8.AF|وراثة الجدول الوحيد]]) مع نماذجك (models)، فلا يمكنك الاعتماد على تعريف السجل في صنف فرعي بمجرد التصريح عن الصنف الأب بأنه موردٌ. سيتعين عليك حينئذٍ تحديد اسم النموذج مع الخيارين <code>:url</code> و <code>:method</code> بشكل صريح.
+
'''تحذير''': عندما تستخدم STI ([[Rails/association basics#.D9.88.D8.B1.D8.A7.D8.AB.D8.A9 .D8.A7.D9.84.D8.AC.D8.AF.D9.88.D9.84 .D8.A7.D9.84.D9.88.D8.AD.D9.8A.D8.AF|وراثة الجدول الوحيد]]) مع نماذجك (models)، فلا يمكنك الاعتماد على تعريف السجل في صنف فرعي بمجرد التصريح عن الصنف الأب بأنه موردٌ. سيتعين عليك حينئذٍ تحديد اسم الاستمارة مع الخيارين <code>:url</code> و <code>:method</code> بشكل صريح.
  
 
==== التعامل مع مجالات الأسماء ====
 
==== التعامل مع مجالات الأسماء ====
 
إذا أنشئت [[Rails/routing|مسارات توجيه]] ضمن مجالات أسماء (namespaced routes)، فإن <code>form_for</code> يحتوي على اختصار أنيق لذلك أيضًا. إذا كان تطبيقك يحتوي على مجال الاسم <code>admin</code>، فالسطر التالي:<syntaxhighlight lang="rails">
 
إذا أنشئت [[Rails/routing|مسارات توجيه]] ضمن مجالات أسماء (namespaced routes)، فإن <code>form_for</code> يحتوي على اختصار أنيق لذلك أيضًا. إذا كان تطبيقك يحتوي على مجال الاسم <code>admin</code>، فالسطر التالي:<syntaxhighlight lang="rails">
 
form_for [:admin, @article]
 
form_for [:admin, @article]
</syntaxhighlight>سينشئ نموذجًا يرسل إلى <code>ArticleController</code> داخل مجال الاسم <code>admin</code> (يرسل إلى <code>admin_article_path(@article)‎</code> في حالة التحديث). إذا كان لديك عدة مستويات من مجالات الأسماء، فستكون الصياغة مشابهة لما سبق:<syntaxhighlight lang="rails">
+
</syntaxhighlight>سينشئ استمارةً تُرسَل إلى <code>ArticleController</code> داخل مجال الاسم <code>admin</code> (يرسل إلى <code>admin_article_path(@article)‎</code> في حالة التحديث). إذا كان لديك عدة مستويات من مجالات الأسماء، فستكون الصياغة مشابهة لما سبق:<syntaxhighlight lang="rails">
 
form_for [:admin, :management, @article]
 
form_for [:admin, :management, @article]
 
</syntaxhighlight>لمزيد من المعلومات حول توجيه المسارات في ريلز وكل ما يتعلق به، يرجى الاطلاع على [[Rails/routing|دليل التوجيه]].
 
</syntaxhighlight>لمزيد من المعلومات حول توجيه المسارات في ريلز وكل ما يتعلق به، يرجى الاطلاع على [[Rails/routing|دليل التوجيه]].
  
 
=== كيف تعمل النماذج مع الطرائق PATCH أو PUT أو DELETE؟ ===
 
=== كيف تعمل النماذج مع الطرائق PATCH أو PUT أو DELETE؟ ===
يشجع إطار ريلز تصميم RESTful لتطبيقاتك، مما يعني أنك ستجعل الكثير من طلبات HTTP عبر الطريقة "PATCH" و "DELETE" (إلى جانب "GET" و "POST"). ومع ذلك، لا تدعم معظم المتصفحات طرائقَ أخرى غير "GET" و "POST" عندما يتعلق الأمر بإرسال النماذج.
+
يشجع إطار ريلز تصميم RESTful لتطبيقاتك، مما يعني أنك ستجعل الكثير من طلبات HTTP عبر الطريقة "PATCH" و "DELETE" (إلى جانب "GET" و "POST"). ومع ذلك، لا تدعم معظم المتصفحات طرائقَ أخرى غير "GET" و "POST" عندما يتعلق الأمر بإرسال الاستمارات.
  
 
تعمل ريلز حول هذه المشكلة بمحاكاة طرائق أخرى عبر POST باستخدام عنصر إدخال مخفي يسمى "method_"، والذي يعيَّن ليعكس الطريقة المطلوبة:<syntaxhighlight lang="html">
 
تعمل ريلز حول هذه المشكلة بمحاكاة طرائق أخرى عبر POST باستخدام عنصر إدخال مخفي يسمى "method_"، والذي يعيَّن ليعكس الطريقة المطلوبة:<syntaxhighlight lang="html">
سطر 248: سطر 251:
  
 
== إنشاء مربعات اختيار بسهولة ==
 
== إنشاء مربعات اختيار بسهولة ==
تتطلب [[HTML/input/checkbox|مربعات الاختيار]] في [[HTML]] مقدارًا لا بأس به من الشيفرة (عنصر <code>[[HTML/option|<option>]]</code> واحد لكل خيار للاختيار من بينها)، لذلك يكون من المنطقي جدًا أن يُنشَئ ديناميكيًا.
+
تتطلب [[HTML/input/checkbox|مربعات الاختيار]] في [[HTML]] كتابة مقدار لا بأس به من الشيفرة (عنصر <code>[[HTML/option|<option>]]</code> واحد لكل خيار للاختيار من بينها)، لذلك يكون من المنطقي جدًا أن يُنشَئ ديناميكيًا.
  
 
هذا ما ستبدو الشيفرة عليه:<syntaxhighlight lang="html">
 
هذا ما ستبدو الشيفرة عليه:<syntaxhighlight lang="html">
سطر 285: سطر 288:
 
</syntaxhighlight>عندما يرى ريلز أن القيمة الداخلية للخيار المراد إنشاؤه تطابق هذه القيمة، فإنها ستضيف الخاصية <code>selected</code> إلى ذلك الخيار لتحديده.
 
</syntaxhighlight>عندما يرى ريلز أن القيمة الداخلية للخيار المراد إنشاؤه تطابق هذه القيمة، فإنها ستضيف الخاصية <code>selected</code> إلى ذلك الخيار لتحديده.
  
'''تحذير''': عندما ينعدم الخيار <code>include_blank:</code> أو <code>prompt:</code>، فستعين قيمة <code>include_blank:</code> إلى القيمة <code>true</code> إذا كانت خاصية الاختيار <code>required</code> هي <code>true</code>، ويكون حجم العرض <code>size</code> واحدًا والمتعدد <code>multiple</code> ليس <code>true</code>.
+
'''تحذير''': عندما ينعدم الخيار <code>include_blank:</code> أو <code>prompt:</code>، فستعين قيمة <code>include_blank:</code> إلى القيمة <code>true</code> إذا كانت قيمة الخاصية <code>[[HTML/select#required|required]]</code> هي <code>true</code>، والخاصية <code>[[HTML/select#size|size]]</code> هي 1، والخاصية <code>[[HTML/select#multiple|multiple]]</code> ليست <code>true</code>.
  
 
يمكنك إضافة خاصيات عشوائية إلى الخيارات باستخدام جدول <code>[[Ruby/Hash|Hash]]</code>:<syntaxhighlight lang="html">
 
يمكنك إضافة خاصيات عشوائية إلى الخيارات باستخدام جدول <code>[[Ruby/Hash|Hash]]</code>:<syntaxhighlight lang="html">
سطر 298: سطر 301:
 
<option value="2" selected="selected" data-size="3.2 million">Madrid</option>
 
<option value="2" selected="selected" data-size="3.2 million">Madrid</option>
 
...
 
...
</syntaxhighlight>3.2 تحدد مربعات للتعامل مع النماذج
+
</syntaxhighlight>
  
في معظم الحالات ، تكون عناصر التحكم في النموذج مرتبطة بنموذج قاعدة بيانات محدد ، وكما قد تتوقع أن توفر ريلز مساعدات مصممة خصيصًا لهذا الغرض. بما يتفق مع المساعدين الآخرين في النموذج ، عند التعامل مع النماذج ، تسقط لاحقة tag_ من select_tag:
+
=== اختيار الخيارات للتعامل مع النماذج ===
#controller:
+
في معظم الحالات، تكون عناصر التحكم في الاستمارة مرتبطة بنموذج قاعدة بيانات محدد، وكما قد تتوقع أن توفر ريلز مساعدات مصممة خصيصًا لهذا الغرض. بما يتفق مع المساعدين الآخرين في الاستمارة، عند التعامل مع النماذج، تسقط اللاحقة <code>tag_</code> من <code>select_tag</code>:<syntaxhighlight lang="rails">
 +
# controller:
 
@person = Person.new(city_id: 2)
 
@person = Person.new(city_id: 2)
#view:
+
 
 +
</syntaxhighlight><syntaxhighlight lang="rails">
 +
# view:
 
<%= select(:person, :city_id, [['Lisbon', 1], ['Madrid', 2], ...]) %>
 
<%= select(:person, :city_id, [['Lisbon', 1], ['Madrid', 2], ...]) %>
 +
</syntaxhighlight>لاحظ أن المعامل الثالث، مصفوفة الخيارات، هو نفس نوع الوسيط المُمرَّر إلى <code>options_for_select</code>. ميزة واحدة هنا هي أنه لا داعي للقلق بشأن التحديد المسبق للمدينة الصحيحة إذا كانت مدينة المستخدم معروفة مسبقًا. ريلز سيقوم بذلك نيابة عنك من خلال القراءة من الخاصية <code>person.city_id@</code>.
  
لاحظ أن المعامل الثالث، مصفوفة الخيارات، هو نفس النوع من الوسيط الذي ينتقل إلى options_for_select. ميزة واحدة هنا هي أنه لا داعي للقلق بشأن التحديد المسبق للمدينة الصحيحة إذا كان المستخدم لديه بالفعل ريلز سيقوم بذلك نيابة عنك من خلال القراءة من سمة person.city_id@.
+
كما هو الحال مع المساعدين الآخرين، إذا كنت ستستخدم المساعد <code>select</code> في أداة إنشاء الاستمارات التي حُدِّدَ نطاقها إلى الكائن ‎<code>@person</code>، فستكون الصياغة كالتالي:<syntaxhighlight lang="rails">
 
+
# select on a form builder
كما هو الحال مع المساعدين الآخرين، إذا كنت ستستخدم المساعد المحدد في أداة إنشاء النماذج التي حُدِّدَ نطاقها على كائن @person، فسيكون طريقة الكتابة كالتالي:
 
#select on a form builder
 
 
<%= f.select(:city_id, ...) %>
 
<%= f.select(:city_id, ...) %>
 
+
</syntaxhighlight>يمكنك أيضًا تمرير كتلة إلى المساعد <code>select</code>:<syntaxhighlight lang="html">
يمكنك أيضًا تمرير الحظر لتحديد المساعد:
 
 
 
 
<%= f.select(:city_id) do %>
 
<%= f.select(:city_id) do %>
 
+
  <% [['Lisbon', 1], ['Madrid', 2]].each do |c| -%>
 <% [['Lisbon', 1], ['Madrid', 2]].each do |c| -%>
+
    <%= content_tag(:option, c.first, value: c.last) %>
 
+
  <% end %>
   <%= content_tag(:option, c.first, value: c.last) %>
 
 
 
 <% end %>
 
 
 
 
<% end %>
 
<% end %>
 +
</syntaxhighlight>'''تحذير''': إذا كنت تستخدم <code>select</code> (أو مساعدين مشابهين له مثل <code>collection_select</code>، أو <code>select_tag</code>) لتعيين [[Rails/association basics#.D8.A7.D8.B1.D8.AA.D8.A8.D8.A7.D8.B7 .D8.A7.D9.84.D8.A7.D9.86.D8.AA.D9.85.D8.A7.D8.A1 .28belongs to.29|الارتباط <code>belongs_to</code>]]، فيجب عليك تمرير اسم المفتاح الخارجي (هو <code>city_id</code> في المثال أعلاه)، وليس اسم الارتباط نفسه. إذا قمت بتحديد <code>city</code> بدلًا من <code>city_id</code> فسيرمي [[Rails/active record|Active Record]] الخطأ:<syntaxhighlight lang="text">
 +
ActiveRecord::AssociationTypeMismatch: City(#17815740) expected, got String(#1138750)
 +
</syntaxhighlight>عند تمرير <code>params</code> إلى <code>Person.new</code> أو <code>update</code>. طريقة أخرى للتعامل مع هذا هي أن مساعدي الاستمارة تعدل الخاصيات. يجب أيضًا أن تكون على دراية بتداعيات الأمان المحتملة الخاصة بالسماح للمستخدمين بتعديل المفاتيح الخارجية مباشرةً.
  
تحذير: إذا كنت تستخدم اختيارًا (أو مساعدين مشابهين مثل collection_select ،select_tag) لتعيين اقترانات belongs_to يجب عليك تمرير اسم المفتاح الخارجي (في المثال أعلاه city_id) ، وليس اسم الاقتران نفسه. إذا قمت بتحديد المدينة بدلاً من city_id فسيرفع Active Record خطأ على طول خطوط ActiveRecord :: AssociationTypeMismatch: City (# 17815740) المتوقعة، احصل على سلسلة (1138750#) عند تمرير التجزئة params إلى Person.new أو تحديث. طريقة أخرى للنظر إلى هذا هو أن مساعدين النموذج فقط تعدل السمات. يجب أيضًا أن تكون على دراية بتداعيات الأمان المحتملة الخاصة بالسماح للمستخدمين بتعديل المفاتيح الخارجية مباشرةً.
+
=== الوسوم <code>[[HTML/option|<option>]]</code> من مجموعة من كائنات اعتباطية ===
 
+
يتطلب توليد الوسوم <code>[[HTML/option|<option>]]</code> باستخدام المساعد <code>options_for_select</code> إنشاء مصفوفة تحتوي على [[HTML/option#label|النص]] و<nowiki/>[[HTML/option#value|القيمة]] لكل خيار. ولكن ماذا لو كان لديك نموذجًا مثل النموذج <code>City</code> (ربما أحد [[Rails/active record|Active Record]]) وكنت تريد توليد وسوم <code>[[HTML/option|<option>]]</code> من مجموعة من تلك الكائنات؟ قد يكون أحد الحلول هو إنشاء مصفوفة متشعبة منهم:<syntaxhighlight lang="html">
== 3.3 وسوم اختيارية من مجموعة من كائنات افتراضية ==
 
يتطلب إنشاء وسوم الخيارات باستخدام options_for_select إنشاء مصفوفة تحتوي على النص والقيمة لكل خيار. ولكن ماذا لو كان لديك نموذج مدينة (ربما أحد السجلات النشطة) وكنت تريد إنشاء وسوم اختيار من مجموعة من تلك الكائنات؟ قد يكون أحد الحلول هو إنشاء مصفوفة متداخلة بتكرارها:
 
 
 
 
<% cities_array = City.all.map { |city| [city.name, city.id] } %>
 
<% cities_array = City.all.map { |city| [city.name, city.id] } %>
 
 
<%= options_for_select(cities_array) %>
 
<%= options_for_select(cities_array) %>
 
+
</syntaxhighlight>هذا حل صالح تمامًا، ولكن ريلز يوفر بديلًا أسهل وأوضح وذلك عبر المساعد <code>options_from_collection_for_select</code>. يتوقع هذا المساعد تمرير مجموعة من الكائنات العشوائية إليه بالإضافة إلى وسيطين آخرين هما: أسماء الدوال لقراءة قيمة ونص الخيار على التوالي:<syntaxhighlight lang="html">
هذا حل صالح تمامًا، ولكن ريلز يوفر بديلاً أقل وضوحًا: options_from_collection_for_select. يتوقع هذا المساعد مجموعة من الكائنات العشوائية ووسيطين إضافيَّين: أسماء الدوال لقراءة قيمة الخيار والنص، على التوالي:
 
 
 
 
<%= options_from_collection_for_select(City.all, :id, :name) %>
 
<%= options_from_collection_for_select(City.all, :id, :name) %>
 
+
</syntaxhighlight>كما يوحي الاسم، هذا يولد فقط الوسوم <code>[[HTML/option|<option>]]</code>. لتوليد مربع تحديد كامل، ستحتاج إلى استخدامه بالتزامن مع المساعد <code>select_tag</code>، تمامًا كما تفعل مع <code>options_for_select</code>. عند العمل مع كائنات النموذج، تمامًا مثلما يجمع <code>select</code> بين <code>select_tag</code> و <code>options_for_select</code>، يجمع <code>collection_select</code> بين <code>select_tag</code> و <code>options_from_collection_for_select</code>.<syntaxhighlight lang="rails">
كما يوحي الاسم، هذا يولد فقط وسوم الخيار. لإنشاء مربع تحديد يعمل، ستحتاج إلى استخدامه بالتزامن مع select_tag، تمامًا كما تفعل مع options_for_select. عند العمل مع كائنات النموذج، تمامًا مثل الجمع بين select_tag وoptions_for_select، يجمع collection_select select_tag مع options_from_collection_for_select.
 
 
 
 
<%= collection_select(:person, :city_id, City.all, :id, :name) %>
 
<%= collection_select(:person, :city_id, City.all, :id, :name) %>
  
كما هو الحال مع المساعدين الآخرين، إذا كنت ستستخدم المساعد collection_select على أداة إنشاء النموذج التي حُدّدت على الكائن person@، فستكون بنية الجملة:
+
</syntaxhighlight>كما هو الحال مع المساعدين الآخرين، إذا كنت ستستخدم المساعد <code>collection_select</code> على باني الاستمارة حُدّدت إلى الكائن <code>person@</code>، فستكون الصياغة بالشكل التالي:<syntaxhighlight lang="html">
 
+
<%= f.collection_select(:city_id, City.all, :id, :name) %>
<%= f.collection_select(:city_id, City.all, :id, :name) %>
+
</syntaxhighlight>'''ملاحظة''': يجب أن تملك الأزواج التي مُرِّرَتْ إلى <code>options_for_select</code> اسمًا أولًا ومُعرِّفًا ثانيًا، ولكن مع <code>options_from_collection_for_select</code> الوسيط الأول هو التابع <code>value</code> والثاني هو التابع <code>text</code>.
 
 
للتلخيص، options_from_collection_for_select هو ل  collection_select ما options_for_select لتحديده.
 
 
 
ملاحظة: يجب أن يكون للأزواج التي مُرِّرَتْ إلى options_for_select اسم أولًا ومُعرِّف ثانيًا، ولكن مع options_from_collection_for_select الوسيط الأول هو دالة القيمة والثاني هو دالة النص.
 
 
 
== 3.4 اختيار المنطقة الزمنية والبلد ==
 
للاستفادة من دعم المنطقة الزمنية في ريلز، يجب عليك أن تسأل المستخدمين عن المنطقة الزمنية الموجودين فيها. سيتطلب القيام بذلك توليد خيارات تحديد من قائمة كائنات TimeZone المُعرَّفة مسبقًا باستخدام collection_select ، ولكن يمكنك ببساطة استخدام time_zone_select المساعد بالفعل يغطي هذا:
 
 
 
<%= time_zone_select(:person, :time_zone) %>
 
  
هناك أيضًا المساعد time_zone_options_for_select  لطريقة أكثر استخدامًا (لذلك أكثر قابلية للتخصيص) للقيام بذلك. اقرأ توثيق API للتعرف على الوسائط الممكنة لهاتين الدالتين.
+
=== اختيار المنطقة الزمنية والبلد ===
 +
للاستفادة من دعم المنطقة الزمنية في ريلز، يجب عليك أن تسأل المستخدمين عن المنطقة الزمنية الموجودين فيها. سيتطلب القيام بذلك توليد خيارات تحديد من قائمة كائنات معرفة سابقًا من النوع <code>TimeZone</code> باستخدام <code>collection_select</code>، ولكن يمكنك ببساطة استخدام المساعد <code>time_zone_select</code> الذي يفعل لك ذلك:<syntaxhighlight lang="html">
 +
<%= time_zone_select(:person, :time_zone) %>
 +
</syntaxhighlight>هناك أيضًا المساعد <code>time_zone_options_for_select</code> الذي يوفر طريقة يدوية للقيام بنفس الأمر وبالتالي يوفر قابلية للتخصيص. اقرأ [http://api.rubyonrails.org/v5.2.2/classes/ActionView/Helpers/FormOptionsHelper.html#method-i-time_zone_options_for_select توثيق الواجهة البرمجية] للتعرف على الوسائط التي تمرَّر إلى هذين التابعين.
  
يُستخدم ريلز لاحتوائه المساعد country_select لاختيار البلاد، ولكن استُبعِد هذا لاستخدام الإضافة country_select. عند استخدام هذا، يجب أن تدرك أن استبعاد أو إدراج أسماء معينة من القائمة يمكن أن يكون خِلافِيًّا (وكان هذا هو السبب وراء استبعاد هذه الدالة من ريلز).
+
اعتاد ريلز على احتوائه على المساعد <code>country_select</code> لتحديد بلد معين من بين مجموعة من البلدان، ولكن جمِّع ذلك في [https://github.com/stefanpenner/country_select الإضافة <code>country_select</code>]. عند استخدام هذه الإضافة، يجب أن تدرك أن استبعاد أو إدراج أسماء بلدان معينة من القائمة يمكن أن يكون خِلافِيًّا (وكان هذا هو السبب وراء استبعاد المساعد من ريلز وتحويله إلى إضافة).
  
= 4 استخدام مساعدي نموذج التاريخ والوقت =
+
== استخدام حقول التاريخ والوقت في الاستمارة ==
يمكنك اختيار عدم استخدام مساعدي النموذج الذين ينشؤون حقول HTML5 لإدخال التاريخ والوقت واستخدام مساعدي التاريخ والوقت البدلاء. يختلف مساعدو التاريخ والوقت هؤلاء عن جميع المساعدين الآخرين في اثنين من النواحي الهامة:
+
يمكنك اختيار عدم استخدام مساعدي الاستمارة الذين ينشؤون حقول HTML5 لإدخال التاريخ والوقت واستخدام مساعدي التاريخ والوقت البدلاء. يختلف مساعدو التاريخ والوقت هؤلاء عن جميع المساعدين الآخرين في ناحيتين مهمتين هما:
* لا يمكن تمثيل التواريخ والأوقات من خلال عنصر إدخال فردي. بدلاً من ذلك لديك عدة، واحد لكل مكون (السنة، الشهر، اليوم، وما إلى ذلك) وبالتالي لا توجد قيمة واحدة في تجزئة المعاملات الخاصة بك مع التاريخ أو الوقت.
+
* لا يمكن تمثيل التواريخ والأوقات من خلال عنصر إدخال واحد فقط. بدلًا من ذلك لديك عدة عناصر، واحد لكل مكون (السنة، الشهر، اليوم، وما إلى ذلك) وبالتالي لا توجد قيمة واحدة في <code>params</code> الخاص بك تمثل التاريخ أو الوقت.
* يستخدم المساعدون الآخرون لاحقة _tag لتوضيح ما إذا كان المساعد مساعدًا مجردًا أو يعمل على كائنات نموذجية. مع التواريخ والأوقات، select_date و select_time و select_datetime هم مساعدون مجردون، وdate_select ، وtime_select وdatetime_select هم مساعدو كائنات نموذج مكافئ.
+
* يستخدم المساعدون الآخرون اللاحقة ‎<code>_tag</code> لتوضيح ما إذا كان المساعد مساعدًا مجردًا (barebones) أو يعمل مع كائنات نموذج (model objects). مع التواريخ والأوقات، المساعد <code>select_date</code> و <code>select_time</code> و <code>select_datetime</code> هم مساعدون مجردون، و <code>date_select</code>، و <code>time_select</code> و <code>datetime_select</code> هم مساعدو كائنات نموذج مكافئة.
كل من هذه العائلات من المساعدين ستُنشِئ سلسلة من صناديق الإختيار للعناصر المختلفة (السنة، الشهر، اليوم، الخ).
+
ستُنشِئ كلا هاتين العائلتين من المساعدين سلسلة من مربعات الاختيار للعناصر المختلفة (السنة، الشهر، اليوم، ...إلخ).
 
 
== 4.1 المساعدون المجردون ==
 
تأخذ مجموعة المساعدين select_ * كوسيطهم الأول مثيلًا للتاريخ أو الوقت أو التاريخ الذي يُستخدم كقيمة محددة حاليًا. يمكنك حذف هذا المعامل، وفي هذه الحالة يُستخدم التاريخ الحالي. فمثلا:
 
 
 
<%= select_date Date.today, prefix: :start_date %>
 
 
 
يخرج (مع حذف قيم الخيار الفعلية للإيجاز)
 
  
 +
=== المساعدون المجردون ===
 +
تأخذ مجموعة المساعدين <code>select_*‎</code> كوسيطهم الأول نسخة من <code>Date</code> أو <code>Time</code> أو <code>DateTime</code> التي تُستخدَم كقيمة محددة حاليًا. يمكنك حذف هذا المعامل، وفي هذه الحالة يُستخدم التاريخ الحالي. فمثلًا:<syntaxhighlight lang="rails">
 +
<%= select_date Date.today, prefix: :start_date %>
 +
</syntaxhighlight>يولد (مع حذف قيم الخيار الفعلية للإيجاز):<syntaxhighlight lang="rails">
 
<select id="start_date_year" name="start_date[year]"> ... </select>
 
<select id="start_date_year" name="start_date[year]"> ... </select>
 
 
<select id="start_date_month" name="start_date[month]"> ... </select>
 
<select id="start_date_month" name="start_date[month]"> ... </select>
 
 
<select id="start_date_day" name="start_date[day]"> ... </select>
 
<select id="start_date_day" name="start_date[day]"> ... </select>
 
+
</syntaxhighlight>تؤدي المدخلات الواردة أعلاه إلى ظهور <code>params[:start_date]‎</code> كجدول <code>[[Ruby/Hash|Hash]]</code> مع المفاتيح <code>year:</code>، و <code>month:</code>، و <code>day:</code>. للحصول على الكائن <code>Date</code> أو <code>Time</code> أو <code>DateTime</code>، يجب عليك استخراج هذه القيم وتمريرها إلى الباني المناسب مثل:<syntaxhighlight lang="rails">
تؤدي المداخلات الواردة أعلاه إلى ظهور المعاملات [start_date:] كالتجزئة مع مفاتيح: year, :month, :day:. للحصول على كائن "التاريخ" أو "الوقت" أو "التاريخ والوقت الفعلي"، يجب عليك استخراج هذه القيم وتمريرها إلى المُنشئ المناسب، على سبيل المثال:
 
 
 
 
Date.civil(params[:start_date][:year].to_i, params[:start_date][:month].to_i, params[:start_date][:day].to_i)
 
Date.civil(params[:start_date][:year].to_i, params[:start_date][:month].to_i, params[:start_date][:day].to_i)
  
الخيار:prefix هو المفتاح المستخدم لاسترداد تجزئة مكونات التاريخ من تجزئة المعاملات. هنا، عُيِّن على start_date ، إذا أُهْمِل، فسيُحدَد افتراضيَّا.
+
</syntaxhighlight>الخيار <code>:prefix</code> هو المفتاح المستخدم لاستعادة جدول <code>[[Ruby/Hash|Hash]]</code> لمكونات التاريخ من <code>params</code>. هنا، ضُبِط إلى <code>start_date</code>؛ إذا حُذِف، فسيُحدَد افتراضيَّا إلى <code>date</code>.
 
 
== 4.2 مساعدو كائن النموذج ==
 
لا يعمل select_date بشكل جيد مع النماذج التي تحدث أو تنشئ كائنات Active Record حيث يتوقع Active Record أن كل عنصر في تجزئة params يتطابق مع سمة واحدة. يقدم مساعدا الكائن النموذجي للتواريخ والأوقات المعاملات بأسماء خاصة؛ عندما يشاهد Active Record معاملات بأسماء من هذا القبيل، فإنه يعلم أنه يجب دمجها مع المعاملات الأخرى وإعطائها لمنشيء مناسب لنوع العمود. فمثلا:
 
 
 
<%= date_select :person, :birth_date %>
 
 
 
يخرج (مع حذف قيم الخيار الفعلية للإيجاز)
 
  
 +
=== مساعدو كائن النموذج ===
 +
لا يعمل <code>select_date</code> بشكل جيد مع الاستمارات التي تحدِّث أو تنشئ كائنات [[Rails/active record|Active Record]] إذ يتوقع [[Rails/active record|Active Record]] أن كل عنصر في <code>params</code> يقابل خاصية واحدة. يرسل مساعدو كائن النموذج (model object helpers) للتواريخ والأوقات المعاملات بأسماء خاصة؛ عندما يشاهد [[Rails/active record|Active Record]] معاملات بأسماء من هذا القبيل، فإنه يعلم أنه يجب دمجها مع المعاملات الأخرى وإعطائها لبانٍ يتناسب مع نوع الحقل (العمود). فمثلًا:<syntaxhighlight lang="html">
 +
<%= date_select :person, :birth_date %>
 +
</syntaxhighlight>ينتج (مع حذف قيم الخيار الفعلية للإيجاز):<syntaxhighlight lang="html">
 
<select id="person_birth_date_1i" name="person[birth_date(1i)]"> ... </select>
 
<select id="person_birth_date_1i" name="person[birth_date(1i)]"> ... </select>
 
 
<select id="person_birth_date_2i" name="person[birth_date(2i)]"> ... </select>
 
<select id="person_birth_date_2i" name="person[birth_date(2i)]"> ... </select>
 
 
<select id="person_birth_date_3i" name="person[birth_date(3i)]"> ... </select>
 
<select id="person_birth_date_3i" name="person[birth_date(3i)]"> ... </select>
 +
</syntaxhighlight>مما يؤدي إلى <code>params</code> مثل:<syntaxhighlight lang="json">
 +
{'person' => {'birth_date(1i)' => '2008', 'birth_date(2i)' => '11', 'birth_date(3i)' => '22'}}
 +
</syntaxhighlight>عندما يُمرَّر هذا إلى <code>Person.new</code> (أو <code>update</code>)، يلاحظ [[Rails/active record|Active Record]] أنه يجب استخدام جميع هذه المعاملات لإنشاء الخاصية <code>birth_date</code> واستخدام المعلومات اللاحقة لتحديد الترتيب الذي يجب أن يمرر هذه المعاملات إلى دوال مثل <code>Date.civil</code>.
  
مما يؤدي إلى تجزئة params مثل
+
=== خيارات مشتركة ===
 
+
تستخدم كلا عائلتي المساعدين نفس مجموعة الدوال الأساسية لتوليد وسوم <code>[[HTML/select|<select>]]</code> فردية، وبالتالي يقبل كلاهما نفس الخيارات إلى حد كبير. على وجه الخصوص، ستولد ريلز بشكل افتراضي في خيارات السنة 5 سنوات على جانبي السنة الحالية. إذا لم يكن هذا نطاقًا مناسبًا، فيمكن تخطي ذلك عبر استعمال الخيارين <code>start_year:</code> و <code>end_year:</code>. للحصول على قائمة شاملة بالخيارات المتاحة، ارجع إلى [http://api.rubyonrails.org/v5.2.2/classes/ActionView/Helpers/DateHelper.html توثيق الواجهة البرمجية].
<nowiki>{'person' => {'birth_date(1i)' => '2008', 'birth_date(2i)' => '11', 'birth_date(3i)' => '22'}}</nowiki>
 
 
 
عندما يُمرر هذا إلى Person.new (أو تحديث)، يلاحظ Active Record أنه يجب استخدام جميع هذه المعاملات لإنشاء سمة birth_date واستخدام المعلومات اللاحقة لتحديد الترتيب الذي يجب أن يمرر هذه المعاملات إلى دوال مثل Date.civil.
 
  
== 4.3 خيارات مشتركة ==
+
كقاعدة عامة، يجب أن تستخدم <code>date_select</code> عند العمل مع كائنات نموذج و <code>select_date</code> في حالات أخرى، مثل استمارة بحث ترشِّح النتائج حسب التاريخ.
تستخدم عائلات المساعدين نفس مجموعة الدوال الأساسية لإنشاء وسوم اختيار فردية، وبالتالي يقبل كلاهما نفس الخيارات إلى حد كبير. على وجه الخصوص، بشكل افتراضي ستولد  ريلز خيارات سنة 5 سنوات على جانبي السنة الحالية. إذا لم يكن هذا نطاقًا مناسبًا، فستتجاوز الخيارات start_year: وend_year: هذا. للحصول على قائمة شاملة بالخيارات المتاحة، ارجع إلى توثيق API.
 
  
كقاعدة عامة، يجب أن تستخدم date_select عند العمل مع كائنات نموذج و select_date في حالات أخرى، مثل نموذج بحث يقوم بتصفية النتائج حسب التاريخ.
+
'''ملاحظة''': في العديد من الحالات، يكون منتقو التاريخ المدمجون غير متقنين لأنهم لا يساعدون المستخدم في تحديد العلاقة بين التاريخ واليوم من الأسبوع.
  
ملاحظة: في العديد من الحالات، يكون ملتقطو التاريخ المدمجون غير متقنين لأنهم لا يساعدون المستخدم في تحديد العلاقة بين التاريخ واليوم من الأسبوع.
+
=== مكونات فردية ===
 
+
تحتاج في بعض الأحيان إلى عرض مكون تاريخ واحد فقط مثل سنة أو شهر. يوفر ريلز سلسلة من المساعدين لهذا الغرض: <code>select_year</code>، و <code>select_month</code>، و <code>select_day</code>، و <code>select_hour</code>، و <code>select_minute</code>، و <code>select_second</code>. يبدو من الواضح ما هي مهمة كل واحد من هؤلاء المساعدين. بشكل افتراضي، سينشئون [[HTML/input|حقل إدخال]] له اسم يتوضع بعد مكون الوقت (على سبيل المثال، الاسم "year" للمساعد <code>select_year</code>، و "month" للمساعد <code>select_month</code> ...إلخ.) على الرغم من إمكانية تجاوز هذا الأمر عبر الخيار <code>:field_name</code>. يعمل الخيار <code>:prefix</code> بالطريقة نفسها التي يعمل فيها مع المساعد <code>select_date</code> و <code>select_time</code> ولديه نفس القيمة الافتراضية.
== 4.4 المكونات الفردية ==
 
تحتاج في بعض الأحيان إلى عرض مكون تاريخ واحد فقط مثل سنة أو شهر. يوفر ريلز سلسلة من المساعدين لهذا، واحد لكل مكون select_year ،select_month ،select_day ،select_hour ،select_minute ،select_second. هؤلاء المساعدين هي واضحة إلى حد ما. بشكل افتراضي، سينشئ حقل إدخال مسمى بعد مكون الوقت (على سبيل المثال، "year" لـselect_year، و "month" لـ select_month إلخ.) على الرغم من إمكانية تجاوز هذا الخيار: field_name. يعمل الخيار prefix: بالطريقة نفسها التي يعمل بها لـ select_date و select_time ولديه نفس القيمة الافتراضية.
 
 
 
يحدد المعامل الأول القيمة التي يجب تحديدها ويمكن أن تكون إما مثيلًا للتاريخ، أو الوقت، أو التاريخ، وفي هذه الحالة سيًزال المكون ذو الصلة، أو قيمة رقمية. فمثلا:
 
 
 
<%= select_year(2009) %>
 
  
 +
يحدد المعامل الأول القيمة التي يجب اختيارها ويمكن أن تكون إما نسخة من <code>Date</code> أو <code>Time</code> أو <code>DateTime</code>، وفي هذه الحالة سيُستخرَج المكون المقابل، أو القيمة العددية المقابلة. إليك الشيفرة التالية:<syntaxhighlight lang="html">
 +
<%= select_year(2019) %>
 
<%= select_year(Time.now) %>
 
<%= select_year(Time.now) %>
  
ستنتج نفس الناتج إذا كان العام الحالي هو 2009 ويمكن استرداد القيمة التي اختارها المستخدم من قبل params[:date][:year].
+
</syntaxhighlight>التي ستولد نفس الناتج إذا كان العام الحالي هو 2019 ويمكن استعادة القيمة التي اختارها المستخدم من قبل <code>params[:date][:year]‎</code>.
 
 
= 5 تحميل الملفات =
 
تتمثل إحدى المهام الشائعة في تحميل نوع ما من الملفات، سواء أكانت صورة شخص أو ملف CSV يحتوي على بيانات لمعالجتها. أهم شيء يجب تذكره من خلال تحميل الملفات هو أنه يجب تعيين تشفير النموذج المقدم على "multipart/form-data". إذا كنت تستخدم form_for، فسيتم ذلك تلقائيًا. إذا كنت تستخدم form_tag، فيجب عليك إعداده بنفسك، وفقًا للمثال التالي.
 
  
يقوم النموذجان التاليان بتحميل ملف.
+
== تحميل الملفات ==
 +
إحدى المهام الشائعة في الاستمارات هي تحميل نوع ما من الملفات، سواء أكانت صورة شخص أو ملف CSV يحتوي على بيانات لمعالجتها. أهم شيء يجب تذكره من خلال تحميل الملفات هو أنه '''يجب''' تعيين تشفير النموذج المصيَّر إلى القيمة "multipart/form-data". إذا كنت تستخدم <code>form_for</code>، فسيتم ذلك تلقائيًا. أمَّا إذا كنت تستخدم <code>form_tag</code>، فيجب عليك إعداده بنفسك، وفقًا للمثال التالي.
  
 +
توفر الاستمارتان التاليتان إمكانية تحميل ملف:<syntaxhighlight lang="html">
 
<%= form_tag({action: :upload}, multipart: true) do %>
 
<%= form_tag({action: :upload}, multipart: true) do %>
 
+
  <%= file_field_tag 'picture' %>
 <%= file_field_tag 'picture' %>
 
 
 
 
<% end %>
 
<% end %>
 
+
 
<%= form_for @person do |f| %>
 
<%= form_for @person do |f| %>
 
+
  <%= f.file_field :picture %>
 <%= f.file_field :picture %>
 
 
 
 
<% end %>
 
<% end %>
 +
</syntaxhighlight>يوفر ريلز الزوج المعتاد من المساعدين: المساعد <code>file_field_tag</code> من العائلة المجرَّدة والمساعد <code>file_field</code> من عائلة كائنات النموذج (أي التي تعمل مع كائنات النموذج). الاختلاف الوحيد مع المساعدين الآخرين هو أنه لا يمكنك تعيين قيمة افتراضية لمدخلات الملف لأن هذا لن يكون له أي معنى. كما تتوقع في الحالة الأولى، يكون الملف الذي حُمِّل في <code>params [:picture]‎</code> وفي الحالة الثانية في <code>params[:person][:picture]‎</code>.
  
يوفر ريلز الزوج المعتاد من المساعدين: المجردة file_field_tag و file_field النموذجية. الاختلاف الوحيد مع المساعدين الآخرين هو أنه لا يمكنك تعيين قيمة افتراضية لمدخلات الملف لأن هذا لن يكون له أي معنى. كما تتوقع في الحالة الأولى، يكون الملف الذي حُمِّل في params [:picture] وفي الحالة الثانية في params [: person] [: picture].
+
=== ما الذي يُحمَّل؟ ===
 
+
الكائن في <code>params</code> ذي النوع <code>[[Ruby/Hash|Hash]]</code> هو نسخة لصنف فرعي من الصنف <code>[[Ruby/IO|IO]]</code>. اعتمادًا على حجم الملف المراد تحميله، قد يكون في الواقع <code>[[Ruby/StringIO|StringIO]]</code> أو نسخة من <code>[[Ruby/File|File]]</code> مدعومًا بملف مؤقت. في كلتا الحالتين، سيحتوي الكائن على الخاصية <code>original_filename</code> التي تحوي الاسم الذي سمي الملف به على حاسوب المستخدم، والخاصية <code>content_type</code> التي تحوي نوع MIME للملف. تحفظ الشيفرة التالية المحتوى الذي حُمِّل في ‎<code>#{Rails.root}/public/uploads</code> تحت نفس اسم الملف الأصلي (بافتراض أن الاستمارة هي نفسها في المثال السابق):<syntaxhighlight lang="rails">
== 5.1 ما يُحمَّل ==
 
الكائن في تجزئة params هو مثيل لفئة فرعية من IO. اعتمادًا على حجم الملف الذي حُمِّل، قد يكون في الواقع StringIO أو مثيلًا للملف مدعومًا بملف مؤقت. في كلتا الحالتين، سيحتوي الكائن على سمة original_filename تحتوي على الاسم الذي يحتوي عليه الملف على كمبيوتر المستخدم وسمة content_type تحتوي على نوع MIME للملف الذي حُمِّل. يحفظ المقتطف التالي المحتوى الذي حُمِّل في # {ريلز.root} / public / uploads تحت نفس الاسم مثل الملف الأصلي (بافتراض أن النموذج هو النموذج الموجود في المثال السابق).
 
 
 
 
def upload
 
def upload
 
+
  uploaded_io = params[:person][:picture]
 uploaded_io = params[:person][:picture]
+
  File.open(Rails.root.join('public', 'uploads', uploaded_io.original_filename), 'wb') do |file|
 
+
    file.write(uploaded_io.read)
 File.open(ريلز.root.join('public', 'uploads', uploaded_io.original_filename), 'wb') do |file|
+
  end
 
 
   file.write(uploaded_io.read)
 
 
 
 end
 
 
 
 
end
 
end
 +
</syntaxhighlight>بمجرد تحميل الملف، هناك العديد من المهام المحتملة، بدءًا من مكان تخزين الملفات (على القرص، أو على Amazon S3 ...إلخ) وربطها بنماذج لتغيير حجم ملفات الصور وتوليد الصور المصغرة (thumbnails). مثل هذه تعقيدات هي خارج نطاق هذا الدليل، ولكن هناك العديد من المكتبات المصممة للمساعدة في هذا الخصوص. اثنتان من أفضلها وأشهرها هما [https://github.com/jnicklas/carrierwave CarrierWave] و [https://github.com/thoughtbot/paperclip Paperclip].
  
بمجرد تحميل الملف، هناك العديد من المهام المحتملة، بدءًا من مكان تخزين الملفات (على القرص ، Amazon S3 ، إلخ) وربطها بنماذج لتغيير حجم ملفات الصور وتوليد الصور المصغرة. تعقيدات هذا خارج نطاق هذا الدليل، ولكن هناك العديد من المكتبات المصممة للمساعدة في ذلك. اثنان من تلك المعروفة أكثر هما CarrierWave  و Paperclip.
+
'''ملاحظة''': إذا لم يحدَّد المستخدم ملفًا، فسيكون المعامل المقابل سلسلة فارغة.
 
 
ملاحظة: إذا لم يحدد المستخدم ملفًا، فسيكون المعامل المقابل سلسلة فارغة.
 
 
 
== 5.2 التعامل مع Ajax ==
 
بخلاف النماذج الأخرى، فإن إنشاء نموذج تحميل غير متزامن ليس بسيطًا مثل توفير form_for مع remote: true. مع نموذج Ajax، يُجرى التسلسل بواسطة JavaScript قيد التشغيل داخل المتصفح، ولأن JavaScript لا تستطيع قراءة الملفات من محرك القرص الصلب، فلا يمكن تحميل الملف. يتمثل الحل الأكثر شيوعًا في استخدام إطار iframe غير مرئي يعمل كهدف لإرسال النموذج.
 
  
= 6 تخصيص بناة النموذج =
+
=== التعامل مع Ajax ===
كما ذُكر سابقًا فإن الكائن الناتج من form_for و fields_for هو مثيل FormBuilder (أو فئة فرعية منه). يغلف بناة النماذج فكرة عرض عناصر النموذج لكائن واحد. بينما يمكنك بالطبع كتابة مساعدين لنماذجك بالطريقة المعتادة، يمكنك أيضًا تصنيف فئة FormBuilder وإضافة المساعدون هناك. فمثلا:
+
بخلاف الاستمارات الأخرى، فإن إنشاء استمارة تحميل ملف بشكل غير متزامن ليس بسيطًا مثل استعمال <code>form_for</code> مع <code>remote: true</code>. مع استمارة Ajax، تُجرَى عملية السَلسَلة (serialization) بواسطة [[JavaScript|جافاسكربت]] قيد التشغيل داخل المتصفح، ولأن [[JavaScript|جافاسكربت]] لا تستطيع قراءة الملفات من محرك القرص الصلب، فلا يمكن تحميل الملف آنذاك. يتمثل الحل الأكثر شيوعًا في استخدام الإطار iframe بشكل غير مرئي يعمل كهدف لإرسال الاستمارة.
  
 +
== تخصيص باني الاستمارة ==
 +
كما ذُكر سابقًا، فإن الكائن الناتج من <code>form_for</code> و <code>fields_for</code> هو نسخة من <code>FormBuilder</code> (أو صنف فرعي منه). يغلف بانو الاستمارة (Form builders) فكرة عرض عناصر الاستمارة لكائن واحد. بينما يمكنك بالطبع كتابة مساعدين لاستماراتك بالطريقة المعتادة، يمكنك أيضًا إنشاء صنف فرعي من <code>FormBuilder</code> وإضافة المساعدين إليه. فمثلًا:<syntaxhighlight lang="html">
 
<%= form_for @person do |f| %>
 
<%= form_for @person do |f| %>
 
+
  <%= text_field_with_label f, :first_name %>
 <%= text_field_with_label f, :first_name %>
 
 
 
 
<% end %>
 
<% end %>
 
+
</syntaxhighlight>يمكن استبدال هذه الشيفرة وكتابة الشيفرة التالية عوضًا عنها:<syntaxhighlight lang="html">
can be replaced with
 
 
 
 
<%= form_for @person, builder: LabellingFormBuilder do |f| %>
 
<%= form_for @person, builder: LabellingFormBuilder do |f| %>
 
+
  <%= f.text_field :first_name %>
 <%= f.text_field :first_name %>
 
 
 
 
<% end %>
 
<% end %>
 
+
</syntaxhighlight>بتعريف الصنف <code>LabellingFormBuilder</code> بشكل مشابه لما يلي:<syntaxhighlight lang="rails">
بتعريف فئة LabellingFormBuilder مشابهة لما يلي:
 
 
 
 
class LabellingFormBuilder < ActionView::Helpers::FormBuilder
 
class LabellingFormBuilder < ActionView::Helpers::FormBuilder
 
+
  def text_field(attribute, options={})
 def text_field(attribute, options={})
+
    label(attribute) + super
 
+
  end
   label(attribute) + super
 
 
 
 end
 
 
 
 
end
 
end
 
+
</syntaxhighlight>إذا أعدت استخدام هذا بشكل متكرر، يمكنك تعريف المساعد <code>labeled_form_for</code> الذي يطبِّق تلقائيًا الخيار <code>builder: LabellingFormBuilder</code>:<syntaxhighlight lang="rails">
إذا أعدت استخدام هذا بشكل متكرر، يمكنك تعريف مساعد  labeled_form_for يطبق تلقائيًا المُنشِئ : LabellingFormBuilder:
 
 
 
 
def labeled_form_for(record, options = {}, &block)
 
def labeled_form_for(record, options = {}, &block)
 
+
  options.merge! builder: LabellingFormBuilder
 options.merge! builder: LabellingFormBuilder
+
  form_for record, options, &block
 
 
 form_for record, options, &block
 
 
 
 
end
 
end
 
+
</syntaxhighlight>يحدِّد باني الاستمارة المستخدَم أيضًا ما يحدث عندما تستعمل:<syntaxhighlight lang="html">
يحدد منشئ النماذج المستخدَم أيضًا ما يحدث عندما تقوم بذلك
 
 
 
 
<%= render partial: f %>
 
<%= render partial: f %>
 +
</syntaxhighlight>إن كان <code>f</code> نسخةً من <code>FormBuilder</code>، فهذا سيصيِّر الجزئية <code>form</code> مع ضبط كائن الجزئية إلى باني الاستمارة. إن كان باني الاستمارة من الصنف <code>LabellingFormBuilder</code>، فستصيَّر الجزئية <code>labelling_form</code> بدلًا من تلك.
  
 
== فهم العرف المتبع في تسمية المعاملات ==
 
== فهم العرف المتبع في تسمية المعاملات ==
كما شاهدت في الأقسام السابقة، يمكن أن تكون القيم من النماذج في المستوى الأعلى من تجزئة المعاملات أو متداخلة في تجزئة أخرى. على سبيل المثال، في حدث إنشاء المعيار لنموذج Person، تكون المعاملات [:person] عادةً علامة تجزئة لجميع سمات الشخص المراد إنشاؤه. يمكن لتجزئة المعاملات أيضًا أن تحتوي على المصفوفات، ومصفوفات التجزئة وما إلى ذلك.
+
كما شاهدت في الأقسام السابقة، يمكن أن تكون القيم من الاستمارات في المستوى الأعلى من <code>params</code> أو متداخلة في جدول <code>[[Ruby/Hash|Hash]]</code> آخر. على سبيل المثال، في الإجراء <code>create</code> القياسي للنموذج <code>Person</code>، تكون <code>params[:person]‎</code> عادةً جدول <code>[[Ruby/Hash|Hash]]</code> يحوي جميع خاصيات الشخص (person) المراد إنشاؤه. يمكن أن يحوي <code>params</code> أيضًا المصفوفات، ومصفوفات من الكائنات <code>[[Ruby/Hash|Hash]]</code> وما إلى ذلك.
 
 
لا تعرف نماذج HTML بشكل أساسي أي نوع من البيانات المنظمة ، وكل ما تولده هو أزواج اسم-قيمة، حيث تكون الأزواج مجرد سلاسل عادية. المصفوفات و التجزئة التي تراها في تطبيقك هي نتيجة لبعض اصطلاحات تسمية المعاملات التي يستخدمها ريلز.
 
  
== 7.1 الهياكل الأساسية ==
+
لا تعرف [[HTML/form|استمارات HTML]] بشكل أساسي أي نوع من البيانات المهيكلة (structured data)، وكل ما تولده هو الأزواج اسم-قيمة، حيث تكون الأزواج مجرد سلاسل نصية عادية. المصفوفات والجداول <code>[[Ruby/Hash|Hash]]</code> التي تراها في تطبيقك هي نتيجة لبعض اصطلاحات تسمية المعاملات التي يستخدمها ريلز.
الهيكلان الأساسيان هما المصفوفات والتجزئة. تعكس التجزئات بنية الجُمل المستخدمة للوصول إلى القيمة في المعاملات. على سبيل المثال، إذا كان النموذج يحتوي على:
 
  
 +
=== هياكل البيانات الأساسية ===
 +
الهيكلان الأساسيان لتخزين البيانت هما [[Ruby/Array|المصفوفات]] و<nowiki/>[[Ruby/Hash|الجدول <code>Hash</code>]]. يعكس الأخير الصياغة المستعملة للوصول إلى القيمة في <code>params</code>. على سبيل المثال، إذا كانت الاستمارة تحتوي على:<syntaxhighlight lang="html">
 
<input id="person_name" name="person[name]" type="text" value="Henry"/>
 
<input id="person_name" name="person[name]" type="text" value="Henry"/>
 +
</syntaxhighlight>سيحتوي <code>params</code> ذي النوع <code>[[Ruby/Hash|Hash]]</code> على:<syntaxhighlight lang="text">
 +
{'person' => {'name' => 'Henry'}}
 +
</syntaxhighlight>و <code>[params[:person][:name</code> سيعيد القيمة المرسلة في المتحكم.
  
ستحتوي تجزئة المعاملات على:
+
يمكن أن تشعيب <code>[[Ruby/Hash|Hash]]</code> على مستويات عدة كما هو مطلوب، على سبيل المثال:<syntaxhighlight lang="html">
 
 
<nowiki>{'person' => {'name' => 'Henry'}}</nowiki>
 
 
 
و[params [:person] [:name سيسترد القيمة المرسلة في المتحكم.
 
 
 
يمكن أن تتداخل التجزئات على العديد من المستويات كما هو مطلوب، على سبيل المثال:
 
 
 
 
<input id="person_address_city" name="person[address][city]" type="text" value="New York"/>
 
<input id="person_address_city" name="person[address][city]" type="text" value="New York"/>
 
+
</syntaxhighlight>سينتج هذا في <code>params</code>:<syntaxhighlight lang="text">
سينتج هذا
+
{'person' => {'address' => {'city' => 'New York'}}}
 
+
</syntaxhighlight>يتجاهل ريلز عادةً أسماء المعاملات المكررة. إذا كان اسم المعامل يحتوي على مجموعة فارغة من الأقواس المعقوفة <code>[]</code>، فستُجمع في مصفوفة. إذا كنت تريد أن يتمكن المستخدمون من إدخال أرقام هواتف متعددة، فيمكنك وضع ذلك في الاستمارة:<syntaxhighlight lang="html">
<nowiki>{'person' => {'address' => {'city' => 'New York'}}}</nowiki>
 
 
 
يتجاهل ريلز عادةً أسماء المعاملات المكررة. إذا كان اسم المعامل يحتوي على مجموعة فارغة من الأقواس المربعة []، فستُجمع في مصفوفة. إذا كنت تريد أن يتمكن المستخدمون من إدخال أرقام هواتف متعددة، فيمكنك وضع ذلك في النموذج:
 
 
 
 
<input name="person[phone_number][]" type="text"/>
 
<input name="person[phone_number][]" type="text"/>
 
 
<input name="person[phone_number][]" type="text"/>
 
<input name="person[phone_number][]" type="text"/>
 
 
<input name="person[phone_number][]" type="text"/>
 
<input name="person[phone_number][]" type="text"/>
 +
</syntaxhighlight>سينتج عن ذلك <code>params[:person][:phone_number]‎</code> بصفته مصفوفة تحتوي على أرقام الهاتف المدخلة.
  
سيؤدي ذلك إلى ظهور المعاملات [person] [:phone_number:] بصفتها مصفوفة تحتوي على أرقام الهاتف المدخلة.
+
=== الدمج بين هياكل البيانات الأساسية ===
 
+
يمكننا خلط ومطابقة هذين المفهومين. قد يكون أحد عناصر [[Ruby/Hash|الجدول Hash]] هو المصفوفة كما في المثال السابق، أو يمكنك الحصول على مصفوفة من [[Ruby/Hash|الجداول Hash]]. على سبيل المثال، قد تسمح لك الاستمارة بإنشاء أي عدد من العناوين بتكرار الجزء التالي من الاستمارة:<syntaxhighlight lang="html">
== 7.2 دمجهم ==
 
يمكننا خلط وتطابق هذين المفهومين. قد يكون أحد عناصر التجزئة هو المصفوفة كما في المثال السابق، أو يمكنك الحصول على مجموعة من التجزئة. على سبيل المثال، قد يسمح لك نموذج بإنشاء أي عدد من العناوين بتكرار جزء النموذج التالي
 
 
 
 
<input name="addresses[][line1]" type="text"/>
 
<input name="addresses[][line1]" type="text"/>
 
 
<input name="addresses[][line2]" type="text"/>
 
<input name="addresses[][line2]" type="text"/>
 
 
<input name="addresses[][city]" type="text"/>
 
<input name="addresses[][city]" type="text"/>
  
سيُنتج هذا في المعاملات [:addresses] مصفوفة من التجزئات باستخدام المفاتيح line1 و line2 والمدينة. يقرر ريلز بدء تجميع القيم في تجزئة جديدة كلما واجهت اسم إدخال موجود بالفعل في التجزئة الحالية.
+
</syntaxhighlight>سيُنتج هذا في <code>params[:addresses]‎</code> مصفوفة من [[Ruby/Hash|الجداول Hash]] مع المفاتيح <code>line1</code> و <code>line2</code> و <code>city</code>. يقرر ريلز بدء تجميع القيم في [[Ruby/Hash|جدول Hash]] جديدة كلما عثرت على اسم عنصر إدخال موجود بالفعل في الجدول الحالي.
 
 
هناك قيود، ومع ذلك، في حين أن التجزئات يمكن أن تكون متداخلة عشوائيًا، يُسمَح بمستوى واحد فقط من "التصفيف". عادةً يمكن استبدال المصفوفات التجزئات، على سبيل المثال، بدلاً من وجود مصفوفة من كائنات النموذج، يمكن أن يحتوى على تجزئة لكائنات نموذجية مقفولة بمعرّفها، أو فهرس مصفوفة، أو معامل آخر.
 
  
تحذير: لا تعمل معاملات المصفوفة بشكل جيد مع المساعد check_box. وفقًا لمربعات تحديد مواصفات HTML التي لم تُحدَّد، لا ترسل أي قيمة. ومع ذلك، غالبًا ما يكون من الملائم اختيار مربع اختيار لإرسال قيمة دائمًا. يزيف المساعد check_box هذا عن طريق إنشاء إدخال مخفي إضافي بنفس الاسم. إذا كان مربع الاختيار غير محدد، فسيُرسَل الإدخال المخفي فقط، وإذا حُدِّد، فسيُرسَلان كليهما، ولكن القيمة المُرسَلة من مربع الاختيار لها الأولوية. عند العمل مع معاملات المصفوفة، سيؤدي هذا الإرسال المكرَّر إلى إرباك ريلز لأن أسماء المدخلات المتكررة هي كيفية تحديد متى تبدأ عنصر صفيف جديد. من الأفضل استخدام check_box_tag أو استخدام التجزئة بدلاً من المصفوفات.
+
هناك قيود، ومع ذلك، في حين أن [[Ruby/Hash|الجداول Hash]] يمكن أن تكون متداخلة عشوائيًا، يُسمَح بمستوى واحد فقط من "المصفوفية" (arrayness). عادةً يمكن تبديل [[Ruby/Hash|الجداول Hash]] مكان [[Ruby/Array|المصفوفات]]، على سبيل المثال، بدلًا من وجود مصفوفة من كائنات نموذج، يمكن استعمال [[Ruby/Hash|جدول Hash]] من كائنات نموذج قيمة مفاتيحها هي معرف كل منها، أو فهرس مصفوفة، أو معامل آخر.
  
== 7.3 استخدام مساعدي النموذج ==
+
'''تحذير''': لا تعمل معاملات المصفوفة بشكل جيد مع المساعد <code>check_box</code>. وفقًا لمواصفات HTML، [[HTML/input/checkbox|مربعات الاختيار]] غير المحدَّدة (unchecked) تُرسَل بدون قيمة. ومع ذلك، غالبًا ما يكون من الملائم [[HTML/input/checkbox|لمربع الاختيار]] أن يرسل قيمةً دائمًا. يُزيِّف المساعد <code>check_box</code> هذا الأمر عبر إنشاء [[HTML/input|عنصر إدخال]] مخفي إضافي بنفس الاسم. إذا كان مربع الاختيار غير محدد، فسيُرسَل عنصر الإدخال المخفي فقط، وإذا حُدِّد، فسيُرسَل كلا العنصرين، ولكن القيمة المُرسَلة من مربع الاختيار المُحدَّد لها الأولوية على قيمة العنصر المخفي. عند العمل مع معاملات المصفوفة، سيؤدي هذا الإرسال المكرَّر إلى إرباك ريلز لأن أسماء عناصر الإدخال المتكررة تحدِّد بداية عنصر مصفوفة جديدة. من الأفضل استخدام المساعد <code>check_box_tag</code> أو استخدام جدول <code>[[Ruby/Hash|Hash]]</code> بدلًا من المصفوفات.
لم تستخدم الأقسام السابقة مساعدي نموذج ريلز على الإطلاق. بينما يمكنك صياغة أسماء المدخلات بنفسك ونقلها مباشرة إلى المساعدين مثل text_field_tag توفر ريلز أيضًا دعمًا أعلى مستوى. إن الأدوات الموجودة تحت تصرفك هنا هي معامل الاسم إلى form_for و fields_for وخيار index: الذي يأخذه المساعدون.
 
  
قد ترغب في عرض نموذج يحتوي على مجموعة من حقول التعديل لكل عنوان من عناوين الشخص. فمثلا:
+
=== استخدام مساعدي الاستمارة ===
 +
لم تستخدم الأقسام السابقة مساعدي الاستمارة لريلز على الإطلاق. لما  كان بإمكانك صياغة أسماء [[HTML/input|عناصر الإدخال]] بنفسك ونقلها مباشرة إلى مساعدين مثل <code>text_field_tag</code>، توفر ريلز أيضًا دعمًا ذا مستوى أعلى. إن الأدوات الموجودة تحت تصرفك هنا هي معامل الاسم إلى <code>form_for</code> و <code>fields_for</code> والخيار <code>:index</code> الذي يأخذه المساعدون.
  
 +
قد ترغب في تصيير نموذج يحتوي على مجموعة من حقول التعديل لكل عنوان من عناوين الشخص. فمثلًا:<syntaxhighlight lang="html">
 
<%= form_for @person do |person_form| %>
 
<%= form_for @person do |person_form| %>
 
+
  <%= person_form.text_field :name %>
 <%= person_form.text_field :name %>
+
  <% @person.addresses.each do |address| %>
 
+
    <%= person_form.fields_for address, index: address.id do |address_form|%>
 <% @person.addresses.each do |address| %>
+
      <%= address_form.text_field :city %>
 
+
    <% end %>
   <%= person_form.fields_for address, index: address.id do |address_form|%>
+
  <% end %>
 
 
     <%= address_form.text_field :city %>
 
 
 
   <% end %>
 
 
 
 <% end %>
 
 
 
 
<% end %>
 
<% end %>
 
+
</syntaxhighlight>بافتراض أن الشخص له عنوانين، بمُعرِّفَين 23 و 45، فهذا من شأنه أن يُنشِئ مخرجات مشابهة لما يلي:<syntaxhighlight lang="html">
بافتراض أن الشخص له عنوانين، بمُعرِّفَين 23 و 45، فهذا من شأنه أن يُنشِئ مخرجات مشابهة لهذه:
 
 
 
 
<form accept-charset="UTF-8" action="/people/1" class="edit_person" id="edit_person_1" method="post">
 
<form accept-charset="UTF-8" action="/people/1" class="edit_person" id="edit_person_1" method="post">
 
+
  <input id="person_name" name="person[name]" type="text" />
 <input id="person_name" name="person[name]" type="text" />
+
  <input id="person_address_23_city" name="person[address][23][city]" type="text" />
 
+
  <input id="person_address_45_city" name="person[address][45][city]" type="text" />
 <input id="person_address_23_city" name="person[address][23][city]" type="text" />
 
 
 
 <input id="person_address_45_city" name="person[address][45][city]" type="text" />
 
 
 
 
</form>
 
</form>
 +
</syntaxhighlight>سيُنتِج هذا في <code>params</code> الناتج:<syntaxhighlight lang="text">
 +
{'person' => {'name' => 'Bob', 'address' => {'23' => {'city' => 'Paris'}, '45' => {'city' => 'London'}}}}
 +
</syntaxhighlight>يعرف ريلز أن جميع هذه المدخلات يجب أن تكون جزءًا من <code>person</code> ذي النوع <code>[[Ruby/Hash|Hash]]</code> لأنك استدعيت <code>fields_for</code> في باني الاستمارة الأول. بتحديد الخيار <code>:index</code>، يمكنك إخبار ريلز أنه بدلًا من تسمية المدخلات <code>person[address][city]‎</code>، يجب أن يضيف هذا الفهرس محاطًا بالقوسين <code>[]</code> بين العنوان والمدينة. هذا مفيد في كثير من الأحيان لأنه من السهل تحديد مكان سجل العنوان المراد تعديله. يمكنك تمرير الأرقام مع بعض الدلالات الأخرى، السلاسل النصية أو حتى القيمة <code>nil</code> (مما يؤدي إلى إنشاء معامل مصفوفة).
  
سيُنتِج هذا معاملات مجزئة تشبه:
+
لإنشاء تداخلات أكثر تعقيدًا، يمكنك تحديد الجزء الأول من اسم عنصر الإدخال (<code>person[address]‎</code> في المثال السابق) بشكل صريح:<syntaxhighlight lang="html">
 
 
<nowiki>{'person' => {'name' => 'Bob', 'address' => {'23' => {'city' => 'Paris'}, '45' => {'city' => 'London'}}}}</nowiki>
 
 
 
يعرف ريلز أن جميع هذه المدخلات يجب أن تكون جزءًا من تجزئة الشخص لأنك استدعيت fields_for في مُنشِئ النموذج الأول. بتحديد خيار index: يمكنك إخبار ريلز أنه بدلاً من تسمية المدخلات person[address][city]، يجب أن يضيف هذا المؤشر محاطًا بـ [] بين العنوان والمدينة. هذا مفيد في كثير من الأحيان لأنه من السهل تحديد مكان تسجيل العنوان الذي يجب تعديله. يمكنك تمرير الأرقام مع بعض الدلالات الأخرى، السلاسل أو حتى لا شيء (مما يؤدي إلى إنشاء معامل مصفوفة).
 
 
 
لإنشاء تداخلات أكثر تعقيدًا، يمكنك تحديد الجزء الأول من اسم الإدخال (person[address] في المثال السابق) بشكل صريح:
 
 
 
 
<%= fields_for 'person[address][primary]', address, index: address do |address_form| %>
 
<%= fields_for 'person[address][primary]', address, index: address do |address_form| %>
 
+
  <%= address_form.text_field :city %>
 <%= address_form.text_field :city %>
 
 
 
 
<% end %>
 
<% end %>
 
+
</syntaxhighlight>سيُنشِئ [[HTML/input|عنصر إدخال]] مثل:<syntaxhighlight lang="html">
سيُنشِئ المدخلات مثل
 
 
 
 
<input id="person_address_primary_1_city" name="person[address][primary][1][city]" type="text" value="bologna" />
 
<input id="person_address_primary_1_city" name="person[address][primary][1][city]" type="text" value="bologna" />
 +
</syntaxhighlight>كقاعدة عامة، يكون اسم عنصر الإدخال النهائي هو جمع الاسم المعطى مع <code>fields_for</code> أو <code>form_for</code>، وقيمة الفهرس (index value) واسم الخاصية. يمكنك أيضًا تمرير الخيار <code>:index</code> مباشرةً إلى المساعدين، مثل <code>text_field</code>، ولكن عادةً ما يكون أقل تكرارًا لتحديد ذلك على مستوى باني الاستمارة بدلًا من عناصر التحكم الفردية للإدخال.
  
كقاعدة عامة، يكون اسم الإدخال النهائي هو سلسلة الاسم المعطى إلى fields_for/form_for، وقيمة الفهرس واسم السمة. يمكنك أيضًا تمرير أحد خيارات :index مباشرةً إلى المساعدين، مثل text_field ، ولكن عادةً ما يكون أقل تكرارًا لتحديد ذلك على مستوى منشئ النموذج بدلاً من عناصر التحكم الفردية للإدخال.
+
كاختصار يمكنك إضافة <code>[]</code> إلى الاسم وحذف الخيار <code>:index</code>. هذا يؤدي نفس الغرض الذي يؤديه تحديد <code>index: address</code>، لذا ستنتج الشيفرة التالية:<syntaxhighlight lang="html">
 
 
كاختصار يمكنك إلحاق [] بالاسم وحذف خيار index:. هذا هو نفس تحديد index:، عنوان ذلك
 
 
 
 
<%= fields_for 'person[address][primary][]', address do |address_form| %>
 
<%= fields_for 'person[address][primary][]', address do |address_form| %>
 
+
  <%= address_form.text_field :city %>
 <%= address_form.text_field :city %>
 
 
 
 
<% end %>
 
<% end %>
 +
</syntaxhighlight>نفس المخرجات تمامًا الناتجة عن المثال السابق.
  
ينتج نفس الإخراج بالضبط مثل المثال السابق.
+
== استمارات لموارد خارجية ==
 
+
يمكن أيضًا استخدام مساعدي استمارات ريلز لإنشاء استمارة لنشر البيانات إلى مورد خارجي (external resource). ومع ذلك، في بعض الأحيان، قد يكون من الضروري تعيين <code>Authenticity_token</code> للمصدر، إذ يمكن تنفيذ ذلك بتمرير المعامل <code>authenticity_token: 'your_external_token'‎</code> إلى خيارات <code>form_tag</code>:<syntaxhighlight lang="html">
= 8 نماذج لمصادر خارجية =
+
<%= form_tag 'http://farfar.away/form', authenticity_token: 'external_token' do %>
يمكن أيضًا استخدام مساعدي نماذج ريلزلإنشاء نموذج لنشر البيانات إلى مصدر خارجي. ومع ذلك، في بعض الأحيان، قد يكون من الضروري تعيين Authenticity_token للمصدر، يمكن  ذلك بتمرير المعامل authenticity_token: 'your_external_token إلى خيارات form_tag:
+
  Form contents
 
 
<%= form_tag '<nowiki>http://farfar.away/form'</nowiki>, authenticity_token: 'external_token' do %>
 
 
 
 Form contents
 
 
 
 
<% end %>
 
<% end %>
 
+
</syntaxhighlight>في بعض الأحيان، عند إرسال البيانات إلى مورد خارجي، مثل بوابة الدفع الإلكترونية، تكون الحقول التي يمكن استخدامها في الاستمارة محدودة بواسطة واجهة برمجة تطبيقات خارجية وقد يكون إنشاء المصادقة غير مرغوب فيه لتوليد <code>authenticity_token</code>. لعدم إرسال رمز، مرر ببساطة القيمة <code>false</code> إلى الخيار <code>:authenticity_token</code>:<syntaxhighlight lang="html">
في بعض الأحيان، عند إرسال البيانات إلى مصدر خارجي، مثل بوابة الدفع، تكون الحقول التي يمكن استخدامها في النموذج محدودة بواسطة واجهة برمجة تطبيقات خارجية وقد يكون إنشاء المصادقة غير مرغوب فيه لتوليد authenticity_token. لعدم إرسال رمز، مرر ببساطة false إلى الخيار: authenticity_token:
+
<%= form_tag 'http://farfar.away/form', authenticity_token: false do %>
 
+
  Form contents
<%= form_tag '<nowiki>http://farfar.away/form'</nowiki>, authenticity_token: false do %>
 
 
 
 Form contents
 
 
 
 
<% end %>
 
<% end %>
 
+
</syntaxhighlight>نفس الطريقة متاحة أيضًا للمساعد <code>form_for</code>:<syntaxhighlight lang="html">
نفس الطريقة متاحة أيضًا للنموذج form_for:
 
 
 
 
<%= form_for @invoice, url: external_url, authenticity_token: 'external_token' do |f| %>
 
<%= form_for @invoice, url: external_url, authenticity_token: 'external_token' do |f| %>
 
+
  Form contents
 Form contents
 
 
 
 
<% end %>
 
<% end %>
 
+
</syntaxhighlight>أو إذا كنت لا ترغب في تصيير الحقل <code>Authenticity_token</code>:<syntaxhighlight lang="html">
أو إذا كنت لا ترغب في عرض حقل Authenticity_token:
 
 
 
 
<%= form_for @invoice, url: external_url, authenticity_token: false do |f| %>
 
<%= form_for @invoice, url: external_url, authenticity_token: false do |f| %>
 
+
  Form contents
 Form contents
 
 
 
 
<% end %>
 
<% end %>
 +
</syntaxhighlight>
  
= 9 بناء نماذج معقدة =
+
== بناء استمارات معقدة ==
تنمو العديد من التطبيقات إلى أبعد من النماذج البسيطة التي تعدل كائنًا واحدًا. على سبيل المثال، عند إنشاء Person، قد ترغب في السماح للمستخدم (على نفس النموذج) بإنشاء سجلات عناوين متعددة (المنزل، العمل، إلخ). عند تعديل هذا الشخص في وقت لاحق، ينبغي أن يكون المستخدم قادرًا على إضافة العناوين أو إزالتها أو تعديلها حسب الضرورة.
+
تتطور التطبيقات إلى أبعد من الاستمارات البسيطة التي تعدل كائنًا واحدًا. على سبيل المثال، عند إنشاء <code>Person</code>، قد ترغب في السماح للمستخدم (على نفس الاستمارة) بإنشاء سجلات عناوين متعددة (عنوان للمنزل، والعمل، ...إلخ). عند تعديل هذا الشخص في وقت لاحق، ينبغي أن يكون المستخدم قادرًا على إضافة العناوين أو إزالتها أو تعديلها حسب الضرورة.
 
 
== 9.1 تكوين النموذج ==
 
يوفر السجل النشط دعمًا لمستوى النموذج عبر دالة accepts_nested_attributes_for:
 
  
 +
=== ضبط النموذج ===
 +
يوفر [[Rails/active record|Active Record]] دعمًا على مستوى النموذج عبر التابع <code>accepts_nested_attributes_for</code>:<syntaxhighlight lang="rails">
 
class Person < ApplicationRecord
 
class Person < ApplicationRecord
 
+
  has_many :addresses, inverse_of: :person
 has_many :addresses, inverse_of: :person
+
  accepts_nested_attributes_for :addresses
 
 
 accepts_nested_attributes_for :addresses
 
 
 
 
end
 
end
 
+
 
class Address < ApplicationRecord
 
class Address < ApplicationRecord
 
+
  belongs_to :person
 belongs_to :person
 
 
 
 
end
 
end
 +
</syntaxhighlight>يُنشئ هذا <code>address_attributes = method</code> في <code>Person</code> الذي يسمح لك بإنشاء وتحديث وإتلاف (اختياريًا) العناوين.
  
يُنشئ هذا address_attributes = method في Person الذي يسمح لك بإنشاء وتحديث و (اختياريًا) إتلاف العناوين.
+
=== استمارات متداخلة ===
 
+
تسمح الاستمارة التالية للمستخدم بإنشاء شخص <code>Person</code> والعناوين المرتبطة به:<syntaxhighlight lang="html">
== 9.2 النماذج المتداخلة ==
 
يسمح النموذج التالي للمستخدم بإنشاء شخص والعناوين المرتبطة به.
 
 
 
 
<%= form_for @person do |f| %>
 
<%= form_for @person do |f| %>
 
+
  Addresses:
 Addresses:
+
  <ul>
 
+
    <%= f.fields_for :addresses do |addresses_form| %>
 <nowiki><ul></nowiki>
+
      <li>
 
+
        <%= addresses_form.label :kind %>
   <%= f.fields_for :addresses do |addresses_form| %>
+
        <%= addresses_form.text_field :kind %>
 
+
     <nowiki><li></nowiki>
+
        <%= addresses_form.label :street %>
 
+
        <%= addresses_form.text_field :street %>
       <%= addresses_form.label :kind %>
+
        ...
 
+
      </li>
       <%= addresses_form.text_field :kind %>
+
    <% end %>
 
+
  </ul>
       <%= addresses_form.label :street %>
 
 
 
       <%= addresses_form.text_field :street %>
 
 
 
       ...
 
 
 
     <nowiki></li></nowiki>
 
 
 
   <% end %>
 
 
 
 <nowiki></ul></nowiki>
 
 
 
 
<% end %>
 
<% end %>
 
+
</syntaxhighlight>عندما تقبل إحدى [[Rails/association basics|الارتباطات]] الخاصيات المتداخلة، يصيِّر التابع <code>field_for</code> الكتلة المرتبطة به مرة واحدة لكل عنصر للارتباط. على وجه الخصوص، إذا لم يكن لدى الشخص عناوين، فلا يصيِّر التابع شيئًا. النمط الشائع هو أن يُنشئ المتحكم ابنًا فارغًا أو أكثر بحيث تُعرَض مجموعة واحدة على الأقل من الحقول للمستخدم. سيُنتِج المثال أدناه مجموعتين من حقول العناوين التي تصيَّر في استمارة الشخص الجديد.<syntaxhighlight lang="rails">
عندما تقبل إحدى الجمعيات الصفات المتداخلة field_for تعرض وحدتها المغلقة مرة واحدة لكل عنصر في الجمعية. على وجه الخصوص، إذا لم يكن لدى الشخص عناوين فإنه لا يعرض شيئًا. النمط الشائع هو أن يُنشئ المتحكم طفلًا فارغًا أو أكثر بحيث تُعرَض مجموعة واحدة على الأقل من الحقول للمستخدم. سيُنتِج المثال أدناه مجموعتين من حقول العناوين في نموذج الشخص الجديد.
 
 
 
 
def new
 
def new
 
+
  @person = Person.new
 @person = Person.new
+
  2.times { @person.addresses.build }
 
 
 2.times { @person.addresses.build }
 
 
 
 
end
 
end
 
+
</syntaxhighlight>يُنتِج <code>fields_for</code> بانيَ استمارة. سيكون اسم المعاملات هو ما يتوقعه التابع <code>accepts_nested_attributes_for</code>. على سبيل المثال، عند إنشاء مستخدم له عنوانين، ستبدو المعاملات المُرسَلة كما يلي:<syntaxhighlight lang="text">
يحصل fields_for على باني نموذج. سيكون اسم المعاملات هو ما تتوقعه accepts_nested_attributes_for. على سبيل المثال، عند إنشاء مستخدم له عنوانين، ستبدو المعاملات المُرسَلة كما يلي:
 
 
 
 
{
 
{
 
+
  'person' => {
 'person' => {
+
    'name' => 'John Doe',
 
+
    'addresses_attributes' => {
   'name' => 'John Doe',
+
      '0' => {
 
+
        'kind' => 'Home',
   'addresses_attributes' => {
+
        'street' => '221b Baker Street'
 
+
      },
     '0' => {
+
      '1' => {
 
+
        'kind' => 'Office',
       'kind' => 'Home',
+
        'street' => '31 Spooner Street'
 
+
      }
       'street' => '221b Baker Street'
+
    }
 
+
  }
     },
 
 
 
     '1' => {
 
 
 
       'kind' => 'Office',
 
 
 
       'street' => '31 Spooner Street'
 
 
 
     }
 
 
 
   }
 
 
 
 }
 
 
 
 
}
 
}
 +
</syntaxhighlight>مفاتيح <code>addresses_attributes:</code> ذي النوع <code>[[Ruby/Hash|Hash]]</code> غير مهمة، فهي تحتاج فقط أن تكون مختلفة لكل عنوان.
  
مفاتيح التجزئة addresses_attributes: غير مهمة، فهي تحتاج فقط أن تكون مختلفة لكل عنوان.
+
إذا حُفِظ الكائن المرتبط مسبقًا، فسيولد <code>field_for</code> تلقائيًا [[HTML/input/hidden|عناصر إدخال مخفية]] بمعرّف السجل المحفوظ. يمكنك تعطيل هذا السلوك بتمرير <code>include_id: false</code> إلى المساعد <code>fields_for</code>. قد ترغب في القيام بذلك إذا وُضِعَ عنصر الإدخال المولد تلقائيًا في موقع حيث يكون هنالك وسم <code>[[HTML/input|<input>]]</code> غير صالح أو عند استخدام [[Rails/active record basics#.D8.B1.D8.A8.D8.B7 .D8.A7.D9.84.D9.83.D8.A7.D8.A6.D9.86.D8.A7.D8.AA .D8.A8.D8.A7.D9.84.D8.B9.D9.84.D8.A7.D9.82.D8.A7.D8.AA|العملية ORM]] حيث لا تملك العناصر الأبناء أيَّ معرف.
 
 
إذا حُفظ الكائن المرتبط بالفعل، فسيولد تلقائيًا field_for ًإدخالا مخفيًا بمعرّف السجل المحفوظ. يمكنك تعطيل هذا بتمرير include_id: false إلى fields_for. قد ترغب في القيام بذلك إذا وُضِعَ الإدخال المُنشَئ تلقائيًا في موقع حيث تكون علامة الإدخالHTML غير صالحة أو عند استخدام ORM حيث لا يوجد لدى الأطفال معرف.
 
 
 
== 9.3 المُتحكم ==
 
كالمعتاد، تحتاج إلى إدراج المعاملات في القائمة البيضاء في المتحكم قبل تمريرها إلى النموذج:
 
  
 +
=== المتحكم ===
 +
كالمعتاد، تحتاج إلى [[Rails/action controller overview#.D8.A7.D9.84.D9.85.D8.B9.D8.A7.D9.85.D9.84.D8.A7.D8.AA .D8.A7.D9.84.D9.82.D9.88.D9.8A.D8.A9|إدراج المعاملات في القائمة البيضاء]] في المتحكم قبل تمريرها إلى النموذج:<syntaxhighlight lang="rails">
 
def create
 
def create
 
+
  @person = Person.new(person_params)
 @person = Person.new(person_params)
+
  # ...
 
 
 # ...
 
 
 
 
end
 
end
 
+
 
private
 
private
 +
  def person_params
 +
    params.require(:person).permit(:name, addresses_attributes: [:id, :kind, :street])
 +
  end
 +
</syntaxhighlight>
  
 def person_params
+
=== حذف الكائنات ===
 
+
يمكنك السماح للمستخدمين بحذف الكائنات المرتبطة بتمرير الخيار <code>allow_destroy: true</code> إلى <code>accepts_nested_attributes_for</code>:<syntaxhighlight lang="html">
<nowiki>   params.require(:person).permit(:name, addresses_attributes: [:id, :kind, :street])</nowiki>
 
 
 
 end
 
 
 
== 9.4 حذف الكائنات ==
 
يمكنك السماح للمستخدمين بحذف الكائنات المرتبطة بتمرير allow_destroy: true to accepts_nested_attributes_for
 
 
 
 
class Person < ApplicationRecord
 
class Person < ApplicationRecord
 
+
  has_many :addresses
 has_many :addresses
+
  accepts_nested_attributes_for :addresses, allow_destroy: true
 
 
 accepts_nested_attributes_for :addresses, allow_destroy: true
 
 
 
 
end
 
end
 
+
</syntaxhighlight>إذا كان [[Ruby/Hash|الجدول Hash]] لخاصيات كائنٍ يحتوي على المفتاح ‎<code>_destroy</code> مع القيمة 1 أو <code>true</code>، فسيُدمَّر آنذاك الكائن. تتيح هذه الاستمارة للمستخدمين إزالة العناوين:<syntaxhighlight lang="html">
إذا كانت تجزئة السمات لكائن تحتوي على key _destroy مع قيمة 1 أو true ، فسيُدمَّر الكائن. يتيح هذا النموذج للمستخدمين إزالة العناوين:
 
 
 
 
<%= form_for @person do |f| %>
 
<%= form_for @person do |f| %>
 
+
  Addresses:
 Addresses:
+
  <ul>
 
+
    <%= f.fields_for :addresses do |addresses_form| %>
 <nowiki><ul></nowiki>
+
      <li>
 
+
        <%= addresses_form.check_box :_destroy%>
   <%= f.fields_for :addresses do |addresses_form| %>
+
        <%= addresses_form.label :kind %>
 
+
        <%= addresses_form.text_field :kind %>
     <nowiki><li></nowiki>
+
        ...
 
+
      </li>
       <%= addresses_form.check_box :_destroy%>
+
    <% end %>
 
+
  </ul>
       <%= addresses_form.label :kind %>
 
 
 
       <%= addresses_form.text_field :kind %>
 
 
 
       ...
 
 
 
     <nowiki></li></nowiki>
 
 
 
   <% end %>
 
 
 
 <nowiki></ul></nowiki>
 
 
 
 
<% end %>
 
<% end %>
 
+
</syntaxhighlight>لا تنسَ تحديث القائمة البيضاء في المتحكم لديك لتشمل أيضًا الحقل <code>destroy_</code>:<syntaxhighlight lang="rails">
لا تنس تحديث القائمة البيضاء في المتحكم لديك ليشمل أيضًا الحقل destroy_:
 
 
 
 
def person_params
 
def person_params
 
+
  params.require(:person).
 params.require(:person).
+
    permit(:name, addresses_attributes: [:id, :kind, :street, :_destroy])
 
 
<nowiki>   permit(:name, addresses_attributes: [:id, :kind, :street, :_destroy])</nowiki>
 
 
 
 
end
 
end
 +
</syntaxhighlight>
  
== 9.5 منع السجلات الفارغة ==
+
=== منع السجلات الفارغة ===
من المفيد غالبًا تجاهل مجموعات الحقول التي لم يملئها المستخدم. يمكنك التحكم في ذلك بتمرير: reject_if proc إلى accepts_nested_attributes_for. سيُستدعى proc هذا مع كل تجزئة من السمات المرسَلة من النموذج. إذا أعاد برس القيمة false، فلن ينشئ السجل النشط عنصرًا مقترنًا لهذه التجزئة. يحاول المثال أدناه فقط إنشاء عنوان إذا عُيِّنت سمة النوع.
+
من المفيد غالبًا تجاهل مجموعات الحقول التي لم يملؤها المستخدم. يمكنك التحكم في ذلك بتمرير <code>reject_if</code> ذي النوع <code>[[Ruby/Proc|Proc]]</code> إلى <code>accepts_nested_attributes_for</code>. سيُستدعى الكائن <code>[[Ruby/Proc|Proc]]</code> هذا مع كل [[Ruby/Hash|جدول Hash]] من الخاصيات المرسَلة عبر الاستمارة. إذا أعاد الكائن <code>[[Ruby/Proc|Proc]]</code> القيمة <code>false</code>، فلن ينشئ [[Rails/active record|Active Record]] كائنًا مقترنًا [[Ruby/Hash|للجدول Hash]] هذا. يحاول المثال أدناه فقط إنشاء عنوان إذا عُيِّنت الخاصية <code>kind</code>:<syntaxhighlight lang="rails">
 
 
 
class Person < ApplicationRecord
 
class Person < ApplicationRecord
 
+
  has_many :addresses
 has_many :addresses
+
  accepts_nested_attributes_for :addresses, reject_if: lambda {|attributes| attributes['kind'].blank?}
 
 
<nowiki> accepts_nested_attributes_for :addresses, reject_if: lambda {|attributes| attributes['kind'].blank?}</nowiki>
 
 
 
 
end
 
end
 +
</syntaxhighlight>للسهولة، يمكنك بدلًا من ذلك تمرير الرمز ‎<code>:all_blank</code> الذي سيُنشِئ كائنًا من النوع <code>[[Ruby/Proc|Proc]]</code> الذي سيرفض السجلات عندما تكون جميع الخاصيات فارغةً باستثناء أي قيمة للخاصية ‎<code>_destroy</code>.
  
كوسيلة راحة، يمكنك بدلًا من ذلك تمرير الرمز: all_blank الذي سيُنشِئ proc والذي سيرفض السجلات حيث تكون جميع الخصائص فارغة باستثناء أي قيمة لـ _destroy.
+
=== إضافة حقول آليًا ===
 +
بدلًا من تصيير مجموعات متعددة من الحقول في وقت مبكر، قد ترغب في إضافتها فقط عندما ينقر المستخدم على زر "إضافة عنوان جديد" (Add new address). لا توفر ريلز أي دعم مضمّن لهذا السلوك للأسف. عند توليد مجموعات جديدة من الحقول، يجب التأكد من أن مفتاح المصفوفة المقابلة فريد؛ يعد تاريخ [[JavaScript|جافاسكربت]] الحالي (ميلي ثانية بعد [[wikipedia:Unix_time|بدء توقيت يونكس]]) اختيارًا شائعًا.
  
== 9.6 إضافة حقول في الهواء ==
+
== مصادر ==
بدلاً من عرض مجموعات متعددة من الحقول في وقت مبكر، قد ترغب في إضافتها فقط عندما ينقر المستخدم على زر "إضافة عنوان جديد". لا توفر ريلز أي دعم مضمّن لهذا. عند إنشاء مجموعات جديدة من الحقول، يجب التأكد من أن مفتاح المصفوفة المرتبط فريد - يعد تاريخ جافا سكريبت الحالي (ميلي ثانية بعد الحقبة) اختيارًا شائعًا.
+
* [https://guides.rubyonrails.org/form_helpers.html صفحة Action View Form Helpers في توثيق Ruby On Rails.]
{{NUMBEROFARTICLES}}
 

المراجعة الحالية بتاريخ 10:12، 24 مارس 2019

تعتبر الاستمارات (forms) في تطبيقات الويب واجهة أساسية للتفاعل مع المستخدم وجلب بيانات منه. ومع ذلك، يمكن أن تصبح عملية إنشاء استمارةٍ وصيانتها عمليةً مملةً بسبب الحاجة إلى التحكم بالتسميات وبخاصياتها الكثيرة.

تخلَّص ريلز من هذا التعقيد بتوفير مساعدين للعرض لإنشاء الاستمارات بسهولة. ومع ذلك، بما أن للمساعدين حالات استخدام مختلفة، يحتاج المطورون إلى معرفة الاختلافات بين توابع كل مساعد قبل استخدامها.

بعد قراءة هذا الدليل، ستتعلم:

  • كيفية إنشاء استمارات للبحث ونوع مماثل من الاستمارات العامة التي لا تمثل أي نموذج (model) محدد في تطبيقك.
  • كيفية عمل استمارات مركزية النموذج (model-centric forms) لإنشاء وتعديل سجلات محددة في قاعدة بيانات.
  • كيفية إنشاء مربعات اختيار من أنواع متعددة من البيانات.
  • مساعدو التاريخ والوقت الذين يوفرهم ريلز.
  • ما الذي يجعل نموذج تحميل الملف مختلفًا.
  • كيفية نشر الاستمارات إلى الموارد الخارجية وتحديد الإعداد authenticity_token.
  • كيفية بناء استمارات معقدة.

ملاحظة: ليس المقصود من هذا الدليل أن يكون توثيقًا كاملًا لمساعدي الاستمارات المتاحة وكل ما يتعلق بها. يرجى زيارة توثيق الواجهة البرمجية لريلز للاطلاع على المرجع الكامل.

تنويه: استعملنا المصطلح "استمارة" ترجمةً للكلمة form، والمصطلح "نموذج" ترجمةً للكلمة model لكي لا يحدث لبسٌ بينهما، إذ تترجم form في كثير من الأحيان في الموسوعة إلى "نموذج". انتبه إلى أنَّ هذه الترجمة قد لا تُطبَّق على بقية توثيق ريلز.

التعامل مع الاستمارات الأساسية

مساعد الاستمارة الأساسي هو form_tag.

<%= form_tag do %>
  Form contents
<% end %>

عندما يستدعى بدون معاملات كما سبق، فإنه ينشئ الوسم <form> والذي، عند إرساله، سيرسل الاستمارة عبر POST إلى الصفحة الحالية. على سبيل المثال، بافتراض أن الصفحة الحالية هي ‎/home/index، ستبدو شيفرة HTML المولدة هي (أضيفت إليها بعض فواصل الأسطر لقابلية القراءة):

<form accept-charset="UTF-8" action="/" method="post">
  <input name="utf8" type="hidden" value="&#x2713;" />
  <input name="authenticity_token" type="hidden" value="J7CBxfHalt49OSHp27hblqK20c9PgwJ108nDHX/8Cts=" />
  Form contents
</form>

ستلاحظ أن شيفرة HTML تحتوي على عنصر إدخال من النوع hidden. هذا العنصر مهم، لأنه لا يمكن إرسال الاستمارة بنجاح بدونه. يجبر عنصر الإدخال المخفي ذو الاسم utf8 المتصفحات أن تراعي ترميز محارف هذه الاستمارة وأنه قد أُنشِئ لجميع الاستمارات سواء كانت طريقة الإرسال عبر "GET" أو "POST".

عنصر الإدخال الثاني ذو الاسم Authenticity_token هو ميزة أمان في ريلز تسمى "حماية هجمات تزوير الطلب عبر الموقع" (cross-site request forgery protection)، يولده كل مساعد لكل استمارة لا ترسل عبر GET (بشرط تفعيل ميزة الأمان هذه). يمكنك قراءة المزيد حول هذا الأمر في دليل تأمين تطبيقات ريلز.

استمارة بحث عامة

إحدى الاستمارات الأساسية التي تراها على الويب هي استمارة البحث. تحتوي هذه الاستمارة على:

  • عنصر استمارة يرسل عبر الطريقة "GET".
  • عنصر تسمية لحقل الإدخال.
  • عنصر إدخال نصي.
  • عنصر إرسال.

لإنشاء هذه الاستمارة، سيُستخدَم المساعدين form_tag و label_tag و text_field_tag و submit_tag على التوالي كالتالي:

<%= form_tag("/search", method: "get") do %>
  <%= label_tag(:q, "Search for:") %>
  <%= text_field_tag(:q) %>
  <%= submit_tag("Search") %>
<% end %>

سيولد هذا شيفرة HTML التالية:

<form accept-charset="UTF-8" action="/search" method="get">
  <input name="utf8" type="hidden" value="&#x2713;" />
  <label for="q">Search for:</label>
  <input id="q" name="q" type="text" />
  <input name="commit" type="submit" value="Search" />
</form>

تنويه: لكل عنصر إدخال للاستمارة، تُنشَئ الخاصية id من اسمه ("q" في المثال أعلاه). يمكن أن تكون هذه المعرفات مفيدة جدًا لتنسيق العناصر عبر CSS أو معالجة عناصر تحكم الاستمارة باستخدام جافاسكربت.

بالإضافة إلى text_field_tag و submit_tag، يوجد مساعد مماثل لكل عنصر تحكم في HTML.

تحذير: استخدم دائمًا الطريقة "GET" لإرسال استمارات البحث. يتيح ذلك للمستخدمين وضع إشارة حفظ (bookmark) على بحث معين للعودة إليه لاحقًا. بشكل عام، يشجعك ريلز على استخدام طريقة HTTP المناسبة لأي إجراء.

استعمال جدول Hash مع استدعائات مساعد الاستمارة

يقبل المساعد form_tag تمرير وسيطين: مسار الإجراء وجدول Hash يحوي الخيارات المتعلقة بالعملية. يحدد ذلك الجدول طريقة إرسال الاستمارة وخيارات HTML الأخرى مثل الصنف (الخاصية class).

كما هو الحال مع المساعد link_to، لا يتوجب أن يكون وسيط المسار سلسلة نصية فقط، بل يمكن أن يكون جدول Hash من معاملات URL يمكن التعرف عليها من خلال آلية التوجيه في ريلز، والتي بدورها تحول الجدول إلى عنوان URL صالح. ومع ذلك، نظرًا لأن كلا المعاملين الممررين إلى form_tag هما من النوع Hash، يمكنك بسهولة أن تواجه مشكلة إذا كنت ترغب في استعمالهما مع بعضهما بعضًا. على سبيل المثال، دعنا نقول أنك كتبت الشيفرة التالية:

form_tag(controller: "people", action: "search", method: "get", class: "nifty_form")
# => '<form accept-charset="UTF-8" action="/people/search?method=get&class=nifty_form" method="post">'

هنا، يضاف method و class إلى سلسلة الاستعلام الخاصة بعنوان URL المُنشَأ لأنه حتى لو كنت تقصد كتابة جدولين من النوع Hash، فأنت بذلك حددت واحدًا فقط. بناءً على ما سبق، عليك أن تخبر روبي أي الجدولين هو للمعامل الأول وأيهما للثاني وذلك عن طريق تغليف الجدول الأولى (أو كليهما) بالأقواس المعقوصة. سيؤدي هذا إلى إنشاء شيفرة HTML التي تريدها بالضبط:

form_tag({controller: "people", action: "search"}, method: "get", class: "nifty_form")
# => '<form accept-charset="UTF-8" action="/people/search" method="get" class="nifty_form">'

مساعدون لتوليد عناصر الاستمارة

يوفر ريلز سلسلة من المساعدين لتوليد مختلف عناصر الاستمارة مثل مربعات الاختيار وحقول نصية وأزرار الانتقاء. ينشئ هؤلاء المساعدون الأساسيون، مع أسماء تنتهي بـ ‎_tag (مثل text_field_tag و check_box_tag)، عنصر <input> وحيد فقط. المعامل الأول المُمرَّر إليهم هو دائما اسم عنصر الإدخال. عند إرسال الاستمارة، سيُمرر الاسم مع بيانات الاستمارة، وسوف يشق طريقه إلى params في المتحكم مرفقًا بالقيمة التي أدخلها المستخدم لهذا الحقل. على سبيل المثال، إذا كانت الاستمارة تحتوي على <‎%= text_field_tag(:query) %‎>، فستتمكن من الحصول على قيمة هذا الحقل في المتحكم باستخدام params[:query]‎.

عند تسمية المدخلات، يستخدم ريلز اصطلاحات معينة تجعل من الممكن إرسال المعاملات مع قيم غير أساسية (non-scalar values) مثل المصفوفات أو جداول Hash، والتي يمكن الوصول إليها أيضًا في params. يمكنك قراءة المزيد عنها في الفصل السابع من هذا الدليل. للحصول على تفاصيل حول الاستخدام الدقيق لهؤلاء المساعدين، يرجى الرجوع إلى توثيق الواجهة البرمجية الكامل.

مربعات الاختيار

مربعات الاختيار هي عناصر تحكم في الاستمارة تمنح المستخدم مجموعة من الخيارات التي يمكن تمكينها أو تعطيلها:

<%= check_box_tag(:pet_dog) %>
<%= label_tag(:pet_dog, "I own a dog") %>
<%= check_box_tag(:pet_cat) %>
<%= label_tag(:pet_cat, "I own a cat") %>

هذا يولد ما يلي:

<input id="pet_dog" name="pet_dog" type="checkbox" value="1" />
<label for="pet_dog">I own a dog</label>
<input id="pet_cat" name="pet_cat" type="checkbox" value="1" />
<label for="pet_cat">I own a cat</label>

المعامل الأول المُمرَّر إلى check_box_tag، بالطبع، هو اسم عنصر الإدخال. المعامل الثاني، بطبيعة الحال، هو قيمة العنصر. ستُشمَل هذه القيمة في بيانات الاستمارة (وتكون موجودة في params) عند اختيار (check) مربع الاختيار.

أزرار الانتقاء

أزرار الانتقاء، على الرغم من تشابهها مع مربعات الاختيار، هي عناصر تحكم تحدد مجموعة من الخيارات لانتقاء أحدها فقط (أي أن المستخدم يمكنه اختيار خيار واحد فقط):

<%= radio_button_tag(:age, "child") %>
<%= label_tag(:age_child, "I am younger than 21") %>
<%= radio_button_tag(:age, "adult") %>
<%= label_tag(:age_adult, "I'm over 21") %>

تنتج:

<input id="age_child" name="age" type="radio" value="child" />
<label for="age_child">I am younger than 21</label>
<input id="age_adult" name="age" type="radio" value="adult" />
<label for="age_adult">I'm over 21</label>

كما هو الحال مع check_box_tag ، فإن المعامل الثاني في radio_button_tag هو قيمة عنصر الإدخال. نظرًا لأن هذين الزرين يشتركان في الاسم نفسه (age)، فسيتمكن المستخدم من تحديد أحدهما فقط، وستتضمن المعاملات [‎:age] إما "child" أو "adult".

ملاحظة: استخدم دومًا لافتةً نصيةً مع مربع الاختيار وزر الانتقاء. فهي تربط نصًا بخيار محدد، وتوسع المنطقة القابلة للنقر، مما يسهل على المستخدمين النقر على المدخلات.

مساعدون آخرون مهمّون

عناصر التحكم الأخرى بالاستمارة الجديرة بالذكر هي مربع نصي متعدد الأسطر، وحقول كلمة المرور، والحقول المخفية، وحقول البحث، وحقول رقم الهاتف، وحقول التاريخ، وحقول الوقت، وحقول اللون، وحقول التوقيت المحلي، وحقول الشهر، وحقول الأسبوع، وحقول عنوان URL، وحقول البريد الإلكتروني، وحقول الأرقام وحقول المجالات:

<%= text_area_tag(:message, "Hi, nice site", size: "24x6") %>
<%= password_field_tag(:password) %>
<%= hidden_field_tag(:parent_id, "5") %>
<%= search_field(:user, :name) %>
<%= telephone_field(:user, :phone) %>
<%= date_field(:user, :born_on) %>
<%= datetime_local_field(:user, :graduation_day) %>
<%= month_field(:user, :birthday_month) %>
<%= week_field(:user, :birthday_week) %>
<%= url_field(:user, :homepage) %>
<%= email_field(:user, :address) %>
<%= color_field(:user, :favorite_color) %>
<%= time_field(:task, :started_at) %>
<%= number_field(:product, :price, in: 1.0..20.0, step: 0.5) %>
<%= range_field(:product, :discount, in: 1..100) %>

ينتج:

<textarea id="message" name="message" cols="24" rows="6">Hi, nice site</textarea>
<input id="password" name="password" type="password" />
<input id="parent_id" name="parent_id" type="hidden" value="5" />
<input id="user_name" name="user[name]" type="search" />
<input id="user_phone" name="user[phone]" type="tel" />
<input id="user_born_on" name="user[born_on]" type="date" />
<input id="user_graduation_day" name="user[graduation_day]" type="datetime-local" />
<input id="user_birthday_month" name="user[birthday_month]" type="month" />
<input id="user_birthday_week" name="user[birthday_week]" type="week" />
<input id="user_homepage" name="user[homepage]" type="url" />
<input id="user_address" name="user[address]" type="email" />
<input id="user_favorite_color" name="user[favorite_color]" type="color" value="#000000" />
<input id="task_started_at" name="task[started_at]" type="time" />
<input id="product_price" max="20.0" min="1.0" name="product[price]" step="0.5" type="number" />
<input id="product_discount" max="100" min="1" name="product[discount]" type="range" />

لا يتم عرض المدخلات المخفية للمستخدم ولكن بدلًا من ذلك يحتفظ بالبيانات مثل أي إدخال نصي. يمكن تغيير القيم الموجودة داخلها باستخدام جافاسكربت.

تحذير: حقول البحث، ورقم الهاتف، والتاريخ، والوقت، واللون، والتاريخ والوقت المحلي، والشهر، والأسبوع، وعنوان URL، والبريد الإلكتروني، والعدد ونطاق المدخلات هي عناصر تحكم أوجدت في HTML5. إذا كنت ترغب في أن يتوافق تطبيق مع المتصفحات القديمة، فستحتاج إلى استعمال تقنية "الترقيع المتعدد" (polyfill، توفرها CSS بمفردها أو مع جافاسكربت). من المؤكد أنه لا يوجد نقص في الحلول لذلك، على الرغم من أن الطريقة الشائعة في الوقت الحالي هي Modernizr، والتي توفر طريقة بسيطة لإضافة وظائف تعتمد على وجود ميزات HTML5 المكتشفة.

تنويه: إذا كنت تستخدم حقول إدخال كلمة المرور (لأي غرض)، فقد ترغب في ضبط تطبيقك لمنع قراءة وتسجيل مثل هذه المعاملات. يمكنك تعلم ذلك في دليل تأمين تطبيقات ريلز.

التعامل مع كائنات النموذج

مساعدو كائن النموذج

من المهام الشائعة الخاصة بالاستمارة هو تعديل أو إنشاء كائن نموذج (Model Object). على الرغم من إمكانية استخدام المساعدين tag_*‎ لأداء هذه المهمة، إلا أنه حل طويل ومضجر بعض الشيء، إذ يجب لكل وسم التأكد من استخدام اسم المعامل الصحيح وتعيين القيمة الافتراضية لعنصر الإدخال بشكل مناسب. يوفر ريلز مساعدين مصممين خصيصًا لهذه المهمة. هؤلاء المساعدون يفتقرون إلى اللاحقة tag_، مثل text_field و text_area.

بالنسبة إلى هؤلاء المساعدين، يكون المعامل الأول هو اسم متغير نسخة والثاني هو اسم تابع (عادةً خاصية) لاستدعائه مع ذلك الكائن. سيعين ريلز قيمة عنصر الإدخال إلى القيمة التي يعيدها ذلك التابع للكائن ثم سيعيِّن اسمًا مناسبًا للعنصر. إذا كان المتحكم الخاصة بك قد عرَّف ‎@person واسم ذلك الشخص هو "هنري" (Henry) مثلًا، فإن النموذج الذي يحتوي على:

<%= text_field(:person, :name) %>

سيولد مخرجات شبيه بالمخرجات التالية:

<input id="person_name" name="person[name]" type="text" value="Henry"/>

عند إرسال الاستمارة، ستُخَزَّن القيمة التي أدخلها المستخدم في [params[:person][:name. إنَّ params[:person]‎ ذي النوع Hash مناسبٌ لتمريره إلى Person.new، أو إلى ‎@person.update إذا كان person@ نسخةً من Person. في حين أن اسم الخاصية يستعمل بشكل شائع مع المعامل الثاني المُمرَّر إلى هؤلاء المساعدين، فهذا ليس إلزاميًا. في المثال أعلاه، طالما أنَّ الكائنات person لها اسم (name) وتابع name=‎، فإن ريلز ستكون سعيدة.

تحذير: يجب عليك تمرير اسم متغير نسخة، أي :person أو "person"، وليس نسخةً فعلية لكائن النموذج (model object) الخاص بك.

يوفر ريلز مساعدين لعرض أخطاء التحقق من الصحة المقترنة مع كائن نموذجٍ (model object). جرى الحديث عن ذلك بالتفصيل في دليل عمليات التحقق من Active Record.

ربط نموذج بكائن

صحيح أن هذا مريحٌ أكثر إلا أنه ليس السلوك المثالي. إذا كان لدى Person العديد من الخاصيات المراد تعديلها، فسنكرر حينئذٍ اسم الكائن المعدَّل عدة مرات. ما نريد القيام به هو ربط استمارة بكائن نموذج، وهو ما يفعله form_for بالضبط.

افترض أنَّه لدينا متحكم للتعامل مع المقالات في الملف app/controllers/articles_controller.rb:

def new
  @article = Article.new
end

العرض المقابل هو app/views/articles/new.html.erb وسيبدو باستخدام form_for كالتالي:

<%= form_for @article, url: {action: "create"}, html: {class: "nifty_form"} do |f| %>
  <%= f.text_field :title %>
  <%= f.text_area :body, size: "60x12" %>
  <%= f.submit "Create" %>
<% end %>

هناك بعض الأشياء التي يجب ملاحظتها هنا:

  • @article هو الكائن الفعلي الذي يجري تعديله.
  • هناك جدول Hash واحد من الخيارات. تُمرر خيارات التوجيه في :url ذي النوع Hash، وتُمرر خيارات HTML في :html ذي النوع Hash. كما يمكنك توفير الخيار :namespace لنموذجك لضمان كون الخاصيات id فريدة في عناصر الاستمارة. ستضاف شرطة سفلية بادئة للخاصية namespace في الخاصيات id في شيفرة HTML المولدة.
  • تنتج الدالة form_for كائنًا بانيًا للاستمارة (المتغير f).
  • تُستدعَى توابع إنشاء عناصر التحكم بالاستمارة في كائن باني النموذج f.

شيفرة HTML الناتجة هي:

<form class="nifty_form" id="new_article" action="/articles" accept-charset="UTF-8" method="post">
  <input name="utf8" type="hidden" value="&#x2713;" />
  <input type="hidden" name="authenticity_token" value="NRkFyRWxdYNfUg7vYxLOp2SLf93lvnl+QwDWorR42Dp6yZXPhHEb6arhDOIWcqGit8jfnrPwL781/xlrzj63TA==" />
  <input type="text" name="article[title]" id="article_title" />
  <textarea name="article[body]" id="article_body" cols="60" rows="12"></textarea>
  <input type="submit" name="commit" value="Create" data-disable-with="Create" />
</form>

الاسم الذي مُرِّر إلى form_for يتحكم في المفتاح المستخدم في params للوصول إلى قيم الاستمارة. الاسم في حالتنا هذه هو article وحتى كل عناصر الإدخال لها الأسماء article[attribute_name]‎ الخاصة بالنموذج. وبناءً على ذلك، ستكون params[:article]‎ في الإجراء create جدول Hash يحوي المفتاح title: و body:. يمكنك قراءة المزيد حول أهمية أسماء عناصر الإدخال في القسم "فهم العرف المتبع في تسمة المعاملات".

توابع المساعد التي تستدعى مع باني النموذج تتشابه مع مساعدي كائن النموذج (model object) باستثناء أنه ليس من الضروري تحديد أي كائن يُعَدَّل لأن ذلك يُدار بالفعل بواسطة باني الاستمارة.

يمكنك إنشاء ربط مشابه بدون إنشاء الوسوم <form> بالفعل مع المساعد field_for. هذا مفيد لتعديل كائنات نموذج إضافي (additional model objects) مع نفس الاستمارة. على سبيل المثال، إذا كان لديك النموذج Person‎ مع النموذج ContactDetail المرتبط، فيمكنك إنشاء استمارة لإنشاء كليهما بالشكل التالي:

<%= form_for @person, url: {action: "create"} do |person_form| %>
  <%= person_form.text_field :name %>
  <%= fields_for @person.contact_detail do |contact_detail_form| %>
    <%= contact_detail_form.text_field :phone_number %>
  <% end %>
<% end %>

الذي ينتج شيفرة HTML التالية:

<form class="new_person" id="new_person" action="/people" accept-charset="UTF-8" method="post">
  <input name="utf8" type="hidden" value="&#x2713;" />
  <input type="hidden" name="authenticity_token" value="bL13x72pldyDD8bgtkjKQakJCpd4A8JdXGbfksxBDHdf1uC0kCMqe2tvVdUYfidJt0fj3ihC4NxiVHv8GVYxJA==" />
  <input type="text" name="person[name]" id="person_name" />
  <input type="text" name="contact_detail[phone_number]" id="contact_detail_phone_number" />
</form>

الكائن المُنشَئ بواسطة fields_for هو باني نموذج (form builder) مثل ذلك الناتج عن form_for (في الحقيقة form_for يستدعي fields_for داخليًا).

الاعتماد على تعريف السجل

النموذج Article‎ متاح مباشرة لمستخدمي التطبيق، لذلك - ولاتباع أفضل ممارسات التطوير مع ريلز - يجب عليك أن تصرِّح على أنه مورد (resource):

resources :articles

تنبيه: التصريح عن مورد له عدد من الآثار الجانبية. اطلع على قسم توجيه المورد في دليل التوجيه لمزيد من المعلومات حول إعداد الموارد واستخدامها. عند التعامل مع موارد RESTful، يمكن أن تصبح استدعاءات form_for أسهل بكثير إذا كنت تعتمد على تعريف السجل. باختصار، يمكنك فقط تمرير نسخة النموذج (model) وترك الباقي على ريلز مثل اكتشاف اسم النموذج:

## إنشاء مقالة جديدة
# نمط طويل
form_for(@article, url: articles_path)
# (تنفيذ نفس الأمر ولكن بنمط أقصر (استعمال تعريف السجل
form_for(@article)
 
## تعديل مقالة موجودة
# نمط طويل
form_for(@article, url: article_path(@article), html: {method: "patch"})
# نمط قصير
form_for(@article)

لاحظ كيف أن النمط القصير لاستدعاء form_for يؤدي نفس الغرض الذي يفعله استدعاء التابع نفسه في النمط الطويل، بغض النظر عن كون السجل جديدًا أو موجودًا. تعريف السجل ذكي بما فيه الكفاية لمعرفة ما إذا كان السجل جديدًا عبر استدعاء ?record.new_record. كما أنه يحدد المسار الصحيح لإرسال الاستمارة إليه والاسم استنادًا على صنف الكائن.

سيعيّن ريلز أيضًا الصنف class والمعرف id للنموذج تلقائيًا بشكل مناسب: الاستمارة التي تنشئ مقالًا سيكون لها الصنف والمعرف new_article. إذا كنت تعدل مقالة ذات المعرف 23، فسيُعيَّن الصنف إلى edit_article والمعرف إلى edit_article_23. سيتم حذف هاتين الخاصيتين للإختصار في بقية هذا الدليل.

تحذير: عندما تستخدم STI (وراثة الجدول الوحيد) مع نماذجك (models)، فلا يمكنك الاعتماد على تعريف السجل في صنف فرعي بمجرد التصريح عن الصنف الأب بأنه موردٌ. سيتعين عليك حينئذٍ تحديد اسم الاستمارة مع الخيارين :url و :method بشكل صريح.

التعامل مع مجالات الأسماء

إذا أنشئت مسارات توجيه ضمن مجالات أسماء (namespaced routes)، فإن form_for يحتوي على اختصار أنيق لذلك أيضًا. إذا كان تطبيقك يحتوي على مجال الاسم admin، فالسطر التالي:

form_for [:admin, @article]

سينشئ استمارةً تُرسَل إلى ArticleController داخل مجال الاسم admin (يرسل إلى admin_article_path(@article)‎ في حالة التحديث). إذا كان لديك عدة مستويات من مجالات الأسماء، فستكون الصياغة مشابهة لما سبق:

form_for [:admin, :management, @article]

لمزيد من المعلومات حول توجيه المسارات في ريلز وكل ما يتعلق به، يرجى الاطلاع على دليل التوجيه.

كيف تعمل النماذج مع الطرائق PATCH أو PUT أو DELETE؟

يشجع إطار ريلز تصميم RESTful لتطبيقاتك، مما يعني أنك ستجعل الكثير من طلبات HTTP عبر الطريقة "PATCH" و "DELETE" (إلى جانب "GET" و "POST"). ومع ذلك، لا تدعم معظم المتصفحات طرائقَ أخرى غير "GET" و "POST" عندما يتعلق الأمر بإرسال الاستمارات.

تعمل ريلز حول هذه المشكلة بمحاكاة طرائق أخرى عبر POST باستخدام عنصر إدخال مخفي يسمى "method_"، والذي يعيَّن ليعكس الطريقة المطلوبة:

form_tag(search_path, method: "patch")

تنتج:

<form accept-charset="UTF-8" action="/search" method="post">
  <input name="_method" type="hidden" value="patch" />
  <input name="utf8" type="hidden" value="&#x2713;" />
  <input name="authenticity_token" type="hidden" value="f755bb0ed134b76c432144748a6d4b7a7ddf2b71" />
  ...
</form>

عند تحليل البيانات المرسلة، ستأخذ ريلز بعين الاعتبار المعامل method_ الخاص وتتصرف كما لو كانت طريقة HTTP هي تلك المحددة داخلها (أي الطريقة "PATCH" في هذا المثال).

إنشاء مربعات اختيار بسهولة

تتطلب مربعات الاختيار في HTML كتابة مقدار لا بأس به من الشيفرة (عنصر <option> واحد لكل خيار للاختيار من بينها)، لذلك يكون من المنطقي جدًا أن يُنشَئ ديناميكيًا.

هذا ما ستبدو الشيفرة عليه:

<select name="city_id" id="city_id">
  <option value="1">Lisbon</option>
  <option value="2">Madrid</option>
  ...
  <option value="12">Berlin</option>
</select>

لديك هنا قائمة من المدن التي ستعرض أسماؤها على المستخدم. داخليًا، يريد التطبيق فقط التعامل مع المعرفات الخاصة بهم بحيث تُستخدَم كالخاصية value للخيار. دعونا نرى كيف يساعد ريلز هنا.

الوسمان <select> و <option>

المساعد الأكثر عمومية هو select_tag، والذي - كما يوحي الاسم - يولد ببساطة الوسم <select> الذي يقوم بتغليف سلسلة من الخيارات <option>:

<%= select_tag(:city_id, '<option value="1">Lisbon</option>...') %>

هذه هي البداية فقط، إذ ذلك لا ينشئ الوسوم <option> بشكل ديناميكي. يمكنك إنشاء هذه الوسوم عبر استعمال المساعد options_for_select:

<%= options_for_select([['Lisbon', 1], ['Madrid', 2], ...]) %>

ينتج:

<%= options_for_select([['Lisbon', 1], ['Madrid', 2], ...]) %>
 
output:
 
<option value="1">Lisbon</option>
<option value="2">Madrid</option>
...

نوع المعامل الأول المُمرَّر إلى options_for_select هو مصفوفة متشعبة كل عنصر فيها مكون من عنصرين هما: نص الخيار (اسم المدينة في حالتنا) وقيمة الخيار (معرف المدينة). قيمة الخيار هي ما سيُرسَل إلى متحكمك. غالبًا ما يكون هذا هو معرف كائن قاعدة البيانات المقابل ولكن لا يجب أن يكون كذلك. الآن، يمكنك الجمع بين select_tag و options_for_select لإنشاء قائمة خيارات كاملة:

<%= select_tag(:city_id, options_for_select(...)) %>

يسمح المساعد options_for_select بتحديد خيار مسبق عبر تمرير قيمته.

<%= options_for_select([['Lisbon', 1], ['Madrid', 2], ...], 2) %>

تنتج:

<option value="1">Lisbon</option>
<option value="2" selected="selected">Madrid</option>
...

عندما يرى ريلز أن القيمة الداخلية للخيار المراد إنشاؤه تطابق هذه القيمة، فإنها ستضيف الخاصية selected إلى ذلك الخيار لتحديده.

تحذير: عندما ينعدم الخيار include_blank: أو prompt:، فستعين قيمة include_blank: إلى القيمة true إذا كانت قيمة الخاصية required هي true، والخاصية size هي 1، والخاصية multiple ليست true.

يمكنك إضافة خاصيات عشوائية إلى الخيارات باستخدام جدول Hash:

<%= options_for_select(
  [
    ['Lisbon', 1, { 'data-size' => '2.8 million' }],
    ['Madrid', 2, { 'data-size' => '3.2 million' }]
  ], 2
) %>

يولد:

<option value="1" data-size="2.8 million">Lisbon</option>
<option value="2" selected="selected" data-size="3.2 million">Madrid</option>
...

اختيار الخيارات للتعامل مع النماذج

في معظم الحالات، تكون عناصر التحكم في الاستمارة مرتبطة بنموذج قاعدة بيانات محدد، وكما قد تتوقع أن توفر ريلز مساعدات مصممة خصيصًا لهذا الغرض. بما يتفق مع المساعدين الآخرين في الاستمارة، عند التعامل مع النماذج، تسقط اللاحقة tag_ من select_tag:

# controller:
@person = Person.new(city_id: 2)
# view:
<%= select(:person, :city_id, [['Lisbon', 1], ['Madrid', 2], ...]) %>

لاحظ أن المعامل الثالث، مصفوفة الخيارات، هو نفس نوع الوسيط المُمرَّر إلى options_for_select. ميزة واحدة هنا هي أنه لا داعي للقلق بشأن التحديد المسبق للمدينة الصحيحة إذا كانت مدينة المستخدم معروفة مسبقًا. ريلز سيقوم بذلك نيابة عنك من خلال القراءة من الخاصية person.city_id@. كما هو الحال مع المساعدين الآخرين، إذا كنت ستستخدم المساعد select في أداة إنشاء الاستمارات التي حُدِّدَ نطاقها إلى الكائن ‎@person، فستكون الصياغة كالتالي:

# select on a form builder
<%= f.select(:city_id, ...) %>

يمكنك أيضًا تمرير كتلة إلى المساعد select:

<%= f.select(:city_id) do %>
  <% [['Lisbon', 1], ['Madrid', 2]].each do |c| -%>
    <%= content_tag(:option, c.first, value: c.last) %>
  <% end %>
<% end %>

تحذير: إذا كنت تستخدم select (أو مساعدين مشابهين له مثل collection_select، أو select_tag) لتعيين الارتباط belongs_to، فيجب عليك تمرير اسم المفتاح الخارجي (هو city_id في المثال أعلاه)، وليس اسم الارتباط نفسه. إذا قمت بتحديد city بدلًا من city_id فسيرمي Active Record الخطأ:

ActiveRecord::AssociationTypeMismatch: City(#17815740) expected, got String(#1138750)

عند تمرير params إلى Person.new أو update. طريقة أخرى للتعامل مع هذا هي أن مساعدي الاستمارة تعدل الخاصيات. يجب أيضًا أن تكون على دراية بتداعيات الأمان المحتملة الخاصة بالسماح للمستخدمين بتعديل المفاتيح الخارجية مباشرةً.

الوسوم <option> من مجموعة من كائنات اعتباطية

يتطلب توليد الوسوم <option> باستخدام المساعد options_for_select إنشاء مصفوفة تحتوي على النص والقيمة لكل خيار. ولكن ماذا لو كان لديك نموذجًا مثل النموذج City (ربما أحد Active Record) وكنت تريد توليد وسوم <option> من مجموعة من تلك الكائنات؟ قد يكون أحد الحلول هو إنشاء مصفوفة متشعبة منهم:

<% cities_array = City.all.map { |city| [city.name, city.id] } %>
<%= options_for_select(cities_array) %>

هذا حل صالح تمامًا، ولكن ريلز يوفر بديلًا أسهل وأوضح وذلك عبر المساعد options_from_collection_for_select. يتوقع هذا المساعد تمرير مجموعة من الكائنات العشوائية إليه بالإضافة إلى وسيطين آخرين هما: أسماء الدوال لقراءة قيمة ونص الخيار على التوالي:

<%= options_from_collection_for_select(City.all, :id, :name) %>

كما يوحي الاسم، هذا يولد فقط الوسوم <option>. لتوليد مربع تحديد كامل، ستحتاج إلى استخدامه بالتزامن مع المساعد select_tag، تمامًا كما تفعل مع options_for_select. عند العمل مع كائنات النموذج، تمامًا مثلما يجمع select بين select_tag و options_for_select، يجمع collection_select بين select_tag و options_from_collection_for_select.

<%= collection_select(:person, :city_id, City.all, :id, :name) %>

كما هو الحال مع المساعدين الآخرين، إذا كنت ستستخدم المساعد collection_select على باني الاستمارة حُدّدت إلى الكائن person@، فستكون الصياغة بالشكل التالي:

<%= f.collection_select(:city_id, City.all, :id, :name) %>

ملاحظة: يجب أن تملك الأزواج التي مُرِّرَتْ إلى options_for_select اسمًا أولًا ومُعرِّفًا ثانيًا، ولكن مع options_from_collection_for_select الوسيط الأول هو التابع value والثاني هو التابع text.

اختيار المنطقة الزمنية والبلد

للاستفادة من دعم المنطقة الزمنية في ريلز، يجب عليك أن تسأل المستخدمين عن المنطقة الزمنية الموجودين فيها. سيتطلب القيام بذلك توليد خيارات تحديد من قائمة كائنات معرفة سابقًا من النوع TimeZone باستخدام collection_select، ولكن يمكنك ببساطة استخدام المساعد time_zone_select الذي يفعل لك ذلك:

<%= time_zone_select(:person, :time_zone) %>

هناك أيضًا المساعد time_zone_options_for_select الذي يوفر طريقة يدوية للقيام بنفس الأمر وبالتالي يوفر قابلية للتخصيص. اقرأ توثيق الواجهة البرمجية للتعرف على الوسائط التي تمرَّر إلى هذين التابعين.

اعتاد ريلز على احتوائه على المساعد country_select لتحديد بلد معين من بين مجموعة من البلدان، ولكن جمِّع ذلك في الإضافة country_select. عند استخدام هذه الإضافة، يجب أن تدرك أن استبعاد أو إدراج أسماء بلدان معينة من القائمة يمكن أن يكون خِلافِيًّا (وكان هذا هو السبب وراء استبعاد المساعد من ريلز وتحويله إلى إضافة).

استخدام حقول التاريخ والوقت في الاستمارة

يمكنك اختيار عدم استخدام مساعدي الاستمارة الذين ينشؤون حقول HTML5 لإدخال التاريخ والوقت واستخدام مساعدي التاريخ والوقت البدلاء. يختلف مساعدو التاريخ والوقت هؤلاء عن جميع المساعدين الآخرين في ناحيتين مهمتين هما:

  • لا يمكن تمثيل التواريخ والأوقات من خلال عنصر إدخال واحد فقط. بدلًا من ذلك لديك عدة عناصر، واحد لكل مكون (السنة، الشهر، اليوم، وما إلى ذلك) وبالتالي لا توجد قيمة واحدة في params الخاص بك تمثل التاريخ أو الوقت.
  • يستخدم المساعدون الآخرون اللاحقة ‎_tag لتوضيح ما إذا كان المساعد مساعدًا مجردًا (barebones) أو يعمل مع كائنات نموذج (model objects). مع التواريخ والأوقات، المساعد select_date و select_time و select_datetime هم مساعدون مجردون، و date_select، و time_select و datetime_select هم مساعدو كائنات نموذج مكافئة.

ستُنشِئ كلا هاتين العائلتين من المساعدين سلسلة من مربعات الاختيار للعناصر المختلفة (السنة، الشهر، اليوم، ...إلخ).

المساعدون المجردون

تأخذ مجموعة المساعدين select_*‎ كوسيطهم الأول نسخة من Date أو Time أو DateTime التي تُستخدَم كقيمة محددة حاليًا. يمكنك حذف هذا المعامل، وفي هذه الحالة يُستخدم التاريخ الحالي. فمثلًا:

<%= select_date Date.today, prefix: :start_date %>

يولد (مع حذف قيم الخيار الفعلية للإيجاز):

<select id="start_date_year" name="start_date[year]"> ... </select>
<select id="start_date_month" name="start_date[month]"> ... </select>
<select id="start_date_day" name="start_date[day]"> ... </select>

تؤدي المدخلات الواردة أعلاه إلى ظهور params[:start_date]‎ كجدول Hash مع المفاتيح year:، و month:، و day:. للحصول على الكائن Date أو Time أو DateTime، يجب عليك استخراج هذه القيم وتمريرها إلى الباني المناسب مثل:

Date.civil(params[:start_date][:year].to_i, params[:start_date][:month].to_i, params[:start_date][:day].to_i)

الخيار :prefix هو المفتاح المستخدم لاستعادة جدول Hash لمكونات التاريخ من params. هنا، ضُبِط إلى start_date؛ إذا حُذِف، فسيُحدَد افتراضيَّا إلى date.

مساعدو كائن النموذج

لا يعمل select_date بشكل جيد مع الاستمارات التي تحدِّث أو تنشئ كائنات Active Record إذ يتوقع Active Record أن كل عنصر في params يقابل خاصية واحدة. يرسل مساعدو كائن النموذج (model object helpers) للتواريخ والأوقات المعاملات بأسماء خاصة؛ عندما يشاهد Active Record معاملات بأسماء من هذا القبيل، فإنه يعلم أنه يجب دمجها مع المعاملات الأخرى وإعطائها لبانٍ يتناسب مع نوع الحقل (العمود). فمثلًا:

<%= date_select :person, :birth_date %>

ينتج (مع حذف قيم الخيار الفعلية للإيجاز):

<select id="person_birth_date_1i" name="person[birth_date(1i)]"> ... </select>
<select id="person_birth_date_2i" name="person[birth_date(2i)]"> ... </select>
<select id="person_birth_date_3i" name="person[birth_date(3i)]"> ... </select>

مما يؤدي إلى params مثل:

{'person' => {'birth_date(1i)' => '2008', 'birth_date(2i)' => '11', 'birth_date(3i)' => '22'}}

عندما يُمرَّر هذا إلى Person.new (أو update)، يلاحظ Active Record أنه يجب استخدام جميع هذه المعاملات لإنشاء الخاصية birth_date واستخدام المعلومات اللاحقة لتحديد الترتيب الذي يجب أن يمرر هذه المعاملات إلى دوال مثل Date.civil.

خيارات مشتركة

تستخدم كلا عائلتي المساعدين نفس مجموعة الدوال الأساسية لتوليد وسوم <select> فردية، وبالتالي يقبل كلاهما نفس الخيارات إلى حد كبير. على وجه الخصوص، ستولد ريلز بشكل افتراضي في خيارات السنة 5 سنوات على جانبي السنة الحالية. إذا لم يكن هذا نطاقًا مناسبًا، فيمكن تخطي ذلك عبر استعمال الخيارين start_year: و end_year:. للحصول على قائمة شاملة بالخيارات المتاحة، ارجع إلى توثيق الواجهة البرمجية.

كقاعدة عامة، يجب أن تستخدم date_select عند العمل مع كائنات نموذج و select_date في حالات أخرى، مثل استمارة بحث ترشِّح النتائج حسب التاريخ.

ملاحظة: في العديد من الحالات، يكون منتقو التاريخ المدمجون غير متقنين لأنهم لا يساعدون المستخدم في تحديد العلاقة بين التاريخ واليوم من الأسبوع.

مكونات فردية

تحتاج في بعض الأحيان إلى عرض مكون تاريخ واحد فقط مثل سنة أو شهر. يوفر ريلز سلسلة من المساعدين لهذا الغرض: select_year، و select_month، و select_day، و select_hour، و select_minute، و select_second. يبدو من الواضح ما هي مهمة كل واحد من هؤلاء المساعدين. بشكل افتراضي، سينشئون حقل إدخال له اسم يتوضع بعد مكون الوقت (على سبيل المثال، الاسم "year" للمساعد select_year، و "month" للمساعد select_month ...إلخ.) على الرغم من إمكانية تجاوز هذا الأمر عبر الخيار :field_name. يعمل الخيار :prefix بالطريقة نفسها التي يعمل فيها مع المساعد select_date و select_time ولديه نفس القيمة الافتراضية.

يحدد المعامل الأول القيمة التي يجب اختيارها ويمكن أن تكون إما نسخة من Date أو Time أو DateTime، وفي هذه الحالة سيُستخرَج المكون المقابل، أو القيمة العددية المقابلة. إليك الشيفرة التالية:

<%= select_year(2019) %>
<%= select_year(Time.now) %>

التي ستولد نفس الناتج إذا كان العام الحالي هو 2019 ويمكن استعادة القيمة التي اختارها المستخدم من قبل params[:date][:year]‎.

تحميل الملفات

إحدى المهام الشائعة في الاستمارات هي تحميل نوع ما من الملفات، سواء أكانت صورة شخص أو ملف CSV يحتوي على بيانات لمعالجتها. أهم شيء يجب تذكره من خلال تحميل الملفات هو أنه يجب تعيين تشفير النموذج المصيَّر إلى القيمة "multipart/form-data". إذا كنت تستخدم form_for، فسيتم ذلك تلقائيًا. أمَّا إذا كنت تستخدم form_tag، فيجب عليك إعداده بنفسك، وفقًا للمثال التالي.

توفر الاستمارتان التاليتان إمكانية تحميل ملف:

<%= form_tag({action: :upload}, multipart: true) do %>
  <%= file_field_tag 'picture' %>
<% end %>
 
<%= form_for @person do |f| %>
  <%= f.file_field :picture %>
<% end %>

يوفر ريلز الزوج المعتاد من المساعدين: المساعد file_field_tag من العائلة المجرَّدة والمساعد file_field من عائلة كائنات النموذج (أي التي تعمل مع كائنات النموذج). الاختلاف الوحيد مع المساعدين الآخرين هو أنه لا يمكنك تعيين قيمة افتراضية لمدخلات الملف لأن هذا لن يكون له أي معنى. كما تتوقع في الحالة الأولى، يكون الملف الذي حُمِّل في params [:picture]‎ وفي الحالة الثانية في params[:person][:picture]‎.

ما الذي يُحمَّل؟

الكائن في params ذي النوع Hash هو نسخة لصنف فرعي من الصنف IO. اعتمادًا على حجم الملف المراد تحميله، قد يكون في الواقع StringIO أو نسخة من File مدعومًا بملف مؤقت. في كلتا الحالتين، سيحتوي الكائن على الخاصية original_filename التي تحوي الاسم الذي سمي الملف به على حاسوب المستخدم، والخاصية content_type التي تحوي نوع MIME للملف. تحفظ الشيفرة التالية المحتوى الذي حُمِّل في ‎#{Rails.root}/public/uploads تحت نفس اسم الملف الأصلي (بافتراض أن الاستمارة هي نفسها في المثال السابق):

def upload
  uploaded_io = params[:person][:picture]
  File.open(Rails.root.join('public', 'uploads', uploaded_io.original_filename), 'wb') do |file|
    file.write(uploaded_io.read)
  end
end

بمجرد تحميل الملف، هناك العديد من المهام المحتملة، بدءًا من مكان تخزين الملفات (على القرص، أو على Amazon S3 ...إلخ) وربطها بنماذج لتغيير حجم ملفات الصور وتوليد الصور المصغرة (thumbnails). مثل هذه تعقيدات هي خارج نطاق هذا الدليل، ولكن هناك العديد من المكتبات المصممة للمساعدة في هذا الخصوص. اثنتان من أفضلها وأشهرها هما CarrierWave و Paperclip.

ملاحظة: إذا لم يحدَّد المستخدم ملفًا، فسيكون المعامل المقابل سلسلة فارغة.

التعامل مع Ajax

بخلاف الاستمارات الأخرى، فإن إنشاء استمارة تحميل ملف بشكل غير متزامن ليس بسيطًا مثل استعمال form_for مع remote: true. مع استمارة Ajax، تُجرَى عملية السَلسَلة (serialization) بواسطة جافاسكربت قيد التشغيل داخل المتصفح، ولأن جافاسكربت لا تستطيع قراءة الملفات من محرك القرص الصلب، فلا يمكن تحميل الملف آنذاك. يتمثل الحل الأكثر شيوعًا في استخدام الإطار iframe بشكل غير مرئي يعمل كهدف لإرسال الاستمارة.

تخصيص باني الاستمارة

كما ذُكر سابقًا، فإن الكائن الناتج من form_for و fields_for هو نسخة من FormBuilder (أو صنف فرعي منه). يغلف بانو الاستمارة (Form builders) فكرة عرض عناصر الاستمارة لكائن واحد. بينما يمكنك بالطبع كتابة مساعدين لاستماراتك بالطريقة المعتادة، يمكنك أيضًا إنشاء صنف فرعي من FormBuilder وإضافة المساعدين إليه. فمثلًا:

<%= form_for @person do |f| %>
  <%= text_field_with_label f, :first_name %>
<% end %>

يمكن استبدال هذه الشيفرة وكتابة الشيفرة التالية عوضًا عنها:

<%= form_for @person, builder: LabellingFormBuilder do |f| %>
  <%= f.text_field :first_name %>
<% end %>

بتعريف الصنف LabellingFormBuilder بشكل مشابه لما يلي:

class LabellingFormBuilder < ActionView::Helpers::FormBuilder
  def text_field(attribute, options={})
    label(attribute) + super
  end
end

إذا أعدت استخدام هذا بشكل متكرر، يمكنك تعريف المساعد labeled_form_for الذي يطبِّق تلقائيًا الخيار builder: LabellingFormBuilder:

def labeled_form_for(record, options = {}, &block)
  options.merge! builder: LabellingFormBuilder
  form_for record, options, &block
end

يحدِّد باني الاستمارة المستخدَم أيضًا ما يحدث عندما تستعمل:

<%= render partial: f %>

إن كان f نسخةً من FormBuilder، فهذا سيصيِّر الجزئية form مع ضبط كائن الجزئية إلى باني الاستمارة. إن كان باني الاستمارة من الصنف LabellingFormBuilder، فستصيَّر الجزئية labelling_form بدلًا من تلك.

فهم العرف المتبع في تسمية المعاملات

كما شاهدت في الأقسام السابقة، يمكن أن تكون القيم من الاستمارات في المستوى الأعلى من params أو متداخلة في جدول Hash آخر. على سبيل المثال، في الإجراء create القياسي للنموذج Person، تكون params[:person]‎ عادةً جدول Hash يحوي جميع خاصيات الشخص (person) المراد إنشاؤه. يمكن أن يحوي params أيضًا المصفوفات، ومصفوفات من الكائنات Hash وما إلى ذلك.

لا تعرف استمارات HTML بشكل أساسي أي نوع من البيانات المهيكلة (structured data)، وكل ما تولده هو الأزواج اسم-قيمة، حيث تكون الأزواج مجرد سلاسل نصية عادية. المصفوفات والجداول Hash التي تراها في تطبيقك هي نتيجة لبعض اصطلاحات تسمية المعاملات التي يستخدمها ريلز.

هياكل البيانات الأساسية

الهيكلان الأساسيان لتخزين البيانت هما المصفوفات والجدول Hash. يعكس الأخير الصياغة المستعملة للوصول إلى القيمة في params. على سبيل المثال، إذا كانت الاستمارة تحتوي على:

<input id="person_name" name="person[name]" type="text" value="Henry"/>

سيحتوي params ذي النوع Hash على:

{'person' => {'name' => 'Henry'}}

و [params[:person][:name سيعيد القيمة المرسلة في المتحكم. يمكن أن تشعيب Hash على مستويات عدة كما هو مطلوب، على سبيل المثال:

<input id="person_address_city" name="person[address][city]" type="text" value="New York"/>

سينتج هذا في params:

{'person' => {'address' => {'city' => 'New York'}}}

يتجاهل ريلز عادةً أسماء المعاملات المكررة. إذا كان اسم المعامل يحتوي على مجموعة فارغة من الأقواس المعقوفة []، فستُجمع في مصفوفة. إذا كنت تريد أن يتمكن المستخدمون من إدخال أرقام هواتف متعددة، فيمكنك وضع ذلك في الاستمارة:

<input name="person[phone_number][]" type="text"/>
<input name="person[phone_number][]" type="text"/>
<input name="person[phone_number][]" type="text"/>

سينتج عن ذلك params[:person][:phone_number]‎ بصفته مصفوفة تحتوي على أرقام الهاتف المدخلة.

الدمج بين هياكل البيانات الأساسية

يمكننا خلط ومطابقة هذين المفهومين. قد يكون أحد عناصر الجدول Hash هو المصفوفة كما في المثال السابق، أو يمكنك الحصول على مصفوفة من الجداول Hash. على سبيل المثال، قد تسمح لك الاستمارة بإنشاء أي عدد من العناوين بتكرار الجزء التالي من الاستمارة:

<input name="addresses[][line1]" type="text"/>
<input name="addresses[][line2]" type="text"/>
<input name="addresses[][city]" type="text"/>

سيُنتج هذا في params[:addresses]‎ مصفوفة من الجداول Hash مع المفاتيح line1 و line2 و city. يقرر ريلز بدء تجميع القيم في جدول Hash جديدة كلما عثرت على اسم عنصر إدخال موجود بالفعل في الجدول الحالي.

هناك قيود، ومع ذلك، في حين أن الجداول Hash يمكن أن تكون متداخلة عشوائيًا، يُسمَح بمستوى واحد فقط من "المصفوفية" (arrayness). عادةً يمكن تبديل الجداول Hash مكان المصفوفات، على سبيل المثال، بدلًا من وجود مصفوفة من كائنات نموذج، يمكن استعمال جدول Hash من كائنات نموذج قيمة مفاتيحها هي معرف كل منها، أو فهرس مصفوفة، أو معامل آخر.

تحذير: لا تعمل معاملات المصفوفة بشكل جيد مع المساعد check_box. وفقًا لمواصفات HTML، مربعات الاختيار غير المحدَّدة (unchecked) تُرسَل بدون قيمة. ومع ذلك، غالبًا ما يكون من الملائم لمربع الاختيار أن يرسل قيمةً دائمًا. يُزيِّف المساعد check_box هذا الأمر عبر إنشاء عنصر إدخال مخفي إضافي بنفس الاسم. إذا كان مربع الاختيار غير محدد، فسيُرسَل عنصر الإدخال المخفي فقط، وإذا حُدِّد، فسيُرسَل كلا العنصرين، ولكن القيمة المُرسَلة من مربع الاختيار المُحدَّد لها الأولوية على قيمة العنصر المخفي. عند العمل مع معاملات المصفوفة، سيؤدي هذا الإرسال المكرَّر إلى إرباك ريلز لأن أسماء عناصر الإدخال المتكررة تحدِّد بداية عنصر مصفوفة جديدة. من الأفضل استخدام المساعد check_box_tag أو استخدام جدول Hash بدلًا من المصفوفات.

استخدام مساعدي الاستمارة

لم تستخدم الأقسام السابقة مساعدي الاستمارة لريلز على الإطلاق. لما كان بإمكانك صياغة أسماء عناصر الإدخال بنفسك ونقلها مباشرة إلى مساعدين مثل text_field_tag، توفر ريلز أيضًا دعمًا ذا مستوى أعلى. إن الأدوات الموجودة تحت تصرفك هنا هي معامل الاسم إلى form_for و fields_for والخيار :index الذي يأخذه المساعدون.

قد ترغب في تصيير نموذج يحتوي على مجموعة من حقول التعديل لكل عنوان من عناوين الشخص. فمثلًا:

<%= form_for @person do |person_form| %>
  <%= person_form.text_field :name %>
  <% @person.addresses.each do |address| %>
    <%= person_form.fields_for address, index: address.id do |address_form|%>
      <%= address_form.text_field :city %>
    <% end %>
  <% end %>
<% end %>

بافتراض أن الشخص له عنوانين، بمُعرِّفَين 23 و 45، فهذا من شأنه أن يُنشِئ مخرجات مشابهة لما يلي:

<form accept-charset="UTF-8" action="/people/1" class="edit_person" id="edit_person_1" method="post">
  <input id="person_name" name="person[name]" type="text" />
  <input id="person_address_23_city" name="person[address][23][city]" type="text" />
  <input id="person_address_45_city" name="person[address][45][city]" type="text" />
</form>

سيُنتِج هذا في params الناتج:

{'person' => {'name' => 'Bob', 'address' => {'23' => {'city' => 'Paris'}, '45' => {'city' => 'London'}}}}

يعرف ريلز أن جميع هذه المدخلات يجب أن تكون جزءًا من person ذي النوع Hash لأنك استدعيت fields_for في باني الاستمارة الأول. بتحديد الخيار :index، يمكنك إخبار ريلز أنه بدلًا من تسمية المدخلات person[address][city]‎، يجب أن يضيف هذا الفهرس محاطًا بالقوسين [] بين العنوان والمدينة. هذا مفيد في كثير من الأحيان لأنه من السهل تحديد مكان سجل العنوان المراد تعديله. يمكنك تمرير الأرقام مع بعض الدلالات الأخرى، السلاسل النصية أو حتى القيمة nil (مما يؤدي إلى إنشاء معامل مصفوفة). لإنشاء تداخلات أكثر تعقيدًا، يمكنك تحديد الجزء الأول من اسم عنصر الإدخال (person[address]‎ في المثال السابق) بشكل صريح:

<%= fields_for 'person[address][primary]', address, index: address do |address_form| %>
  <%= address_form.text_field :city %>
<% end %>

سيُنشِئ عنصر إدخال مثل:

<input id="person_address_primary_1_city" name="person[address][primary][1][city]" type="text" value="bologna" />

كقاعدة عامة، يكون اسم عنصر الإدخال النهائي هو جمع الاسم المعطى مع fields_for أو form_for، وقيمة الفهرس (index value) واسم الخاصية. يمكنك أيضًا تمرير الخيار :index مباشرةً إلى المساعدين، مثل text_field، ولكن عادةً ما يكون أقل تكرارًا لتحديد ذلك على مستوى باني الاستمارة بدلًا من عناصر التحكم الفردية للإدخال. كاختصار يمكنك إضافة [] إلى الاسم وحذف الخيار :index. هذا يؤدي نفس الغرض الذي يؤديه تحديد index: address، لذا ستنتج الشيفرة التالية:

<%= fields_for 'person[address][primary][]', address do |address_form| %>
  <%= address_form.text_field :city %>
<% end %>

نفس المخرجات تمامًا الناتجة عن المثال السابق.

استمارات لموارد خارجية

يمكن أيضًا استخدام مساعدي استمارات ريلز لإنشاء استمارة لنشر البيانات إلى مورد خارجي (external resource). ومع ذلك، في بعض الأحيان، قد يكون من الضروري تعيين Authenticity_token للمصدر، إذ يمكن تنفيذ ذلك بتمرير المعامل authenticity_token: 'your_external_token'‎ إلى خيارات form_tag:

<%= form_tag 'http://farfar.away/form', authenticity_token: 'external_token' do %>
  Form contents
<% end %>

في بعض الأحيان، عند إرسال البيانات إلى مورد خارجي، مثل بوابة الدفع الإلكترونية، تكون الحقول التي يمكن استخدامها في الاستمارة محدودة بواسطة واجهة برمجة تطبيقات خارجية وقد يكون إنشاء المصادقة غير مرغوب فيه لتوليد authenticity_token. لعدم إرسال رمز، مرر ببساطة القيمة false إلى الخيار :authenticity_token:

<%= form_tag 'http://farfar.away/form', authenticity_token: false do %>
  Form contents
<% end %>

نفس الطريقة متاحة أيضًا للمساعد form_for:

<%= form_for @invoice, url: external_url, authenticity_token: 'external_token' do |f| %>
  Form contents
<% end %>

أو إذا كنت لا ترغب في تصيير الحقل Authenticity_token:

<%= form_for @invoice, url: external_url, authenticity_token: false do |f| %>
  Form contents
<% end %>

بناء استمارات معقدة

تتطور التطبيقات إلى أبعد من الاستمارات البسيطة التي تعدل كائنًا واحدًا. على سبيل المثال، عند إنشاء Person، قد ترغب في السماح للمستخدم (على نفس الاستمارة) بإنشاء سجلات عناوين متعددة (عنوان للمنزل، والعمل، ...إلخ). عند تعديل هذا الشخص في وقت لاحق، ينبغي أن يكون المستخدم قادرًا على إضافة العناوين أو إزالتها أو تعديلها حسب الضرورة.

ضبط النموذج

يوفر Active Record دعمًا على مستوى النموذج عبر التابع accepts_nested_attributes_for:

class Person < ApplicationRecord
  has_many :addresses, inverse_of: :person
  accepts_nested_attributes_for :addresses
end
 
class Address < ApplicationRecord
  belongs_to :person
end

يُنشئ هذا address_attributes = method في Person الذي يسمح لك بإنشاء وتحديث وإتلاف (اختياريًا) العناوين.

استمارات متداخلة

تسمح الاستمارة التالية للمستخدم بإنشاء شخص Person والعناوين المرتبطة به:

<%= form_for @person do |f| %>
  Addresses:
  <ul>
    <%= f.fields_for :addresses do |addresses_form| %>
      <li>
        <%= addresses_form.label :kind %>
        <%= addresses_form.text_field :kind %>
 
        <%= addresses_form.label :street %>
        <%= addresses_form.text_field :street %>
        ...
      </li>
    <% end %>
  </ul>
<% end %>

عندما تقبل إحدى الارتباطات الخاصيات المتداخلة، يصيِّر التابع field_for الكتلة المرتبطة به مرة واحدة لكل عنصر للارتباط. على وجه الخصوص، إذا لم يكن لدى الشخص عناوين، فلا يصيِّر التابع شيئًا. النمط الشائع هو أن يُنشئ المتحكم ابنًا فارغًا أو أكثر بحيث تُعرَض مجموعة واحدة على الأقل من الحقول للمستخدم. سيُنتِج المثال أدناه مجموعتين من حقول العناوين التي تصيَّر في استمارة الشخص الجديد.

def new
  @person = Person.new
  2.times { @person.addresses.build }
end

يُنتِج fields_for بانيَ استمارة. سيكون اسم المعاملات هو ما يتوقعه التابع accepts_nested_attributes_for. على سبيل المثال، عند إنشاء مستخدم له عنوانين، ستبدو المعاملات المُرسَلة كما يلي:

{
  'person' => {
    'name' => 'John Doe',
    'addresses_attributes' => {
      '0' => {
        'kind' => 'Home',
        'street' => '221b Baker Street'
      },
      '1' => {
        'kind' => 'Office',
        'street' => '31 Spooner Street'
      }
    }
  }
}

مفاتيح addresses_attributes: ذي النوع Hash غير مهمة، فهي تحتاج فقط أن تكون مختلفة لكل عنوان.

إذا حُفِظ الكائن المرتبط مسبقًا، فسيولد field_for تلقائيًا عناصر إدخال مخفية بمعرّف السجل المحفوظ. يمكنك تعطيل هذا السلوك بتمرير include_id: false إلى المساعد fields_for. قد ترغب في القيام بذلك إذا وُضِعَ عنصر الإدخال المولد تلقائيًا في موقع حيث يكون هنالك وسم <input> غير صالح أو عند استخدام العملية ORM حيث لا تملك العناصر الأبناء أيَّ معرف.

المتحكم

كالمعتاد، تحتاج إلى إدراج المعاملات في القائمة البيضاء في المتحكم قبل تمريرها إلى النموذج:

def create
  @person = Person.new(person_params)
  # ...
end
 
private
  def person_params
    params.require(:person).permit(:name, addresses_attributes: [:id, :kind, :street])
  end

حذف الكائنات

يمكنك السماح للمستخدمين بحذف الكائنات المرتبطة بتمرير الخيار allow_destroy: true إلى accepts_nested_attributes_for:

class Person < ApplicationRecord
  has_many :addresses
  accepts_nested_attributes_for :addresses, allow_destroy: true
end

إذا كان الجدول Hash لخاصيات كائنٍ يحتوي على المفتاح ‎_destroy مع القيمة 1 أو true، فسيُدمَّر آنذاك الكائن. تتيح هذه الاستمارة للمستخدمين إزالة العناوين:

<%= form_for @person do |f| %>
  Addresses:
  <ul>
    <%= f.fields_for :addresses do |addresses_form| %>
      <li>
        <%= addresses_form.check_box :_destroy%>
        <%= addresses_form.label :kind %>
        <%= addresses_form.text_field :kind %>
        ...
      </li>
    <% end %>
  </ul>
<% end %>

لا تنسَ تحديث القائمة البيضاء في المتحكم لديك لتشمل أيضًا الحقل destroy_:

def person_params
  params.require(:person).
    permit(:name, addresses_attributes: [:id, :kind, :street, :_destroy])
end

منع السجلات الفارغة

من المفيد غالبًا تجاهل مجموعات الحقول التي لم يملؤها المستخدم. يمكنك التحكم في ذلك بتمرير reject_if ذي النوع Proc إلى accepts_nested_attributes_for. سيُستدعى الكائن Proc هذا مع كل جدول Hash من الخاصيات المرسَلة عبر الاستمارة. إذا أعاد الكائن Proc القيمة false، فلن ينشئ Active Record كائنًا مقترنًا للجدول Hash هذا. يحاول المثال أدناه فقط إنشاء عنوان إذا عُيِّنت الخاصية kind:

class Person < ApplicationRecord
  has_many :addresses
  accepts_nested_attributes_for :addresses, reject_if: lambda {|attributes| attributes['kind'].blank?}
end

للسهولة، يمكنك بدلًا من ذلك تمرير الرمز ‎:all_blank الذي سيُنشِئ كائنًا من النوع Proc الذي سيرفض السجلات عندما تكون جميع الخاصيات فارغةً باستثناء أي قيمة للخاصية ‎_destroy.

إضافة حقول آليًا

بدلًا من تصيير مجموعات متعددة من الحقول في وقت مبكر، قد ترغب في إضافتها فقط عندما ينقر المستخدم على زر "إضافة عنوان جديد" (Add new address). لا توفر ريلز أي دعم مضمّن لهذا السلوك للأسف. عند توليد مجموعات جديدة من الحقول، يجب التأكد من أن مفتاح المصفوفة المقابلة فريد؛ يعد تاريخ جافاسكربت الحالي (ميلي ثانية بعد بدء توقيت يونكس) اختيارًا شائعًا.

مصادر