最新消息:Welcome to the puzzle paradise for programmers! Here, a well-designed puzzle awaits you. From code logic puzzles to algorithmic challenges, each level is closely centered on the programmer's expertise and skills. Whether you're a novice programmer or an experienced tech guru, you'll find your own challenges on this site. In the process of solving puzzles, you can not only exercise your thinking skills, but also deepen your understanding and application of programming knowledge. Come to start this puzzle journey full of wisdom and challenges, with many programmers to compete with each other and show your programming wisdom! Translated with DeepL.com (free version)

javascript - Vanilla Custom Element repeater for <option>, <li>, <td> - Stack Overflow

matteradmin7PV0评论

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

Here's my current code:

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

<select name="" id="">
    <pany-repeat datas='[{"name": "NameValeur", "value": "valeurId"}, {"name": "NameObject", "value": "objectId"}]'>
        <option value="${value}">${name}</option>
    </pany-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 pany-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('pany-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.

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

Here's my current code:

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

<select name="" id="">
    <pany-repeat datas='[{"name": "NameValeur", "value": "valeurId"}, {"name": "NameObject", "value": "objectId"}]'>
        <option value="${value}">${name}</option>
    </pany-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 pany-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('pany-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.

Share Improve this question edited Jul 28, 2018 at 22:27 Supersharp 31.3k11 gold badges102 silver badges147 bronze badges asked Mar 29, 2017 at 7:00 LoïcRLoïcR 5,0491 gold badge36 silver badges53 bronze badges
Add a ment  | 

1 Answer 1

Reset to default 8

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./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.

Post a comment

comment list (0)

  1. No comments so far