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.routing.yml
  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:

Controller/News.php
  <?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:

/ce-api/news
{

    "title": "News Listing",
    "content_format": "json",
    "content": {
          "element": "teaser-listing",
          "title": "Latest news",
          "icon": "news",
          "content": [
                {
                      "element": "article-teaser",
                      "href": "https://example.com/news/1",
                      "excerpt": "The excerpt of the news entry."
                },
                {
                      "element": "article-teaser",
                      "href": "https://example.com/news/2",
                      "excerpt": "The excerpt of another news entry."
                }
          ]
    }
}
/ce-api/news?_content_format=markup
{
    "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 suiting:
      $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);
    }
  }

}