@helga-agency/dynamic-content-loader
v1.3.0
Published
Extensible, loosely coupled custom elements that load content asynchronously, e.g. for filtered search results
Readme
Dynamic Content Loader
Modular components to update multiple content blocks on a page in a synchronized way, e.g. to update pagination, filters and content separately (to keep the UI as responsive as possible) when a filter is changed.
Different components are wrapped in a dynamic-content-orchestrator and play together in a
loosely coupled way. There are two types of components:
Listeners handle DOM or other events and request an update by dispatching an event.Updaters that return a config from where their content should be fetched and handle the content once it arrives.
Basic Flow:
- All
Updaters register themselves at theDynamicContentOrchestratorby dispatching anaddDynamicContentUpdaterevent with{ updateResponseStatus, getRequestConfig }. - Once a user interaction happens, a
Listenerdispatches aloadDynamicContentevent with{ requestConfiguration: { searchParams, action?, reset? } }. ThesearchParamscorrespond to the filters that should be applied to the fetched content;resetis a boolean that indicates if everything should be reset (e.g. when user clicks "Reset filters");actionis either'paginateAppend'or'paginateReplace'and indicates if the main content should be appended or replaced. - The
DynamicContentOrchestratorcallsgetRequestConfigon eachUpdaterand collects the returned configuration. - The
DynamicContentOrchestratorfetches the content from every URL and callsupdateResponseStatuson eachUpdaterwith the corresponding content.
Why the Orchestrator?
- If user interactions happen at a fast pace, we must make sure that all previous requests are
canceled (or at least discarded). If not, we might run into race conditions and display outdated
content. The orchestrator cancels all previous requests (for all
Updaters) once a new user interaction happens. - If multiple
Updaters intend to fetch the same URL, it batches those requests and makes sure a URL is only fetched once.
An element can at the same time be a Listener and an Updater if it implements the the functions
and events of both types.
Some of those components are provided within this package, others can be developed and integrated for specific use cases.
Example
<dynamic-content-orchestrator>
<content-updater data-endpoint-url="/results" data-is-main-content>
<div data-loading hidden>Loading…</div>
<div data-error hidden><!-- Will be populated when needed --></div>
<div data-content>Initial content</div>
</content-updater>
<link-listener data-append>
<!-- This content-updater fetches the new pagination when the page changes-->
<content-updater data-endpoint-url="/page">
<div data-loading hidden>Loading…</div>
<div data-error hidden><!-- Will be populated when needed --></div>
<div data-content><a href="/page?q=5">Page 5</a></div>
</content-updater>
<link-listener>Components
Dynamic Content Orchestrator
Exposed Element
<dynamic-content-orchestrator></dynamic-content-orchestrator>
Structure
Serves as a wrapper around all other components below and ensures that they play togehter nicely.
Handles two events:
addDynamicContentUpdater ({ detail: { getRequestConfig: function, updateResponseStatus: function } }). The argument signatures are:getRequestConfig (function({ searchParams: SearchParams }))will be called when new content should be fetched. Must return a string ornullif nothing should be fetched.updateResponseStatus (function({ status: string, content: string }))will be called when the orchestrator receives new content. Validstatusareloading,loadedandfailed.contentwill be the content that will be displayed (empty if the status isloading).
loadDynamicContent ({detail: { requestConfiguration: { searchParams: SearchParams } }}): Dispatched byListeners when they want to fetch new content.
Attributes
None
Content Updater
Exposed Element
<content-udpater></content-updater>
Attributes
data-endpoint-url(string, attribute is required but value may be empty): URL that should be fetched; a query string may be automatically attached if it is requested by a listener (e.g. to paginate or filter the view).data-is-main-content(boolean, optional): If set, content will be appended (instead of replaced) whenlink-listeneruses thedata-appendattribute. If set, the browser will scroll to the top of this element if action ispaginateReplace(see<link-listener>);
Content
The following elements must be provided within <aync-loader>:
- An element matching
[data-content](required): If the request succeeds, it will be added to this element and the element will be displayed. - An element matching
[data-loading](required): It will be displayed while the data is loading. - An element matching
[data-error](required): It will be displayed if loading data fails; the error message will be added to this element.
Events
addDynamicContentUpdater(see DynamicContentOrchestrator)
Link Listener
Exposed Element
<link-listener></link-listener>
Attributes
data-reset(boolean, optional): If set, clicking the button will emit an event withreset: truein therequestConfiguration(seeinput-updater).data-append(boolean, optional): If set, new content will be appended to the maincontent-updater(and not replace it) by setting theactiontopaginateAppend.
Structure
Wrap the link-listener around any number of links to update the content dynamically when they're
clicked. It listens to clicks on any child element and takes the href from the first surrounding
element that has a href attribute. The href's query string will be used to dispatch a
loadDynamicContent event.
Example
<link-listener>
<a href="/page?p=1"><span>1</span></a>
<a href="/page?p=2"><span>2</span></a>
</link-listener> The click happens on the <span> elements (which do not have a href attribute); therefore
the query string will be taken from the clicked span's first ancestor with a href attribute.
Events
loadDynamicContent(see DynamicContentOrchestrator) with the clicked link's (or its ancestral element's)hrefquery string assearchParams.
Filter Change Listener
Exposed Element
<filter-change-listener></filter-change-listener>
Attributes
None
Structure
Wrap the filter-change-listener around a form to update the content dynamically when
the form is changed.
Query String Updater
Exposed Element
<query-string-updater></query-string-updater>
Attributes
None
Structure
Updates the window location (query string) to represent the currently selected filters. Place it
anywhere in the DOM (below the <dynamic-content-orchestrator>).
Facets Updater
Exposed Element
<facets-updater></facets-updater>
Attributes
data-endpoint(required, but may be empty): URL that should be fetched; a query string may be attached if it is requested by a listener.
Structure
Wrap around the facets provided by Drupal; component is quite Drupal-specific.
Input Updater
Very Drupal-specific component to fetch and update the HTML of the filter inputs to reseet them to their initial state (e.g. remove all input values) if the filters are reset.
Exposed Element
<input-updater></input-updater>
Attributes
data-endpoint-url(string, attribute is required but value may be empty): URL that should be fetched to get the HTML (as key of a JSON-stringified object).
