2012-07-04 50 views
14

Tengo un ExtJS (4.0.7) GridPanel que estoy completando en una tienda. Los valores que se muestran en la columna de GridPanel deben tener una vista diferente según el tipo de datos que están en el registro.Renderizar componentes dinámicos en ExtJS 4 GridPanel Columna con Ext.create

El objetivo final es que los registros con valor "doble" o "entero" para la propiedaddel registro presentan un control deslizante al usuario que pueden ajustarse, y un tipo de "cadena" solo representa texto de solo lectura.

He creado una Columna personalizada para hacer esto. Inspecciona el tipo en el renderizador y determina qué renderizar.

Tengo la "cadena" funcionando bien con el siguiente código, pero tengo dificultades para crear dinámicamente y renderizar el control deslizante más complicado en la columna.

Este ejemplo simplificado solo está tratando de representar un Panel con un control de fecha, como si pudiera lograrlo, puedo descifrar el resto del control deslizante.

Ext.define('MyApp.view.MyColumn', { 
    extend: 'Ext.grid.column.Column', 
    alias: ['widget.mycolumn'], 

    stringTemplate: new Ext.XTemplate('code to render {name} for string items'), 

    constructor: function(cfg){ 
     var me = this; 
     me.callParent(arguments); 

     me.renderer = function(value, p, record) { 
      var data = Ext.apply({}, record.data, record.getAssociatedData()); 

      if (data.type == "string") { 
       return me.renderStringFilter(data); 
      } else if (data.type == "double" || data.type == "integer") { 
       return me.renderNumericFilter(data); 
      } else { 
       log("Unknown data.type", data); 

     }; 
    }, 

    renderStringFilter: function(data) { 
     // this works great and does what I want 
     return this.stringTemplate.apply(data); 
    }, 

    renderNumericFilter: function(data) { 

     // ***** How do I get a component I "create" to render 
     // ***** in it's appropriate position in the gridpanel? 
     // what I really want here is a slider with full behavior 
     // this is a placeholder for just trying to "create" something to render 

     var filterPanel = Ext.create('Ext.panel.Panel', { 
      title: 'Filters', 
      items: [{ 
       xtype: 'datefield', 
       fieldLabel: 'date' 
      }], 
      renderTo: Ext.getBody() // this doesn't work 
     }); 
     return filterPanel.html; // this doesn't work 
    } 
}); 

Mi problema realmente es, ¿cómo puedo Ext.create un componente, y tienen que rinden en una columna en el gridpanel?

+0

Gracias a todos por las grandes respuestas y opciones. Yo les otorgaría todos los puntos (o más puntos de los que podría por votaciones ascendentes) si pudiera, pero creo que la respuesta de John Rice es la más cercana a la que estaba buscando sin el retraso explícito en el renderizador. –

Respuesta

9

Hay algunas maneras en que he visto esto logrado. Como la columna de cuadrícula no es un contenedor Ext, no puede tener componentes Ext como elementos secundarios como parte de ninguna configuración, de la misma forma que otros componentes del contenedor. Se requiere una lógica posterior a la representación de cuadrícula para agregar componentes Ext a las celdas.

Esta solución modifica el procesamiento de columnas personalizadas para que coloque una clase CSS especial en la etiqueta TD procesada. Una vez que la vista de cuadrícula está lista, se recorren los registros y se encuentra la clase personalizada para las columnas especiales apropiadas. Un control deslizante se representa en cada columna encontrada.

El siguiente código es una versión modificada del ejemplo de cuadrícula ext js proporcionada en los ejemplos de Sencha. La modificación se mezcla en el renderizador de columnas personalizadas y la representación posterior a la cuadrícula de controles deslizantes a elementos TD.

Este ejemplo solo incluye modificaciones suficientes del ejemplo de Sencha para mostrar las ideas de implementación. Carece de vista separada y lógica de controlador.

Esta es una modificación de: http://docs.sencha.com/ext-js/4-0/#!/example/grid/array-grid.html

Ext.require([ 
    'Ext.grid.*', 
    'Ext.data.*', 
    'Ext.util.*', 
    'Ext.data.Model' 
]); 


Ext.onReady(function() { 

    // sample static data for the store 
    Ext.define('Company', { 
     extend: 'Ext.data.Model', 
     fields: ['name', 'price', 'change', 'pctChange', 'lastUpdated', 'type'] 
    }); 

    var myData = [ 
     ['3m Co',        71.72, 2, 0.03, '9/1/2011', 'integer'], 
     ['Alcoa Inc',       29.01, 4, 1.47, '9/1/2011', 'string'], 
     ['Altria Group Inc',     83.81, 6, 0.34, '9/1/2011', 'string'], 
     ['American Express Company',   52.55, 8, 0.02, '9/1/2011', 'string'], 
     ['American International Group, Inc.', 64.13, 2, 0.49, '9/1/2011', 'integer'], 
     ['AT&T Inc.',       31.61, 4, -1.54, '9/1/2011', 'integer'], 
     ['Boeing Co.',       75.43, 6, 0.71, '9/1/2011', 'string'], 
     ['Caterpillar Inc.',     67.27, 8, 1.39, '9/1/2011', 'integer'], 
     ['Citigroup, Inc.',      49.37, 1, 0.04, '9/1/2011', 'integer'], 
     ['E.I. du Pont de Nemours and Company', 40.48, 3, 1.28, '9/1/2011', 'integer'], 
     ['Exxon Mobil Corp',     68.1, 0, -0.64, '9/1/2011', 'integer'], 
     ['General Electric Company',   34.14, 7, -0.23, '9/1/2011', 'integer'] 
    ]; 

    // create the data store 
    var store = Ext.create('Ext.data.ArrayStore', { 
     model: 'Company', 
     data: myData 
    }); 

    // existing template 
    stringTemplate = new Ext.XTemplate('code to render {name} for string items'); 

    // custom column renderer 
    specialRender = function(value, metadata, record) { 
     var data; 

     data = Ext.apply({}, record.data, record.getAssociatedData()); 

     if (data.type == "string") { 
      return stringTemplate.apply(data);; 
     } else if (data.type == "double" || data.type == "integer") { 
      // add a css selector to the td html class attribute we can use it after grid is ready to render the slider 
      metadata.tdCls = metadata.tdCls + 'slider-target'; 
      return ''; 
     } else { 
      return("Unknown data.type"); 

     } 
    }; 

    // create the Grid 
    grid = Ext.create('Ext.grid.Panel', { 
     rowsWithSliders: {}, 
     store: store, 
     stateful: true, 
     stateId: 'stateGrid', 
     columns: [ 
      { 
       text  : 'Company', 
       flex  : 1, 
       sortable : false, 
       dataIndex: 'name' 
      }, 
      { 
       text  : 'Price', 
       width : 75, 
       sortable : true, 
       renderer : 'usMoney', 
       dataIndex: 'price' 
      }, 
      { 
       text  : 'Change', 
       width : 75, 
       sortable : true, 
       dataIndex: 'change', 
       renderer: specialRender, 
       width: 200 
      }, 
      { 
       text  : '% Change', 
       width : 75, 
       sortable : true, 
       dataIndex: 'pctChange' 
      }, 
      { 
       text  : 'Last Updated', 
       width : 85, 
       sortable : true, 
       renderer : Ext.util.Format.dateRenderer('m/d/Y'), 
       dataIndex: 'lastUpdated' 
      } 
     ], 
     height: 350, 
     width: 600, 
     title: 'Irm Grid Example', 
     renderTo: 'grid-example', 
     viewConfig: { 
      stripeRows: true 
     } 
    }); 

    /** 
    * when the grid view is ready this method will find slider columns and render the slider to them 
    */ 
    onGridViewReady = function() { 
     var recordIdx, 
      colVal, 
      colEl; 

     for (recordIdx = 0; recordIdx < grid.store.getCount(); recordIdx++) { 
      record = grid.store.getAt(recordIdx); 
      sliderHolder = Ext.DomQuery.select('.slider-target', grid.view.getNode(recordIdx)); 
      if (sliderHolder.length) { 
       colEl = sliderHolder[0]; 

       // remove div generated by grid template - alternative is to use a new template in the col 
       colEl.innerHTML = ''; 

       // get the value to be used in the slider from the record and column 
       colVal = record.get('change'); 

       // render the slider - pass in the full record in case record data may be needed by change handlers 
       renderNumericFilter(colEl, colVal, record) 
      } 
     } 

    } 

    // when the grids view is ready, render sliders to it 
    grid.on('viewready', onGridViewReady, this); 

    // modification of existing method but removed from custom column 
    renderNumericFilter = function(el, val, record) { 

     var filterPanel = Ext.widget('slider', { 
      width: 200, 
      value: val, 
      record: record, 
      minValue: 0, 
      maxValue: 10, 
      renderTo: el 
     }); 

    } 

}); 
+2

Para ver el código anterior en jsfiddle, haga clic en [aquí] (http://jsfiddle.net/unKWv/) – GreenGiant

+0

+1 @GreenGiant. Es bueno tener la versión en funcionamiento disponible de esa manera. –

+1

La forma jsfiddle GreenGiant ya no funciona, pero aquí está una versión actualizada: http://jsfiddle.net/unKWv/24/ –

2

intentar algo como esto:

renderNumericFilter: function() { 
    var id = Ext.id(); 
    Ext.defer(function() { 
     Ext.widget('slider', { 
      renderTo: id, 
      width: 200, 
      value: 50, 
      increment: 10, 
      minValue: 0, 
      maxValue: 100, 
     }); 
    }, 50); 
    return Ext.String.format('<div id="{0}"></div>', id); 
} 

Pero debo decir lo que estamos tratando de hacer - que no suena bien :) no creo que un montón de controles deslizantes dentro de la cuadrícula se verá bueno para el usuario.

+0

¡Gracias por la respuesta! Estoy de acuerdo en que el UX de esto podría no ser óptimo, pero me pidieron que lo probara para que podamos ver cómo es. Había estado esperando evitar algo así como un 'aplazamiento' que se siente potencialmente frágil (especialmente en máquinas lentas de IE), pero tal vez está bien. De manera óptima, lo que me gustaría saber es qué método debo anular en la columna (o en el panel de conexiones) para poder representar algo que no sea una cadena. O de alguna manera, use los parámetros col y row en el renderizador para escribir el valor directamente sin esperar una devolución de llamada 'defer'. –

+0

Sobreescribe la función 'renderer()' para la columna. Si desea evitar el uso de 'diferir', es posible que desee ver el evento' render' para este div que está devolviendo. – sha

+0

pero 'renderer()' espera un valor de retorno de cadena y luego lo renderiza en la columna. Estoy implementando el método 'renderer' de la columna en mi ejemplo anterior. Me pregunto qué método llama realmente al método 'renderizador' y luego toma la cadena devuelta y la escribe en el DOM. –

7

Hice algo así cuando tuve que renderizar un pequeño gráfico (esencialmente un gráfico de chispa) en una columna de cuadrícula. Esta solución es similar a la de sha, pero es más robusta y delega la representación en el componente que se está representando en lugar del Column, que en realidad no tiene una cadena de representación.

En primer lugar, la clase de la columna:

Ext.define("MyApp.view.Column", { 
    extend: "Ext.grid.column.Column", 

    // ... 

    renderer: function (value, p, record) { 
     var container_id = Ext.id(), 
      container = '<div id="' + container_id + '"></div>'; 

     Ext.create("MyApp.view.Chart", { 
      type: "column", 
      // ... 
      delayedRenderTo: container_id 
     }); 

     return container; 
    } 
}); 

cuenta que la opción de configuración delayedRenderTo. Al igual que renderTo, esta será la ID DOM del elemento al que se representará el componente del gráfico, excepto que no necesita estar presente en el DOM en el momento de la creación.

A continuación, la clase de componente:

Ext.define("MyApp.view.Chart", { 
    extend: "Ext.chart.Chart", 

    // ... 

    initComponent: function() { 
     if (this.delayedRenderTo) { 
      this.delayRender(); 
     } 

     this.callParent(); 
    }, 

    delayRender: function() { 
     Ext.TaskManager.start({ 
      scope: this, 
      interval: 100, 
      run: function() { 
       var container = Ext.fly(this.delayedRenderTo); 

       if (container) { 
        this.render(container); 
        return false; 
       } else { 
        return true; 
       } 
      } 
     }); 
    } 
}); 

Así que durante initComponent(), comprobamos para retrasó render a preparar para que en caso de necesidad. De lo contrario, se renderiza de forma normal.

La función delayRender() en sí programa una tarea para comprobar cada cierto tiempo (100 ms en este caso) la existencia de un elemento con la ID dada, es decir, para comprobar si la columna ha procesado. De lo contrario, devuelve verdadero para reprogramar la tarea. Si es así, representa el componente y devuelve falso para cancelar la tarea.

Hemos tenido buena suerte con esto en el campo, por lo que espero que funcione para usted también.


Por cierto, yo estaba desarrollando esto como parte de responder my own question about ExtJS charting. Ese hilo tiene los resultados de mi prueba de rendimiento.Estaba renderizando 168 componentes de gráficos en columnas de cuadrícula en 3-4 en la mayoría de los navegadores y sistemas operativos. Imagino que tus controles deslizantes se renderizarían mucho más rápido que eso.

+2

Un problema con este enfoque (y probablemente con el de sha también) es que el componente que se procesa en el div 'delayedRenderTo' está 'separado' desde la perspectiva del contenedor Ext. En otras palabras, no es un elemento secundario de ningún otro contenedor, por lo tanto, no se invocará su método 'destruir' cuando se destruya la red; tienes que hacerlo explícitamente o el componente y todo lo que haga referencia se mantendrá después de que se destruya la cuadrícula. –