Rails/layouts and rendering

من موسوعة حسوب
اذهب إلى التنقل اذهب إلى البحث

تخطيط الصفحات والتصيير في ريلز

يغطي هذا الدليل ميزات التخطيط الأساسية لوحدتي التحكم والعرض.

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

  • كيفية استخدام توابع التصيير المختلفة المضمنة ‎في ريلز.
  • كيفية إنشاء تخطيطات (layouts) تحتوي على أقسام محتوى متعددة.
  • كيفية استخدام الأجزاء مع واجهات العرض.
  • كيفية استخدام تخطيطات متداخلة (قوالب فرعية).

نظرة عامة: كيف تتراكب قطع الأحجية معًا

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

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

إنشاء الردود

من وجهة نظر المتحكم، هناك ثلاث طرق لإنشاء استجابة HTTP:

  1. استدعاء render لإنشاء استجابة كاملة لإرسالها مرة أخرى إلى المتصفح، أو
  2. استدعاء redirect_to لإرسال شيفرة إعادة التوجيه HTTP إلى المتصفح، أو
  3. استدعاء head لإنشاء استجابة تتكون من ترويسات HTTP فقط لإرسالها مرة أخرى إلى المتصفح.

التصيير افتراضيًا: العرف فوق الضبط في الإجراء

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

class BooksController < ApplicationController
end

وما يلي في ملف مساراتك الموجهة:

resources :books

ولديك ملف العرض app/views/books/index.html.erb الذي يحوي:

<h1>Books are coming soon!</h1>

ستصيِّر ريلز العرض app/views/books/index.html.erb عند الانتقال إلى books/ وستشاهد العبارة "Books are coming soon!‎" على شاشتك. ومع ذلك، لا تكون هذه الشاشة مفيدةً إلا قليلًا، لذلك ستُنشئ قريبًا النموذج Book الخاص بك وتضيف الإجراء index إلى BooksController:

class BooksController < ApplicationController
  def index
    @books = Book.all
  end
end

لاحظ أننا لا نملك تصييرًا صريحًا في نهاية إجراء الفهرسة (index) وفقًا لمبدأ "العرف فوق الضبط" (convention over configuration). القاعدة هي أنك إذا لم تصيِّر صراحةً شيئًا في نهاية إجراء المتحكم، فسيبحث ريلز تلقائيًا عن القالب action_name.html.erb في مسار عرض المتحكم ويصيِّره. في هذه الحالة، سيصيِّر ريلز الملف app/views/books/index.html.erb. إذا أردنا إظهار خصائص جميع الكتب في عرضنا، فيمكننا القيام بذلك باستخدام القالب ERB على هذا النحو:

<h1>Listing Books</h1>
 
<table>
  <thead>
    <tr>
      <th>Title</th>
      <th>Content</th>
      <th colspan="3"></th>
    </tr>
  </thead>
 
  <tbody>
    <% @books.each do |book| %>
      <tr>
        <td><%= book.title %></td>
        <td><%= book.content %></td>
        <td><%= link_to "Show", book %></td>
        <td><%= link_to "Edit", edit_book_path(book) %></td>
        <td><%= link_to "Destroy", book, method: :delete, data: { confirm: "Are you sure?" } %></td>
      </tr>
    <% end %>
  </tbody>
</table>
 
<br>
 
<%= link_to "New book", new_book_path %>

ملاحظة: يجرى التصيير الفعلي بواسطة أصناف مشتعبة من النموذج ActionView::Template::Handlers. لا يتعمق هذا الدليل في هذه العملية، ولكن من المهم معرفة أن امتداد الملف في عرضك يتحكم في اختيار معالج القالب.

استخدام التابع render

في معظم الحالات، ينفِّقذ التابع ActionController::Base.render المهام الشاقَّة في تصيير محتوى التطبيق الخاص بك لاستخدامه من قبل المستعرض. هناك مجموعة متنوعة من الطرق لتخصيص سلوك التابع render. يمكنك تصيير العرض الافتراضي لقالب ريلز، أو قالب محدد، أو ملف، أو شيفرة مضمنة، أو لا شيء على الإطلاق. تستطيع تصيير نص أو JSON أو XML. يمكنك تحديد نوع المحتوى أو حالة HTTP للاستجابة المصيَّرة أيضًا.

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

عرض شكل الحدث

إذا أردت تصيير العرض الذي يطابق قالبًا مختلفًا داخل وحدة التحكم نفسها، فيمكنك استخدام render مع اسم العرض نفسه:

def update
  @book = Book.find(params[:id])
  if @book.update(book_params)
    redirect_to(@book)
  else
    render "edit"
  end
end

في حالة فشل استدعاء update، يؤدي استدعاء الإجراء update في هذا المتحكم إلى تصيير القالب edit.html.erb المنتمي إلى نفس المتحكم. إذا فَضَّلْتَ ذلك، فيمكنك استخدام رمز بدلًا من سلسلة نصية لتحديد الإجراء المطلوب تصييره:

def update
  @book = Book.find(params[:id])
  if @book.update(book_params)
    redirect_to(@book)
  else
    render :edit
  end
end

تصيير قالب إجراء من متحكم آخر

ماذا لو أردتَ تصيير قالبٍ من متحكم مختلف تمامًا عن ذلك الذي يحتوي على شيفرة الإجراء؟ يمكنك أيضًا فِعل ذلك باستخدام التابع render الذي يقبل المسار الكامل (نسبة إلى app/views) للقالب لتصييره. على سبيل المثال، إذا كنت تُشغِّل شيفرة في المتحكم AdminProductsController توجد في المسار app/controllers/admin، فيمكنك تصيير نتائج الإجراء على قالب في المسار app/views/products بالشكل التالي:

render "products/show"

يعرف ريلز أن هذا العرض ينتمي إلى متحكم مختلف بسبب محرف الخط المائل المضمن في السلسلة النصية. إذا كنت تريد أن تكون صريحًا، فيمكنك استخدام الخيار :template (الذي كان مطلوبًا في ريلز 2.2 والإصدارات الأقدم):

render template: "products/show"

تصيير ملف عشوائي

يمكن للتابع render أيضًا استخدام عرض يقع خارج تطبيقك كليًّا:

render file: "/u/apps/warehouse_app/current/app/views/products/show"

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

ملاحظة: يمكن أن يؤدي استخدام الخيار :file بالتزامن مع إدخالات المستخدمين إلى مشاكل أمنية إذ يمكن للمهاجم استخدام هذا الإجراء للوصول إلى ملفات أمنية حساسة في نظام ملفاتك.

ملاحظة: افتراضيًّا، يصيَّر الملف باستخدام التخطيط الحالي.

فائدة: إذا كنت تُشغَّل ريلز على نظام ويندوز ، يجب استخدام الخيار :file لتصيير ملف، لأن أسماء الملفات في ويندوز لا تملك نفس تنسيق أسماء الملفات في الأنظمة الشبيهة بيونكس.

التغليف

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

في الواقع، في الصنف BooksController داخل الإجراء update حيث نريد تصيير قالب التعديل (edit template)، إذا لم يُحَدَّث الكتاب (book) بنجاح، فإن جميع إستدعاءات التابع render التالية ستصيِّر القالب edit.html.erb في المسار views/books:

render :edit
render action: :edit
render "edit"
render "edit.html.erb"
render action: "edit"
render action: "edit.html.erb"
render "books/edit"
render "books/edit.html.erb"
render template: "books/edit"
render template: "books/edit.html.erb"
render "/path/to/rails/app/views/books/edit"
render "/path/to/rails/app/views/books/edit.html.erb"
render file: "/path/to/rails/app/views/books/edit"
render file: "/path/to/rails/app/views/books/edit.html.erb"

أيٌّ منهم تستخدمه هو في الحقيقة مسألة أسلوب وتوافق، ولكن القاعدة الأساسية هي استخدام أبسط طريقة منطقية للشيفرة التي تكتبها.

استخدام render مع الخيار :inline

يمكن استخدام التابع render بدون عرض تمامًا، وذلك إذا كنت ترغب في استخدام الخيار :inline لتزويد القالب ERB كجزء من استدعاء الدالة. هذا صحيح تمامًا:

render inline: "<% products.each do |p| %><p><%= p.name %></p><% end %>"

تحذير: نادرًا ما يكون هناك أي سبب وجيه لاستخدام هذا الخيار. مزج القالب ERB مع المتحكم الخاص بك يُغير اتجاه النمط MVC في ريلز وسيجعل من الصعب على مطورين آخرين تتبع منطق المشروع الخاص بك. استخدم طريقة عرض قالب ERB منفصلة بدلًا من ذلك. بشكل افتراضي، يستخدم التصيير المضمَّن القالب ERB. يمكنك إجباره على استخدام القالب Builder بدلًا من ذلك باستخدام الخيار :type:

render inline: "xml.p {'Horrid coding practice!'}", type: :builder

تصيير النصوص

يمكنك إرسال نص مجرد - بدون تنسيق على الإطلاق - إلى المتصفح مرة أخرى باستخدام الخيار :plain مع التابع render:

render plain: "OK"

فائدة: تصيير النص المجرد مفيدٌ للغاية عندما ترد على طلبات Ajax أو خدمة الويب التي تتوقع شيئًا غير شيفرة HTML.

ملاحظة: بشكل افتراضي، إذا استخدمت الخيار :plain، فسيُصيَّر النص بدون استخدام التخطيط الحالي. إذا كنت تريد أن يضع ريلز النص في التخطيط الحالي، فأنت بحاجة إلى إضافة الخيار layout: true واستخدام اللاحقة ‎.text.erb لملف التخطيط.

تصيير شيفرة HTML

يمكنك إرسال سلسلة HTML نصية مرة أخرى إلى المتصفح باستخدام الخيار :html مع التابع render:

render html: helpers.tag.strong('Not Found')

فائدة: هذا الخيار مفيد عند تصيير قطعة صغير من شيفرة HTML. ومع ذلك، قد ترغب في النظر في نقلها إلى ملف قالب إذا كانت الشيفرة معقدةً.

ملاحظة: عند استخدام الخيار :html، ستُهْرَب كيانات HTML إذا لم تكن السلسلة النصية مكونة (composed) مع واجهات برمجة التطبيقات html_safe-aware.

تصيير بيانات بتنسيق JSON

إنَّ JSON هو تنسيق بيانات جافاسكربت تستخدمه العديد من مكتبات Ajax. يحتوي ريلز على دعم مُضمَّن لتحويل الكائنات إلى JSON وتصييرها مجددًا للمتصفح:

render json: @product

تنويه: لا تحتاج إلى استدعاء to_json في الكائن الذي تريد تصييره. إذا كنت تستخدم الخيار :json ، فسيستدعي التابع render التابعَ to_json لك تلقائيًا.

تصيير بيانات بتنسيق XML

يحتوي ريلز أيضًا على دعم مضمن لتحويل الكائنات إلى XML وتصييره مرة أخرى للمستدعِي:

render xml: @product

تنويه: لا تحتاج إلى استدعاء to_xml في الكائن الذي تريد تصييره. إذا كنت تستخدم الخيار :xml، فسوف يستدعي التابع render تلقائيًا to_xml نيابةً عنك.

تصيير شيفرات جافاسكربت الصرفة (VanillaJS)

بإمكان ريلز أن تصيِّر شيفرات جافاسكربت الصرفة (تدعى vanilla JavaScript أو VanillaJS اختصارًا):

render js: "alert('Hello Rails');"

سيرسل هذا السلسلة النصية المعطاة إلى المتصفح باستخدام نوع MIME text/javascript.

تصيير محتوى خام

يمكنك إعادة إرسال محتوى خام إلى المتصفح، دون تعيين أي نوع للمحتوى، باستخدام الخيار :body مع التابع render:

render body: "raw"

تنويه: يجب استخدام هذا الخيار فقط إذا كنت لا تهتم بنوع محتوى الرد (الاستجابة). استخدام الخيار :plain أو :html قد يكون أكثر ملاءمة معظم الأوقات.

ملاحظة: ما لم يُستبدَل، فإن ردك المُعاد من استعمال الخيار هذا مع التابع render سيكون من النوع text/plain، لأن هذا هو نوع المحتوى الافتراضي لرد الإجراء Dispatch.

خيارات أخرى

يقبل التابع render عمومًا -إضافةً للخيارات السابقة- خمسةَ خيارات هي:

  1. :content_type
  2. :layout
  3. :location
  4. :status
  5. :formats
الخيار :content_type

بشكل افتراضي، ستخدم (serve) ريلز نتائجَ عملية التصيير مع النوع MIME text/html للمحتوى (أو النوع MIME application/json إذا كنت تستخدم الخيار :json أو النوع MIME application/json إن كنت تستعمل الخيار :xml). هناك أوقات قد ترغب في تغيير هذا، ويمكنك فعل ذلك عن طريق الخيار :content_type بالشكل التالي:

render file: filename, content_type: "application/rss"
الخيار :layout

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

يمكنك استخدام الخيار :layout لإخبار ريلز باستخدام ملف معين كتخطيط للإجراء الحالي:

render layout: "special_layout"

يمكنك أيضًا إخبار ريلز بالتصيير دون تخطيط على الإطلاق:

render layout: false
الخيار :location

يمكنك استخدام الخيار :location لضبط الترويسة HTTP Location:

render xml: photo, location: photo_url(photo)
الخيار :status

سينشئ ريلز تلقائيًا استجابةً باستخدام رمز حالة HTTP الصحيحة (تكون في معظم الحالات هي "200 OK"). يمكنك استخدام الخيار :status لتغيير هذا السلوك:

render status: 500
render status: :forbidden

يفهم ريلز كلًا من رمز الحالة الذي هو عددٌ صحيحٌ والرمز المقابل الموضح في الجدول التالي:

صنف الرد (الاستجابة) رمز حالة HTTP الرمز
Informational 100 ‎:continue
101 ‎:switching_protocols
102 ‎:processing
Success 200 ‎:ok
201 ‎:created
202 ‎:accepted
203 ‎:non_authoritative_information
204 ‎:no_content
205 ‎:reset_content
206 ‎:partial_content
207 ‎:multi_status
208 ‎:already_reported
226 ‎:im_used
Redirection 300 ‎:multiple_choices
301 ‎:moved_permanently
302 ‎:found
303 ‎:see_other
304 ‎:not_modified
305 ‎:use_proxy
307 ‎:temporary_redirect
308 ‎:permanent_redirect
Client Error 400 ‎:bad_request
401 ‎:unauthorized
402 ‎:payment_required
403 ‎:forbidden
404 ‎:not_found
405 ‎:method_not_allowed
406 ‎:not_acceptable
407 ‎:proxy_authentication_required
408 ‎:request_timeout
409 ‎:conflict
410 ‎:gone
411 ‎:length_required
412 ‎:precondition_failed
413 ‎:payload_too_large
414 ‎:uri_too_long
415 ‎:unsupported_media_type
416 ‎:range_not_satisfiable
417 ‎:expectation_failed
421 ‎:misdirected_request
422 ‎:unprocessable_entity
423 ‎:locked
424 ‎:failed_dependency
426 ‎:upgrade_required
428 ‎:precondition_required
429 ‎:too_many_requests
431 ‎:request_header_fields_too_large
451 ‎:unavailable_for_legal_reasons
Server Error 500 ‎:internal_server_error
501 ‎:not_implemented
502 ‎:bad_gateway
503 ‎:service_unavailable
504 ‎:gateway_timeout
505 ‎:http_version_not_supported
506 ‎:variant_also_negotiates
507 ‎:insufficient_storage
508 ‎:loop_detected
510 ‎:not_extended
511 ‎:network_authentication_required

ملاحظة: إذا حاولت تصيير محتوى مع رمز حالة انعدام المحتوى (non-content status code، أي 100-199 أو 204 أو 205 أو 304) ، فسيُحذَف من الاستجابة.

الخيار :format

يستخدم ريلز التنسيق المحدد في الطلب (أو :html بشكل افتراضي). يمكنك تغيير هذا بتمرير الخيار :formats مع رمز أو مصفوفة:

render formats: :xml
render formats: [:json, :xml]

في حالة عدم وجود قالب بالتنسيق المحدد، فسيرمى الخطأ ActionView::MissingTemplate.

البحث عن التخطيطات

للعثور على التخطيط الحالي، تبحث ريلز أولًا عن ملف في app/views/layouts بنفس الاسم الأساسي للمتحكم. على سبيل المثال، سيؤدي استخدام إجراءات من الصنف PhotosController إلى استخدام app/views/layouts/photos.html.erb (أو app/views/layouts/photos.builder). إذا لم يكن هناك تخطيط محدد لهذا المتحكم، فستستخدم ريلز app/views/layouts/application.html.erb أو app/views/layouts/application.builder. إذا لم يكن هناك تخطيط ‎.erb، فسوف يستخدم ريلز التخطيط ‎.builder إذا كان موجودًا. توفر ريلز أيضًا عدة طرق لتعيين تخطيطات محددة بدقة أكبر لمتحكمات وأحداث فردية.

تحديد التخطيطات للمتحكمات

يمكنك تخطي العرف المتعلق بالتخطيط الافتراضي في متحكماتك الخاصة عبر استعمال التصريح layout. فمثلا:

class ProductsController < ApplicationController
  layout "inventory"
  #...
end

باستخدام هذا التصريح، ستستخدم جميع العروض المصيَّرة بواسطة ProductsController الملف app/views/layouts/inventory.html.erb كتخطيط لها. لتعيين تخطيط محدد للتطبيق بالكامل، استخدم التصريح layout في الصنف ApplicationController الخاصة بك:

class ApplicationController < ActionController::Base
  layout "main"
  #...
end

باستخدام هذا التصريح، ستَستخدم جميعُ العروض في التطبيق الملف app/views/layouts/main.html.er تخطيطًا لها.

اختيار التخطيطات في وقت التشغيل

يمكنك استخدام رمز لتأجيل اختيار التخطيط حتى تتم معالجة الطلب:

class ProductsController < ApplicationController
  layout :products_layout
 
  def show
    @product = Product.find(params[:id])
  end
 
  private
    def products_layout
      @current_user.special? ? "special" : "products"
    end
 
end

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

class ProductsController < ApplicationController
  layout Proc.new { |controller| controller.request.xhr? ? "popup" : "application" }
end
التخطيطات الشرطية

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

class ProductsController < ApplicationController
  layout "product", except: [:index, :rss]
end

باستخدام هذا التعريف، سيُستخدَم التخطيط product لكل شيء باستثناء التابعين rss و index.

وراثة التخطيط

تتوالى (cascade) تصريحات التخطيط لأسفل في التسلسل الهرمي، وتحل تصريحات التخطيط الأكثر تحديدًا دائمًا مكان التصريحات الأكثر عامية. فمثلًا:

  • الملف application_controller.rb
class ApplicationController < ActionController::Base
  layout "main"
end
  • الملف articles_controller.rb
class ArticlesController < ApplicationController
end
  • الملف special_articles_controller.rb
class SpecialArticlesController < ArticlesController
  layout "special"
end
  • الملف old_articles_controller.rb
class OldArticlesController < SpecialArticlesController
  layout false
 
  def show
    @article = Article.find(params[:id])
  end
 
  def index
    @old_articles = Article.older
    render layout: "old"
  end
  # ...
end

في هذا التطبيق:

  • بشكل عام، ستصيَّر العروض في التخطيط main.
  • سيستخدم ArticlesController.index التخطيط main.
  • سيستخدم SpecialArticlesController.index التخطيط special.
  • لن يستخدم OldArticlesController.show أي تخطيط على الإطلاق.
  • سيستخدم OldArticlesController.index التخطيط old.
وراثة القالب

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

# app/controllers/application_controller في
class ApplicationController < ActionController::Base
end
 
# app/controllers/admin_controller في
class AdminController < ApplicationController
end
 
# app/controllers/admin/products_controller في
class Admin::ProductsController < AdminController
  def index
  end
end

سيكون ترتيب البحث للإجراء index/products.index هو:

  • /app/views/admin/products
  • /app/views/admin
  • /app/views/application

وهذا يجعل app/views/application/ مكانًا رائعًا لجزئياتك (partials) المشتركة، والتي يمكن تصييرها في القالب ERB كالتالي:

<%# app/views/admin/products/index.html.erb %>
<%= render @products || "empty_list" %>
 
<%# app/views/application/_empty_list.html.erb %>
There are no items in this list <em>yet</em>.
تجنب أخطاء التصيير المضاعف

عاجلًا أم آجلًا، سيشاهد معظم مطوري ريلز رسالة الخطأ "Can only render or redirect once per action" (أي لا يمكن عرض أو إعادة التوجيه إلا مرة واحدة لكل إجراء). في حين أن هذا أمر مزعج، فإنه من السهل نسبيًا إصلاحه. عادة ما يحدث ذلك بسبب سوء فهم أساسي للطريقة التي تعمل التابع render بها.

على سبيل المثال، إليك بعض الشيفرات التي ستؤدي إلى ظهور هذا الخطأ:

def show
  @book = Book.find(params[:id])
  if @book.special?
    render action: "special_show"
  end
  render action: "regular_show"
end

إذا كان الاستدعاء ‎@book.special?‎ يعيد القيمة true، سيبدأ ريلز عملية التصيير لتفريغ المتغير book@ في العرض special_show. ولكن هذا لن يوقف بقية الشيفرة في الإجراء show من العمل، وعندما تصل ريلز إلى نهاية الإجراء، فإنه سيبدأ في بتصيير العرض regular_show، وإلقاء خطأ. الحل بسيط: تأكد من استدعاء render أو redirect مرةً واحدةً في الوقت ذاته في الشيفرة نفسها. شيء آخر يمكن أن يساعد هو استعمال and return. وإليك نسخة مصححة من المثال السابق:

def show
  @book = Book.find(params[:id])
  if @book.special?
    render action: "special_show" and return
  end
  render action: "regular_show"
end

تأكد من استخدام and return بدلًا من return && لأن الأخيرة لن تعمل نتيجة أسبقية المعامل في لغة روبي. لاحظ أنَّ التصيير الضمني الذي قام به المتحكم ActionController يكتشف إذا استُدعي render، لذلك ستعمل الشيفرة التالية دون أخطاء:

def show
  @book = Book.find(params[:id])
  if @book.special?
    render action: "special_show"
  end
end

هذا سيصيِّر كتابًا (book) مع ضبط special?‎ مع القالب special_show، بينما ستصيَّر الكتب الأخرى باستخدام القالب show الافتراضي.

استخدام redirect_to

هناك طريقة أخرى لمعالجة الاستجابات العائدة من طلبية HTTP وهي redirect_to. كما شاهدت، يخبر التابع render ريلز أي عرض (أو أصل آخر) سيُستخدَم في إنشاء استجابة. يفعل التابع redirect_to شيئًا مختلفًا تمامًا؛ فهو يخبر المتصفح بإرسال طلبية جديدة لعنوان URL مختلف. على سبيل المثال، يمكنك إعادة التوجيه من أي مكان توجد فيه في شيفرتك إلى فهرس الصور في تطبيقك باستخدام هذا الاستدعاء:

redirect_to photos_url

يمكنك استخدام redirect_back لإعادة المستخدم إلى الصفحة التي جاء منها للتو. يُسحَب هذا الموقع من رأس HTTP_REFERER الذي لا يضمنه المتصفح، لذلك يجب توفير fallback_location لاستخدامه في هذه الحالة.

redirect_back(fallback_location: root_path)

ملاحظة: لا يتوقف كل من redirect_to و redirect_back ويعودان فورًا من تنفيذ الدالة، ولكن ببساطة يعينان استجابات HTTP. ستنفذ العبارات التي تحدث بعدها في الدالة. يمكنك التوقف عن طريق العودة الصريحة أو بعض آلية التوقف الأخرى، إذا لزم الأمر.

2.3.1 الحصول على شيفرة مختلفة لإعادة توجيه الحالة

يستخدم ريلز شيفرة الحالة HTTP 302 ، وهي شيفرة إعادة توجيه مؤقتة، عندما تتصل بـ redirect_to. إذا كنت ترغب في استخدام شيفرة حالة مختلفة، ربما 301، أو إعادة توجيه دائمة، فيمكنك استخدام الخيار: status:

redirect_to photos_path, status: 301

تمامًا مثل: خيار status للعارض كذلك لـ redirect_to يقبل كل من تعيينات رؤوس رقمية ورمزية للعارض.

2.3.2 الفرق بين العارض و redirect_to

يفكر مطورو البرامج قليل الخبرة أحيانًا في redirect_to كنوع من أوامر goto، وينقلون التنفيذ من مكان إلى آخر في ريلز. هذا خطأ. تتوقف شيفرتك عن العمل وتنتظر طلبًا جديدًا للمتصفح. ما يحدث هو فقط أنك أخبرت المتصفح بالطلب الذي يجب أن تقدمه بعد ذلك، عن طريق إرسال شيفرة حالة HTTP 302.

فكر في هذه الأحداث لمعرفة الفرق:

def index

 @books = Book.all

end

def show

 @book = Book.find_by(id: params[:id])

 if @book.nil?

   render action: "index"

 end

end

مع الشيفرة في هذا النموذج، من المحتمل أن يكون هناك مشكلة إذا كان متغير @book فارغًا. تذكر: العارض: لا يُشغِّل الحدث أي شيفرة في الحدث المستهدف، لذلك لن يقوم أي شيء بإعداد متغير @books الذي قد يتطلبه عرض الفهرس على الأرجح. إحدى طرق حل هذه المشكلة هي إعادة التوجيه بدلاً من العرض:

def index

 @books = Book.all

end

def show

 @book = Book.find_by(id: params[:id])

 if @book.nil?

   redirect_to action: :index

 end

end

باستخدام هذه الشيفرة، سيقدم المتصفح طلبًا جديدًا لصفحة الفهرس، وستُشغَّل الشيفرة في دالة الفهرسة، وستكون جميعها جيدة.

الجانب السلبي الوحيد لهذه الشيفرة هو أنه يتطلب رحلة ذهابًا وإيابًا إلى المستعرض: طَلَبَ المستعرض الحدث المعروض مع /books /1، ويجد المتحكم أنه لا توجد كتب، لذلك يرسل المتحكم 302 استجابة لإعادة التوجيه إلى المتصفح لإخباره بالذهاب إلى /books/ ، يمتثل المتصفح ويرسل طلب جديد مرة أخرى إلى المتحكم يطلب الآن إجراء الفهرس، ثم يحصل المتحكم على جميع الكتب من قاعدة البيانات ويعرض قالب الفهرس، وترسله إلى المتصفح الذي يعرضها على شاشتك.

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

def index

 @books = Book.all

end

def show

 @book = Book.find_by(id: params[:id])

 if @book.nil?

   @books = Book.all

   flash.now[:alert] = "Your book was not found"

   render "index"

 end

end

سيكشف هذا عن عدم وجود كتب ذات معرّف محدد، وملء متغير مثيل @books بكافة الكتب في النموذج، ثم عرض قالب index.html.erb مباشرة، وإعادته إلى المتصفح برسالة تنبيه فلاش لتخبر المستخدم بما حدث.

2.4 استخدام الرأس لإنشاء استجابات للرأس فقط

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

head :bad_request

سُينتج هذا العنوانَ التالي:

HTTP/1.1 400 Bad Request

Connection: close

Date: Sun, 24 Jan 2010 12:15:53 GMT

Transfer-Encoding: chunked

Content-Type: text/html; charset=utf-8

X-Runtime: 0.013483

Set-Cookie: _blog_session=...snip...; path=/; HttpOnly

Cache-Control: no-cache

أو يمكنك استخدام رؤوس HTTP أخرى لنقل معلومات أخرى:

head :created, location: photo_path(@photo)

التي ستنتج:

HTTP/1.1 201 Created

Connection: close

Date: Sun, 24 Jan 2010 12:16:44 GMT

Transfer-Encoding: chunked

Location: /photos/1

Content-Type: text/html; charset=utf-8

X-Runtime: 0.083496

Set-Cookie: _blog_session=...snip...; path=/; HttpOnly

Cache-Control: no-cache

3 هيكلة الطبقات

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

  • وسوم الأصول
  • العائد و content_for
  • جزئيات

3.1 مساعدو وسم الأصول

يوفر مساعدو وسم الأصول دوالًا لتوليد HTML تربط عروضًا بالخلاصات، وملفات الجافا سكريبت، وصحائف الأنماط، والصور، ومقاطع الفيديو، والتسجيلات الصوتية. يوجد ستة مساعدين لوسوم الأصول في ريلز:

  • auto_discovery_link_tag
  • javascript_include_tag
  • stylesheet_link_tag
  • image_tag
  • video_tag
  • audio_tag

يمكنك استخدام هذه الوسوم في الطبقات أو طرق عرض أخرى، على الرغم من استخدام auto_discovery_link_tag وjavascript_include_tag وstylesheet_link_tag بشكل شائع في قسم <head> في التخطيط.

تحذير: لا يتحقق مساعدو وسم الأصول من وجود الأصول في المواقع المحددة، إذ يفترضون ببساطة أنك تعرف ما تفعله وينتجون الرابط.

3.1.1 ربط الخلاصات مع auto_discovery_link_tag

ينشئ المساعد auto_discovery_link_tag شيفرة HTML يمكن لمعظم المتصفحات وقارئات الخلاصات استخدامها للكشف عن وجود خلاصات RSS أو Atom أو JSON. إنها تأخذ نوع الرابط (: rss ، أو atom ، أو: json)، أو التجزئة من الخيارات التي تُمَرَّر إلى url_for ، وتجزئة الخيارات للوسم:

<%= auto_discovery_link_tag(:rss, {action: "feed"},

 {title: "RSS Feed"}) %>

هناك ثلاثة خيارات متاحة للوسم لـ auto_discovery_link_tag:

  • :rel تحدد قيمة rel في الرابط. القيمة الافتراضية هي "alternate".
  • :type يحدد نوع MIME الواضح. سينشئ ريلز نوع MIME مناسبًا تلقائيًا.
  • :title يحدد عنوان الرابط. القيمة الافتراضية هي الأحرف الكبيرة: نوع القيمة، على سبيل المثال ، "ATOM" أو "RSS".

3.1.2 ربط ملفات JavaScript مع javascript_include_tag

يعيد المساعد javascript_include_tag وسم script الخاص بلغة HTML لكل مصدر مُقَدَّم.

إذا كنت تستخدم ريلز مع تمكين Asset Pipeline، سينشئ هذا المساعد رابطًا إلى /assets/javascripts/ بدلاً من public/javascripts الذي كان مستخدَمًا في الإصدارات السابقة من ريلز. ثم هذا الرابط يُخدَم بواسطةأصول pipeline.

يدخل ملف جافا سكريبت داخل أحد تطبيقات ريلز أو محرك ريلز في أحد المواقع الثلاثة: app/assets ، lib/assets أو vendor/assets. شُرحَت هذه المواقع بالتفصيل في قسم تنظيم الأصول في دليل Pipeline الأصول.

تستطيع تحديد مسار كامل يتعلق بجذر المستند، أو عنوان URL، إذا فضلتَ ذلك. على سبيل المثال، للارتباط بملف JavaScript موجود داخل دليل يسمى javascripts داخل أحد app/assets، أو lib/assets، أو vendor/assets، يمكنك القيام بذلك:

<%= javascript_include_tag "main" %>

بعد ذلك، سيصدر ريلز وسم script مثل هذا:

<script src='/assets/main.js'></script>

ثم يُقدَّم الطلب إلى هذا الأصل بواسطة جوهرة Sprockets.

لتضمين ملفات متعددة مثل app/assets/javascripts/main.js و app/assets/javascripts/columns.js في الوقت نفسه:

<%= javascript_include_tag "main", "columns" %>

لتضمين app/assets/javascripts/main.js وapp/assets/javascripts/photos/columns.js:

<%= javascript_include_tag "main", "/photos/columns" %>

لتضمين http://example.com/main.js:

<%= javascript_include_tag "http://example.com/main.js" %>

3.1.3 ربط ملفات CSS باستخدام stylesheet_link_tag

يُعيد المساعد stylesheet_link_tag وسم <link> لكل مصدر مُقدَّم.

إذا كنت تستخدم ريلز مع تمكين "Asset Pipeline"، فسينشئ هذا المساعد رابطًا إلى /assets/stylesheets/. ثم يُعالَج هذا الرابط بعد ذلك بواسطة جوهرة Sprockets. يمكن تخزين ملف stylesheet في أحد المواقع الثلاثة: app/assets app/assets، أو lib/assets، أو vendor/assets.

تستطيع تحديد مسار كامل يتعلق بجذر المستند، أو عنوان URL، على سبيل المثال، للارتباط بملف stylesheet موجود داخل دليل يسمى stylesheets داخل أحد app/assets، أو lib/assets، أو vendor/assets، يمكنك القيام بذلك:

<%= stylesheet_link_tag "main" %>

لتضمين app/assets/stylesheets/main.css و app/assets/stylesheets/columns.css:

<%= stylesheet_link_tag "main", "columns" %>

لتضمين app/assets/stylesheets/main.css وapp/assets/stylesheets/photos/columns.css:

<%= stylesheet_link_tag "main", "photos/columns" %>

لتضمين http://example.com/main.css:

<%= stylesheet_link_tag "http://example.com/main.css" %>

بشكل افتراضي، يُنشئ stylesheet_link_tag روابط مع media= "screen" rel= "stylesheet". يمكنك تجاوز أيَّا من هذه الافتراضات عن طريق تحديد الخيار المناسب (media, :rel:):

<%= stylesheet_link_tag "main_print", media: "print" %>

3.1.4 ربط الصور باستخدام image_tag

ينشئ المساعد image_tag وسم <img /> إلى الملف المحدد. بشكل افتراضي، تُحمَّل الملفات من public/images.

تحذير: لاحظ أنه يجب عليك تحديد امتداد الصورة.

<%= image_tag "header.png" %>

يمكنك توفير مسار للصورة إذا أردت:

<%= image_tag "icons/delete.gif" %>

يمكنك توفير تجزئة من خيارات HTML الإضافية:

<%= image_tag "icons/delete.gif", {height: 45} %>

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

<%= image_tag "home.gif" %>

<%= image_tag "home.gif", alt: "Home" %>

يمكنك أيضًا تحديد وسم حجم خاص بالتنسيق "{width} x {height}":

<%= image_tag "home.gif", size: "50x20" %>

بالإضافة إلى الوسومات الخاصة المذكورة أعلاه، يمكنك توفير تجزئة نهائية لخيارات HTML القياسية، مثل: class:، أو id:، أو name:

<%= image_tag "home.gif", alt: "Go Home",

                         id: "HomeImage",

                         class: "nav_bar" %>

3.1.5 ربط مقاطع الفيديو مع video_tag

ينشئ المساعد video_tag وسم HTML5 <video> على الملف المحدد. بشكل افتراضي، تُحمَّل الملفات من public/videos.

<%= video_tag "movie.ogg" %>

يُنتِج

<video src="/videos/movie.ogg" />

مثل image_tag يمكنك توفير مسار ، إما مطلق، أو يناسب إلى الدليل public/videos. بالإضافة إلى ذلك، يمكنك تحديد خيار الحجم: "#{width}x#{height}" تمامًا مثل image_tag. يمكن أن تحتوي وسومات الفيديو أيضًا على أي من خيارات HTML المحددة في النهاية (id ، class et al).

يدعم وسم الفيديو أيضًا جميع خيارات <video> من خلال تجزئة خيارات HTML، شاملًا:

  • poster: "image_name.png"، يوفر صورة لوضعها بدلاً من الفيديو قبل بدء تشغيله.
  • autoplay: true، يبدأ تشغيل الفيديو عند تحميل الصفحة.
  • loop: true، يكرر الفيديو بمجرد وصوله إلى النهاية.
  • controls: true، يوفر عناصر التحكم التي يوفرها المتصفح للمستخدم للتفاعل مع الفيديو.
  • autobuffer: true، سيحمل الفيديو الملف للمستخدم على الصفحة المحملة.

يمكنك أيضًا تحديد عدة مقاطع فيديو لتشغيلها بتمرير مصفوفة من مقاطع الفيديو إلى video_tag:

<%= video_tag ["trailer.ogg", "movie.ogg"] %>

هذا سينتج:

<video>

 <source src="/videos/trailer.ogg">

 <source src="/videos/movie.ogg">

</video>

3.1.6 ربط الملفات الصوتية مع audio_tag

ينشئ المساعد audio_tag وسم <audio>  على الملف المحدد. بشكل افتراضي، تُحمَّل الملفات من public/audios.

<%= audio_tag "music.mp3" %>

يمكنك توفير مسار للملف الصوتي إذا كنت ترغب:

<%= audio_tag "music/first_song.mp3" %>

يمكنك أيضًا توفير مجموعة متنوعة من الخيارات، مثل: class: و id: إلخ.

مثل video_tag، يحتوي audio_tag على خيارات خاصة:

  • autoplay: true، تبدأ تشغيل الصوت عند تحميل الصفحة
  • controls: true، توفر عناصر التحكم التي يوفرها المتصفح للمستخدم للتفاعل مع الصوت.
  • autobuffer: true، سيحمل ملف الصوت الملف للمستخدم على الصفحة المحملة..

3.2 فهم yield

في سياق التخطيط، يحدد yield قسمًا حيث يجب إدراج المحتوى من العرض. تتمثل أبسط طريقة لاستخدام هذا في الحصول على yield واحد تُدرَج فيه محتويات العرض التي تعرَض حاليًا:

<html>

 <head>

 </head>

 <body>

 <%= yield %>

 </body>

</html>

يمكنك أيضًا إنشاء تخطيط بمناطق عائد متعددة:

<html>

 <head>

 <%= yield :head %>

 </head>

 <body>

 <%= yield %>

 </body>

</html>

سيظهر دائمًا الجسم الرئيسي في yieldغير المسمى. لعرض محتوى إلى yieldمحدد، تستطيع استخدم دالة content_for.

3.3 استخدام طريقة content_for

تسمح لك طريقة content_for بإدراج محتوى في كتلة yield المحددة في طبقتك. على سبيل المثال، قد يعمل هذا العرض مع التخطيط التي شاهدتها للتو:

<% content_for :head do %>

 <title>A simple page</title>

<% end %>

<p>Hello, ريلز!</p>

ستكون نتيجة عرض هذه الصفحة في التخطيط الذي عُرِضَ هي شيفرة الـ HTML هذه:

<html>

 <head>

 <title>A simple page</title>

 </head>

 <body>

 <p>Hello, ريلز!</p>

 </body>

</html>

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

3.4 استخدام الجزئيات

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

3.4.1 تسمية الجزئيات

لعرض جزء كجزء من عرض، فإنك تستخدم العارض في العرض:

<%= render "menu" %>

سيؤدي ذلك إلى عرض ملف named _menu.html.erb في تلك النقطة داخل العرض الذي يُعرض. لاحظ حرف الشرطة السفلية البادئة: تُسمَّى الجزئيات باستخدام شرطة سفلية بادئة لتمييزها عن العروض العادية، حتى لو أشير إليها بدون الشرطة السفلية. هذا صحيح حتى عند سحب جزء من مجلد آخر:

<%= render "shared/menu" %>

سيسحب هذا الرمز في الجزئية من app/views/shared/_menu.html.erb.

3.4.2 استخدام Partials لتبسيط العروض

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

<%= render "shared/ad_banner" %>

<h1>Products</h1>

<p>Here are a few of our fine products:</p>

...

<%= render "shared/footer" %>

هنا، يمكن أن يحتوي كل من _ad_banner.html.erb و _footer.html.erb على محتوى مشترك من قبل العديد من الصفحات في تطبيقك. لست بحاجة إلى الاطلاع على تفاصيل هذه الأقسام عند التركيز على صفحة معينة.

كما هو موضح في الأقسام السابقة من هذا الدليل، تعد yield أداةً قويةً جدًا لتنظيف طبقاتك. ضع في اعتبارك أنه روبي نقي، لذلك يمكنك استخدامه في كل مكان تقريبا. على سبيل المثال، يمكننا استخدام DRY لتعريفات نموذج تخطيط لعدة مصادر متشابهة:

  • users/index.html.erb

<%= render "shared/search_filters", search: @q do |f| %>

 <p>

   Name contains: <%= f.text_field :name_contains %>

 </p>

<% end %>

  • roles/index.html.erb

<%= render "shared/search_filters", search: @q do |f| %>

 <p>

   Title contains: <%= f.text_field :title_contains %>

 </p>

<% end %>

  • shared/_search_filters.html.erb

<%= form_for(search) do |f| %>

 <h1>Search form:</h1>

 <fieldset>

   <%= yield f %>

 </fieldset>

 <p>

   <%= f.submit "Search" %>

 </p>

<% end %>

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

3.4.3 الطبقات الجزئية

بإمكان الجزء استخدام ملف طبقته، تمامًا كما يمكن للعرض استخدام التخطيط. على سبيل المثال، يمكنك الاتصال بجزء هكذا:

<%= render partial: "link_area", layout: "graybar" %>

سيبحث هذا عن جزئية اسمها _link_area.html.erb ويعرضها باستخدام layout _graybar.html.erb. لاحظ أن الطبقات الخاصة بالجزئيات تتبع نفس التسمية بالشرطة السفلية كتسمية جزئية منتظمة، وتوضع في نفس المجلد مع الجزئية التي تنتمي إليها (وليس في مجلد الطبقات الرئيسي).

لاحظ أيضًا هذا التحديد الصريح: partial مطلوب عند تمرير خيارات إضافية مثل:layout.

3.4.4 تمرير المتغيرات المحلية

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

  • new.html.erb

<h1>New zone</h1>

<%= render partial: "form", locals: {zone: @zone} %>

  • edit.html.erb

<h1>Editing zone</h1>

<%= render partial: "form", locals: {zone: @zone} %>

  • form.html.erb_

<%= form_for(zone) do |f| %>

 <p>

   <b>Zone name</b><br>

   <%= f.text_field :name %>

 </p>

 <p>

   <%= f.submit %>

 </p>

<% end %>

على الرغم من أن الجزئية نفسها ستُعرضَ في كل من عرضين، فسيعيد مساعد إرسال عرض الحدث "Create Zone" للحدث الجديد و "Update Zone" لحدث التعديل.

لتمرير متغير محلي إلى جزئية في حالات محددة فقط، استخدم local_assigns.

  • show.html.erb

<%= render article, full: true %>

  • article.html.erb_

<h2><%= article.title %></h2>

<% if local_assigns[:full] %>

 <%= simple_format article.body %>

<% else %>

 <%= truncate article.body %>

<% end %>

هذه الطريقة  ممكنة لاستخدام الجزئية دون الحاجة إلى الإعلان عن جميع المتغيرات المحلية.

تحتوي كل جزئية أيضًا على متغير محلي بنفس الاسم مثل جزئية (minus the leading underscore). يمكنك تمرير كائن إلى هذا المتغير المحلي عبر خيار object:

<%= render partial: "customer", object: @new_customer %>

داخل الجزئية customer، سيشير المتغير customer إلى @new_customer من العرض الرئيسي.

إذا كان لديك مثيل لنموذج لعرضه جزئيًا، فيمكنك استخدام بنية مختصرة:

<%= render @customer %>

بافتراض أن متغير المثيل @customer يحتوي على مثال للنموذج Customer، سيستخدم هذا _customer.html.erb لعرضه وسيمرر المتغير المحلي customer إلى الجزئية التي ستشير إلى متغير المثيل @customer في العرض الأصلي.

3.4.5 مجموعات العرض

الجزئيات مفيدة للغاية في عرض المجموعات. عند تمريرك مجموعة إلى جزئية عبر خيار :collection، ستُدرَج الجزئية مرة واحدة لكل عضو في المجموعة:

  • index.html.erb

<h1>Products</h1>

<%= render partial: "product", collection: @products %>

  • product.html.erb_

<p>Product Name: <%= product.name %></p>

عندما تُستدعى جزئية ما مع مجموعة متعددة، فإن المثيلات الفردية للجزئية يكون لها حق الوصول إلى عضو المجموعة التي تُعرَض عبر متغير مسمى بعد الجزئية. في هذه الحالة، الجزئية هي product_، وضمن الجزئية  product_، يمكنك الإشارة إلى المنتج للحصول على المثيل الذي يُعرَض.

هناك أيضا اختزال لهذا. بافتراض أن @products عبارة عن مجموعة من مثيلات منتج، يمكنك ببساطة كتابة هذا في index.html.erb للحصول على النتيجة نفسها:

<h1>Products</h1>

<%= render @products %>

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

  • index.html.erb

<h1>Contacts</h1>

<%= render [customer1, employee1, customer2, employee2] %>

  • customers/_customer.html.erb

<p>Customer: <%= customer.name %></p>

  • employees/_employee.html.erb

<p>Employee: <%= employee.name %></p>

في هذه الحالة، سيستخدم ريلز جزئيات customer أو جزئيات employee حسب الاقتضاء لكل عضو في المجموعة.

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

<h1>Products</h1>

<%= render(@products) || "There are no products available." %>

3.4.6 المتغيرات المحلية

لاستخدام اسم متغير محلي مخصص داخل الجزئية، حدد الخيار :as في الاستدعاء إلى الجزئية:

<%= render partial: "product", collection: @products, as: :item %>

باستخدام هذا التغيير، يمكنك الوصول إلى مثيل لمجموعة @products باعتباره العنصر المتغير المحلي داخل الجزئية.

يمكنك أيضًا تمرير متغيرات محلية عشوائية إلى أي جزئية تعرضها مع خيار المحليين:{} :

<%= render partial: "product", collection: @products,

          as: :item, locals: {title: "Products Page"} %>

في هذه الحالة، ستحصل الجزئية على حق الوصول إلى عنوان متغير محلي بقيمة "Products Page".

يجعل ريلز أيضًا متغير عداد متاحًا داخل جزئية تستدعى بواسطة المجموعة، يسمى بعد عنوان الجزئية متبوعًا بـ counter_. على سبيل المثال، عند عرض مجموعة products@، يمكن للجزئية product.html.erb_ الوصول إلى المتغير product_counter الذي يفهرس عدد المرات التي عُرضَ فيها داخل العرض المرفق.

يمكنك أيضًا تحديد جزئية ثانية لعرضها بين مثيلات الجزئية الرئيسية باستخدام الخيار spacer_template:

3.4.7 قوالب الفاصل

<%= render partial: @products, spacer_template: "product_ruler" %>

سيعرض rails ال  product_ruler_ الجزئي (دون تمرير بيانات إليه) بين كل زوج من جزئيات product_.

3.4.8 مجموعة الطبقات الجزئية

عند عرض المجموعات، يمكن أيضًا استخدام خيار layout:

<%= render partial: "product", collection: @products, layout: "special_layout" %>

ستُعرَض التخطيط مع الجزئية لكل عنصر في المجموعة. سيتوفر المتغيران الكائن و object_counter الحاليان في التخطيط أيضًا، تمامًا كما هما داخل الجزئية.

3.5 استخدام الطبقات المتداخلة

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

لنفترض أن لديك تخطيط ApplicationController التالية

  • app/views/layouts/application.html.erb

<html>

<head>

 <title><%= @page_title or "Page Title" %></title>

 <%= stylesheet_link_tag "layout" %>

 <style><%= yield :stylesheets %></style>

</head>

<body>

 <div id="top_menu">Top menu items here</div>

 <div id="menu">Menu items here</div>

 <div id="content"><%= content_for?(:content) ? yield(:content) : yield %></div>

</body>

</html>

على الصفحات التي المُنشئَة بواسطة NewsController، تريد إخفاء القائمة العلوية وإضافة قائمة إلى اليمين:

  • app/views/layouts/news.html.erb

<% content_for :stylesheets do %>

 #top_menu {display: none}

 #right_menu {float: right; background-color: yellow; color: black}

<% end %>

<% content_for :content do %>

 <div id="right_menu">Right menu items here</div>

 <%= content_for?(:news_content) ? yield(:news_content) : yield %>

<% end %>

<%= render template: "layouts/application" %>

هذا هو. ستَستخدم عروض الأخبار التخطيط الجديدة، وإخفاء القائمة العلوية وإضافة قائمة جديدة في "div "content.

هناك عدة طرق للحصول على نتائج مشابهة مع أنظمة التخطيط الفرعية المختلفة باستخدام هذه التقنية. لاحظ أنه لا يوجد حد في مستويات التداخل. يمكن استخدام دالة ActionView::render عبر قالب العرض: 'layouts/news' لتأسيس تخطيط جديدة في تخطيط الأخبار. إذا كنت متأكدًا من أنك لن تدون تخطيط الأخبار، فيمكنك استبدال content_for?(:news_content) ? yield(:news_content) : yield مع yield ببساطة.