Skip to content
This repository has been archived by the owner on Sep 16, 2023. It is now read-only.
/ generic Public archive

Wesib: Generic Components

License

Notifications You must be signed in to change notification settings

wesib/generic

Repository files navigation

Wesib: Generic Components

NPM Build Status Code Quality Coverage GitHub Project API Documentation

Component Shares

Shares allow enclosing component to share and update some data with nested ones.

To share something:

  1. Create a Share class (or its subclass) instance.

    import { Share } from '@wesib/generic';
    
    const MyShare = new Share<MyData>('my-share');
  2. Amend enclosing component's property containing a data to share with @Shared() decorator:

    import { Shared } from '@wesib/generic';
    import { Component } from '@wesib/wesib';
    
    @Component('my-container')
    class MyContainerComponent {
      @Shared(MyShare)
      mySharedData?: MyData;
    
      // Update the `mySharedData` property when the data is ready to be shared.
    }
  3. Consume the shared data in nested component.

    import { AfterEvent } from '@proc7ts/fun-events';
    import { Component, ComponentContext } from '@wesib/wesib';
    
    @Component('my-nested')
    class MyNestedComponent {
      readonly myContainerData: AfterEvent<[MyShare?]>;
    
      constructor(context: ComponentContext) {
        this.myContainerData = MyShare.valueFor(context);
      }
    
      // Consume shared data when it is reported by `myContainerData`.
    }

The sharing mechanism works despite sharer and consumer components instantiation order.

Buffered Fragment Rendering

Buffered rendering is useful when there is a lot of content to add to the page. This content may even contain nested components.

For the sake of performance, it is reasonable to add such content to DocumentFragment first, initialize nested components, and then add already rendered fragment to the document. E.g. using requestAnimationFrame.

A @RenderFragment() amendment (and decorator) handle this:

import { Attribute, Component } from '@wesib/wesib';
import { FragmentRendererExecution, RenderFragment } from '@wesib/generic';

@Component('my-list')
class MyList {
  /**
   * Declares `item-list` attribute.
   *
   * Contains comma-separated `name=value` pairs.
   */
  @Attribute()
  itemList: string = '';

  /**
   * Renders item list.
   *
   * This method will be called each time the `item-list` attribute updated.
   */
  @RenderFragment()
  render({ content }: FragmentRendererExecution) {
    // `content` is a `DocumentFragment` instance.
    // This method fills this `content` on each call.
    // The `content` will be added to the document at proper time.
    const ul = content.appendChild(document.createElement('ul'));

    for (const item of this.itemList.split(',')) {
      const [name, value] = item.split(item, '=');
      const li = ul.appendChild(document.createElement('li'));

      // Each `my-item` component will be settled after this method call, and _before_ it is added to the document.
      // If `my-item` renders its own content, this content will be added to this component's `content` fragment,
      // rather directly to document.
      const link = li.appendChild(document.createelement('my-item'));

      link.setAttribute('name', name);
      link.setAttribute('value', value);
    }
  }
}

HTTP Fetch

An HttpFetch is a function available is bootstrap context. It resembles the Fetch API, but returns an OnEvent event sender, that sends a Response event(s), rather a promise resolving to the one.

An HttpFetchAgent can be provided in bootstrap context to intercept HTTP requests. E.g. to modify the request and/or response.