Web Components
![Laptop computer - Photo by Lukas: https://www.pexels.com/photo/turned-on-laptop-computer-574073/](../../images/hero-web-components.jpg)
In 2021 we rewrote Responsive UI using web components, a standards-based component technology that can run within any web UI framework. This was an exciting move since it opened the door to Responsive UI being used in web technologies other than Angular (as well as in Angular too). It also allowed us to remove our reliance on older technologies like Bootstrap and jQuery.
In the version 8.0 release of Responsive UI, we replaced the Angular components with thin Angular wrappers around the Responsive UI web components. These wrappers provide the same API (properties, methods, and events) as before; each wrapper translates the Angular component API to the web component API internally, shielding you from the web component API. This move got us down to one true codebase (the web components) with only a light codebase for the wrappers.
The web components approach was a technology shift, but it also opened up some exciting new opportunities for us:
- While the Responsive UI web components fully support Angular apps, they also work in other modern web technologies other than Angular. This provides an opportunity for teams who are more versed in other web development technologies to use Responsive UI without having to learn and use Angular.
- Moving to web components removed our dependence on a single framework. Web components technology is standards-based, is supported in all modern browsers, and does not require dependence on any one framework.
- We were able to remove dependencies on older technologies like Bootstrap and jQuery when we built the web components codebase. We originally built Responsive UI on top of Bootstrap 3 — essentially “paving over” Bootstrap — which generated quite a bit of bloat.
- The web components codebase was a complete rewrite of all the code we had before. This rewrite allowed us to fix some bugs and remove bloat from the CSS. It also provided the opportunity to fix some visual styling inconsistencies. The end result is leaner and cleaner code with more consistent and legible visual styling.
- We were able to rewrite some of the 3rd party components as Jack Henry components, which allowed us to provide enhancements to these components that were not possible before.
- The theming support in the web components codebase uses CSS variables, an approach that opened up some really cool possibilities in our theming.
- We used to publish CSS classes that applied atomic values in our theming (think individual colors, sizes, spacing) to specific CSS classes.
- With CSS variables, you’re now able to apply atomic themed values (say an individual color) to anything.
- This opens the door to more product-level theming of elements, including new one-off third-party elements you might want to incorporate into your applications.
Do the web components look and feel different from the Angular wrappers?
The Angular wrappers embed the web components, so both libraries have the same appearance.
The web component API uses a slightly different naming scheme from the Angular wrapper API, but similar. The web components are prefixed with “rui-” while the Angular wrappers are prefixed with “jha-”. Also, all properties and events in the Angular wrappers are prefixed with “jha”, while the web components do not prefix their properties and events.
Are we still supporting Angular?
We are not removing support for Angular. We’ve grown a community around Angular development here at Jack Henry and that isn’t going away. The web components work in Angular just like the Angular wrappers. You can consume the web components directly from an Angular application, and you can use the Angular wrappers in Angular too.
So, we haven’t removed support for Angular, we just removed Angular as a requirement for working with Responsive UI, which is a healthier place for us to be.
What are my options for working with the Responsive UI components?
You have 3 approaches for using Responsive UI in your applications…
Web Components in a non-Angular application
- Your app is built using a technology other than Angular, such as React or Blazor.
- Your app uses the Responsive UI web components.
- Your app imports the responsive-ui-wc npm package to get the Responsive UI web components.
Web Components in an Angular application
- Your app is built using the Angular framework.
- Your app imports the responsive-ui-wc npm package to get the Responsive UI web components.
- Your app imports the responsive-ui-angular npm package to get the Responsive UI Angular extensions.
Angular wrappers in an Angular application
- Your app is built using the Angular framework
- Your app imports the responsive-ui-wc npm package to get the Responsive UI web components.
- Your app imports the responsive-ui-angular npm package to get the Responsive UI Angular wrappers.
- This approach should only be used by teams who started using Responsive UI before we built the web components.
What about third-party support?
Responsive UI supports a handful of third-party components. There are certain components (say a data grid) that we would never want to reinvent ourselves. Our support for these third-party components involves creating CSS to style the third-party elements and inclusion in our sample application and our development documentation.
Responsive UI — whether you’re using the web components or using the Angular wrappers — provides the same styling and documentation support for third-party components as before.
Also, our CSS variable-based theming makes it easy for you to extend the Responsive UI theming to any elements, including components that you create and third-party components not officially supported by our team.
Can I mix Responsive UI web components and Angular wrappers in the same app/view?
Absolutely. Both libraries provide custom HTML tags.
The Angular wrappers use “jha-” as a tag prefix while the web components use “rui-” as a prefix (e.g., jha-record-detail vs rui-record-detail).
HTML doesn’t support namespaces, so we used different tag prefixes for each library so both types of tags could be used in the same app/view.
How do the web components differ from the Angular wrappers?
The Responsive UI web components provide the same functionality, look, and feel as the Angular web components. Here are the differences between the two:
- The web components are implemented differently internally.
- We used LitElement to build the web components and Angular to build the Angular wrappers.
- Regardless of that, you should be able to use the web components in any modern web front-end technology.
- The Angular wrapper tags use a prefix of “jha” while the web components use a prefix of “rui”. We used different prefixes so that the two different sets of components could coexist.
- This is important for you at the product level so you can mix the 2 types of components equally in your apps.
- It’s even more important for the Angular wrappers since they wrap the web components.
- For example, we couldn’t have an Angular wrapper named
jha-display-block
that wrapped a web component of the same name, since the browser wouldn’t know how to resolve two different components that have the same tag name. So instead, thejha-display-block
Angular wrapper wraps therui-display-block
web component, providing a built-in translation between the 2 component interfaces.
- The Angular wrappers prefix all component properties and events with “jha” to avoid collisions with other property names. The web components drop this prefix convention.
- For example, the button style property in the Angular
jha-button
component is namedjhaButtonStyle
, but the same property in therui-button
web component is simplybuttonStyle
. - This practice did result in a few naming collisions, but we stuck with it. For example, some of the Angular wrappers have a jhaTitle property. Removing the “jha” prefix results in that property being just “title”, which collides with the HTML “title” property (used to specify a tooltip for an element). In those cases, we had to change the property name more, so we used “titleText” for those properties in the web components.
- For example, the button style property in the Angular
- We were able to combine some related Angular wrappers into a single web component in some cases.
- For example, the Angular wrappers include separate components for toolbars, depending on their location:
jha-function-toolbar
,jha-function-toolbar-right
,jha-display-block-toolbar
,jha-display-block-toolbar-right,
andjha-group-box-toolbar
. - We were able to collapse that down to a single
rui-toolbar
component in the web components. You specify where that component displays using a separate “slot” property on the component.
- For example, the Angular wrappers include separate components for toolbars, depending on their location:
- Angular supports two-way binding for components that have properties whose state can change internally within the component, such as input elements. Two-way binding isn’t supported in JavaScript; it’s just a convention within pure Angular wrappers. So any web components that maintain state internally require 1 extra line of code to sync the component’s value with the backing value in your app.
As an example, here’s a snippet from a view using the Responsive UI Angular wrappers:
<jha-function-view jhaTitle="Record Detail">
<jha-function-toolbar-right>
<jha-button-group class="jha-hidden-phone" [(jhaValue)]="recordDetailSpacing"
[jhaOptions]="recordDetailSpacingOptions"
(jhaValueChange)="recordDetailSpacingChange()"></jha-button-group>
<jha-toolbar-separator class="jha-hidden-phone"></jha-toolbar-separator>
<jha-button jhaText="Record Detail Pattern" class="jha-hidden-phone" (jhaClick)="openPattern()">
</jha-button>
</jha-function-toolbar-right>
<jha-display-block>
<jha-record-detail [jhaIsFullWidth]="recordDetailContainerIsFullWidth">
<jha-record-detail-block [jhaWidth]="recordDetailSpacing">
<jha-record-detail-subheader jhaText="Customer Information"></jha-record-detail-subheader>
<jha-record-detail-field jhaLabel="Name">Nancy Davolio</jha-record-detail-field>
<jha-record-detail-field jhaLabel="SSN">111-22-3333</jha-record-detail-field>
<jha-record-detail-field jhaLabel="Birthdate">04/01/1976</jha-record-detail-field>
<jha-record-detail-field jhaLabel="Active Customer">Yes</jha-record-detail-field>
<jha-record-detail-field jhaLabel="Interest Rate">8.750%</jha-record-detail-field>
<jha-record-detail-field jhaLabel="Employer">Vandelay Industries</jha-record-detail-field>
<jha-record-detail-field jhaLabel="Gross Income">$65,000.00</jha-record-detail-field>
<jha-record-detail-field jhaLabel="Relationship Level">Silver</jha-record-detail-field>
</jha-record-detail-block>
And here’s a code snippet for the same view, using the Responsive UI web components:
<rui-function-view titleText="Record Detail">
<rui-toolbar slot="function-toolbar-right">
<rui-button-group [value]="recordDetailWidth"
(rui-button-group-value-change)="recordDetailWidth = $event.detail" class="rui-hidden-mobile">
<rui-button-group-option value="narrow">Narrow Width</rui-button-group-option>
<rui-button-group-option value="medium">Medium Width</rui-button-group-option>
<rui-button-group-option value="wide">Wide Width</rui-button-group-option>
<rui-button-group-option value="full">Full Width</rui-button-group-option>
</rui-button-group>
<rui-toolbar-separator context="function" class="rui-hidden-mobile"></rui-toolbar-separator>
<rui-button text="Record Detail Pattern" class="rui-hidden-mobile" (rui-click)="openPattern()">
</rui-button>
</rui-toolbar>
<rui-display-block>
<rui-record-detail [width]="recordDetailWidth">
<rui-record-detail-block>
<rui-record-detail-subheader text="Customer Information"></rui-record-detail-subheader>
<rui-record-detail-field labelText="Name">Nancy Davolio</rui-record-detail-field>
<rui-record-detail-field labelText="SSN">111-22-3333</rui-record-detail-field>
<rui-record-detail-field labelText="Birthdate">04/01/1976</rui-record-detail-field>
<rui-record-detail-field labelText="Active Customer">Yes</rui-record-detail-field>
<rui-record-detail-field labelText="Interest Rate">8.750%</rui-record-detail-field>
<rui-record-detail-field labelText="Employer">Vandelay Industries
</rui-record-detail-field>
<rui-record-detail-field labelText="Gross Income">$65,000.00</rui-record-detail-field>
<rui-record-detail-field labelText="Relationship Level">Silver</rui-record-detail-field>
</rui-record-detail-block>