Skip to content

View Class

The View class supports tools necessary for HTML templating, HTMLElement creation, and event handling, and is used when creating UI components.

Definition

typescript
import { View } from 'rune-ts';

interface SwitchData {
  on: boolean;
}

class SwitchView extends View<SwitchData> {
  override template() {
    return html`
      <button class="${this.data.on ? 'on' : ''}">
        <div class="toggle"></div>
      </button>
    `;
  }
}

Create

new (data: T) => View<T>;

The passed data is registered as this.data and passed to the template() method like this.template(data: T);.

typescript
new SwitchView({ on: false });

template()

protected template(): Html;

Inside the template method, use html to create an HTML template. (More on Template API)

typescript
import { View, html } from 'rune-ts';

class DessertView extends View<{ name: string; rating: number }> {
  override template() {
    return html`
      <div>
        <div class="name">${this.data.name}</div>
        <div class="rating">${this.data.rating}</div>
      </div>
    `;
  }
}

toHtml()

public toHtml(): string;

typescript
const dessertView = new DessertView({ name: 'Choco', rating: 2.8 });
dessertView.toHtml();
html
<div class="DessertView">
  <div class="name">Choco</div>
  <div class="rating">2.8</div>
</div>

data

public data: T;

typescript
dessertView.data.name = 'Latte';
dessertView.data.rating = 3.5;
dessertView.toHtml();
html
<div class="DessertView">
  <div class="name">Latte</div>
  <div class="rating">3.5</div>
</div>

render()

public render(): HTMLElement;

Internally, it creates an HTML string using this.template(this.data) and returns the created HTMLElement stored in this._element.

typescript
const element: HTMLElement = dessertView.render();
// div.DessertView

element()

public element(): HTMLElement;

Returns the already created HTMLElement.

typescript
const element: HTMLElement = dessertView.element();
// div.DessertView

isRendered()

public isRendered(): boolean;

isRendered() checks if an HTMLElement has been created inside the View. It's useful for differentiating code that should only run in a rendered state.

renderCount

public renderCount: number;

Counts how many times the template() function has been executed internally. This property can be utilized to implement lazy rendering by defining the template() function to only partially render.

hydrateFromSSR()

public hydrateFromSSR(element: HTMLElement): this;

Rehydrates a View using the same data that was used to render an HTML-drawn HTMLElement. (Tutorial - Solo Component SSR)

typescript
// Server Side
new ProductView({
  name: 'Phone Case',
  price: 13,
}).toHtml();

// Client Side
new ProductView({
  name: 'Phone Case',
  price: 13,
}).hydrateFromSSR(document.querySelector('.ProductView')!);

redraw()

public redraw(): this;

Redraws the View object according to its current data state. The default behavior is to update the outermost element's html attributes, and change the interior with innerHTML. Developers can optimize the redraw function by overriding it.

typescript
class PhotoView extends View<{ src: string; alt: string }> {
  override template({ src, alt }) {
    return html`<div><img src="${src}" alt="${alt}" /></div>`;
  }

  override redraw() {
    const img = this.element().querySelector('img')!;
    img.setAttribute('src', this.data.src);
    img.setAttribute('alt', this.data.alt);
  }
}

The above code can be written more concisely using Rune's DOM manipulation helper class $Element.

typescript
override redraw() {
  $(this.element()).find('img')!.setAttributes(this.data);
}

subView()

protected subView<T extends ViewConstructor>(
  SubView: T,
  selector?: string
): InstanceType<T> | null;

Returns the first subView created within the template() method that matches the constructor passed as an argument. The optional second argument, selector,

allows for further querying conditions using a CSS Selector.

typescript
class ProductView extends View<Product> {
  override template(product: Product) {
    return html`
      <div>
        ${new PhotoView({ src: product.thumbnail, alt: product.name })}
        <div class="name">${product.name}</div>
        <div class="price">$${product.price}</div>
      </div>
    `;
  }

  override onRender() {
    console.log(this.subView(PhotoView)!.data.src);
  }
}

subViews()

protected subViews<T extends ViewConstructor>(
  SubView: T,
  selector?: string
): InstanceType<T>[];

Returns an array of subViews.

subViewIn()

protected subViewIn<T extends ViewConstructor>(
  selector: string,
  SubView: T
): InstanceType<T> | null;

Returns a single subView drawn inside a parent element found using the selector.

subViewsIn()

protected subViewsIn<T extends ViewConstructor>(
  selector: string,
  SubView: T,
): InstanceType<T>[];

Returns an array of subViews drawn inside a parent element found using the selector.

redrawOnlySubViews()

protected redrawOnlySubViews(): this;

Iterates through subViews and executes their redraw().

chain()

chain(f: (this: this, view: this) => void): this;

typescript
view.chain((view) => view.data.quantity++).redraw();
typescript
view.chain((view) => view.data.quantity++).redraw();

safely()

safely(f: (this: this, view: this) => void): this;

typescript
safely(f: (this: this, view: this) => void): this {
  return this.isRendered() ? this.chain(f) : this;
}

The implementation of safely is as described above. Use it to chain code that should only operate in a rendered state.

toString()

Returns the class name of the View.

typescript
new MyView('hi').toString();
// MyView

onRender()

Executes immediately after the View's element is created.

onMount()

Executes right after the View is added to document.body.

onUnmount()

Executes right after the View is removed from document.body.

Event Handling

The View class inherits event handling methods from the Base class. (API - Event Handling)