Summary
This proposal adds first-class frontend metadata to `lupus_decoupled_views` custom-element output so decoupled clients can build robust, native filter/sort/pagination UX without project-specific processors.

Problem
Today, custom-element view payloads include rows, title, and pager, but typically do not expose:

- Exposed filter definitions (labels, identifiers, options, submitted values)
- Exposed sort definitions (sort field, order options, submitted values)
- Empty-state text/markup configured in View "No results behavior"

As a result, decoupled frontends must:

- Reconstruct View form semantics manually, or
- Add custom per-project Drupal processors to extract this data

This creates duplicated logic and inconsistent API contracts across projects.

Proposed Change
In `lupus_decoupled_views` display payload generation (`CustomElementsViewsDisplayTrait::buildCustomElement`), add attributes:

- `exposed_filters`
- `exposed_sorts`
- `no_results`

Data sources
- Exposed handlers from the View executable (filter/sort handlers + exposed input)
- Existing pager plugin data (already present)
- Empty-state content from rendered `#empty` and/or configured display empty handlers

Suggested payload shape

json
{
  "viewId": "podcast",
  "displayId": "block_1",
  "exposedFilters": [
    {
      "label": "Content type",
      "identifier": "type",
      "queryParamName": "type",
      "required": false,
      "multiple": false,
      "options": {
        "article": "Article",
        "page": "Page"
      },
      "submittedValues": []
    }
  ],
  "exposedSorts": [
    {
      "label": "Title",
      "fieldIdentifier": "title",
      "queryParamSortBy": "sort_by",
      "queryParamSortOrder": "sort_order",
      "sortByValue": "title",
      "sortOrderOptions": {
        "asc": "Sort ascending",
        "desc": "Sort descending"
      },
      "submittedOrder": "asc"
    }
  ],
  "pager": {
    "current": 0,
    "totalPages": 2
  },
  "noResults": "<p>No matching content found.</p>"
}

Why this is valuable
1) Better decoupled UX out of the box
Frontends can immediately render:
- Filter controls
- Sort controls
- Correct query param wiring
- Empty-state messaging from Drupal-authored content

2) Less custom code in consuming projects
Projects no longer need ad-hoc processors for common View concerns, reducing maintenance and drift.

3) Stronger API contract for Views in decoupled mode
This aligns the CE payload with how Views are configured in Drupal admin and makes integration predictable.

4) Backward compatible
Existing consumers that only use rows/pager continue to work. New attributes are additive.

Test Coverage Requested
Add/extend functional tests in `lupus_decoupled_views` to validate:

- Exposed filters metadata present and normalized
- Exposed sorts metadata present and normalized
- Empty-state message is present when result set is empty
- Payload remains valid when no exposed handlers are configured

Optional Follow-up
- Document these attributes in module README/API docs
- Add an alter hook or display option to enable/disable metadata blocks if maintainers prefer opt-in behavior

Frontend Impact
This significantly improves frontend filtering/sorting ergonomics in Nuxt/React/etc. by turning Views configuration into consumable runtime metadata rather than requiring custom parsing or duplicated definitions.

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Comments

glynster created an issue. See original summary.

fago’s picture

I agree that would be a very valuable addition.

I think a first step would be to make sure this is supported by passing through all the necessary information, such that the built-in view component can do the sorting + add its own custom filter form.

As a second, later step, we could look into shipping with working drupal-forms for exposed filters and sorts - after #3336148: Add support for drupal forms in the frontend landed.

That said, we don't currently have plans to work on this, since we are focussing on RC1 topics to get that out of the door, see https://www.drupal.org/project/lupus_decoupled/issues/3311105#comment-15...

That said, of course contributions are welcome if you'd like to give it a go. I'd happy to chat and provide pointers for necessary changes.

glynster’s picture

Great news and it make sense, due to the forms support. We have been playing around a little to get things working. This module and code provided some useful hints:

https://git.drupalcode.org/project/views_better_rest/-/blob/1.0.x/src/Pl...

If we get any success we would be happy to add this to a PR.

glynster’s picture

StatusFileSize
new209.83 KB

I just ran a quick test with the module and this is exactly what you want.

glynster’s picture

StatusFileSize
new4.34 KB

Added as a patch and works well. I was not sure how to do a PR. Let me know if I need to adjust anything.

glynster’s picture

Is it possible for me to do a MR to help include the code better?

fago’s picture

Status: Active » Needs work

@glynster: Sure! Everone can create MRs, just add an issue fork and push your code there!

thx for the patch. As I understand the code is taken from views_better_rest module?

I'm wondering whether we should include this information always or maybe better make some checkboxes in the style-plugin to allow turning it on and off? I guess someone not needing that information does not want to bloat the API response of the view.

+ foreach ($handlers as $id => $item) {
+ if (!empty($item['exposed'])) {
+ $info = $item['expose'];
+ if ($type === 'filter') {

I see the code handles the filter case, but I miss 'sort'. Is that missing?

How are you handling things on the vue side? Anything we should document here then?

glynster’s picture

@fago, I guess I missed the connection, I will go ahead and do the MR.

I don't think we need to to do a check as in views you have an option to expose. So if they never expose the filters then it should not return anything in the API output. To me this makes sense. Let me add in the if check and take it from there.

Once I have this done yes I can jump into the Vue side of things. We usually use something like vue multi-select. We have also been looking at Nuxt UI lately. Our current setup is Bootstrap 5.3. Totally off topic but would it make sense to offer a basic vue theme using Nuxt UI?

glynster’s picture

Added to git now. As I have looked no filtering or sorting is added to the API unless it is set to expose in Views. So I think that works fine. Let me know.

glynster’s picture

Title: Views API to include filters/sorting » Expose View Controls Metadata and Empty-State Content in `lupus_decoupled_views` Custom Element Payload
Issue summary: View changes
glynster’s picture

Issue summary: View changes
Status: Needs work » Needs review
glynster’s picture

@fargo I have gone ahead and fully updated this PR. We have this successfully working in our production sites so wanted to port this back so your module can benefit.