2012-07-04 3 views
14

У меня есть ExtjS (4.0.7) GridPanel, который я заселяю из магазина. Значения, отображаемые в столбце GridPanel, должны иметь другое представление в зависимости от типа данных, находящихся в записи.Render динамические компоненты в ExtJS 4 GridPanel Column with Ext.create

Конечная цель состоит в том, что записи с «двойным» или «целочисленным» значением для свойства записи type представляют собой слайдер для пользователя, который они могут настраивать, а тип «строка» просто отображает некоторый текст только для чтения.

Я создал специальный столбец для этого. Он проверяет тип в рендерере и определяет, что делать.

У меня есть «строка», работающая отлично с приведенным ниже кодом, но борясь с тем, как я могу динамически создавать и визуализировать более сложный элемент управления ползунком в столбце.

Этот упрощенный пример просто пытается отобразить Panel с контролем даты в нем, как если бы я мог это сделать, я могу выяснить остальную часть материала слайдера.

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

Моя проблема на самом деле, как я Ext.create компонент, и может быть это делают в колонну в GridPanel?

+0

Спасибо всем за прекрасные ответы и варианты. Я бы наградил всех вас очками (или больше очков, чем мог бы, увеличив), если бы мог, но я думаю, что ответ Джона Райса ближе всего к тому, что я искал без явной задержки в рендерере. –

ответ

9

Есть несколько способов, которыми я видел это. Поскольку столбец сетки не является контейнером Ext, он не может иметь компоненты Ext в качестве дочерних элементов как часть любой конфигурации, как это делают другие компоненты контейнера. Для добавления компонентов Ext к ячейкам требуется логика вывода сетки.

Это решение изменяет ваш пользовательский рендеринг столбцов так, что он помещает специальный класс css в отображаемый тег TD. После того, как представление сетки будет готово, пройдены записи и пользовательский класс будет найден для соответствующих специальных столбцов. Ползунок отображается каждому найденному столбцу.

Код ниже представляет собой модифицированную версию примера массива ext js, представленную в примерах Sencha. Модификация смешивается в настраиваемом визуализаторе столбцов и рендеринг слайдеров почтовой сетки по элементам TD.

Этот пример включает только достаточную модификацию примера Sencha для демонстрации идей реализации. Ему не хватает разделенного представления и логики контроллера.

Это редактировался: 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

Чтобы увидеть код выше в jsfiddle, нажмите [здесь] (http://jsfiddle.net/unKWv/) – GreenGiant

+0

+1 @GreenGiant приятно иметь доступную версию, такую ​​как –

+1

Форма jsfiddle GreenGiant больше не работает, но здесь обновленная версия: http://jsfiddle.net/unKWv/24/ –

2

попробовать что-то вроде этого:

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

Но я должен сказать, что вы пытаетесь сделать - это не звучит :) Я не думаю, что кучу ползунков внутри сетки будет выглядеть хорошо для пользователя.

+0

Спасибо за ответ! Я согласен с тем, что UX этого может быть не оптимальным, но меня попросили дать ему шанс, чтобы мы могли понять, что это такое. Я надеялся избежать чего-то вроде «дефер», который кажется потенциально хрупким (особенно на медленных машинах IE), но, возможно, все в порядке. Оптимально то, что я хотел бы знать, это то, какой метод мне нужно переопределить в столбце (или сетке), чтобы позволить мне отображать нечто, отличное от строки. Или каким-то образом используйте параметры col & row на рендерере для записи значения напрямую, не дожидаясь обратного вызова 'defer'. –

+0

Вы переопределите функцию renderer() 'для столбца. Если вы хотите избежать использования 'defer' - вы можете посмотреть событие' render' для этого div, который вы возвращаете. – sha

+0

, но 'renderer()' ожидает возвращаемое значение строки, а затем отображает это в столбец. Я использую метод «renderer» в моем примере выше. Мне интересно, какой метод на самом деле вызывает метод «renderer», а затем берет возвращаемую строку и записывает ее в DOM. –

7

Я сделал что-то подобное, когда мне нужно было отобразить небольшой график (по существу, искровой диаграммы) в столбце сетки. Это решение похоже на sha, но оно более надежное и делегирует рендеринг отображаемому компоненту, а не Column, который на самом деле не имеет цепочки рендеринга.

Во-первых, класс колонка:

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

Примечание опция delayedRenderTo конфигурации. Точно так же, как renderTo, это будет идентификатор DOM элемента, который будет отображаться компонентом диаграммы, за исключением того, что он не должен присутствовать в DOM во время создания.

Тогда компонент класса:

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

Так что во время initComponent(), мы проверяем для отложенной визуализации и подготовки, что в случае необходимости. В противном случае это проявляется как обычно.

Функция delayRender() сама назначает задачу для проверки каждого так часто (в этом случае 100 мс) для существования элемента с данным идентификатором - то есть для проверки, был ли обработан столбец. Если нет, возвращает true, чтобы перенести задачу. Если это так, отображает компонент и возвращает false, чтобы отменить задачу.

Нам повезло с этим в этой области, поэтому я надеюсь, что это сработает и для вас.


Кстати, я разрабатывал это как часть ответа на my own question about ExtJS charting. Этот поток имеет результаты моего тестирования производительности.Я составлял 168 компонентов диаграммы в столбцах сетки в 3-4 раза в большинстве браузеров и ОС. Я предполагаю, что ваши слайдеры будут делать намного быстрее, чем это.

+2

Один из них с этим подходом (и, вероятно, также ша), заключается в том, что компонент, который отображается в div «delayedRenderTo», «отсоединен» от перспективы контейнера Ext. Другими словами, это не дочерний элемент другого контейнера, поэтому его метод «destroy» не будет вызываться, когда сетка будет уничтожена; вы должны сделать это явно или компонент, и все, что он ссылается, будет зависать после разрушения сетки. –

Смежные вопросы