Extends existing flow component (for template usage)

Hi!
It’s not clear how to extends existing flow component (for example - grid) so that it can be used in templates (!).
Should it have its own tag in this case? What should be the frontend for this case?

@Tag("my-grid")
@HtmlImport("frontend://ui/components/my-grid.html")
public class MyGrid<T> extends Grid<T> {

    public MyGrid() {
        super();
        ...
    }
}

Yeah, basically you need a new custom element/tag for this.

On the client side you can also extend the <vaadin-grid> element.

class MyGrid extends Vaadin.GridElement {
  constructor() {
    super();
	...
  }
}

window.customElements.define('my-grid', MyGrid);

class MyGrid extends Vaadin.GridElement {
constructor() {
super();

}
}

Great, it worked! Thanks!
Before, I declared the client class in the same way as in a template usage examples (with define “static get is()”)… and this way didn’t work.

Is it possible to make an injection of styles right there or through “custom styles” / “global styles” only?

<link rel="import" href="../bower_components/vaadin-grid/vaadin-grid.html">
<dom-module id="styled-grid">
  <template>
    <style>
      styled-grid {
        /*border: none;*/
      }
      styled-grid [part="row"]
 [part~="header-cell"]
 {
        border-top: 0;
        --lumo-base-color: #e0e8f0;
      }
      styled-grid [part="row"]
 [part~="header-cell"]
[colspan]
:not([colspan="1"]
) {
        border-bottom: var(--_lumo-grid-border-width) solid var(--_lumo-grid-secondary-border-color);
      }
    </style>
  </template>
  <script>
      class StyledGrid extends Vaadin.GridElement {
          constructor() {
              super();
          }
      }
      window.customElements.define('styled-grid', StyledGrid);
  </script>
</dom-module>

I think if you use static get is() the styles (and possibly the template even) are the thing that will stop working.

You can include your style module yourself if you want to, instead of letting the ThemableMixin do it for you (by using <dom-module theme-for="vaadin-grid">. That way you can inject the styles only to your custom grid instead of all vaadin-grid instances.

Here’s the gist of it:

<link rel="import" href="../bower_components/vaadin-grid/vaadin-grid.html">
<dom-module id="styled-grid">
  <template>
    <style>
      :host {
        /*border: none;*/
      }
      [part="row"]
 [part~="header-cell"]
 {
        border-top: 0;
        --lumo-base-color: #e0e8f0;
      }
      [part="row"]
 [part~="header-cell"]
[colspan]
:not([colspan="1"]
) {
        border-bottom: var(--_lumo-grid-border-width) solid var(--_lumo-grid-secondary-border-color);
      }
    </style>
  </template>
  <script>
      class StyledGrid extends Vaadin.GridElement {
		  static get template() {
			let template = super.template;
			const styleEl = document.createElement('style');
			styleEl.setAttribute('include', 'styled-grid');
			template.content.appendChild(styleEl);
			return template;
		  }
      }
      window.customElements.define('styled-grid', StyledGrid);
  </script>
</dom-module>

Not tested, beware :slight_smile: But basically you should include <style include="styled-grid"> into the template of your custom grid. The exact techique for that could be different from what I proposed.

Edit: added the missing return template;

Yes, this is a good way for cases where only an injection of style. But existing polymer docs recommend cloning the element template. Something like this:

<link rel="import" href="../../bower_components/vaadin-text-field/vaadin-text-field.html">
<link rel="import" href="../../bower_components/iron-icon/iron-icon.html">
<link rel="import" href="../../bower_components/vaadin-icons/vaadin-icons.html">

<dom-module id="search-text-field">
  <template id="shadow">
    <style>
      ...
    </style>
  </template>

  <script>
      { // Brackets are used to hide the savedTemplate.
          let savedTemplate;

          class SearchTextField extends Vaadin.TextFieldElement {
              static get is() {
                  return 'search-text-field'
              }

              static get template() {
                  if (!savedTemplate) {
                      savedTemplate = Vaadin.TextFieldElement.template.cloneNode(true);
// -- For append all template as is. --
//                      let templ = Polymer.DomModule.import(this.is, 'template#shadow');
//                      if (templ) {
//                          savedTemplate.content.appendChild(templ.content);
//                      }
                      // Append style with include. For styling only. 
                      const styleEl = document.createElement('style');
                      styleEl.setAttribute('include', this.is);
                      savedTemplate.content.appendChild(styleEl);
                  }
                  return savedTemplate; // Измененный родительский шаблон.
              }
          }
          customElements.define(SearchTextField.is, SearchTextField);
      }
  </script>
</dom-module>

The java code for this template:

package app.ui.components;

import com.vaadin.flow.component.Tag;
import com.vaadin.flow.component.dependency.HtmlImport;
import com.vaadin.flow.component.html.Div;
import com.vaadin.flow.component.icon.VaadinIcon;
import com.vaadin.flow.component.textfield.TextField;

@Tag("search-text-field")
@HtmlImport("frontend://ui/components/search-text-field.html")
public class SearchTextField extends TextField {

    public SearchTextField(String label, ValueChangeListener<? super ComponentValueChangeEvent<TextField, String>> listener) {
        super(label, listener);
        init();
    }

    @SuppressWarnings("unchecked")
    private void init() {
        Div clear = new Div();
        clear.getElement()
                .setAttribute("part", "clear-button")
                .setAttribute("role", "button")
                .setAttribute("aria-label", "Clear");
        clear.addClickListener(e -> this.clear());

        setPrefixComponent(VaadinIcon.SEARCH.create());
        setSuffixComponent(clear);
    }
}

Multiple templates usage for styles and slots injection:

<dom-module id="search-text-field">
  <template id="shadow">
    <style>
      ...
    </style>
  </template>

  <template id="body">
    <iron-icon icon="vaadin:search" slot="prefix"></iron-icon>
    <div part="clear-button" aria-label="Clear" role="button" slot="suffix"></div>
  </template>

  <script>
      {
          let savedTemplate;
          let savedBodyTemplate;

          class SearchTextField extends Vaadin.TextFieldElement {
              static get is() { return 'search-text-field' }
              static get template() {
                  if (!savedTemplate) {
                      savedTemplate = Vaadin.TextFieldElement.template.cloneNode(true);
                      let temp = Polymer.DomModule.import(this.is, 'template#shadow');
                      if (temp) savedTemplate.content.appendChild(temp.content);
                  }
                  if (!savedBodyTemplate) savedBodyTemplate = Polymer.DomModule.import(this.is, 'template#body');
                  return savedTemplate;
              }

              ready() {
                  super.ready();
                  if (savedBodyTemplate) this.appendChild(savedBodyTemplate.cloneNode(true).content);
              }
          }
          customElements.define(SearchTextField.is, SearchTextField);
      }
  </script>
</dom-module>

But I dont know how to add link between “onclick” and java method call (@Id and @EventHandler works for extends PolymerTemplate only?).