Skip to main content
Version: 0.1.x

Dynamic components

obsolete: these have been replaced with react components

Dynamic components are a new feature to SALVO-TS. They are components which can be created and mounted at runtime (i.e. by other components.)

There are two types of supported dynamic components:

  • Dynamic HTML components work like normal SALVO-TS components, except that they also specify a template which can be used to generate them at runtime.
  • Dynamic Vue components allow you to use the full power of the Vue framework in your component, with reactive data binding, Vue template syntax, and everything else.

Dynamic HTML Components

To define a Dynamic HTML component, extend BaseDynamicComponent and implement the template() method.

The template() method should return lit-html template, in which you can use to bind data or event handlers.

Dynamic components can call this.render() at any time to re-render themselves using the template. SALVO-TS will call render() automatically after ready() has run.

typescript
import {Component, BaseDynamicComponent, Event} from 'salvo-ts';
import {html} from 'lit-html';
import {classMap} from 'lit-html/directives/class-map';
@Component()
class AlertBanner extends BaseDynamicComponent {
open: boolean = false;
message: string = 'Example popover';
onDismiss: Event<void> = new Event();
template() {
const cssClasses = classMap({
'AlertBanner': true,
'AlertBanner--open': this.open,
})
return html`
<div class=${cssClasses}>
<div class="AlertBanner__Message">
${this.message}
</div>
<button class="AlertBanner__Dismiss" @click=${this.handleDismiss}>
Okay
</button>
</div>
`;
}
show(message: string) {
this.message= message;
this.open = true;
this.render();
}
hide() {
this.open = false;
this.render();
}
handleDismiss(e: ClickEvent) {
this.onDismiss.emit();
}
}
typescript
import {Component, BaseDynamicComponent, Event} from 'salvo-ts';
import {html} from 'lit-html';
import {classMap} from 'lit-html/directives/class-map';
@Component()
class AlertBanner extends BaseDynamicComponent {
open: boolean = false;
message: string = 'Example popover';
onDismiss: Event<void> = new Event();
template() {
const cssClasses = classMap({
'AlertBanner': true,
'AlertBanner--open': this.open,
})
return html`
<div class=${cssClasses}>
<div class="AlertBanner__Message">
${this.message}
</div>
<button class="AlertBanner__Dismiss" @click=${this.handleDismiss}>
Okay
</button>
</div>
`;
}
show(message: string) {
this.message= message;
this.open = true;
this.render();
}
hide() {
this.open = false;
this.render();
}
handleDismiss(e: ClickEvent) {
this.onDismiss.emit();
}
}
Important: Pre-render dynamic components server-side

A dynamic component's HTML template is not rendered until render() is called at runtime. This means it has no content before this point.

It has been omitted from this example for brevity, but you should always define a normal Liquid template for your dynamic component as well, to ensure that something is rendered by the server. (The only exceptions to this are components that are only expected to become visible at runtime anyway, like a toast alert.)

For SEO and performance reasons, you should try to make the static template look as close as possible to what the final dynamically-rendered component will look like.

Rendering a dynamic components should enhance it and provide more interactivity - the component should not be missing entirely until the render is called.

Using dynamic components

You can use a dynamic component anywhere in your project by using the theme.mount(component, parentEl) function.

typescript
import {Component, BaseComponent} from 'salvo-ts';
import AlertBanner from './AlertBanner';
@Component()
class CartComponent extends BaseComponent {
@Ref()
bannerContainer: HTMLElement;
alertBanner: null|AlertBanner;
customerDidSomethingWrong() {
if (!this.alertBanner) {
this.alertBanner = this.theme.mount(AlertBanner, this.bannerContainer);
this.alertBanner.onDismiss.subscribe(() => this.enableCheckout());
}
this.disableCheckout();
this.alertBanner.show(`Please don't do that`);
}
}
typescript
import {Component, BaseComponent} from 'salvo-ts';
import AlertBanner from './AlertBanner';
@Component()
class CartComponent extends BaseComponent {
@Ref()
bannerContainer: HTMLElement;
alertBanner: null|AlertBanner;
customerDidSomethingWrong() {
if (!this.alertBanner) {
this.alertBanner = this.theme.mount(AlertBanner, this.bannerContainer);
this.alertBanner.onDismiss.subscribe(() => this.enableCheckout());
}
this.disableCheckout();
this.alertBanner.show(`Please don't do that`);
}
}

When should I use a Dynamic Vue component instead of a Dynamic HTML component?

Dynamic Vue components are more powerful then Dynamic HTML components, but they can be more cumbersome to work with in Liquid.

// TODO