Components
Note
Since version
0.1.0
of the framework, Skate / JSX componets are no longer available by default and we have switched to using lit html. The documentation below has been updated to reflect lit components but you can still see the old Skate docs here.
The fundamental building blocks of the framework are components. These are very similar to components in other frameworks like React and Vue. They should represent a small portion of your UI and should be composed together to create your application.
If you haven't worked with components before, then a good place to start reading up on how we view components is the following article:
https://reactjs.org/docs/thinking-in-react.html
Our components are very similar to React components and therefore the same theory can be applied when designing applications. With that in mind, a simple component will look like:
import { component, BaseLitComponent, TemplateResult, html } from "@commontimeltd/infinity-framework";
import { MyAppManager } from "client/manager";
@component({ tag: "wc-my-first-component", styles: [ require("./my-first-component.scss") ] })
export class MyFirstComponent extends BaseLitComponent<MyAppManager> {
async _init(): Promise<void> {}
_setupEventListeners(): void {}
componentMarkup(): TemplateResult {
return html`<p>This is my first component</p>`;
}
}
Lets break this down to explain everything.
Component Registration
@component({ tag: "wc-my-first-component", styles: [ require("./my-first-component.scss") ] })
The first line is decorator on our web component class
. If you've not seen decorators before, then here is some information to read up on. Our decorator is used to tell the framework that this class is a component.
Under the hood, our components are simply web components. Web components require a small amount of setup / registration which is handled by the decorator. The decorator is simply a function which takes one argument of an object
with the following properties:
tag
(string): This must be a hypen delimited string that becomes the DOM html node name. Valid examples are:wc-my-first-component
,my-first-component
. Invalid examples areWcMyFirstComponent
orFirstComponent
.styles
(string[]): A string that encapsulates all the CSS rules for the component. The framework is designed to userequire("./my-first-component.scss")
to import style rules from another file. You can have multiplerequire
s within the the array to import multiple stylesheet files, e.g.
@component({
tag: "wc-my-first-component",
styles: [
require("./my-first-component-stylesheet-1.scss"),
require("./my-first-component-stylesheet-2.scss"),
require("./my-first-component-stylesheet-3.scss")
]
})
shadow
(boolean)?: This is optional and by default is eithertrue
orfalse
depending on if the browser supports ShadowDOM or not. Generally, you should leave it like this unless you need to specifically supoort browsers that do not have ShadowDOM.
Class Definition
The next line is where we define our class:
export class MyFirstComponent extends BaseLitComponent<MyAppManager>
There isn't anything too interesting here other than we need to give our component class a name and ensure we are extending from BaseComponent<T>
. In this instance T
is a generic in Typescript which should be the type for the manager
in your application.
We'll go into more detail about this a little later.
Note
Generally, you should not extend from another class for a component. If you need to do something where your first instinct is inheritance, then be sure to read Component Composition first.
Component Initialisation
When a component is first loaded it goes through an initlisation period. This is where the component will call certain methods to set itself up and is a handy place to do stuff like bind events and load initial state (including via HTTP requests, etc).
To do this, there is a handy little method:
async _init(): Promise<void> {}
This is called fairly early in the component lifecycle and is an ideal place to set your component up. For example, lets say we need to bind some state to the component which needs to display as soon as the component can. We could then use this:
async _init(): Promise<void> {
this.myState = "Let's set some state";
}
This method will widely be used throughout your application for tasks such as this. You may have noticed that the method is also marked as async
. Therefore, you can also use await
inside your _init
for async loading of state, e.g.
async _init(): Promise<void> {
this.myState = await this.manager.rest.getSomeDataFromAnApi();
}
Event Listeners
The framework comes with an internal event model (more details here). Components have a dedicated method to setup any listeners:
_setupEventListeners(): void {}
This is called before _init
but is designed to be very quick (therefore, you should not be setting state or do anything complicated in this method). It's a quick, generic way to set some event listeners up like so:
_setupEventListeners(): void {
this.manager.on("my-first-event", () => alert("My First Event Fired!"));
this.manager.on("my-second-event", () => alert("My Second Event Fired!"));
}
Component Markup
The last standard part of a component is the componentMarkup
function. This is where you use Javascript template strings to describe how your component looks.
Our example:
componentMarkup(): TemplateResult {
return html`<p>This is my first component</p>`;
}
This is the simple component that will only render a paragraph tag. You can use almost any HTML tag inside the markup. Most components will follow a standard rule of returning only one node with everything else encapsulated. For example:
componentMarkup(): TemplateResult {
return html`
<div>
<p>This is my first component</p>
</div>
`;
}
You can also use any references to the class within your markup like so:
// Somewhere in _init:
this.name = "Nick";
//... in the markup:
html`<p>Hello, ${this.name}</p>`;
The curly braces allows you to call out to your code which includes the ability to call functions. This is useful when looping:
html`
<ul>
${
[1, 2, 3].map((x, idx) => <li>{ idx } index</li>)
}
</ul>
`;
Finally, you can also use other components in your markup:
html`
<div>
<p>Hello</p>
<wc-my-second-component></wc-my-second-component>
</div>
`;
Components Wrap Up
That's it for the basics of components. The next step is start exploring component properties and see how we can turn components into powerful blocks of functionality.