React से प्रेरित, ViewComponents Ruby ऑब्जेक्ट्स हैं जिनका उपयोग व्यू रेंडर करने के लिए मार्कअप बनाने के लिए किया जाता है। ViewComponent Rails में पुन: प्रयोज्य, टेस्टेबल, और एनकैप्सुलेटेड व्यू कंपोनेंट बनाने के लिए एक फ्रेमवर्क है। आमतौर पर, पुन: प्रयोज्य व्यू Rails में पार्शियल का उपयोग करके बनाए जाते हैं और फिर आवश्यकतानुसार अलग-अलग व्यू में रेंडर किए जाते हैं, लेकिन ViewComponent जेम की शुरूआत के साथ, पार्शियल को व्यू कंपोनेंट्स के लिए स्वैप किया जा सकता है, क्योंकि वे अधिक लाभ प्रदान करते हैं। आइए देखते हैं कि ये फायदे क्या हैं।
मुझे ViewComponents का उपयोग कब और क्यों करना चाहिए?
जैसा कि पहले कहा गया है, व्यू कंपोनेंट्स पुन: प्रयोज्य और टेस्टेबल हैं। इसलिए, उन्हें तब लागू किया जा सकता है जब किसी व्यू का पुन: उपयोग किया जाना हो या उससे सीधे टेस्ट होने से लाभ हो। इसके दस्तावेज़ीकरण में बताए गए व्यू कंपोनेंट्स के कुछ लाभों में निम्न शामिल हैं:
- वे पार्शियल से ~10x तेज़ हैं।
- वे Ruby ऑब्जेक्ट्स हैं; इसलिए, उनका इनिशियलाइज़ मेथड स्पष्ट रूप से परिभाषित करता है कि व्यू रेंडर करने के लिए क्या आवश्यक है। इसका अर्थ है कि उन्हें समझना और कई अन्य व्यू में पुन: उपयोग करना आसान है। इसके अलावा, Ruby कोड के गुणवत्ता मानकों को लागू किया जा सकता है, जिससे बग का जोखिम कम होता है।
- उन्हें यूनिट टेस्ट किया जा सकता है, जबकि Rails के पारंपरिक व्यू के लिए इंटीग्रेशन टेस्ट की आवश्यकता होती है जो व्यू के अलावा रूटिंग और कंट्रोलर लेयर्स को भी एक्सरसाइज करते हैं।
ViewComponent कार्यान्वयन
ViewComponents ViewComponent::Base के सबक्लास हैं और app/components में रहते हैं। उनके नाम -Component में समाप्त होते हैं, और उन्हें उनके द्वारा रेंडर किए जाने के लिए नामित किया जाना चाहिए न कि उनके द्वारा स्वीकार किए जाने के लिए। हम जेम को अपने जेमफाइल में जोड़कर और पहले bundle install चलाकर या तो मैन्युअली या कंपोनेंट जनरेटर के माध्यम से एक व्यू कंपोनेंट जनरेट कर सकते हैं।
gem "view_component", require: "view_component/engine"
जनरेटर का उपयोग करने के लिए, निम्न कमांड चलाएँ:
rails generate component
उदाहरण के लिए, यदि हम एक लर्निंग प्लेटफॉर्म पर उपलब्ध इन-क्लास कोर्स की सूची प्रदर्शित करने के लिए जिम्मेदार एक कंपोनेंट जनरेट करना चाहते हैं, तो हम इस कमांड का उपयोग कर सकते हैं:
rails g component Course course
हम course आर्गुमेंट को कमांड में पास कर रहे हैं क्योंकि हम इस Ruby ऑब्जेक्ट को उस कोर्स के साथ इनिशियलाइज़ करेंगे जिसे हम इसे प्रदर्शित करने की उम्मीद करते हैं, और हमने इसका नाम Course रखा क्योंकि यह एक कोर्स रेंडर करता है। क्या संयोग है!

जैसा कि हम ऊपर देख सकते हैं, कंपोनेंट और उसका संबंधित व्यू app/components फ़ोल्डर में बनाए गए हैं, साथ ही एक टेस्ट फाइल।
ViewComponent erb, haml, और slim टेम्पलेट इंजन के लिए टेम्पलेट जनरेटर शामिल करता है लेकिन config.generators.template_engine में निर्दिष्ट टेम्पलेट इंजन पर डिफ़ॉल्ट होगा। फिर भी, आप निम्न का उपयोग करके अपने पसंदीदा टेम्पलेट इंजन को इंगित कर सकते हैं:
rails generate component Course course --template-engine
आइए हमारे Course मॉडल और प्रदर्शित करने के लिए कुछ कोर्स बनाने के लिए आगे बढ़ें।
rails g model Course title:string price:decimal location:string
rails db:migrate
हमारे कंसोल में, हम जल्दी से दो नए कोर्स बना सकते हैं:
Course.create(title: 'The Art of Learning', price: 125.00, location: 'Denmark')
Course.create(title: 'Organizing your Time', price: 55.00, location: 'London')
course_component.rb फाइल इनिशियलाइज़ मेथड के साथ जनरेट होती है, जैसा कि नीचे दिखाया गया है।

हमें एक कोर्स कंट्रोलर बनाने की आवश्यकता है जो हमें कोर्स की सूची पर रूट करे।
rails g controller Courses index
हमारे routes.rb फाइल में, हम निम्न जोड़कर अपने रूट रूट को इंगित करते हैं:
root 'courses#index'
अब जब हम पूरी तरह से सेट हैं, तो अगला कदम व्यू निर्माण है। यह पहले से जनरेटेड course_component.html.erb में किया जाता है।
<%= @course.title %>
<%= number_to_currency(@course.price, :unit => "€") %>
<%= @course.location %>
हमारे व्यू में, हम @course वेरिएबल का उपयोग करके कोर्स शीर्षक, मूल्य, और स्थान प्रदर्शित करते हैं, जिसे हमारे CourseComponent के इनिशियलाइज़ मेथड में पहले से परिभाषित किया गया था। यह तब समान है जब आप एक कंट्रोलर मेथड में एक वेरिएबल बनाते हैं, और फिर यह एक व्यू में उपलब्ध होता है।
यह जानते हुए कि कंट्रोलर कैसे काम करते हैं, हम अपने इंडेक्स मेथड के संबंधित index.html.erb पर रूट होंगे। इसलिए, यहीं हम अपना कंपोनेंट रेंडर करते हैं।
#app/views/courses/index.html.erb
<%= render(CourseComponent.new(course: Course.find(1))) %>
ऊपर देखा गया है कि हम अपने CourseComponent के एक नए इंस्टेंस को उस कोर्स के साथ इनिशियलाइज़ करके रेंडर करते हैं जिसे हम इसके व्यू में रेंडर करने का इरादा रखते हैं। यह कोर्स @course वेरिएबल बन जाता है जो course_component.html.erb फाइल को उपलब्ध कराया जाता है।
इस कंपोनेंट को सीधे हमारे कंट्रोलर से रेंडर करना भी संभव है, जिससे इंडेक्स फाइल व्यू को बायपास किया जाता है:
class CoursesController < ApplicationController
def index
render(CourseComponent.new(course: Course.find(1)))
end
end
आप चाहे जिस मेथड को चुनें, यह सर्वर पर दिखाई देगा:

अतिरिक्त सामग्री को निम्न में से किसी एक तरीके से कंपोनेंट में भी पास किया जा सकता है:
<%= render(CourseComponent.new(course: Course.find(1))) do %> container <% end %>
<%= render(CourseComponent.new(course: Course.find(1)).with_content("container")) %>
हमारे व्यू कंपोनेंट फाइल में, हम सामग्री को जहाँ चाहें शामिल कर सकते हैं। इस मामले में, हम इसे एक क्लास के रूप में शामिल करेंगे हमारे डिव को ऐसा दिखाने के लिए:
>
उपरोक्त सभी अतिरिक्त सामग्री रेंडर करने के तरीके नीचे दी गई छवि देते हैं:

कलेक्शन रेंडर करना
क्या होगा यदि हम अपनी पूरी कोर्स सूची रेंडर करना चाहते थे? ViewComponent with_collection टैग का उपयोग करके इसे करने का एक बहुत सीधा तरीका प्रदान करता है। कंपोनेंट को .new का उपयोग करके इनिशियलाइज़ करने के बजाय, इसे .with_collection का उपयोग करके इनिशियलाइज़ किया जाता है, और कलेक्शन को एक वेरिएबल के रूप में पास किया जाता है, जैसा कि नीचे दिखाया गया है।
CourseComponent.with_collection(Course.all)
यह निम्नलिखित देता है:

एक with_collection_parameter टैग भी उपलब्ध है यदि हम कलेक्शन को किसी अलग नाम से संबोधित करना चाहते हैं।
class CourseComponent < ViewComponent::Base
with_collection_parameter :item
def initialize(item:)
@item = item
end
end
उपरोक्त मामले में, कोर्स पैरामीटर को item के रूप में संबोधित किया गया है। इसलिए, संबंधित व्यू में, @course को @item से बदल दिया जाएगा ताकि वही परिणाम प्राप्त हो सके।
कलेक्शन में अतिरिक्त पैरामीटर भी जोड़े जा सकते हैं। ये पैरामीटर कलेक्शन में प्रति आइटम प्रदर्शित किए जाएंगे। आइए इस मेथड के माध्यम से प्रत्येक आइटम में Buy Me टेक्स्ट जोड़ें।
#app/views/courses/index.html.erb
<%= render(CourseComponent.with_collection(Course.all, notice: "Buy Me")) %>
# app/components/course_component.rb
class CourseComponent < ViewComponent::Base
with_collection_parameter :item
def initialize(item:, notice:)
@item = item
@notice = notice
end
end
हम नए नोटिस वेरिएबल के लिए टेक्स्ट इंगित करने के लिए app/components/course_component.html.erb फाइल में एक नया पैराग्राफ जोड़ते हैं।
यह निम्नलिखित देता है:

अंत में, कलेक्शन के तहत, हमारे पास एक काउंटर वेरिएबल है जिसे व्यू में आइटम नंबर देने के लिए सक्षम किया जा सकता है। यह कलेक्शन पैरामीटर में _counter जोड़कर और इसे इनिशियलाइज़ मेथड के माध्यम से व्यू के लिए उपलब्ध कराकर सक्षम किया जाता है।
#app/components/course_component.rb
def initialize(item:, notice:, item_counter:)
@item = item
@notice = notice
@counter = item_counter
end
हमारे व्यू में, आइटम शीर्षक के बगल में, हम अपना काउंटर जोड़ते हैं:
<%= @counter %>. <%= @item.title %>
आइए कंसोल से एक तीसरा कोर्स जनरेट करें ताकि काउंटर घटना को बेहतर ढंग से समझा जा सके।
Course.create(title: 'Understanding Databases', price: '100', location: 'Amsterdam')
यह निम्नलिखित देता है

सशर्त रेंडरिंग
ViewComponent में एक render? हुक है, जिसका उपयोग करने पर यह निर्धारित करता है कि किसी व्यू को रेंडर किया जाना चाहिए या नहीं। इसे लागू करने के लिए, हम 100 यूरो के बराबर या उससे अधिक कीमत वाले कोर्स पर 10% छूट देने जा रहे हैं। आइए इस उद्देश्य के लिए एक कंपोनेंट बनाएँ।
rails generate component Discount item

यह कंपोनेंट पहले से ही उस आइटम के साथ स्वचालित रूप से इनिशियलाइज़ है जिसके लिए इसे छूट प्रदर्शित करनी चाहिए, जैसा कि नीचे देखा गया है।

इसलिए, discount_component.html.erb फाइल में, हम वह टेक्स्ट जोड़ते हैं जिसे हम प्रदर्शित करना चाहते हैं।
A 10% discount is available on this course
अपने सीएसएस फाइल में green क्लास जोड़ने में संकोच न करें और अपनी पसंद का कोई भी हरा रंग असाइन करें। इसके अलावा, हमारे discount_component.rb फाइल में, हम render? मेथड जोड़ते हैं ताकि यह निर्धारित किया जा सके कि इस कंपोनेंट को कब रेंडर किया जाना चाहिए।
def render?
@item.price >= 100
end
अब, हम आगे बढ़ सकते हैं और प्रत्येक कोर्स को रेंडर करने वाले व्यू के भीतर डिस्काउंट कंपोनेंट को रेंडर कर सकते हैं।
# app/components/course_component.html.erb
<%= render(DiscountComponent.new(item: @item)) %>
यह निम्नलिखित देता है:

क्या यह बहुत बढ़िया नहीं है?
हेल्पर्स
पारंपरिक Rails व्यू में, हम मेथड नाम को अपने व्यू में कॉल करके आसानी से अपने हेल्पर्स में प्लग इन कर सकते हैं, लेकिन यह व्यू कंपोनेंट्स के साथ अलग तरह से काम करता है। व्यू कंपोनेंट्स में, हेल्पर मेथड्स को व्यू में सीधे कॉल नहीं किया जा सकता लेकिन एक कंपोनेंट में शामिल किया जा सकता है। हमारे पास पहले से ही एक courses_helper.rb फाइल है जो CoursesController बनाए जाने पर स्वचालित रूप से जनरेट हुई थी, तो आइए इसका लाभ उठाएं। पहले, आइए एक हेल्पर मेथड बनाएं जो हमें बताता है कि अब तक एक कोर्स में कितने लोग नामांकित हुए हैं। आइए मान को कीमत का एक चौथाई बनाएं :).
module CoursesHelper
def count_enrollees(course)
count = (course.price / 4).round()
tag.p "#{count} enrollees so far"
end
end
अगला, हम एक कंपोनेंट बनाएंगे जिसमें हम हेल्पर को कॉल करेंगे। यह वह कंपोनेंट है जिसे हमारे व्यू में रेंडर किया जाएगा। इसमें, हम एक include स्टेटमेंट जोड़ेंगे, जिसमें हेल्पर शामिल होगा, और फिर हम इस कंपोनेंट के भीतर हेल्पर में किसी भी मेथड को कॉल कर सकते हैं।
# app/components/enrollee_component.rb
class EnrolleeComponent < ViewComponent::Base
include CoursesHelper
def total_enrollees(course)
count_enrollees(course)
end
end
अंतिम चरण हमारे कोर्स प्रदर्शित करने वाले व्यू में EnrolleeComponent को जोड़ना है।
# app/components/course_component.html.erb
<%= EnrolleeComponent.new.total_enrollees(@item) %>
ध्यान दें कि हम EnrolleeComponent के लिए रेंडर शब्द का उपयोग नहीं कर रहे हैं, क्योंकि इसका कोई व्यू नहीं है, और इसका आउटपुट कॉल किए गए हेल्पर मेथड का होगा। यह निम्नलिखित देता है:

हेल्पर्स का उपयोग आइकन, ग्रेविटार, या जो भी आप चुन सकते हैं के लिए किया जा सकता है। ViewComponent हेल्पर्स के उपयोग को नहीं बदलता; यह सिर्फ बदलता है कि हम उन्हें अपने कंपोनेंट में कैसे कॉल करते हैं।
before_render मेथड
ViewComponent एक before_render मेथड प्रदान करता है जिसे कंपोनेंट रेंडर होने से पहले बुलाया जा सकता है। आइए हमारे डिस्काउंट नोटिस के बगल में एक स्टार जोड़ें। हम एक हेल्पर मेथड जोड़कर शुरू करते हैं जो स्टार लाता है; एक star.png इमेज भी डाउनलोड की गई है और app/assets/images फ़ोल्डर में रखी गई है।
#app/helpers/courses_helper.rb
def star_icon
image_tag("/assets/star.png", width: "1%")
end
आइए अपने Discount कंपोनेंट में एक before_render मेथड जोड़ें जो इस हेल्पर मेथड को कॉल करता है।
# app/components/discount_component.rb
def before_render
@star_icon = helpers.star_icon
end
जैसा कि हम ऊपर देख सकते हैं, हेल्पर्स को कॉल करने का एक और तरीका पेश किया गया है। हेल्पर्स को कंपोनेंट.rb फाइल में helpers.method का उपयोग करके भी बुलाया जा सकता है। फिर, DiscountComponent के लिए हमारे रेंडर मेथड में, हम अपना स्टार आइकन इनपुट करते हैं, जो अब @star_icon वेरिएबल के माध्यम से उपलब्ध है।
# app/components/discount_component.html.erb
<%= @star_icon %> A 10% discount is available on this course
यह निम्नलिखित देता है:

हम before_render मेथड के काम करने के लिए हेल्पर्स का उपयोग करना आवश्यक नहीं है। मैंने एक और दृष्टिकोण पेश करने के लिए हेल्पर्स का उपयोग किया है।
प्रीव्यू
Action Mailer की तरह, ViewComponent कंपोनेंट्स का प्रीव्यू करना संभव बनाता है। इसे पहले config/application.rb फाइल में सक्षम करना होगा।
config.view_component.preview_paths << "#{Rails.root}/lib/component_previews"
प्रीव्यू कंपोनेंट्स test/components/previews में स्थित हैं और कई अलग-अलग इनपुट्स के साथ एक कंपोनेंट का प्रीव्यू करने के लिए उपयोग किए जा सकते हैं। हम अपने DiscountComponent का प्रीव्यू करेंगे।
# test/components/previews/discount_component_preview.rb
class DiscountComponentPreview < ViewComponent::Preview
def with_first_course
render(DiscountComponent.new(item: Course.find(1)))
end
def with_second_course
render(DiscountComponent.new(item: Course.find(2)))
end
end
दो मेथड्स डिस्काउंट कंपोनेंट को विभिन्न परिदृश्यों में प्रीव्यू करने के लिए जोड़े गए हैं। इसे देखने के लिए, https://localhost:3000/rails/view_components पर जाएँ, जहां हमें सभी बनाए गए प्रीव्यू कंपोनेंट्स और उनके मेथड्स मिलते हैं। हम उनमें से किसी पर भी क्लिक करके देख सकते हैं कि वे कैसे दिखते हैं, जैसा कि नीचे दिखाया गया है।

जैसा कि आप ऊपर वीडियो में देख सकते हैं, पहला कोर्स डिस्काउंट कंपोनेंट को रेंडर करता है, लेकिन दूसरे कोर्स के लिए कुछ भी रेंडर नहीं होता। क्या आप जानते हैं कि ऐसा क्यों हुआ? render? मेथड चेक हुआ। पहला परिदृश्य वह है जब लागत मूल्य 100 यूरो से अधिक है (पहला कोर्स), लेकिन दूसरे कोर्स के लिए लागत 100 यूरो से कम है। मेथड नाम अधिक वर्णनात्मक नहीं थे ताकि आप इसे यहाँ हाइलाइट होने से पहले पता लगा सकें :).
प्रीव्यू को किसी भी वातावरण में show_previews विकल्प का उपयोग करके सक्षम या अक्षम किया जा सकता है, लेकिन विकास और टेस्टिंग वातावरण में, वे डिफ़ॉल्ट रूप से सक्षम हैं।
# config/environments/test.rb
config.view_component.show_previews = false
JS और CSS शामिल करना
कंपोनेंट्स के साथ JavaScript और CSS शामिल करना संभव है, जिसे कभी-कभी "साइडकार" एसेट्स या फाइलें कहा जाता है। यह अभी भी एक एक्सपेरिमेंटल फीचर है। इसलिए, हम इस लेख में इसके आंतरिक कामकाज में गोता नहीं लगाएंगे, लेकिन आप इस ViewComponent फीचर के बारे में यहाँ और अधिक जान सकते हैं।
टेम्पलेट्स
व्यू कंपोनेंट का टेम्पलेट कई तरीकों से परिभाषित किया जा सकता है। सरल विकल्प व्यू और कंपोनेंट को एक ही फ़ोल्डर में डालना है, जैसा कि नीचे दिखाया गया है।

जैसा कि हम यहाँ देख सकते हैं, हमारे पास app/components फ़ोल्डर में हर कंपोनेंट और व्यू है।
एक अन्य विकल्प व्यू और अन्य एसेट्स को कंपोनेंट के समान नाम वाले सबडायरेक्टरी में रखना है। इस प्रकार, app/components फ़ोल्डर में, हमारे पास component.rb फाइल है, जिसमें कंपोनेंट होता है, और फिर एक अलग course_component फ़ोल्डर, जिसमें व्यू course_component.html.erb और कोर्स_कंपोनेंट से संबंधित हर दूसरी संपत्ति होती है।

कमांड लाइन से इस तरह कंपोनेंट फाइल जनरेट करने के लिए, --sidecar फ्लैग आवश्यक है:
rails g component Example name --sidecar
यह आपको अपने सीएसएस और जेएस फाइलों को कंपोनेंट फ़ोल्डर में जोड़ने में सक्षम बनाता है। ViewComponents एक call मेथड को परिभाषित करके टेम्पलेट फाइल के बिना भी रेंडर कर सकते हैं। इसका एक उदाहरण अगले भाग में प्रदान किया गया है, जहां हम स्लॉट्स पर चर्चा करते हैं।
स्लॉट्स
कंटेंट के कई ब्लॉक्स को एक सिंगल ViewComponent में स्लॉट्स का उपयोग करके पास किया जा सकता है। Rails मॉडल में has_one और has_many एट्रिब्यूट्स के समान, स्लॉट्स को renders_one और renders_many के साथ परिभाषित किया जाता है:
- renders_one एक स्लॉट को परिभाषित करता है जिसे प्रति कंपोनेंट अधिकतम एक बार रेंडर किया जाएगा: renders_one :header.
- renders_many एक स्लॉट को परिभाषित करता है जिसे प्रति कंपोनेंट कई बार रेंडर किया जा सकता है: renders_many :titles.
कल्पना करें कि हम एक पेज चाहते हैं जो एक हेडर और हमारे पास उपलब्ध सभी कोर्स के शीर्षक रेंडर करता है; इसे स्लॉट्स का उपयोग करके प्राप्त किया जा सकता है। आइए एक ListComponent बनाएँ, जिसमें एक हेडर होगा जो केवल एक बार रेंडर होगा, और एक TitleComponent, जो कई शीर्षक रेंडर करता है।
#app/components/list_component.rb
class ListComponent < ViewComponent::Base
renders_one :header, "HeaderComponent"
# `HeaderComponent` को इस कंपोनेंट के भीतर परिभाषित किया गया है, इसलिए हम इसे स्ट्रिंग का उपयोग करके संदर्भित करते हैं।
renders_many :titles, TitleComponent
# `titleComponent` को किसी अन्य फाइल में परिभाषित किया जाएगा, इसलिए हम इसे क्लास नाम से संदर्भित कर सकते हैं।
class HeaderComponent < ViewComponent::Base
def call
content_tag :h1, content
end
end
end
ऊपर के कंपोनेंट में, हमने कहा कि हेडर एक बार रेंडर होता है, लेकिन शीर्षक कई बार रेंडर होते हैं, क्योंकि इस पेज में कई शीर्षक होंगे। हमने ListComponent के भीतर HeaderComponent को भी परिभाषित किया है। हाँ, यह ViewComponent के साथ संभव है; एक क्लास को दूसरी क्लास के भीतर परिभाषित किया जा सकता है। आइए टेम्पलेट्स सेक्शन के तहत पहले चर्चा किए गए कॉल मेथड पर भी ध्यान दें और यह HeaderComponent में h1 टैग रेंडर करने के लिए कैसे उपयोग किया जा रहा है, जिससे संबंधित व्यू (html.erb फाइल) की आवश्यकता समाप्त हो जाती है। ListComponent के लिए संबंधित HTML फाइल में निम्न होगा;
#app/components/list_component.html.erb
<%= header %>
<% titles.each do |title| %>
<%= title %>
<% end %>
HTML फाइल में, हमने हेडर को शामिल किया, कंपोनेंट में पास किए गए सभी शीर्षकों के माध्यम से पुनरावृति की, और उन्हें रेंडर किया। जैसा कि आप देख सकते हैं, हमें लिस्ट व्यू फाइल में रेंडर किए जाने वाले कंपोनेंट का नाम निर्दिष्ट करने की आवश्यकता नहीं है; हमारे स्लॉट्स ने इसका ध्यान रखा है। इसलिए, हम उन्हें केवल header और title के रूप में पहचानते हैं।
अगला कदम हमारे TitleComponent और इसकी संबंधित HTML फाइल बनाना है, क्योंकि यह वही है जो पास किए गए हर शीर्षक के लिए रेंडर किया जाएगा।
# app/components/title_component.rb
class TitleComponent < ViewComponent::Base
def initialize(title:)
@title = title
end
end
# app/components/title_component.html.erb
<%= @title %>
अंत में, हमारे index.html फाइल में, आइए अस्थायी रूप से जो हमारे पास है उसे मिटा दें और इसे ListComponent रेंडर से बदलें।
#app/views/courses/index.html.erb
<%= render ListComponent.new do |c| %>
<% c.header do %>
<%= link_to "List of Available Courses", root_path %>
<% end %>
<%= c.title(title: "First title") %>
<%= c.title(title: "Second title!") %>
<% end %>

अब, आइए अपने कोर्स को इस व्यू में कलेक्शन के रूप में पास करें। एक कलेक्शन को स्लॉट में पास करने के लिए, हमें इनिशियलाइज़ेशन के लिए आवश्यक वेरिएबल वाले ऑब्जेक्ट्स की एक सरणी के रूप में कलेक्शन पास करना होगा। जैसा कि हम देख सकते हैं, पास किया गया हर कोर्स शीर्षक आर्गुमेंट के साथ इनिशियलाइज़ होना चाहिए। हम अपने डेटाबेस में सभी कोर्स के शीर्षक को हैश की एक सरणी में निकाल सकते हैं और इसे रेंडर कर सकते हैं।

अब हम अपने कई शीर्षकों की सूची को एक सिंगल c.titles से कलेक्शन के लिए बदल सकते हैं और इसे हमारे हैश के शीर्षकों पर पास कर सकते हैं, जिसे हम वेरिएबल course_titles का उपयोग करके परिभाषित करते हैं।
# app/views/courses/index.html.erb
<% course_titles = Course.all.pluck(:title).map { |title| {title: title}} %>
<% c.titles(course_titles) %>
यह निम्नलिखित देता है:

यही स्लॉट्स काम करते हैं: एक सिंगल ViewComponent के भीतर कई कंपोनेंट रेंडर करना। स्लॉट्स को कई अन्य तरीकों से रेंडर किया जा सकता है, और आप यहाँ अधिक जानकारी पा सकते हैं।
टेस्टिंग
व्यू कंपोनेंट्स का टेस्टिंग टेस्ट फाइल में "view_component/test_case" की आवश्यकता करके और render_inline टेस्ट हेल्पर का उपयोग करके किया जाता है ताकि रेंडर्ड आउटपुट के विरुद्ध दावे किए जा सकें। आइए अपने DiscountComponent का टेस्ट करके शुरू करें।
require "test_helper"
require "view_component/test_case"
class DiscountComponentTest < ViewComponent::TestCase
def test_render_component
render_inline(DiscountComponent.new(item: "my item"))
end
end
जब हम इस टेस्ट को rails test test/components/discount_component_test.rb कमांड का उपयोग करके चलाते हैं, तो हमें निम्न त्रुटि मिलती है:

यह हमें साबित करता है कि टेस्ट सही कंपोनेंट तक पहुँच रहा है लेकिन इसमें एक उपयुक्त प्रॉप की कमी है, क्योंकि आइटम एक कोर्स होना चाहिए जिसमें प्राइस प्रॉपर्टी हो न कि एक स्ट्रिंग, जैसा कि हमने पास किया। यह यह भी बताता है कि इस कंपोनेंट के रेंडर होने से पहले render? मेथड चेक किया जा रहा है। आइए अब सही वेरिएबल पास करें।
def test_render_component
course = Course.create(title: 'Organizing your Time', price: 55.00, location: 'London')
render_inline(DiscountComponent.new(item: course))
end
यह सफलतापूर्वक चलता है। आइए इस टेस्ट में दावे जोड़ें।
def test_render_component
course = Course.create(title: 'Organizing your Time', price: 155.00, location: 'London')
render_inline(DiscountComponent.new(item: course))
assert_selector 'p[class="green"]'
assert_text "10% discount"
end
यह टेस्ट भी पास होता है। याद रखें कि इस कंपोनेंट के लिए एक रेंडर कंडीशन है। चिंता न करें, क्योंकि ViewComponent यह टेस्ट करने का एक तरीका भी प्रदान करता है कि कोई कंपोनेंट रेंडर नहीं किया गया है, refute_component_rendered का उपयोग करके। हम 100 यूरो से कम कीमत वाले कोर्स का उपयोग करके इसका टेस्ट कर सकते हैं।
def test_component_not_rendered
course = Course.create(title: 'Organizing your Time', price: 55.00, location: 'London')
render_inline(DiscountComponent.new(item: course))
refute_component_rendered
end
यह टेस्ट भी पास होता है।
आइए अपने CourseComponent के लिए एक और टेस्ट लिखें ताकि यह टेस्ट किया जा सके कि यह इसके भीतर नेस्टेड सभी कंपोनेंट्स को रेंडर करता है।
require "test_helper"
require "view_component/test_case"
class CourseComponentTest < ViewComponent::TestCase
def test_component_renders_all_children
course = Course.create(title: 'Organizing your Time', price: 155.00, location: 'London')
render_inline(CourseComponent.new(item: course, notice: 'A new test', item_counter: 1))
assert_selector("h2", text: "Organizing your Time")
assert_selector("h4", text: "€155.00")
assert_selector("h4", text: "London")
assert_text("enrollees")
assert_text("discount")
end
end
यह टेस्ट भी पास होता है। यह टेस्ट करता है कि Enrollee और Discount कंपोनेंट्स भी ठीक से रेंडर हो रहे हैं।
याद रखें कि हमारे पास एक स्लॉट कंपोनेंट है, और जैसा कि नीचे दी गई छवि में दिखाया गया है, यह एक हेडर और कई शीर्षक रेंडर करता है।

इसे टेस्ट करने के लिए, हम कंपोनेंट को कोड का एक ब्लॉक पास करते हैं जिसमें हेडर और शीर्षक होते हैं जिन्हें इसे रेंडर करना चाहिए, और फिर हम रेंडर्ड कंपोनेंट के विरुद्ध दावा कर सकते हैं।
require "test_helper"
require "view_component/test_case"
class ListComponentTest < ViewComponent::TestCase
def test_renders_slots_with_content
render_inline(ListComponent.new) do |component|
component.header { "A Test List" }
component.titles [{title: 'Test title 1'}, {title: 'Test title 2'}]
end
assert_selector("h1", text: "A Test List")
assert_text("Test title 1")
assert_text("Test title 2")
end
end
यह टेस्ट भी पास होता है :).
Rspec टेस्टिंग
उपरोक्त टेस्टिंग के बारे में जो कुछ भी कहा गया है, उसके अलावा, यदि पसंदीदा टेस्टिंग फ्रेमवर्क RSpec है, तो ViewComponents के लिए RSpec को सक्षम करने के लिए कुछ अतिरिक्त कॉन्फ़िगरेशन किए जाने चाहिए।
# spec/rails_helper.rb
require "view_component/test_helpers"
require "capybara/rspec"
RSpec.configure do |config|
config.include ViewComponent::TestHelpers, type: :component
config.include Capybara::RSpecMatchers, type: :component
end
हमारा DiscountComponent टेस्ट Rspec का उपयोग करके फिर से लिखा और पुनः परीक्षण किया जा सकता है, जैसा कि नीचे दिखाया गया है:
require "rails_helper"
RSpec.describe DiscountComponent, type: :component do
it "renders the component correctly" do
course = Course.create(title: 'Organizing your Time', price: 155.00, location: 'London')
render_inline(DiscountComponent.new(item: course))
expect(rendered_component).to have_css "p[class='green']", text: "10% discount"
expect(rendered_component).to have_css "img[src*='/assets/star.png']"
end
end
यह टेस्ट सुरुचिपूर्ण ढंग से पास होता है। हाँ, हम अपना स्टार आइकन देख सकते हैं।
निष्कर्ष
अपने Rails ऐप के लिए कई व्यू कंपोनेंट लिखना न केवल आपके कोड को अधिक पठनीय और अवांछित जटिलताओं से होने वाली त्रुटियों से कम प्रवण बनाता है बल्कि आपके व्यू को केवल HTTP अनुरोधों के दौरान ही नहीं बल्कि अलगाव में टेस्ट करना भी संभव बनाता है। व्यू कंपोनेंट्स को मौजूदा Rails ऐप में शामिल करना आसान है, और सबसे अच्छा यह है कि उन व्यू से शुरू करें जो ज्यादातर पुन: उपयोग किए जाते हैं। अब तक सीखी गई हर चीज के साथ, यह एक आसान काम होना चाहिए। फिर भी, यदि आप ViewComponent पर अधिक जानकारी चाहते हैं, तो इसके दस्तावेज़ीकरण या RubyDoc जानकारी को देखने में संकोच न करें।