Routes
The Lupus CE Renderer module takes care of rendering routes of the format custom_elements via Drupal's regular routing system. That means a route can be defined as usual in Drupal, additionally define the _format to be custom_elements:
MODULE.listing:
path: '/news'
defaults:
_title: 'News Listing'
_controller: '\Drupal\MODULE\Controller\NewsController::buildNewsListing'
requirements:
_format: 'custom_elements'
_permission: 'access content'
Next, the controller may simple return a custom element object:
<?php
class NewsController extends ControllerBase {
public function buildNewsListing() {
$articles[] = CustomElement::create('article-teaser')
->setAttribute('href', 'https://example.com/news/1')
->setAttribute('excerpt', 'The excerpt of the news entry.');
$articles[] = CustomElement::create('article-teaser')
->setAttribute('href', 'https://example.com/news/2')
->setAttribute('excerpt', 'The excerpt of another news entry.');
return CustomElement::create('teaser-listing')
->setSlotFromNestedElements('default', $articles);
}
}
The Lupus CE renderer module is taking care of rendering the element into either markup or JSON serialization:
{
"title": "News Listing",
"content_format": "json",
"content": {
"element": "teaser-listing",
"props": {
"title": "Latest news",
"icon": "news"
},
"slots": {
"default": [
{
"element": "article-teaser",
"props": {
"href": "https://example.com/news/1",
"excerpt": "The excerpt of the news entry."
}
},
{
"element": "article-teaser",
"props": {
"href": "https://example.com/news/2",
"excerpt": "The excerpt of another news entry."
}
}
]
}
}
}
{
"title": "News Listing",
"content_format": "markup",
"content": "
<teaser-listing title=\"Latest news\" icon=\"news\">
<article-teaser to=\"https://example.com/news/1\" excerpt=\"The excerpt of the news entry.\" slot=\"default\"></article-teaser>
<article-teaser to=\"https://example.com/news/2\" excerpt=\"The excerpt of another news entry.\" slot=\"default\"></article-teaser>
</teaser-listing>"
}
Multiple routes at one path
Note that the routing system supports routes at the same path with varying formats, thus a html and a custom_elements formatted route can co-exist at the same path. This may be used to clone routes and to customize them as needed.
/**
* Creates CE variants for user forms.
*/
class RouteSubscriber extends RouteSubscriberBase {
/**
* {@inheritdoc}
*/
protected function alterRoutes(RouteCollection $collection) {
// Provide CE variants for user forms.
$form_route_ids = ['user.login', 'user.pass', 'user.register'];
foreach ($form_route_ids as $form_route_id) {
$route = $collection->get($form_route_id);
$ce_route = clone $route;
$ce_route->setRequirement('_format', 'custom_elements');
// Then customize the new route as needed:
$form = $route->hasDefault('_entity_form') ? 'entity_form' : 'form';
$ce_route->setDefault('_controller', "lupus_decoupled_form.controller.$form:getContentResult");
// Add it to the route collection.
$collection->add("lupus_decoupled.{$form_route_id}", $ce_route);
}
}
}
Custom elements
Providing blocks
In order to provide blocks that render into custom elements, for example for using as part of the Layout Builder, the block cannot directly return a custom element object, since its interface requires a render array. Instead, simply return the custom element as render array via its helper toRenderArray(). When done so, the custom element is going to be picked up correctly when the layout is rendered.