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

من موسوعة حسوب
اذهب إلى التنقل اذهب إلى البحث
(أنشأ الصفحة ب'مساعدو النموذج في وحدة العرض في ريلز تعتبر النماذج في تطبيقات الويب واجهة أساسية للتفاعل مع...')
 
سطر 74: سطر 74:
  
 
==== مربعات الاختيار ====
 
==== مربعات الاختيار ====
مربعات الاختيار هي عناصر تحكم في النموذج تمنح المستخدم مجموعة من الخيارات التي يمكن تمكينها أو تعطيلها:
+
[[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") %>
 
 
<%= check_box_tag(:pet_cat) %>
 
<%= check_box_tag(:pet_cat) %>
 
 
<%= label_tag(:pet_cat, "I own a cat") %>
 
<%= label_tag(:pet_cat, "I own a cat") %>
 
+
</syntaxhighlight>هذا يولد ما يلي:<syntaxhighlight lang="html">
هذا ينشئ ما يلي:
 
 
 
 
<input id="pet_dog" name="pet_dog" type="checkbox" value="1" />
 
<input id="pet_dog" name="pet_dog" type="checkbox" value="1" />
 
 
<label for="pet_dog">I own a dog</label>
 
<label for="pet_dog">I own a dog</label>
 
 
<input id="pet_cat" name="pet_cat" type="checkbox" value="1" />
 
<input id="pet_cat" name="pet_cat" type="checkbox" value="1" />
 
 
<label for="pet_cat">I own a cat</label>
 
<label for="pet_cat">I own a cat</label>
  
المعامل الأول إلى check_box_tag ، بالطبع، هو اسم الإدخال. المعامل الثاني، بطبيعة الحال، هو قيمة المدخل. ستُشمَل هذه القيمة في بيانات النموذج (وتكون موجودة في params) عند تحديد مربع الاختيار.
+
</syntaxhighlight>المعامل الأول المُمرَّر إلى <code>check_box_tag</code>، بالطبع، هو اسم عنصر الإدخال. المعامل الثاني، بطبيعة الحال، هو قيمة العنصر. ستُشمَل هذه القيمة في بيانات النموذج (وتكون موجودة في <code>params</code>) عند اختيار (check) مربع الاختيار.
 
 
=== 1.3.2 أزرار Radio ===
 
أزرار Radio، على الرغم من تشابهها مع مربعات الاختيار، هي عناصر تحكم تحدد مجموعة من الخيارات تكون فيها حصرية (أي أن المستخدم يمكنه اختيار واحد فقط):
 
  
 +
==== أزرار الانتقاء ====
 +
[[HTML/input/radio|أزرار الانتقاء]]، على الرغم من تشابهها مع [[HTML/input/checkbox|مربعات الاختيار]]، هي عناصر تحكم تحدد مجموعة من الخيارات لانتقاء أحدها فقط (أي أن المستخدم يمكنه اختيار خيار واحد فقط):<syntaxhighlight lang="html">
 
<%= radio_button_tag(:age, "child") %>
 
<%= radio_button_tag(:age, "child") %>
 
 
<%= label_tag(:age_child, "I am younger than 21") %>
 
<%= label_tag(:age_child, "I am younger than 21") %>
 
 
<%= radio_button_tag(:age, "adult") %>
 
<%= radio_button_tag(:age, "adult") %>
 
 
<%= label_tag(:age_adult, "I'm over 21") %>
 
<%= label_tag(:age_adult, "I'm over 21") %>
 
+
</syntaxhighlight>تنتج:<syntaxhighlight lang="html">
تنتج:
 
 
 
 
<input id="age_child" name="age" type="radio" value="child" />
 
<input id="age_child" name="age" type="radio" value="child" />
 
 
<label for="age_child">I am younger than 21</label>
 
<label for="age_child">I am younger than 21</label>
 
 
<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".
  
كما هو الحال مع check_box_tag ، فإن المعامل الثاني في radio_button_tag هو قيمة الإدخال. نظرًا لأن هذين الزرين يشتركان في الاسم نفسه (age)، فسيتمكن المستخدم من تحديد أحدهما فقط، وستتضمن المعاملات [: age] إما "child" أو "adult".
+
'''ملاحظة''': استخدم دومًا [[HTML/label|لافتةً نصيةً]] مع مربع الاختيار وزر الانتقاء. فهي تربط نصًا بخيار محدد، وتوسع المنطقة القابلة للنقر، مما يسهل على المستخدمين النقر على المدخلات.
 
 
ملاحظة: استخدم دومًا ملصقات أسماء لمربع الاختيار وأزرار الاختيار. فهي تربط النص بخيار محدد، وبتوسيع المنطقة القابلة للنقر، تسهل على المستخدمين النقر على المدخلات.
 
 
 
==== 1.4 مساعدون أخرون مهمون ====
 
ضوابط النموذج الأخرى الجديرة بالذكر هي النصوص، حقول كلمة المرور، الحقول المخفية، حقول البحث، حقول الهاتف، حقول التاريخ، حقول الوقت، حقول اللون، حقول التوقيت المحلي، حقول الشهر، حقول الأسبوع، حقول عنوان URL، حقول البريد الإلكتروني، حقول الأرقام والنطاق مجالات:
 
  
 +
=== مساعدون آخرون مهمون ===
 +
عناصر التحكم الأخرى بالنموذج الجديرة بالذكر هي [[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) %>
 
 
<%= hidden_field_tag(:parent_id, "5") %>
 
<%= hidden_field_tag(:parent_id, "5") %>
 
 
<%= search_field(:user, :name) %>
 
<%= search_field(:user, :name) %>
 
 
<%= telephone_field(:user, :phone) %>
 
<%= telephone_field(:user, :phone) %>
 
 
<%= date_field(:user, :born_on) %>
 
<%= date_field(:user, :born_on) %>
 
 
<%= datetime_local_field(:user, :graduation_day) %>
 
<%= datetime_local_field(:user, :graduation_day) %>
 
 
<%= month_field(:user, :birthday_month) %>
 
<%= month_field(:user, :birthday_month) %>
 
 
<%= week_field(:user, :birthday_week) %>
 
<%= week_field(:user, :birthday_week) %>
 
 
<%= url_field(:user, :homepage) %>
 
<%= url_field(:user, :homepage) %>
 
 
<%= email_field(:user, :address) %>
 
<%= email_field(:user, :address) %>
 
 
<%= color_field(:user, :favorite_color) %>
 
<%= color_field(:user, :favorite_color) %>
 
 
<%= time_field(:task, :started_at) %>
 
<%= time_field(:task, :started_at) %>
 
 
<%= number_field(:product, :price, in: 1.0..20.0, step: 0.5) %>
 
<%= number_field(:product, :price, in: 1.0..20.0, step: 0.5) %>
 
 
<%= range_field(:product, :discount, in: 1..100) %>
 
<%= range_field(:product, :discount, in: 1..100) %>
 
+
</syntaxhighlight>ينتج:<syntaxhighlight lang="html">
ينتج:
 
 
 
 
<textarea id="message" name="message" cols="24" rows="6">Hi, nice site</textarea>
 
<textarea id="message" name="message" cols="24" rows="6">Hi, nice site</textarea>
 
 
<input id="password" name="password" type="password" />
 
<input id="password" name="password" type="password" />
 
 
<input id="parent_id" name="parent_id" type="hidden" value="5" />
 
<input id="parent_id" name="parent_id" type="hidden" value="5" />
 
 
<input id="user_name" name="user[name]" type="search" />
 
<input id="user_name" name="user[name]" type="search" />
 
 
<input id="user_phone" name="user[phone]" type="tel" />
 
<input id="user_phone" name="user[phone]" type="tel" />
 
 
<input id="user_born_on" name="user[born_on]" type="date" />
 
<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_graduation_day" name="user[graduation_day]" type="datetime-local" />
 
 
<input id="user_birthday_month" name="user[birthday_month]" type="month" />
 
<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_birthday_week" name="user[birthday_week]" type="week" />
 
 
<input id="user_homepage" name="user[homepage]" type="url" />
 
<input id="user_homepage" name="user[homepage]" type="url" />
 
 
<input id="user_address" name="user[address]" type="email" />
 
<input id="user_address" name="user[address]" type="email" />
 
 
<input id="user_favorite_color" name="user[favorite_color]" type="color" value="#000000" />
 
<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="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_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" />
 
<input id="product_discount" max="100" min="1" name="product[discount]" type="range" />
  
لا يتم عرض المدخلات المخفية للمستخدم ولكن بدلاً من ذلك يحتفظ بالبيانات مثل أي إدخال نصي. يمكن تغيير القيم الموجودة داخلها باستخدام JavaScript.
+
</syntaxhighlight>لا يتم عرض المدخلات المخفية للمستخدم ولكن بدلًا من ذلك يحتفظ بالبيانات مثل أي إدخال نصي. يمكن تغيير القيم الموجودة داخلها باستخدام جافاسكربت.
  
تحذير: البحث، والهاتف، والتاريخ، والوقت، واللون، والتاريخ والوقت، التاريخ المحلي، والشهر، والأسبوع، وعنوان URL ، البريد الإلكتروني، وعدد ونطاق المدخلات  هي عناصر تحكم HTML5. إذا كنت ترغب في أن يتمتع تطبيقك بتجربة متناسقة في المتصفحات القديمة، فستحتاج إلى تعدد HTML5 (يوفره CSS وحده / أو مع JavaScript). من المؤكد أنه لا يوجد نقص في الحلول لذلك، على الرغم من أن الأداة الشائعة في الوقت الحالي هي 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|تأمين تطبيقات ريلز]].
  
= 2 التعامل مع كائنات النموذج =
+
== التعامل مع كائنات النموذج (Model Objects) ==
  
== 2.1 مساعدو كائن النموذج ==
+
=== مساعدو كائن النموذج (Model Object) ===
مهمة شائعة خاصة للنموذج هو تعديل أو إنشاء كائن نموذج. على الرغم من إمكانية استخدام مساعدي tag_* لهذه المهمة، إلا أنها مطولة بعض الشيء مثل لكل وسم يجب عليك التأكد من استخدام اسم المعامل الصحيح وتعيين القيمة الافتراضية للإدخال بشكل مناسب. يوفر ريلز مساعدين مصممين خصيصًا لهذه المهمة. هؤلاء المساعدون يفتقرون إلى لاحقة tag_، على سبيل المثال text_field  text_area.
+
من المهام الشائعة الخاصة بالنموذج هو تعديل أو إنشاء كائن نموذج (model object، سيذكر المصطلح الأجنبي في هذا الدليل دومًا لمنع الالتباس مع المصطلح "نموذج" [form] الذي لا يمت بصلة للمصطلح "نموذج" [model]). على الرغم من إمكانية استخدام المساعدين <code>tag_*‎</code> لأداء هذه المهمة، إلا أنه حل طويل ومضجر بعض الشيء، إذ يجب لكل وسم التأكد من استخدام اسم المعامل الصحيح وتعيين القيمة الافتراضية لعنصر الإدخال بشكل مناسب. يوفر ريلز مساعدين مصممين خصيصًا لهذه المهمة. هؤلاء المساعدون يفتقرون إلى الاحقة <code>tag_</code>، مثل <code>text_field</code> و <code>text_area</code>.
 
 
بالنسبة إلى هؤلاء المساعدين، يكون المعامل الأول هو اسم متغير مثيل والثاني هو اسم دالة (عادةً سمة) لاستدعاء ذلك الكائن. سيعين ريلز قيمة عنصر التحكم في الإدخال إلى القيمة المعادة لتلك الطريقة للكائن وتعيين اسم إدخال مناسب. إذا كانت المتحكم الخاصة بك قد حدد @person واسم ذلك الشخص هو هنري، فإن النموذج يحتوي على:
 
 
 
<%= text_field(:person, :name) %>
 
 
 
ستنتج مخرجًا مماثلًا ل
 
  
 +
بالنسبة إلى هؤلاء المساعدين، يكون المعامل الأول هو اسم متغير نسخة والثاني هو اسم تابع (عادةً خاصية) لاستدعائه مع ذلك الكائن. سيعين ريلز قيمة [[HTML/input|عنصر الإدخال]] إلى القيمة التي يعيدها ذلك التابع للكائن ثم سيعيِّن اسمًا مناسبًا للعنصر. إذا كان المتحكم الخاصة بك قد عرَّف ‎<code>@person</code> واسم ذلك الشخص هو "هنري" (Henry) مثلًا، فإن النموذج الذي يحتوي على:<syntaxhighlight lang="html">
 +
<%= text_field(:person, :name) %>
 +
</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>، فإن ريلز ستكون سعيدة.
  
عند تسليم النموذج، ستُخَزَّن القيمة التي أدخلها المستخدم في [params[:person[:name. إنَّ params [: person] hash مناسبة للانتقال إلى Person.new، أو إذا كان person@ مثال لـ Person، @person.update. في حين أن اسم السمة هو المعامل الثاني الأكثر شيوعًا لهذه العوامل المساعدة، فهذا ليس إلزامياً. في المثال أعلاه، طالما أن كائنات person لها اسم (name) وname= method فإن ريلز ستكون سعيدة.
+
'''تحذير''': يجب عليك تمرير اسم متغير نسخة، أي <code>:person</code> أو <code>"person"</code>، وليس نسخةً فعلية لكائن النموذج (model object) الخاص بك.
  
تحذير: يجب عليك تمرير اسم متغير مثيل، بمعنى: شخص أو "person"، وليس مثيلًا فعليَّا لكائن نموذجك.
+
يوفر ريلز مساعدين لعرض أخطاء التحقق من الصحة المقترنة مع كائن نموذجٍ (model object). جرى الحديث عن ذلك بالتفصيل في دليل [[Rails/active record validations|عمليات التحقق من السجل الفعال]].
  
يوفر ريلز مساعدين لعرض أخطاء التحقق من صحة الإقتران بكائن نموذجي. غُطِّيَتْ بالتفصيل من خلال دليل التحقق من صحة السجلات النشطة.
+
=== ربط نموذج بكائن ===
 
+
صحيح أن هذا مريحٌ أكثر إلا أنه ليس السلوك المثالي. إذا كان لدى <code>Person</code> العديد من الخاصيات المراد تعديلها، فسنكرر حينئذٍ اسم الكائن المعدَّل عدة مرات. ما نريد القيام به هو ربط نموذج بكائن نموذج (model object)، وهو ما يفعله <code>form_for</code> بالضبط.
== 2.2 ربط نموذج بكائن ==
 
في حين أن هذا هو زيادة في الراحة فإنه بعيد عن الكمال. إذا كان لدى Person العديد من السمات للتعديل، فسنكرر اسم الكائن الذي عُدِّل عدة مرات. ما نريد القيام به هو بطريقة ما ربط نموذج بكائن نموذج، وهو ما يفعله form_for بالضبط.
 
 
 
افترض لدينا متحكم للتعامل مع المقالات app/controllers/articles_controller.rb:
 
  
 +
افترض أنَّه لدينا متحكم للتعامل مع المقالات في الملف app/controllers/articles_controller.rb:<syntaxhighlight lang="html">
 
def new
 
def new
 
+
  @article = Article.new
 @article = Article.new
 
 
 
 
end
 
end
 
+
</syntaxhighlight>العرض المقابل هو app/views/articles/new.html.erb وسيبدو باستخدام <code>form_for</code> كالتالي:<syntaxhighlight lang="html">
العرض المقابل app/views/articles/new.html.erb باستخدام form_for كالتالي:
 
 
 
 
<%= form_for @article, url: {action: "create"}, html: {class: "nifty_form"} do |f| %>
 
<%= form_for @article, url: {action: "create"}, html: {class: "nifty_form"} do |f| %>
 
+
  <%= f.text_field :title %>
 <%= f.text_field :title %>
+
  <%= f.text_area :body, size: "60x12" %>
 
+
  <%= f.submit "Create" %>
 <%= f.text_area :body, size: "60x12" %>
 
 
 
 <%= f.submit "Create" %>
 
 
 
 
<% end %>
 
<% end %>
 
+
</syntaxhighlight>هناك بعض الأشياء التي يجب ملاحظتها هنا:
هناك بعض الأشياء التي يجب ملاحظتها هنا:
+
* ‎<code>@article</code> هو الكائن الفعلي الذي يجري تعديله.
* @article هو الكائن الفعلي الذي يُعَدَّل.
+
* هناك [[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 المولدة.
* هناك تجزئة واحدة من الخيارات. تُمرر خيارات التوجيه في: تجزئة عنوان URL، تُمرر خيارات HTML في تجزئة html:. كما يمكنك توفيرخيار :namespace لنموذجك لضمان تفرد سِمات الهوية (id) في عناصر النموذج. ستُبدَأ سمة namespace بشرطة أسفل السطر في مُعرف HTML المُنشَئ.
+
* تنتج الدالة <code>form_for</code> كائنًا بانيًا لنموذج (المتغير <code>f</code>).
* تنتج الدالة form_for كائن منشئ النموذج (متغير f).
+
* تُستدعَى توابع إنشاء عناصر التحكم بالنموذج في كائن باني النموذج <code>f</code>.
* تُستدعَى دوال إنشاء عناصر تحكم النموذج في كائن منشِئ النموذج f.
+
شيفرة HTML الناتجة هي:<syntaxhighlight lang="html">
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">
 
+
  <input name="utf8" type="hidden" value="&#x2713;" />
 <input name="utf8" type="hidden" value="&amp;#x2713;" />
+
  <input type="hidden" name="authenticity_token" value="NRkFyRWxdYNfUg7vYxLOp2SLf93lvnl+QwDWorR42Dp6yZXPhHEb6arhDOIWcqGit8jfnrPwL781/xlrzj63TA==" />
 
+
  <input type="text" name="article[title]" id="article_title" />
 <input type="hidden" name="authenticity_token" value="NRkFyRWxdYNfUg7vYxLOp2SLf93lvnl+QwDWorR42Dp6yZXPhHEb6arhDOIWcqGit8jfnrPwL781/xlrzj63TA==" />
+
  <textarea name="article[body]" id="article_body" cols="60" rows="12"></textarea>
 
+
  <input type="submit" name="commit" value="Create" data-disable-with="Create" />
 <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>
 
+
</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>. يمكنك قراءة المزيد حول أهمية أسماء عناصر الإدخال في القسم "فهم العرف المتبع في تسمة المعاملات".
الاسم الذي مُرِّر إلى form_for يتحكم في المفتاح المستخدم في params للوصول إلى قيم النموذج. هنا الاسم هو مقالة وحتى كل المدخلات لها أسماء مقالة النموذج [attribute_name]. وبناءً على ذلك، في params إنشاء الحدث [article:] سيكون هناك تجزئة بمفاتيح title: و body:. يمكنك قراءة المزيد حول أهمية أسماء الإدخال في قسم parameter_names.
 
  
 
تتشابه دوال المساعدة التي يطلق عليها منشئ النموذج مع مساعدي كائن النموذج باستثناء أنه ليس من الضروري تحديد أي كائن يُعَدَّل لأن ذلك يُدار بالفعل بواسطة منشئ النموذج.
 
تتشابه دوال المساعدة التي يطلق عليها منشئ النموذج مع مساعدي كائن النموذج باستثناء أنه ليس من الضروري تحديد أي كائن يُعَدَّل لأن ذلك يُدار بالفعل بواسطة منشئ النموذج.
سطر 277: سطر 205:
 
<form class="new_person" id="new_person" action="/people" accept-charset="UTF-8" method="post">
 
<form class="new_person" id="new_person" action="/people" accept-charset="UTF-8" method="post">
  
 <input name="utf8" type="hidden" value="&amp;#x2713;" />
+
 <input name="utf8" type="hidden" value="&amp;amp;#x2713;" />
  
 
 <input type="hidden" name="authenticity_token" value="bL13x72pldyDD8bgtkjKQakJCpd4A8JdXGbfksxBDHdf1uC0kCMqe2tvVdUYfidJt0fj3ihC4NxiVHv8GVYxJA==" />
 
 <input type="hidden" name="authenticity_token" value="bL13x72pldyDD8bgtkjKQakJCpd4A8JdXGbfksxBDHdf1uC0kCMqe2tvVdUYfidJt0fj3ihC4NxiVHv8GVYxJA==" />
سطر 348: سطر 276:
 
 <input name="_method" type="hidden" value="patch" />
 
 <input name="_method" type="hidden" value="patch" />
  
 <input name="utf8" type="hidden" value="&amp;#x2713;" />
+
 <input name="utf8" type="hidden" value="&amp;amp;#x2713;" />
  
 
 <input name="authenticity_token" type="hidden" value="f755bb0ed134b76c432144748a6d4b7a7ddf2b71" />
 
 <input name="authenticity_token" type="hidden" value="f755bb0ed134b76c432144748a6d4b7a7ddf2b71" />
سطر 642: سطر 570:
 
<%= render partial: f %>
 
<%= render partial: f %>
  
= 7 فهم اتفاقيات تسمية المعاملات =
+
== فهم العرف المتبع في تسمية المعاملات ==
 
كما شاهدت في الأقسام السابقة، يمكن أن تكون القيم من النماذج في المستوى الأعلى من تجزئة المعاملات أو متداخلة في تجزئة أخرى. على سبيل المثال، في حدث إنشاء المعيار لنموذج Person، تكون المعاملات [:person] عادةً علامة تجزئة لجميع سمات الشخص المراد إنشاؤه. يمكن لتجزئة المعاملات أيضًا أن تحتوي على المصفوفات، ومصفوفات التجزئة وما إلى ذلك.
 
كما شاهدت في الأقسام السابقة، يمكن أن تكون القيم من النماذج في المستوى الأعلى من تجزئة المعاملات أو متداخلة في تجزئة أخرى. على سبيل المثال، في حدث إنشاء المعيار لنموذج Person، تكون المعاملات [:person] عادةً علامة تجزئة لجميع سمات الشخص المراد إنشاؤه. يمكن لتجزئة المعاملات أيضًا أن تحتوي على المصفوفات، ومصفوفات التجزئة وما إلى ذلك.
  

مراجعة 08:12، 29 يناير 2019

مساعدو النموذج في وحدة العرض في ريلز

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

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

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

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

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

التعامل مع النماذج الأساسية

مساعد النموذج الأساسي هو 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 Objects)

مساعدو كائن النموذج (Model Object)

من المهام الشائعة الخاصة بالنموذج هو تعديل أو إنشاء كائن نموذج (model object، سيذكر المصطلح الأجنبي في هذا الدليل دومًا لمنع الالتباس مع المصطلح "نموذج" [form] الذي لا يمت بصلة للمصطلح "نموذج" [model]). على الرغم من إمكانية استخدام المساعدين 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). جرى الحديث عن ذلك بالتفصيل في دليل عمليات التحقق من السجل الفعال.

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

صحيح أن هذا مريحٌ أكثر إلا أنه ليس السلوك المثالي. إذا كان لدى Person العديد من الخاصيات المراد تعديلها، فسنكرر حينئذٍ اسم الكائن المعدَّل عدة مرات. ما نريد القيام به هو ربط نموذج بكائن نموذج (model object)، وهو ما يفعله 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:. يمكنك قراءة المزيد حول أهمية أسماء عناصر الإدخال في القسم "فهم العرف المتبع في تسمة المعاملات".

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

يمكنك إنشاء ربط مشابه بدون إنشاء وسم <form> بالفعل مع field_for helper. هذا مفيد لتعديل كائنات النماذج الإضافية بنفس الشكل. على سبيل المثال، إذا كان لديك نموذج 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 %>

الذي ينتج الإخراج التالي:

<form class="new_person" id="new_person" action="/people" accept-charset="UTF-8" method="post">

 <input name="utf8" type="hidden" value="&amp;#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_for (في الحقيقة form_for تستدعي fields_for داخليًا).

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

نموذج المقالة متاح مباشرة لمستخدمي التطبيق، لذلك - اتباع أفضل الممارسات لتطوير مع ريلز - يجب عليك أن تعلن أنه مورد:

resources :articles

تنبيه: الإعلان عن مورد له عدد من الآثار الجانبية. انظر توجيه ريلز من الخارج في لمزيد من المعلومات حول إعداد الموارد واستخدامها.

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

## Creating a new article

# long-style:

form_for(@article, url: articles_path)

# same thing, short-style (record identification gets used):

form_for(@article)

## Editing an existing article

# long-style:

form_for(@article, url: article_path(@article), html: {method: "patch"})

# short-style:

form_for(@article)

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

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

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

2.3.1 التعامل مع Namespaces

إذا أنشئت مسارات namespace، فإن form_for يحتوي على اختصار أنيق لذلك أيضًا. إذا كان تطبيقك يحتوي على namespace مسؤول إذًا

form_for [:admin, @article]

سينشئ نموذج يرسل إلى ArticleController داخل namespace المسؤول (إرسال إلى admin_article_path (@article) في حالة التحديث). إذا كان لديك عدة مستويات من namespacing، فسيكون بناء الجملة مشابهًا:

form_for [:admin, :management, @article]

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

2.4 كيف تعمل النماذج باستخدام دوال PATCH أوPUT أوDELETE؟

يشجع إطار ريلز تصميم RESTful لتطبيقاتك، مما يعني أنك ستجعل الكثير من طلبات "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="&amp;#x2713;" />

 <input name="authenticity_token" type="hidden" value="f755bb0ed134b76c432144748a6d4b7a7ddf2b71" />

 ...

</form>

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

3 إنشاء مربعات الاختيار مع Ease

تتطلب مربعات التحديد في 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>

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

3.1 وسوم التحديد والخيار

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

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

هذه بداية، ولكنها لا تنشئ وسومات الخيار بشكل ديناميكي. يمكنك إنشاء وسومات خيار مع المساعد options_for_select:

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

ينتج:

<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>

...

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

تحذير: عندما include_blank: أوprompt: غير موجود، include_blank: يُجبر على القيمة true إذا كانت سمة select المطلوبة صحيحة، ويكون حجم العرض واحدًا والمتعدد خطأ.

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

<%= options_for_select(

 [

   ['Lisbon', 1, { 'data-size' => '2.8 million' }],

   ['Madrid', 2, { 'data-size' => '3.2 million' }]

 ], 2

) %>

output:

<option value="1" data-size="2.8 million">Lisbon</option>

<option value="2" selected="selected" data-size="3.2 million">Madrid</option>

...

3.2 تحدد مربعات للتعامل مع النماذج

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

  1. controller:

@person = Person.new(city_id: 2)

  1. view:

<%= select(:person, :city_id, [['Lisbon', 1], ['Madrid', 2], ...]) %>

لاحظ أن المعامل الثالث، مصفوفة الخيارات، هو نفس النوع من الوسيط الذي ينتقل إلى options_for_select. ميزة واحدة هنا هي أنه لا داعي للقلق بشأن التحديد المسبق للمدينة الصحيحة إذا كان المستخدم لديه بالفعل ريلز سيقوم بذلك نيابة عنك من خلال القراءة من سمة person.city_id@.

كما هو الحال مع المساعدين الآخرين، إذا كنت ستستخدم المساعد المحدد في أداة إنشاء النماذج التي حُدِّدَ نطاقها على كائن @person، فسيكون طريقة الكتابة كالتالي:

  1. select on a form builder

<%= f.select(:city_id, ...) %>

يمكنك أيضًا تمرير الحظر لتحديد المساعد:

<%= f.select(:city_id) do %>

 <% [['Lisbon', 1], ['Madrid', 2]].each do |c| -%>

   <%= content_tag(:option, c.first, value: c.last) %>

 <% end %>

<% end %>

تحذير: إذا كنت تستخدم اختيارًا (أو مساعدين مشابهين مثل collection_select ،select_tag) لتعيين اقترانات belongs_to يجب عليك تمرير اسم المفتاح الخارجي (في المثال أعلاه city_id) ، وليس اسم الاقتران نفسه. إذا قمت بتحديد المدينة بدلاً من city_id فسيرفع Active Record خطأ على طول خطوط ActiveRecord :: AssociationTypeMismatch: City (# 17815740) المتوقعة، احصل على سلسلة (1138750#) عند تمرير التجزئة params إلى Person.new أو تحديث. طريقة أخرى للنظر إلى هذا هو أن مساعدين النموذج فقط تعدل السمات. يجب أيضًا أن تكون على دراية بتداعيات الأمان المحتملة الخاصة بالسماح للمستخدمين بتعديل المفاتيح الخارجية مباشرةً.

3.3 وسوم اختيارية من مجموعة من كائنات افتراضية

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

<% 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) %>

كما يوحي الاسم، هذا يولد فقط وسوم الخيار. لإنشاء مربع تحديد يعمل، ستحتاج إلى استخدامه بالتزامن مع 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@، فستكون بنية الجملة:

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

للتلخيص، 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 للتعرف على الوسائط الممكنة لهاتين الدالتين.

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

4 استخدام مساعدي نموذج التاريخ والوقت

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

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

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

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

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

<%= 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>

تؤدي المداخلات الواردة أعلاه إلى ظهور المعاملات [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)

الخيار:prefix هو المفتاح المستخدم لاسترداد تجزئة مكونات التاريخ من تجزئة المعاملات. هنا، عُيِّن على start_date ، إذا أُهْمِل، فسيُحدَد افتراضيَّا.

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

لا يعمل select_date بشكل جيد مع النماذج التي تحدث أو تنشئ كائنات Active Record حيث يتوقع Active Record أن كل عنصر في تجزئة params يتطابق مع سمة واحدة. يقدم مساعدا الكائن النموذجي للتواريخ والأوقات المعاملات بأسماء خاصة؛ عندما يشاهد 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 (أو تحديث)، يلاحظ Active Record أنه يجب استخدام جميع هذه المعاملات لإنشاء سمة birth_date واستخدام المعلومات اللاحقة لتحديد الترتيب الذي يجب أن يمرر هذه المعاملات إلى دوال مثل Date.civil.

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

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

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

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

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) %>

<%= select_year(Time.now) %>

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

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

تتمثل إحدى المهام الشائعة في تحميل نوع ما من الملفات، سواء أكانت صورة شخص أو ملف 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].

5.1 ما يُحمَّل

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

def upload

 uploaded_io = params[:person][:picture]

 File.open(ريلز.root.join('public', 'uploads', uploaded_io.original_filename), 'wb') do |file|

   file.write(uploaded_io.read)

 end

end

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

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

5.2 التعامل مع Ajax

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

6 تخصيص بناة النموذج

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

<%= form_for @person do |f| %>

 <%= text_field_with_label f, :first_name %>

<% end %>

can be replaced with

<%= 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 يطبق تلقائيًا المُنشِئ : LabellingFormBuilder:

def labeled_form_for(record, options = {}, &block)

 options.merge! builder: LabellingFormBuilder

 form_for record, options, &block

end

يحدد منشئ النماذج المستخدَم أيضًا ما يحدث عندما تقوم بذلك

<%= render partial: f %>

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

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

لا تعرف نماذج HTML بشكل أساسي أي نوع من البيانات المنظمة ، وكل ما تولده هو أزواج اسم-قيمة، حيث تكون الأزواج مجرد سلاسل عادية. المصفوفات و التجزئة التي تراها في تطبيقك هي نتيجة لبعض اصطلاحات تسمية المعاملات التي يستخدمها ريلز.

7.1 الهياكل الأساسية

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

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

ستحتوي تجزئة المعاملات على:

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

و[params [:person] [:name سيسترد القيمة المرسلة في المتحكم.

يمكن أن تتداخل التجزئات على العديد من المستويات كما هو مطلوب، على سبيل المثال:

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

سينتج هذا

{'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"/>

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

7.2 دمجهم

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

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

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

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

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

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

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

7.3 استخدام مساعدي النموذج

لم تستخدم الأقسام السابقة مساعدي نموذج ريلز على الإطلاق. بينما يمكنك صياغة أسماء المدخلات بنفسك ونقلها مباشرة إلى المساعدين مثل 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>

سيُنتِج هذا معاملات مجزئة تشبه:

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

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

لإنشاء تداخلات أكثر تعقيدًا، يمكنك تحديد الجزء الأول من اسم الإدخال (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 مباشرةً إلى المساعدين، مثل text_field ، ولكن عادةً ما يكون أقل تكرارًا لتحديد ذلك على مستوى منشئ النموذج بدلاً من عناصر التحكم الفردية للإدخال.

كاختصار يمكنك إلحاق [] بالاسم وحذف خيار index:. هذا هو نفس تحديد index:، عنوان ذلك

<%= fields_for 'person[address][primary][]', address do |address_form| %>

 <%= address_form.text_field :city %>

<% end %>

ينتج نفس الإخراج بالضبط مثل المثال السابق.

8 نماذج لمصادر خارجية

يمكن أيضًا استخدام مساعدي نماذج ريلزلإنشاء نموذج لنشر البيانات إلى مصدر خارجي. ومع ذلك، في بعض الأحيان، قد يكون من الضروري تعيين 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 %>

9 بناء نماذج معقدة

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

9.1 تكوين النموذج

يوفر السجل النشط دعمًا لمستوى النموذج عبر دالة 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 الذي يسمح لك بإنشاء وتحديث و (اختياريًا) إتلاف العناوين.

9.2 النماذج المتداخلة

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

<%= 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: غير مهمة، فهي تحتاج فقط أن تكون مختلفة لكل عنوان.

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

9.3 المُتحكم

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

def create

 @person = Person.new(person_params)

 # ...

end

private

 def person_params

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

 end

9.4 حذف الكائنات

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

class Person < ApplicationRecord

 has_many :addresses

 accepts_nested_attributes_for :addresses, allow_destroy: true

end

إذا كانت تجزئة السمات لكائن تحتوي على key _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

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

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

class Person < ApplicationRecord

 has_many :addresses

 accepts_nested_attributes_for :addresses, reject_if: lambda {|attributes| attributes['kind'].blank?}

end

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

9.6 إضافة حقول في الهواء

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