metadatos ejemplos body attribute html css dom web-component custom-element

html - ejemplos - Crear un elemento personalizado con diferentes subtipos



title html (2)

Lo que se puede hacer es usar extensiones de tipo <td> :

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

Todas las extensiones comparten el mismo antecesor prototipo. Ejemplo con v0 :

//common cell var cellProto = Object.create( HTMLTableCellElement.protoype ) cellProto.sharedMethod = function () { ... } //typed cell var cellStringProto = Object.create( cellProto ) cellStringProto.specificMethod = function () { ... } cellStringProto.sharedMethod = function () { ... } document.registerElement( ''data-string'', { prototype: cellStringProto, extends: ''td'' } )

De esta forma, todas las celdas extienden el mismo elemento <td> , comparten un prototipo común pero tienen sus propias implementaciones de métodos.

Puede anular el método compartido y el método compartido puede llamar al método específico del objeto prototipo derivado.

Vea un ejemplo en ejecución aquí:

//table var tableProto = Object.create( HTMLTableElement.prototype ) tableProto.createdCallback = function () { console.info( ''data-table created'' ) } document.registerElement( ''data-table'', { prototype: tableProto, extends: ''table'' } ) //cell var cellProto = Object.create( HTMLTableCellElement.prototype ) cellProto.createdCallback = function () { console.info( ''cell created'' ) } cellProto.attachedCallback = function () { console.info( ''cell attached'' ) if ( typeof this.renderContent === ''function'' ) this.renderContent() } //cell string var cellStringProto = Object.create( cellProto ) cellStringProto.createdCallback = function () { console.info( ''data-string created'' ) } cellStringProto.renderContent = function () { console.info( ''data-string render'' ) this.innerHTML = ''"'' + this.textContent.trim() + ''"'' } document.registerElement( ''data-string'', { prototype: cellStringProto, extends: ''td'' } )

table { border-collapse: collapse ; } td, th { border: 1px solid gray ; padding: 2px }

<h4>Test Table Extension v0</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

Nota : Si no desea la extensión de tipo, también puede hacerlo con etiquetas personalizadas. La idea es tener un prototipo común y diferentes elementos personalizados que lo compartan (gracias a la herencia de prototipo estándar).

Actualmente estoy implementando un elemento de tabla de datos utilizando elementos personalizados (componentes web). La tabla puede tener diferentes tipos de celdas (texto, número, fecha, etc.) que se utilizan para representar cada fila.

P.ej

<my-table> <my-table-cell-text column="name"></my-table-cell-text> <my-table-cell-date column="dob" format="YYYY-MM-DD"></my-table-cell-date> <my-table-cell-number column="salary" decimals="2"></my-table-cell-number > </my-table>

También tengo una clase MyTableCell que extiende todos los elementos de la celda. Esto funciona bien para compartir funciones comunes, sin embargo, el estilo puede ser un problema, ya que cada tipo de celda es su propia etiqueta html. Actualmente, estoy agregando una clase css al extender MyTableCell , pero, por razones de argumento, digamos que no quiero hacer eso.

La solución ideal sería poder ampliar un elemento personalizado, utilizando la palabra clave is , por ejemplo, <my-table-cell is="my-table-cell-text"> , pero eso solo está permitido para los elementos html integrados .

Puedo pensar en 3 enfoques para resolver este problema:

  1. Tener una sintaxis similar a <input type=""> , pero eso es mucho más trabajo, ya que ya no estás ampliando una clase base, sino creando variaciones del mismo elemento y esto significa que necesitas una forma personalizada de registrar las diferentes variaciones , algo así como MyTableCell.registerType estático

  2. Un enfoque composable, donde envuelvo un elemento de representación, <my-table-renderer-text> , dentro de una <my-table-cell> genérica. Esto evita el método de registro personalizado, pero es más difícil de escribir y los resultados en más elementos y más código repetitivo, que a su vez significa un golpe de rendimiento.

  3. Una combinación de ambos, donde el usuario escribe <my-table-cell type="text"> y la celda utiliza algo como document.createElement(''my-table-rendener-''+ type) internamente. Esto mantiene la sintaxis más simple de la opción 1, al tiempo que se evita el método de registro personalizado, pero tiene las mismas implicaciones de rendimiento de la opción 2.

¿Puedes sugerir alguna mejor alternativa? ¿Me estoy perdiendo algo?


NB: Esta respuesta está separada de la otra, ya que es bastante extensa por sí misma y totalmente independiente.

Si usa elementos personalizados autónomos (es decir, etiquetas personalizadas) con un atributo de type (opcional):

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

... podrías usar el patrón MVC:

  • definir una clase para la vista de celda genérica (y / o modelo )
  • definir una subclase para las Vistas especializadas (Fecha, Número, Cadena)

Ejemplo con vista genérica y de cadena :

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 + ''"'' } }

En la definición del elemento personalizado (que se puede ver como el Controlador ):

  • en la creación, crea una instancia de la Vista (o Modelo ).
  • cuando desee renderizar la celda (o procesar los datos), llame al método (anulado o no) de la Vista (o Modelo ).

Ejemplo con una clase Elemento personalizado v1 :

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() } }

Debajo hay un fragmento en vivo:

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