Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Vanilla Custom Element repeater for <option>, <li>, <td>

I'm currently trying to implement a repeater WebComponent to allow the company to easily create front-end without depending on any framework (decision took by architecture).

Here's my current code:

<ul>
    <company-repeat datas='[{"name": "NameValeur", "value": "valeurId"}, {"name": "NameObject", "value": "objectId"}]'>
        <li>${name}</option>
    </company-repeat>
</ul>

<select name="" id="">
    <company-repeat datas='[{"name": "NameValeur", "value": "valeurId"}, {"name": "NameObject", "value": "objectId"}]'>
        <option value="${value}">${name}</option>
    </company-repeat>
</select>

The list is rightly working since it seems to have no limitation on which tag allowed inside, but the select is not allowing the customElement company-repeat in it and by extension, break the feature and just display <option value="${value}">${name}</option>

Here's the source code of my WebComponent

class CompanyRepeater extends HTMLElement {
    connectedCallback() {
        this.render();
    }

    render() {
        let datas = JSON.parse(this.getAttribute('datas'));
        let elementType = this.getAttribute('element');
        this.template = this.innerHTML;
        console.log(elementType);

        let htmlContent = elementType !== null ? `<${elementType.toLowerCase()}>` : '';

        datas.forEach(elem => {
            htmlContent += this.interpolate(this.template, elem)}
        );

        htmlContent += elementType !== null ? `</${elementType.toLowerCase()}>` : '';

        this.innerHTML = htmlContent;
    }

    interpolate(template, obj) {
        for(var key in obj) {
            const pattern = "${" + key + "}";

            if(template.indexOf(pattern) > -1) {
                template = template.replace(pattern, obj[key]);
                delete(obj[key]);
            }
        };

        return template;
    }
}

customElements.define('company-repeat', CompanyRepeater);

My question now is, how can I make it work, no matter what's the parent element? I've added a property element to my repeater, but it's not allowing me to declare more attribute, and it'll stick not work inside a table.

This is the only thing to prevent me from moving everything to WebComponent.

like image 604
LoïcR Avatar asked Mar 29 '17 07:03

LoïcR


People also ask

How to use the repeater in WordPress form builder?

Formidable Forms is the best WordPress Form Builder plugin. Get it for free! With the Repeater, you can choose the layout you would like for the fields within that section. To set the layout, go into the Field Options setting and select the Repeat Layout that you would like. The Inline format will display all the fields in the Repeater on one row.

How do I customize the Order of the repeater buttons?

To customize the order of the repeater buttons, go to Formidable → Styles → Custom CSS page and add the following CSS. Since section headings can't be used inside a repeater, you could create a two-column layout by following the steps below:

How do I add repeater controls in Elementor pro?

In Elementor Pro, in the Form Integration mechanism, we used a different method for adding repeater controls. Instead of using the add_control method for each nested control of the repeater, we added an array of controls directly into the 'fields' arguments.

What is the ADD_control () method of a repeater?

The Repeater’s add_control () method is a slightly modified version of the “regular” add_control () method used in all Elementor widgets, but it accepts the exact same parameters. Please note that the instance you created of the repeater class does NOT create the repeater control itself.


1 Answers

Solution 1

Put the repeater around your elements. Ex. for a minimal <data-repeater> custom element :

customElements.define('data-repeater', class extends HTMLElement 
{
  connectedCallback() 
  {
    const parent = this.firstElementChild
    const data = JSON.parse(this.dataset.values)

    const interpolate = obj => parent.innerHTML.replace(
      /\${(\w+)}/g,
      (match, key) => obj[key]
    )

    parent.innerHTML = data.map(interpolate).join('')
  }
})
<data-repeater data-values='[{"label": "Item 1", "id":1}, {"label": "Item 2", "id": 2}]'>
  <ul>
    <li id="${id}">${label}</li>
  </ul>
</data-repeater>

<data-repeater data-values='[{"name": "option 1", "value":1}, {"name": "option 2", "value": 2}]'>
  <select>
      <option value="${value}">${name}</option>
  </select>
</data-repeater>

Solution 2

Use customized built-in elements. You need to choose a new name for each standard element you want to extend, but you can reuse internally a unique base class to render the elements:

<select is="repeat-option" data-values="[...]">
   <option value="${value}">${name}</option>
</select>

customElements.define('select-repeater', class extends HTMLSelectElement {
  connectedCallback() { render(this) }
}, { extends: 'select' })

customElements.define('ul-repeater', class extends HTMLUListElement {
  connectedCallback() { render(this) }
}, { extends: 'ul' })

function render(view) {
  const data = JSON.parse(view.dataset.values)

  const interpolate = obj => view.innerHTML.replace(
    /\${(\w+)}/g,
    (match, key) => obj[key]
  )

  view.innerHTML = data.map(interpolate).join('')
}
<script src="https://rawgit.com/WebReflection/document-register-element/master/build/document-register-element.js"></script>


<ul is="ul-repeater" data-values='[{"label": "Item 1", "id":1}, {"label": "Item 2", "id": 2}]'>
    <li id="${id}">${label}</li>
</ul>


<select is="select-repeater" data-values='[{"name": "option 1", "value":1}, {"name": "option 2", "value": 2}]'>
		<option value="${value}">${name}</option>
</select>

If the rendering is very different depending on the element you could decide to create a class for rendering and to use derived classes for each type fo rendering ( select, ul, tr, td ), like in this example for tables.

like image 51
Supersharp Avatar answered Nov 14 '22 21:11

Supersharp