Note: All examples in this tutorial are taken from the Demo UI5 Web Component (ui5-demo
), generated by the package initialization script.
For more information on creating a new package with a demo web component inside, click here.
The main file, representing the web component is Demo.js
.
import UI5Element from "@ui5/webcomponents-base/dist/UI5Element.js";
import litRender from "@ui5/webcomponents-base/dist/renderer/LitRenderer.js";
import { fetchI18nBundle, getI18nBundle } from "@ui5/webcomponents-base/dist/i18nBundle.js";
// Template
import DemoTemplate from "./generated/templates/DemoTemplate.lit.js";
// Styles
import DemoCss from "./generated/themes/Demo.css.js";
import { PLEASE_WAIT } from "./generated/i18n/i18n-defaults.js";
const metadata = {
tag: "ui5-demo",
properties: {
},
slots: {
},
events: {
},
};
class Demo extends UI5Element {
constructor() {
super();
this.i18nBundle = getI18nBundle("my-ui5-web-components");
}
get pleaseWaitText() {
return this.i18nBundle.getText(PLEASE_WAIT);
}
static get metadata() {
return metadata;
}
static get render() {
return litRender;
}
static get template() {
return DemoTemplate;
}
static get styles() {
return DemoCss;
}
static async onDefine() {
await fetchI18nBundle("my-ui5-web-components");
}
}
Demo.define();
export default Demo;
Every UI5 Web Component must extend the base class UI5Element
, provided by the @ui5/webcomponents-base
package:
import UI5Element from "@ui5/webcomponents-base/dist/UI5Element.js";
class Demo extends UI5Element {
}
export default Demo;
Metadata is a JavaScript object, containing information about the public interface of a UI5 Web Component (tag name, properties, etc...).
Metadata is passed via the metadata
static getter:
import UI5Element from "@ui5/webcomponents-base/dist/UI5Element.js";
const metadata = {
tag: "ui5-demo",
properties: {
},
slots: {
},
events: {
},
};
class Demo extends UI5Element {
static get metadata() {
return metadata;
}
}
export default Demo;
So far, we've defined a ui5-demo
web component with no properties, slots or events.
For a complete reference to all metadata entities, click here.
UI5 Web Components are agnostic of the DOM render engine used. The standard UI5 Web Components (@ui5/webcomponents
, @ui5/webcomponents-fiori
, etc...)
however all use lit-html as the rendering technology of choice.
The render engine is defined via the render
static getter:
import UI5Element from "@ui5/webcomponents-base/dist/UI5Element.js";
import litRender from "@ui5/webcomponents-base/dist/renderer/LitRenderer.js";
const metadata = {
tag: "ui5-demo",
properties: {
},
slots: {
},
events: {
},
};
class Demo extends UI5Element {
static get metadata() {
return metadata;
}
static get render() {
return litRender;
}
}
export default Demo;
Here we import LitRenderer.js
from the @ui5/webcomponents-base
package which is a very tiny wrapper around lit-html
.
Now that we've defined the rendering technology of choice, we can pass a template in that technology's syntax.
This is done via the template
static getter:
import UI5Element from "@ui5/webcomponents-base/dist/UI5Element.js";
import litRender from "@ui5/webcomponents-base/dist/renderer/LitRenderer.js";
// Template
import DemoTemplate from "./generated/templates/DemoTemplate.lit.js";
const metadata = {
tag: "ui5-demo",
properties: {
},
slots: {
},
events: {
},
};
class Demo extends UI5Element {
static get metadata() {
return metadata;
}
static get render() {
return litRender;
}
static get template() {
return DemoTemplate;
}
}
export default Demo;
The standard UI5 Web Components use handlebars templates that are automatically converted
to lit-html
syntax by the build script.
If you have a Demo.hbs
file along with the Demo.js
file, the build script is going to create for you generated/templates/DemoTemplate.lit.js
file.
Therefore, we pass the content of this file to the template
static getter.
For more information on that, see the next chapter of this tutorial.
You can pass CSS to be inserted in the Shadow Root of the UI5 Web Component by using the styles
static getter:
import UI5Element from "@ui5/webcomponents-base/dist/UI5Element.js";
import litRender from "@ui5/webcomponents-base/dist/renderer/LitRenderer.js";
// Template
import DemoTemplate from "./generated/templates/DemoTemplate.lit.js";
// Styles
import DemoCss from "./generated/themes/Demo.css.js";
const metadata = {
tag: "ui5-demo",
properties: {
},
slots: {
},
events: {
},
};
class Demo extends UI5Element {
static get metadata() {
return metadata;
}
static get render() {
return litRender;
}
static get template() {
return DemoTemplate;
}
static get styles() {
return DemoCss;
}
}
export default Demo;
If you have a themes/Demo.css
file, the build script will automatically generate for you a generated/themes/Demo.css.js
file, which
in addition to your component's CSS, also contains contain definitions for all CSS Vars for the default theme. You can define your own CSS
vars for each theme in the respective theme's directory in themes/
:
File | Descriptions |
---|---|
themes/sap_belize/parameters-bundle.css |
Values for the component-specific CSS Vars for the sap_belize theme. |
themes/sap_belize_hcb/parameters-bundle.css |
Values for the component-specific CSS Vars for the sap_belize_hcb theme. |
themes/sap_fiori_3/parameters-bundle.css |
Values for the component-specific CSS Vars for the sap_fiori_3 theme. |
themes/sap_fiori_3_dark/parameters-bundle.css |
Values for the component-specific CSS Vars for the sap_fiori_3_dark theme. |
For more on that, see the CSS chapter of this tutorial.
Defining a Web Component is necessary in order to register it in the browser.
This is done by calling the UI5Element.define
static method:
import UI5Element from "@ui5/webcomponents-base/dist/UI5Element.js";
import litRender from "@ui5/webcomponents-base/dist/renderer/LitRenderer.js";
// Template
import DemoTemplate from "./generated/templates/DemoTemplate.lit.js";
// Styles
import DemoCss from "./generated/themes/Demo.css.js";
const metadata = {
tag: "ui5-demo",
properties: {
},
slots: {
},
events: {
},
};
class Demo extends UI5Element {
static get metadata() {
return metadata;
}
static get render() {
return litRender;
}
static get template() {
return DemoTemplate;
}
static get styles() {
return DemoCss;
}
}
Demo.define();
export default Demo;
There are generally 3 steps to that purpose:
-
Fetch an i18n bundle during component definition
await fetchI18nBundle("my-ui5-web-components");
-
(optional) Get a reference to the bundle in the constructor for convenience
this.i18nBundle = getI18nBundle("my-ui5-web-components");
-
Get texts from the bundle, according to the currently configured language.
return this.i18nBundle.getText(PLEASE_WAIT);
The fetchI18nBundle
and getI18nBundle
methods are provided by the i18nBundle.js
module from the @ui5/webcomponents-base
package.
So the final source code is:
import UI5Element from "@ui5/webcomponents-base/dist/UI5Element.js";
import litRender from "@ui5/webcomponents-base/dist/renderer/LitRenderer.js";
import { fetchI18nBundle, getI18nBundle } from "@ui5/webcomponents-base/dist/i18nBundle.js";
// Template
import DemoTemplate from "./generated/templates/DemoTemplate.lit.js";
// Styles
import DemoCss from "./generated/themes/Demo.css.js";
import { PLEASE_WAIT } from "./generated/i18n/i18n-defaults.js";
const metadata = {
tag: "ui5-demo",
properties: {
},
slots: {
},
events: {
},
};
class Demo extends UI5Element {
constructor() {
super();
this.i18nBundle = getI18nBundle("my-ui5-web-components");
}
get pleaseWaitText() {
return this.i18nBundle.getText(PLEASE_WAIT);
}
static get metadata() {
return metadata;
}
static get render() {
return litRender;
}
static get template() {
return DemoTemplate;
}
static get styles() {
return DemoCss;
}
static async onDefine() {
await fetchI18nBundle("my-ui5-web-components");
}
}
Demo.define();
export default Demo;
Please note that here we use the onDefine
method of UI5Element
in order to ensure that i18n resources have been fetched
before the web component has been defined. The used namespace for resource registration (in this example my-ui5-web-components
)
is the name
property of your package.json
file.
The template of the web component is in the Demo.hbs
file.
In this particular example it looks like this:
<div>This is: ui5-demo. {{pleaseWaitText}}</div>
The context in the template is the web component instance, therefore you can directly use any properties/getters on the object.
Here we see the pleaseWaitText
getter, defined in the previous step.
As explained above, the .hbs
file is transformed by the build script to a .js
file in the lit-html
syntax. Namely this file
is provided to the web component class.
Let's inspect the following files (one with CSS declarations, the others with CSS Vars values for the themes).
File | Purpose |
---|---|
themes/Demo.css |
All CSS rules for the web component, same for all themes. Will be inserted in the shadow root. |
themes/sap_belize/parameters-bundle.css |
Values for the component-specific CSS Vars for the sap_belize theme. |
themes/sap_belize_hcb/parameters-bundle.css |
Values for the component-specific CSS Vars for the sap_belize_hcb theme. |
themes/sap_fiori_3/parameters-bundle.css |
Values for the component-specific CSS Vars for the sap_fiori_3 theme. |
themes/sap_fiori_3_dark/parameters-bundle.css |
Values for the component-specific CSS Vars for the sap_fiori_3_dark theme. |
In the Demo.css
file we have:
:host {
border: 2px solid var(--ui5-demo-border-color);
background-color: var(--sapBackgroundColor);
color: var(--sapTextColor);
}
in addition to other selectors. The CSS vars, starting with --sap
are standard and provided by the framework.
All the rest are custom for the specific web component.
Respectively the definitions file for, let's say sap_fiori_3
, contains:
:root {
--ui5-demo-border-color: green;
}
What's important to understand here is that you author all the .css
files, listed in the table above, but the build script
generates from them a single .js
file for you, and this is namely the file you pass to the web component class: generated/themes/Demo.css.js
.