Skip to content

Slots

এই পৃষ্ঠাটি ধরে নেওয়া হচ্ছে আপনি ইতিমধ্যেই Components Basics পড়েছেন। আপনি যদি কম্পোনেন্টগুলিতে নতুন হন তবে প্রথমে এটি পড়ুন।

Slot Content and Outlet

আমরা শিখেছি যে কম্পোনেন্টগুলি প্রপস গ্রহণ করতে পারে, যা যেকোনো ধরনের জাভাস্ক্রিপ্ট মান হতে পারে। কিন্তু কিভাবে টেমপ্লেট বিষয়অবজেক্ট সম্পর্কে? কিছু ক্ষেত্রে, আমরা চাইল্ড কম্পোনেন্টে একটি টেমপ্লেট ফ্র্যাগমেন্ট পাঠাতে চাই এবং চাইল্ড কম্পোনেন্টকে তার নিজস্ব টেমপ্লেটের মধ্যে টুকরো রেন্ডার করতে দিতে পারি।

উদাহরণস্বরূপ, আমাদের একটি <FancyButton> কম্পোনেন্ট থাকতে পারে যা এই ধরনের ব্যবহার সমর্থন করে:

template
<FancyButton>
  Click me! <!-- slot content -->
</FancyButton>

<FancyButton> এর টেমপ্লেটটি দেখতে এইরকম:

template
<button class="fancy-btn">
  <slot></slot> <!-- slot outlet -->
</button>

<slot> কম্পোনেন্ট হল একটি স্লট আউটলেট যা নির্দেশ করে যে অভিভাবক-প্রদত্ত স্লট সামগ্রী কোথায় রেন্ডার করা উচিত।

Diagram showing slot content from the parent being injected into the slot outlet in the child component

এবং চূড়ান্ত রেন্ডার করা DOM:

html
<button class="fancy-btn">Click me!</button>

স্লটের সাথে, <FancyButton> বাইরের <button> (এবং এর অভিনব স্টাইলিং) রেন্ডার করার জন্য দায়ী, যখন ভিতরের বিষয়অবজেক্ট মূল কম্পোনেন্ট দ্বারা সরবরাহ করা হয়।

স্লট বোঝার আরেকটি উপায় হল জাভাস্ক্রিপ্ট ফাংশনের সাথে তুলনা করা:

js
// parent component passing slot content
FancyButton('Click me!')

// FancyButton renders slot content in its own template
function FancyButton(slotContent) {
  return `<button class="fancy-btn">
      ${slotContent}
    </button>`
}

স্লট বিষয়অবজেক্ট শুধু পাঠ্যের মধ্যে সীমাবদ্ধ নয়। এটা কোনো বৈধ টেমপ্লেট বিষয়অবজেক্ট হতে পারে. উদাহরণস্বরূপ, আমরা একাধিক কম্পোনেন্ট, বা এমনকি অন্যান্য কম্পোনেন্ট পাস করতে পারি:

template
<FancyButton>
  <span style="color:red">Click me!</span>
  <AwesomeIcon name="plus" />
</FancyButton>

স্লট ব্যবহার করে, আমাদের <FancyButton> আরও নমনীয় এবং পুনরায় ব্যবহারযোগ্য। আমরা এখন এটিকে বিভিন্ন জায়গায় বিভিন্ন অভ্যন্তরীণ বিষয়অবজেক্ট সহ ব্যবহার করতে পারি, তবে একই অভিনব স্টাইলিং সহ।

Vue কম্পোনেন্টের স্লট মেকানিজম native Web Component <slot> element দ্বারা অনুপ্রাণিত, কিন্তু অতিরিক্ত ক্ষমতা সহ যা আমরা পরে দেখব।

Render Scope

স্লট বিষয়অবজেক্টর প্যারেন্ট কম্পোনেন্টের ডেটা স্কোপের অ্যাক্সেস আছে, কারণ এটি প্যারেন্টে সংজ্ঞায়িত করা হয়েছে। উদাহরণ স্বরূপ:

template
<span>{{ message }}</span>
<FancyButton>{{ message }}</FancyButton>

এখানে উভয় {{ message }} ইন্টারপোলেশন একই বিষয়অবজেক্ট রেন্ডার করবে।

স্লট বিষয়অবজেক্টর চাইল্ড কম্পোনেন্টের ডেটাতে অ্যাক্সেস না থাকে। Vue টেমপ্লেটের অভিব্যক্তিগুলি শুধুমাত্র জাভাস্ক্রিপ্টের আভিধানিক স্কোপিংয়ের সাথে সামঞ্জস্যপূর্ণ, এটি সংজ্ঞায়িত স্কোপ অ্যাক্সেস করতে পারে। অন্য কথায়:

অভিভাবক টেমপ্লেটের অভিব্যক্তিগুলির শুধুমাত্র অভিভাবক সুযোগে অ্যাক্সেস আছে; চাইল্ড টেমপ্লেটের এক্সপ্রেশনের শুধুমাত্র চাইল্ড স্কোপের অ্যাক্সেস আছে।

Fallback Content

এমন কিছু ক্ষেত্রে আছে যখন একটি স্লটের জন্য ফলব্যাক (অর্থাৎ ডিফল্ট) বিষয়অবজেক্ট নির্দিষ্ট করা উপযোগী, শুধুমাত্র তখনই রেন্ডার করা হবে যখন কোনো বিষয়অবজেক্ট প্রদান করা হয় না। উদাহরণস্বরূপ, একটি <SubmitButton> কম্পোনেন্টে:

template
<button type="submit">
  <slot></slot>
</button>

অভিভাবক যদি কোনো স্লট সামগ্রী প্রদান না করেন তাহলে আমরা হয়ত "জমা দিন" পাঠ্যটিকে <button>-এর মধ্যে রেন্ডার করতে চাই। ফলব্যাক বিষয়অবজেক্ট "জমা দিন" করতে, আমরা এটিকে <slot> ট্যাগের মধ্যে রাখতে পারি:

template
<button type="submit">
  <slot>
    Submit <!-- fallback content -->
  </slot>
</button>

এখন যখন আমরা একটি প্যারেন্ট কম্পোনেন্টে <SubmitButton> ব্যবহার করি, স্লটের জন্য কোনো বিষয়অবজেক্ট প্রদান করি না:

template
<SubmitButton />

এটি ফলব্যাক সামগ্রী রেন্ডার করবে, "Submit":

html
<button type="submit">Submit</button>

কিন্তু যদি আমরা সামগ্রী প্রদান করি:

template
<SubmitButton>Save</SubmitButton>

তারপর প্রদত্ত বিষয়অবজেক্ট পরিবর্তে রেন্ডার করা হবে:

html
<button type="submit">Save</button>

Named Slots

এমন সময় আছে যখন একটি একক কম্পোনেন্টে একাধিক স্লট আউটলেট থাকা দরকারী। উদাহরণস্বরূপ, নিম্নলিখিত টেমপ্লেট সহ একটি <BaseLayout> কম্পোনেন্টে:

template
<div class="container">
  <header>
    <!-- We want header content here -->
  </header>
  <main>
    <!-- We want main content here -->
  </main>
  <footer>
    <!-- We want footer content here -->
  </footer>
</div>

এই ক্ষেত্রে, <slot> কম্পোনেন্টটির একটি বিশেষ বৈশিষ্ট্য রয়েছে, name, যা বিভিন্ন স্লটে একটি অনন্য আইডি বরাদ্দ করতে ব্যবহার করা যেতে পারে যাতে আপনি নির্ধারণ করতে পারেন যে সামগ্রী কোথায় রেন্ডার করা হবে:

template
<div class="container">
  <header>
    <slot name="header"></slot>
  </header>
  <main>
    <slot></slot>
  </main>
  <footer>
    <slot name="footer"></slot>
  </footer>
</div>

নাম ছাড়া একটি <slot> আউটলেটের অন্তর্নিহিত নাম "ডিফল্ট" আছে।

<BaseLayout> ব্যবহার করে একটি প্যারেন্ট কম্পোনেন্টে, আমাদের একাধিক স্লট কন্টেন্ট ফ্র্যাগমেন্ট পাস করার একটি উপায় প্রয়োজন, প্রতিটি আলাদা স্লট আউটলেটকে লক্ষ্য করে। এখানেই নামকৃত স্লট আসে।

একটি নামযুক্ত স্লট পাস করার জন্য, আমাদের v-slot নির্দেশের সাথে একটি <template> কম্পোনেন্ট ব্যবহার করতে হবে এবং তারপরে v-slot-এ আর্গুমেন্ট হিসেবে স্লটের নাম পাস করতে হবে:

template
<BaseLayout>
  <template v-slot:header>
    <!-- content for the header slot -->
  </template>
</BaseLayout>

v-slot এর একটি ডেডিকেটেড শর্টহ্যান্ড # আছে, তাই <template v-slot:header> কে শুধু <template #header>-এ ছোট করা যেতে পারে। এটিকে " চাইল্ড কম্পোনেন্টের 'শিরোনাম' স্লটে এই টেমপ্লেট খণ্ডটি রেন্ডার করুন" হিসাবে ভাবুন৷

Diagram showing multiple named slots in a layout component, with content from the parent being directed to the corresponding header, main, and footer slots

শর্টহ্যান্ড সিনট্যাক্স ব্যবহার করে <BaseLayout>-এ তিনটি স্লটের জন্য কোড পাস করার বিষয়অবজেক্ট এখানে রয়েছে:

template
<BaseLayout>
  <template #header>
    <h1>Here might be a page title</h1>
  </template>

  <template #default>
    <p>A paragraph for the main content.</p>
    <p>And another one.</p>
  </template>

  <template #footer>
    <p>Here's some contact info</p>
  </template>
</BaseLayout>

যখন একটি কম্পোনেন্ট একটি ডিফল্ট স্লট এবং নামযুক্ত স্লট উভয়ই গ্রহণ করে, তখন সমস্ত শীর্ষ-স্তরের অ-<টেমপ্লেট> নোডগুলিকে ডিফল্ট স্লটের জন্য বিষয়অবজেক্ট হিসাবে অন্তর্নিহিতভাবে বিবেচনা করা হয়। সুতরাং উপরেরটি এভাবেও লেখা যেতে পারে:

template
<BaseLayout>
  <template #header>
    <h1>Here might be a page title</h1>
  </template>

  <!-- implicit default slot -->
  <p>A paragraph for the main content.</p>
  <p>And another one.</p>

  <template #footer>
    <p>Here's some contact info</p>
  </template>
</BaseLayout>

এখন <template> কম্পোনেন্টের ভিতরের সবকিছু সংশ্লিষ্ট স্লটে পাঠানো হবে। চূড়ান্ত রেন্ডার করা HTML হবে:

html
<div class="container">
  <header>
    <h1>Here might be a page title</h1>
  </header>
  <main>
    <p>A paragraph for the main content.</p>
    <p>And another one.</p>
  </main>
  <footer>
    <p>Here's some contact info</p>
  </footer>
</div>

আবার, এটি আপনাকে জাভাস্ক্রিপ্ট ফাংশন সাদৃশ্য ব্যবহার করে নামযুক্ত স্লটগুলি আরও ভালভাবে বুঝতে সাহায্য করতে পারে:

js
// passing multiple slot fragments with different names
BaseLayout({
  header: `...`,
  default: `...`,
  footer: `...`
})

// <BaseLayout> renders them in different places
function BaseLayout(slots) {
  return `<div class="container">
      <header>${slots.header}</header>
      <main>${slots.default}</main>
      <footer>${slots.footer}</footer>
    </div>`
}

Conditional Slots

কখনও কখনও আপনি বিষয়বস্তু একটি স্লটে পাস করা হয়েছে কিনা তার উপর ভিত্তি করে কিছু রেন্ডার করতে চান।

আপনি এটি অর্জন করতে v-if এর সাথে একত্রে $slots বৈশিষ্ট্য ব্যবহার করতে পারেন।

নীচের উদাহরণে আমরা তিনটি শর্তসাপেক্ষ স্লট সহ একটি কার্ড উপাদান সংজ্ঞায়িত করি: header, footer এবং default একটি। যখন শিরোনাম / পাদচরণ / ডিফল্টের জন্য বিষয়বস্তু উপস্থিত থাকে, আমরা অতিরিক্ত স্টাইলিং প্রদানের জন্য এটি মোড়ানো করতে চাই:

template
<template>
  <div class="card">
    <div v-if="$slots.header" class="card-header">
      <slot name="header" />
    </div>
    
    <div v-if="$slots.default" class="card-content">
      <slot />
    </div>
    
    <div v-if="$slots.footer" class="card-footer">
      <slot name="footer" />
    </div>
  </div>
</template>

চেষ্টা করুন

Dynamic Slot Names

Dynamic directive arguments এছাড়াও v-slot-এ কাজ করে, যা গতিশীল স্লট নামের সংজ্ঞাকে অনুমতি দেয়:

template
<base-layout>
  <template v-slot:[dynamicSlotName]>
    ...
  </template>

  <!-- with shorthand -->
  <template #[dynamicSlotName]>
    ...
  </template>
</base-layout>

মনে রাখবেন এক্সপ্রেশনটি ডায়নামিক নির্দেশিক আর্গুমেন্টের সিনট্যাক্স সীমাবদ্ধতা এর অধীন।

Scoped Slots

Render Scope এ যেমন আলোচনা করা হয়েছে, স্লট কন্টেন্টের চাইল্ড কম্পোনেন্টে স্টেটের অ্যাক্সেস নেই।

যাইহোক, এমন কিছু ক্ষেত্রে এটি কার্যকর হতে পারে যদি একটি স্লটের বিষয়অবজেক্ট প্যারেন্ট স্কোপ এবং চাইল্ড স্কোপ উভয়ের ডেটা ব্যবহার করতে পারে। এটি অর্জন করার জন্য, রেন্ডার করার সময় চাইল্ডর একটি স্লটে ডেটা পাস করার জন্য আমাদের একটি উপায় প্রয়োজন।

প্রকৃতপক্ষে, আমরা ঠিক সেটাই করতে পারি - একটি কম্পোনেন্টে প্রপস পাস করার মতোই একটি স্লট আউটলেটে অ্যাট্রিবিউট পাস করতে পারি। প্যারেন্ট টেমপ্লেট v-slot এর মাধ্যমে স্লট প্রপস গ্রহণ করে, অন্যদিকে চাইল্ড টেমপ্লেট রেন্ডার করার সময় স্লট আউটলেটে প্রপস পাস করে:

template
<!-- Parent template (usage) -->
<ChildComponent v-slot="receivedProps">
  {{ receivedProps.text }} {{ receivedProps.count }}
</ChildComponent>
template
<!-- Child template (slot definition) -->
<!-- render with props! -->
<slot
  text="hello"
  :count="1"
/>

একটিমাত্র ডিফল্ট স্লট ব্যবহার করার ক্ষেত্রে এবং নামযুক্ত স্লট ব্যবহার করার ক্ষেত্রে স্লট প্রপস গ্রহণ করার পদ্ধতি কিছুটা ভিন্ন। উপরের উদাহরণটিতে, ChildComponent ট্যাগে সরাসরি v-slot ব্যবহার করে একটিমাত্র ডিফল্ট স্লটের মাধ্যমে প্রপস গ্রহণ করা হয়েছে।

Diagram showing a scoped slot where the child component passes data back to the parent-provided slot content

সন্তানের দ্বারা স্লটে পাস করা প্রপগুলি সংশ্লিষ্ট v-slot নির্দেশের মান হিসাবে উপলব্ধ, যা স্লটের ভিতরে অভিব্যক্তি দ্বারা অ্যাক্সেস করা যেতে পারে।

আপনি একটি স্কোপড স্লটকে চাইল্ড কম্পোনেন্টে একটি ফাংশন পাস করার মতো ভাবতে পারেন। চাইল্ড কম্পোনেন্টটি তখন এটিকে কল করে, আর্গুমেন্ট হিসাবে প্রপস পাস করে:

js
ChildComponent({
  // passing the default slot, but as a function
  default: (receivedProps) => {
    return `${receivedProps.text} ${receivedProps.count}`
  }
})

function ChildComponent(slots) {
  // call the slot function with props!
  return slots.default({ text: 'hello', count: 1 })
}

প্রকৃতপক্ষে, এটি কীভাবে স্কোপড স্লটগুলি সংকলিত হয় এবং কীভাবে আপনি ম্যানুয়ালটিতে স্কোপড স্লটগুলি ব্যবহার করবেন তার খুব কাছাকাছি।render functions.

লক্ষ্য করুন কিভাবে v-slot="receivedProps" স্লট ফাংশন সিগনেচারের সাথে মিলে যায়। ফাংশন আর্গুমেন্টের মতোই, আমরা v-slot-এও ডিস্ট্রাকচারিং ব্যবহার করতে পারি:

template
<ChildComponent v-slot="{ text, count }">
  {{ text }} {{ count }}
</ChildComponent>

Named Scoped Slots

নেমড স্কোপড স্লটগুলোও একইভাবে কাজ করে - স্লট প্রপসগুলো v-slot ডিরেক্টিভের ভ্যালু হিসেবে অ্যাক্সেস করা যায়: v-slot:name="receivedProps"। শর্টহ্যান্ড ব্যবহার করার সময়, এটি দেখতে এইরকম হয়:

template
<MyComponent>
  <template #header="headerProps">
    {{ headerProps }}
  </template>

  <template #default="defaultProps">
    {{ defaultProps }}
  </template>

  <template #footer="footerProps">
    {{ footerProps }}
  </template>
</MyComponent>

একটি নামযুক্ত স্লটে প্রপস পাস করা:

template
<slot name="header" message="hello" />

নোট করুন একটি স্লটের name প্রপস-এ অন্তর্ভুক্ত করা হবে না কারণ এটি সংরক্ষিত - তাই ফলস্বরূপ headerProps হবে { message: 'hello' }

আপনি যদি ডিফল্ট স্কোপড স্লটের সাথে নামযুক্ত স্লটগুলি মিশ্রিত করেন তবে আপনাকে ডিফল্ট স্লটের জন্য একটি স্পষ্ট <template> ট্যাগ ব্যবহার করতে হবে। কম্পোনেন্টে সরাসরি v-slot নির্দেশনা রাখার চেষ্টা করলে একটি সংকলন ত্রুটি দেখা দিবে। এটি ডিফল্ট স্লটের প্রপসের সুযোগ সম্পর্কে কোনও অস্পষ্টতা এড়াতে। উদাহরণ স্বরূপ:

template
<!-- <MyComponent> template -->
<div>
  <slot message="hello" />
  <slot name="footer" />
</div>
template
<!-- This template won't compile -->
<MyComponent v-slot="{ message }">
  <p>{{ message }}</p>
  <template #footer>
    <!-- message belongs to the default slot, and is not available here -->
    <p>{{ message }}</p>
  </template>
</MyComponent>

ডিফল্ট স্লটের জন্য একটি স্পষ্ট <template> ট্যাগ ব্যবহার করা এটি স্পষ্ট করতে সাহায্য করে যে অন্য স্লটের ভিতরে message প্রপ উপলব্ধ নেই:

template
<MyComponent>
  <!-- Use explicit default slot -->
  <template #default="{ message }">
    <p>{{ message }}</p>
  </template>

  <template #footer>
    <p>Here's some contact info</p>
  </template>
</MyComponent>

Fancy List Example

আপনি হয়তো ভাবছেন যে স্কোপড স্লটের জন্য একটি ভাল ব্যবহারের ক্ষেত্রে কী হবে। এখানে একটি উদাহরণ দেওয়া হল: একটি <FancyList> কম্পোনেন্টের কল্পনা করুন যা আইটেমগুলির একটি তালিকা রেন্ডার করে - এটি দূরবর্তী ডেটা লোড করার জন্য, একটি তালিকা প্রদর্শন করতে ডেটা ব্যবহার করে, এমনকি পৃষ্ঠা সংখ্যা বা অসীম স্ক্রলিংয়ের মতো উন্নত বৈশিষ্ট্যগুলিকে এনক্যাপসুলেট করতে পারে৷ যাইহোক, আমরা চাই যে প্রতিটি আইটেম দেখতে কেমন হবে তার সাথে এটি নমনীয় হোক এবং প্রতিটি আইটেমের স্টাইলিংটি মূল কম্পোনেন্টের উপর ছেড়ে দিন যা এটি ব্যবহার করে। তাই পছন্দসই ব্যবহার এই মত দেখতে পারে:

template
<FancyList :api-url="url" :per-page="10">
  <template #item="{ body, username, likes }">
    <div class="item">
      <p>{{ body }}</p>
      <p>by {{ username }} | {{ likes }} likes</p>
    </div>
  </template>
</FancyList>

<FancyList>-এর ভিতরে, আমরা একই <slot> বিভিন্ন আইটেম ডেটার সাথে একাধিকবার রেন্ডার করতে পারি (লক্ষ্য করুন আমরা একটি অবজেক্টকে স্লট প্রপস হিসেবে পাস করতে v-bind ব্যবহার করছি):

template
<ul>
  <li v-for="item in items">
    <slot name="item" v-bind="item" />
  </li>
</ul>

Renderless Components

আমরা উপরে আলোচনা করা <FancyList> ব্যবহারের ক্ষেত্রে স্কোপড স্লটের মাধ্যমে ভিজ্যুয়াল আউটপুটের অংশ ভোক্তাদের কাছে অর্পণ করার সময় পুনঃব্যবহারযোগ্য লজিক (ডেটা আনা, পেজিনেশন ইত্যাদি) এবং ভিজ্যুয়াল আউটপুট উভয়কেই অন্তর্ভুক্ত করে।

আমরা যদি এই ধারণাটিকে আরও কিছুটা এগিয়ে দেই, তাহলে আমরা এমন কম্পোনেন্ট নিয়ে আসতে পারি যা শুধুমাত্র যুক্তিকে এনক্যাপসুলেট করে এবং নিজে থেকে কিছু রেন্ডার করে না - ভিজ্যুয়াল আউটপুট সম্পূর্ণরূপে স্কোপড স্লট সহ ভোক্তা কম্পোনেন্টের কাছে অর্পণ করা হয়। আমরা এই ধরনের কম্পোনেন্টকে রেন্ডারলেস কম্পোনেন্ট বলি।

একটি উদাহরণ রেন্ডারলেস কম্পোনেন্ট হতে পারে যা বর্তমান মাউসের অবস্থান ট্র্যাক করার যুক্তিকে এনক্যাপসুলেট করে:

template
<MouseTracker v-slot="{ x, y }">
  Mouse is at: {{ x }}, {{ y }}
</MouseTracker>

While an interesting pattern, most of what can be achieved with Renderless Components can be achieved in a more efficient fashion with Composition API, without incurring the overhead of extra component nesting. Later, we will see how we can implement the same mouse tracking functionality as a Composable.

এটি বলেছে, স্কোপড স্লটগুলি এখনও সেই ক্ষেত্রে কার্যকর যেখানে আমাদের যুক্তি এবং উভয়ই ভিজ্যুয়াল আউটপুট রচনা করতে হবে, যেমন <FancyList> উদাহরণে।

Slots has loaded