Create a Custom Element with different sub-types

前端 未结 2 954
长发绾君心
长发绾君心 2020-12-11 08:38

I am currently implementing a data-table element using custom elements (web components). The table can have different types of cells (text, number, date, etc) that are used

相关标签:
2条回答
  • 2020-12-11 09:08

    What could be done is using <td> Customized Built-in Element:

    <table is="data-table>
       <tr>
           <td is="data-string">Bob</td>
           <td is="data-date">11/1/2017</td>
           <td is="data-number">44<td>
       </tr>
    </table>
    

    All extensions share the same prototype ancestor. Example:

    //common cell
    class DataCell extends HTMLTableCellElement {...}
    
    //typed cell
    class StringCell extends DataCell {
        renderContent() { ... }
    } 
    customElements.define( 'data-string', StringCell, { extends: 'td' } )
    

    This way all cells extend the same <td> element, share a common prototype but have their own method implementations.

    You can override shared method, and shared method can call specific method of the derived prototype object.

    See a running example here:

    //table
    class DataTable extends HTMLTableElement {
        constructor() { 
            super()
            console.info( 'data-table created' )
        }
    } 
    customElements.define( 'data-table', DataTable, { extends: 'table' } );
    
    //cell
    class DataCell extends HTMLTableCellElement {
        connectedCallback() { 
            console.info( 'cell connected' )
            if ( typeof this.renderContent === 'function' ) 
                this.renderContent()
        }
    } 
    
    //cell string
    class StringCell extends DataCell {
        renderContent()
        {
            console.info( 'data-string render' )
            this.innerHTML = '"' + this.textContent.trim() + '"'
        }
    } 
    customElements.define( 'data-string', StringCell, { extends: 'td' } )
    table {
        border-collapse: collapse ;
    }
    td, th {
        border: 1px solid gray ;
        padding: 2px
    }
    <h4>Test Table Extension v1</h4>
    <table is="data-table">
        <tr>
            <th>Id      <th>Name    <th>Age
        <tr>    
            <td>1       <td is="data-string">An      <td>20
        <tr>
            <td>2       <td is="data-string">Bob     <td>31

    Note: If you don't want type extension you can also do it with custom tags. The idea is to have a common prototype and different custom elements that share it (thanks to standard prototype inheritance).

    0 讨论(0)
  • 2020-12-11 09:26

    NB: This answer is separeted from the other as it's quite extensive by itself and totally independant.

    If you use Autonomous Custom Elements (i.e. custom tags) with a (optionnal) type attribute:

    <data-table>
        <data-row>    
            <data-cell>1</data-cell>       
            <data-cell type="string">An</data-cell>
            <data-cell type="number">20</data-cell>
        </data-row>
    </data-table>
    

    ...you could use the MVC pattern:

    • define a class for the generic cell View (and/or Model)
    • define a subclass for the specialized Views (Date, Number, String)

    Example with generic and string view:

    class CellView {
        constructor ( view ) {
            this.view = view
        }
        render () {
            //default rendering
        }       
    }
    
    //String View
    class CellStringView extends CellView {
        render () {
            console.info( 'special rendering', this.view )
            this.view.innerHTML = '"' + this.view.textContent + '"'
        }
    }
    

    In the custom element definition (which can be viewed as the Controller):

    • on creation, instantiate the View (or Model).
    • when you want to render the cell (or process the data), call the (overridden, or not) method of the View (or Model).

    Example with a Custom Element v1 class:

    class CellElement extends HTMLElement {
        constructor () {
            super()
            //create cell
            switch ( this.getAttribute( 'type' ) )
            {
                case 'string': 
                    this.view = new CellStringView( this ) 
                    break
    
                default:
                    this.view = new CellView( this )
            }
        }
        connectedCallback () {
            //render cell
            this.view.render()
        }
    } 
    

    Below is a live snippet:

    //View (MVC View)
    class CellView {
      constructor(view) {
        this.view = view
      }
      render() {}
    }
    
    //String View
    class CellStringView extends CellView {
      render() {
        console.info('special rendering', this.view)
        this.view.innerHTML = '"' + this.view.textContent + '"'
      }
    }
    
    //Element (MVC controller)
    class CellElement extends HTMLElement {
      constructor() {
        super()
        //create cell
        switch (this.getAttribute('type')) {
          case 'string':
            this.view = new CellStringView(this)
            break
    
          default:
            this.view = new CellView(this)
        }
      }
      connectedCallback() {
        //render cell
        this.view.render()
      }
    }
    customElements.define('data-cell', CellElement)
    data-table {
      display: table ;
      border-collapse: collapse ;
      border: 1px solid gray ;
    }
    
    data-row {
      display: table-row ;
    }
    
    data-cell {
      display: table-cell ;
      border: 1px solid #ccc ;
      padding: 2px ;
    }
    <h4>Custom Table v1</h4>
    <data-table>
      <data-row>
        <data-cell>Id</data-cell>
        <data-cell>Name</data-cell>
        <data-cell>Age</data-cell>
      </data-row>
      <data-row>
        <data-cell>1</data-cell>
        <data-cell type="string">An</data-cell>
        <data-cell>20</data-cell>
      </data-row>
      <data-row>
        <data-cell>2</data-cell>
        <data-cell type="string">Bob</data-cell>
        <data-cell>31</data-cell>
      </data-row>
    </data-table>

    0 讨论(0)
提交回复
热议问题