2012-07-04 12 views
14

Ho un GridPanel ExtJS (4.0.7) che sto popolando da un negozio. I valori che visualizzo nella colonna di GridPanel devono avere una vista diversa a seconda del tipo di dati presente nel record.Rendering di componenti dinamici nella colonna GridPanel ExtJS 4 con Ext.create

L'obiettivo finale è che i record con valore "doppio" o "intero" per la proprietà type del record presentino all'utente un controllo che possono essere regolati e un tipo di "stringa" restituisce solo del testo di sola lettura.

Ho creato una Colonna personalizzata per fare questo. Ispeziona il tipo nel renderer e determina cosa renderizzare.

Ho la "stringa" che funziona bene con il codice sottostante, ma ho difficoltà a creare e rendere più complicato il controllo del cursore nella colonna.

Questo esempio semplificato sta solo cercando di rendere un Panel con un controllo di data in esso come se riesco a farlo funzionare, posso capire il resto del materiale di scorrimento.

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

Il mio problema è davvero, come posso Ext.create un componente, e lo hanno rendere in una colonna nel gridpanel?

+0

Grazie a tutti per le grandi risposte e le opzioni. Premetterei tutti voi punti (o più punti di quanti ne potrei con le upvoting) se potessi, ma penso che la risposta di John Rice sia la più vicina a quello che stavo cercando senza il ritardo esplicito nel renderer. –

risposta

9

Ci sono alcuni modi in cui ho visto questo realizzato. Poiché la colonna della griglia non è un contenitore Ext, non può avere componenti Ext come elementi secondari come parte di alcuna configurazione come gli altri componenti del contenitore. È richiesta la logica di post grid-rendering per aggiungere componenti Ext alle celle.

Questa soluzione consente di modificare il rendering della colonna personalizzata in modo da posizionare una classe css speciale sul tag TD renderizzato. Dopo che la vista griglia è pronta, i record vengono attraversati e viene trovata la classe personalizzata per le colonne speciali appropriate. Un cursore viene reso in ogni colonna trovata.

Il codice seguente è una versione modificata dell'estensione di griglia ext js fornita negli esempi Sencha. La modifica si mescola nel renderer di colonne personalizzato e nel rendering di griglia post dei cursori agli elementi TD.

Questo esempio include solo una modifica sufficiente dell'esempio Sencha per mostrare le idee di implementazione. Manca la vista separata e la logica del controller.

Questo viene modificato da: 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

Per vedere il codice qui sopra in jsfiddle, fare clic su [qui] (http://jsfiddle.net/unKWv/) – GreenGiant

+0

+1 @GreenGiant bello avere la versione corrente disponibile come quella –

+1

Il modulo jsfiddle GreenGiant non funziona più ma qui è una versione aggiornata: http://jsfiddle.net/unKWv/24/ –

2

provare qualcosa di simile:

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

Ma devo dire quello che stai cercando di fare - non suona giusto :) non credo che un gruppo di cursori all'interno della griglia sarà buono per l'utente.

+0

Grazie per la risposta! Sono d'accordo che la UX di questo potrebbe non essere ottimale, ma mi è stato chiesto di dargli una possibilità in modo che possiamo vedere come è. Speravo di evitare qualcosa di simile a un "differimento" che sembra potenzialmente fragile (specialmente su macchine IE lente), ma forse è ok. In modo ottimale, quello che vorrei sapere è quale metodo devo sovrascrivere in column (o gridpanel) per permettermi di renderizzare qualcosa di diverso da una stringa. O in qualche modo usare i parametri col & row sul renderer per scrivere direttamente il valore senza aspettare un callback 'differito'. –

+0

Si esegue l'override della funzione 'renderer()' per la colonna. Se vuoi evitare di usare 'differisci' - potresti voler guardare l'evento' render' per questo div che stai restituendo. – sha

+0

ma 'renderer()' si aspetta un valore di restituzione della stringa e quindi lo visualizza nella colonna. Sto implementando il metodo 'renderer' della colonna nel mio esempio sopra. Mi chiedo quale metodo stia effettivamente chiamando il metodo 'renderer' e poi prendendo la stringa restituita e scrivendola sul DOM. –

7

Ho fatto qualcosa di simile quando avevo bisogno di rendere un piccolo grafico (essenzialmente una tabella di accensione) in una colonna della griglia. Questa soluzione è simile a quella di sha, ma è più robusta e delega il rendering al componente sottoposto a rendering piuttosto che allo Column, che in realtà non ha una catena di rendering.

In primo luogo, la classe della colonna:

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

Nota l'opzione delayedRenderTo config. Proprio come renderTo, questo sarà l'ID DOM dell'elemento a cui verrà eseguito il rendering del componente grafico, tranne che non è necessario che sia presente nel DOM al momento della creazione.

Poi la classe del 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; 
       } 
      } 
     }); 
    } 
}); 

Così durante initComponent(), controlliamo per ritardo di rendering e preparare che, se necessario. Altrimenti, si presenta come normale.

La funzione delayRender() pianifica un'attività da controllare ogni tanto (100 ms in questo caso) per l'esistenza di un elemento con l'ID specificato, ad esempio per verificare se la colonna ha eseguito il rendering. In caso contrario, restituisce true per riprogrammare l'attività. In tal caso, esegue il rendering del componente e restituisce false per annullare l'attività.

Abbiamo avuto fortuna in questo campo, quindi spero che funzioni anche per voi.


A proposito, stavo sviluppando questo come una parte di rispondere my own question about ExtJS charting. Quel thread ha i risultati dei miei test delle prestazioni.Stavo visualizzando 168 componenti grafici nelle colonne della griglia in 3-4 secondi nella maggior parte dei browser e dei sistemi operativi. Immagino che i tuoi cursori renderebbero molto più veloce di quello.

+2

Un approccio con questo approccio (e probabilmente anche sha) è che il componente sottoposto a rendering nel div "delayedRenderTo" è "scollegato" da una prospettiva del contenitore Ext. In altre parole, non è un elemento figlio di nessun altro contenitore, quindi il suo metodo 'destroy' non verrà chiamato quando la griglia viene distrutta; devi farlo esplicitamente o il componente e tutto ciò che fa riferimento rimarrà in sospeso dopo che la griglia è stata distrutta. –

Problemi correlati