Dynamic Content Projection with Vue Slots
Vue's slot mechanism is a powerful tool for creating reusable components with flexible content. This challenge will test your understanding of how to effectively utilize and implement slots in a Vue 3 application using TypeScript. You'll build a flexible Card component that can accept and display various types of content provided by its parent.
Problem Description
You need to create a reusable Vue 3 component called Card that accepts content through named slots. The Card component should have a consistent structure (e.g., a border, a title area) but allow the parent component to customize its header, body, and footer content. This is a fundamental pattern for building adaptable UI elements in Vue.
Key Requirements:
- Create a
Cardcomponent. - The
Cardcomponent must accept content via three named slots:header,default(orbody), andfooter. - The
headerslot content should appear at the top of the card. - The
defaultslot content should appear in the main body of the card. - The
footerslot content should appear at the bottom of the card. - The
Cardcomponent should have basic styling (e.g., a border, padding) to visually distinguish it. - Implement this using Vue 3 and TypeScript.
Expected Behavior:
When a parent component uses the Card component, it should be able to pass different HTML structures, text, or other Vue components into each of the named slots. The Card component will then render this content in the designated areas.
Edge Cases to Consider:
- What happens if a slot is not provided by the parent? The
Cardcomponent should gracefully handle the absence of content. - What if the parent provides complex nested structures within a slot? The
Cardcomponent should render them correctly.
Examples
Example 1:
Parent Component Template:
<template>
<Card>
<template #header>
<h2>Card Title</h2>
</template>
<template #default>
<p>This is the main content of the card.</p>
<ul>
<li>Item 1</li>
<li>Item 2</li>
</ul>
</template>
<template #footer>
<button>Action Button</button>
</template>
</Card>
</template>
Card Component (Conceptual Rendering):
<div class="card-container">
<div class="card-header">
<!-- Content from #header slot -->
<h2>Card Title</h2>
</div>
<div class="card-body">
<!-- Content from #default slot -->
<p>This is the main content of the card.</p>
<ul>
<li>Item 1</li>
<li>Item 2</li>
</ul>
</div>
<div class="card-footer">
<!-- Content from #footer slot -->
<button>Action Button</button>
</div>
</div>
Explanation: The parent component provides distinct content for the header, default, and footer slots. The Card component renders this content in its predefined structure.
Example 2:
Parent Component Template:
<template>
<Card>
<template #header>
<h3>Another Card</h3>
</template>
<p>Only the default slot is provided.</p>
<!-- No #footer slot provided -->
</Card>
</template>
Card Component (Conceptual Rendering):
<div class="card-container">
<div class="card-header">
<!-- Content from #header slot -->
<h3>Another Card</h3>
</div>
<div class="card-body">
<!-- Content from #default slot -->
<p>Only the default slot is provided.</p>
</div>
<div class="card-footer">
<!-- #footer slot is empty -->
</div>
</div>
Explanation: In this example, the footer slot is not provided. The Card component should render an empty footer area without errors.
Constraints
- The solution must be implemented in Vue 3.
- All component code (including the
Cardcomponent and any example usage) must use TypeScript. - The
Cardcomponent should have a maximum width of 300px. - The
Cardcomponent should have a border of 1px solid #ccc. - The
Cardcomponent should have padding of 16px. - The
Cardcomponent should have a margin-bottom of 20px.
Notes
- Consider using
defineSlotsin Vue 3's Composition API for type-safety with slots. - You can define basic CSS within the
Cardcomponent's<style>block. - Think about how you would conditionally render parts of the card if a slot is empty, though for this challenge, simply rendering an empty slot area is sufficient.
- The primary goal is to demonstrate understanding of named slots and their projection within a component.