So lastly, before we actually move on to creating our components, let's cover slots. Slots allow us to insert specific HTML content into designated areas within our components.
The Main Slot
First, let's understand the main slot, which we've already touched upon in our app layout. Anything enclosed between the component's start and end tags is rendered as the default slot.
@layout.app({ title: request.params().name }) {{-- π start tag --}} Our main slot content {{-- π main slot --}} @end {{-- π end tag --}}Copied!
The main slot is accessible in the component by encompassing await $slots.main() with three curly braces. Remember, three curly braces allow us to plop raw HTML within EdgeJS.
<div class="max-w-4xl mx-auto my-8"> <header class="bg-gray-100 p-6 -mx-6 mb-6 rounded-md"> @if (title != 'Home') <a href="/" class="text-xs">Home</a> @endif <h1 class="text-2xl font-bold">{{ titleCase(title) }}</h1> </header> <main {{ $props.serializeExcept(['title']) }}> {{{ await $slots.main() }}} {{-- π renders our main slot content --}} </main> </div>Copied!
- resources
- views
- components
- layout
- app.edge
The main slot is always going to be provided by default, so we don't need to worry about this particular method being undefined; we're always safe to go ahead and call it.
Named Slots
In addition to that main slot, we can incorporate named slots within our component as well. Named slots work very similarly to the main slot, the main difference being theyβre named differently and require us to specify what their content is when we use our component.
Letβs say, within the head section of our app layout, we want to be able to define a different title per page within our application. Currently our head section looks like the below.
<head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Adocasts' EdgeJS Components Series</title> @entryPointStyles('app') @entryPointScripts('app') </head>Copied!
- resources
- components
- layout
- app.edge
We can add in a named slot, called title, which will then give us the option to overwrite the default title we have above.
<head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> @if ($slots.title) {{{ await $slots.title() }}} @else <title>Adocasts' EdgeJS Components Series</title> @endif @entryPointStyles('app') @entryPointScripts('app') </head>Copied!
- resources
- components
- layout
- app.edge
Now unlike the main slot, name slots aren't always going to exist by default. So, in this case, our title slot is not going to be defined unless we explicitly provide the slot within our component usage.
We can provide content to the βtitleβ named slot, by calling the @slot tag within our component usage, passing it the name of the slot, and providing the content as itβs children.
@layout.app({ title: request.params().name }) {{-- starts our named "title" slot --}} @slot('title') This will now be our title @endslot {{-- starts our main slot --}} Our main slot content @endCopied!
Optional Versus Required Named Slots
Above, since weβve wrapped our βtitleβ named slot within an if check, which is checking the existence of $slots.title, weβre making this slot optional. Meaning, if we donβt provide content to this slot within our component usage, everything will work just fine.
However, if we remove the if check to leave just the below. Weβll now get an error if we donβt provide content to this βtitleβ slot.
<head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> {{{ await $slots.title() }}} @entryPointStyles('app') @entryPointScripts('app') </head>Copied!
- resources
- components
- layout
- app.edge
Meaning, weβve essentially changed this slot from being optional to required, since our app no longer works properly without it.
Slot Scope
In addition to slots, similarly to Vue, you can pass slot scope data to access component state within the slot content. All you have to do is pass the data in as the first argument to the slot method, like below.
{{{ await $slots.title({ title }) }}}Copied!
For example, weβre still passing the param name into our component as title, so we can pass that along as the slot scope in both our named and main slots.
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> @if ($slots.title) {{{ await $slots.title({ title }) }}} {{{ await $slots.title() }}} @else <title>{{ title ? titleCase(title) + ' | ' : '' }} Adocasts' EdgeJS Components Series</title> @endif @entryPointStyles('app') @entryPointScripts('app') </head> <body> <div class="max-w-4xl mx-auto my-8"> <header class="bg-gray-100 p-6 -mx-6 mb-6 rounded-md"> @if (title != 'Home') <a href="/" class="text-xs">Home</a> @endif <h1 class="text-2xl font-bold">{{ titleCase(title) }}</h1> </header> <main {{ $props.serializeExcept(['title']) }}> {{{ await $slots.main({ title }) }}} {{{ await $slots.main() }}} </main> </div> </body> </html>Copied!
- resources
- components
- layout
- app.edge
For our named slot, this will be passed along as the second parameter of the slot tag.
@layout.app({ title: request.params().name }) @slot('title', scope) {{ scope.title }} @slot('title') This will now be our title @endslot Our main slot content @endCopied!
For our main slot, in order to access the slot scope, weβll need to wrap it within the @slot tag and use it the same as a named slot.
@layout.app({ title: request.params().name }) @slot('title', scope) {{ scope.title }} @endslot @slot('main', scope) {{ scope.title }} @endslot Our main slot content @endCopied!
So, in the case that we have above for both our named and main slots, our title prop is doing a full round trip.
First, itβs passed as a prop into layout component
Then, passed from our layout component back to our page as a slot scope
Lastly, weβre rendering it as slot content inside of our component from our page.
In summary, slots within EdgeJS Components enable you to define flexible content areas, both for main content and for named slots, and you can pass scope data to access component state within those slots. Which allows you to make the actual content of your components fluid and extendable.