Giter VIP home page Giter VIP logo

vaadin-select's People

Contributors

abdonrd avatar artur- avatar azorlogh avatar diegocardoso avatar haprog avatar jouni avatar limonte avatar manolo avatar marcushellberg avatar platosha avatar samiheikki avatar shadikhani avatar sohrabtaee avatar tomalec avatar tomivirkki avatar vicsstar avatar web-padawan avatar yuriy-fix avatar

Watchers

 avatar

vaadin-select's Issues

Document renderer patterns

Need to document any difficulties that you may encounter while using selects with a non-static (potentially changing, potentially loaded from an api) list of items.

For all of the examples below, imagine that we have a basic component that loops over an array, someProperty, to produce a list of <vaadin-items>

class MyElement extends LitElement {
  @property someProperty: String[];

  render(): TemplateResult {
    return html`...`;
  }

  renderItems: TemplateResult[] {
    return someProperty.map(item => {
      return html`<vaadin-item>item</vaadin-item>`;
    });
  }
}

Issue 1 - Binding render FNs

Typically, our goal will be to bind a render FN once and only once in the constructor. Binding to "this" is needed in some cases where render fns must consult / access the host element.

private boundSelectRenderer: Function;

constructor() {
  super();
  this.boundSelectRenderer = (root) => this.selectRenderer.(root);
}

render(): TemplateResult {
  return html`<cpsi-select .renderer="${this.boundSelectRenderer}">`
}

selectRenderer(root: HTMLElement): void {
  render(html`<vaadin-list-box>${this.renderItems()}</vaadin-list-box>`, root);
}

However, this can cause the select to "flicker" and force itself to close on the first render

bind-renderer-once-only

Instead, at odds with our general goal of binding once, here we have to bind at least each time the properties that impact the render FN are changed. I think we could bind in every render cycle, but that would be wasteful. We can use the lit-html guard directive to only bind the renderer when certain properties that impact rendering are changed

render(): TemplateResult {
  return html`
     <cpsi-select
       .renderer="${guard([this.someProperty], () => (root) => this.selectRenderer(root))}">
   `;
}

selectRenderer(root: HTMLElement): void {
  render(html`<vaadin-list-box>${this.renderItems()}</vaadin-list-box>`, root);
}

Issue 2 - implementation details of render fn

Bad impl -- simple render into root

Ideally, our renderer would be quite simple... something like this:

  private selectRenderer(root: HTMLElement): void {
    const listbox: TemplateResult = html`
       <vaadin-list-box>${this.renderItems()}</vaadin-list-box>
    `;
    render(listbox, root);
  }

Problem(s) with this impl

  • any updates to items / properties relevant to rendering produces an empty list

render-root-lit

Bad impl -- conditional render into root or existing listbox

Previously, some research led me to conclude that you must conditionally render into root: if a listbox was previously rendered into root, then you must render into the listbox. So we can modify the example above a bit to have an if-else

  private selectRenderer(root: HTMLElement): void {
    if (root.firstElementChild) {
      // render items into existing listbox
      const listbox: HTMLElement = root.firstElementChild as HTMLElement;
      render(this.renderItems(), listbox);
    } else {
       // render listbox and items into root
      const listbox: TemplateResult = html`
         <vaadin-list-box>${this.renderItems()}</vaadin-list-box>
      `;
      render(listbox, root);
    }
  }

Problem(s) with this impl

  • flicker on initial opening
  • any updates to items / properties relevant to rendering produces an empty list

conditional-render-root-lit

Good impl

The successful implementation has none of the problems from the previous example, but it is effectively the same. I'm not quite sure why this has none of the issues from the previous example - in theory they should be similar. I expect that some lit-html caching is playing a role. In this example, we always renderItems into the listbox and - if needed, the listbox is appended to root. We re-use previously created listbox instance

  private selectRenderer(root: HTMLElement): void {
    if (root.firstElementChild) {
      const listbox: HTMLElement = root.firstElementChild as HTMLElement;
      render(this.renderItems(), listbox);
    } else {
      const listbox: HTMLElement = document.createElement('vaadin-list-box');
      render(this.renderItems(), listbox);
      root.appendChild(listbox);
    }
  }

no-guard-items

This works, but here too we will make use of the guard directive to avoid mapping items via renderItems when we don't need to.. you can note in the gif below that extra rendering of items have been reduced. lit-html is intelligent about dom updates.. so just because we rendered / mapped items into TemplateResults - that does not mean dom updates will actually be made. This is just a micro-optimization

  private selectRenderer(root: HTMLElement): void {
    if (root.firstElementChild) {
      gaurd([this.someProperty], () => {
        const listbox: HTMLElement = root.firstElementChild as HTMLElement;
        render(this.renderItems(), listbox);
      });
    } else {
      // initial listbox should always have items rendered into it
      const listbox: HTMLElement = document.createElement('vaadin-list-box');
      render(this.renderItems(), listbox);
      root.appendChild(listbox);
    }
  }

guard-items

Summary

Putting it all together --

  • Bind renderer in each render cycle - perhaps optimize via guard
  • Selectively render either into root or root.firstElementChild
  • Do not render into root via lit-html - render into a listbox and use dom apis to append the listbox to root as needed

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.