Шаблон легенды - Диаграмма

Я получил этот шаблон (по умолчанию)

<span class="x-legend-item-marker {[values.disabled?'x-legend-inactive':'']}" style="background:{mark};"></span>{name}

которые производят это:

введите здесь описание изображения

Я хочу иметь один и тот же шаблон со всеми его функциями. Но мне нужно еще одно условие if. Я не хочу, чтобы элемент был «легендарным», если его значение равно 0.

Вот полный код

{
xtype: 'container',
                title: 'Chart',
                iconCls: 'chart',
                itemId: 'chart_Tab',
                layout: {
                    type: 'fit'
                },
                items: [
                    {
                        xtype: 'polar',
                        itemId: 'pie',
                        colors: [
                            '#115fa6',
                            '#94ae0a',
                            '#a61120',
                            '#ff8809',
                            '#ffd13e',
                            '#a61187',
                            '#24ad9a',
                            '#7c7474',
                            '#a66111',
                            '#222222',
                            '#115ea6',
                            '#94cc0a',
                            '#b61120',
                            '#dd8809',
                            '#11d13e',
                            '#a68887',
                            '#94df9d',
                            '#7f74f4',
                            '#112341',
                            '#abcdef1'
                        ],
                        store: 'relativedata',
                        series: [
                            {
                                type: 'pie',
                                label: {
                                    textBaseline: 'middle',
                                    textAlign: 'center',
                                    font: '9px Helvetica'
                                },
                                labelField: 'strName',
                                labelOverflowPadding: 0,
                                xField: 'numValue'
                            }
                        ],
                        interactions: [
                            {
                                type: 'rotate'
                            }
                        ],
                        listeners: [
                            {
                                fn: function(element, eOpts) {
                                    var relStore = Ext.getStore('relativedata');
                                    var eleStore = Ext.getStore('element');
                                    var relModel;
                                    var eleModel;

                                    relStore.removeAll();

                                    //Convert to CO2 qty
                                    for(var i = 0; i< eleStore.getCount();i++)
                                    {
                                        eleModel = eleStore.getAt(i);
                                        relModel = Ext.create(APPNAME + '.model.RelativeElement');
                                        relModel.set('strName',eleModel.get('strName'));
                                        relModel.set('numValue', eleModel.get('numValue')*eleModel.getFactor());
                                        relStore.add(relModel);
                                    }

                                    relStore.sync();

                                    //Hide arrows-legend
                                    this._series[0]._label.attr.hidden=true;
                                },
                                event: 'painted'
                            }
                        ],
                        legend: {
                            xtype: 'legend',
                            docked: 'bottom',
                            itemId: 'pie_legend',
                            itemTpl: [
                                '<span class="x-legend-item-marker {[values.disabled?\'x-legend-inactive\':\'\']}" style="background:{mark};"></span>{name}'
                            ],
                            maxItemCache: 100,
                            store: 'element'
                        }
                    }
                ]
            }

Я прошу помощи, потому что я не очень хорошо разбираюсь в шаблонах. Я бы не осмелился сказать, что понимаю все по умолчанию.


person Fawar    schedule 30.07.2013    source источник
comment
Это не кажется необоснованной идеей, к сожалению, вы не сможете сделать это, только настроив шаблон... Немного осмотрев, я увидел, что значения недоступны во время рендеринга легенды. .   -  person rixo    schedule 30.07.2013
comment
Хм, хорошо. Что мне сделать, чтобы сделать их доступными в рамках шаблона? Или как мне поступить?   -  person Fawar    schedule 30.07.2013
comment
Насколько я могу судить сейчас... Много чего :-/   -  person rixo    schedule 31.07.2013


Ответы (1)


Я вернулся! Но за это меня никто не называет стройной шэдди... К несчастью!

Итак, чтобы ответить на ваш первоначальный вопрос, шаблон, который вам нужен, будет примерно таким:

// Configuration of the chart legend
legend: {
    // Finally, we can use the value field to customize our templates.
    itemTpl: [
        '<tpl if="value != 0">', // <= template condition
            '<span class="x-legend-item-marker {[values.disabled?\'x-legend-inactive\':\'\']}" style="background:{mark};"></span>{name}',
        '</tpl>'
    ]
    // ...
}

К сожалению, как я сказал в своем предыдущем комментарии, быстрая проверка отладчика показывает, что эта переменная value или любая эквивалентность недоступна во время применения этого шаблона.

Теперь я собираюсь дать вам подробное объяснение того, как мне удалось преодолеть эту досаду. Отчасти потому, что это такой сложный лайфхак, что вам лучше знать, что вы делаете, если решите применить его, а отчасти потому, что вы узнаете гораздо больше, наблюдая за методами ловли, а не получая от рыбы право. прочь - в этом случае рыба все равно недоступна для розничной продажи. А еще, должен признаться, во многом потому, что я люблю лирично относиться к вещам, в которые я вложил немного сил, а уже поздно, и моя защита от самовосхваления немного ослабла...

Итак, глядя на Ext.chart.Legend' s показывает, что там ничего не поделаешь, это просто несколько облегченное расширение Ext.dataview.Dataview. Таким образом, он должен иметь привязанное к нему хранилище, которое, очевидно (и, к сожалению), не связано с диаграммой для предоставления своих данных.

Другая разумная точка останова (в методе setStore Legend) показывает, что это хранилище исходит из Ext.chart.AbstractChart, а в код этого класса мы видим две вещи: выделенное хранилище легенд создается в конструкторе и ряды диаграмм реализуют метод заполнения этого хранилища, а именно provideLegendInfo.

Мы приближаемся к нашей цели. Что нам нужно сделать, так это добавить поле значения в хранилище легенд, и наша серия предоставит данные для этого поля. Здорово!

Мудрым подходом сейчас будет реализация этих модификаций с минимальным количеством репликации кода Ext... Но после того, как вы потратите extjs-4/10588523#comment26244891_10588523">незначительное количество времени безуспешно пытаясь сделать это, я просто соглашусь на дикое переопределение этих двух методов и даю совет поместить большое жирное предупреждение, чтобы проверить, что код этих методов не меняется в следующих версиях Touch:

if (Ext.getVersion().isGreaterThan('2.2.1')) {
    // Give yourself a big warning to check that the overridden methods' code
    // bellow has not changed (see further comments).
}

С этим покончено, давайте перейдем к делу, не обращая внимания на будущие поколения.

То есть сначала мы добавляем поле значения в хранилище легенд:

/**
 * Adds a value field to legend store.
 */
Ext.define(null, {
    override: 'Ext.chart.AbstractChart'
    // Berk, what a lot of code replication :( Let's just hope that this method's code
    // won't change in the future...
    ,constructor: function() {
        var me = this;
        me.itemListeners = {};
        me.surfaceMap = {};
        me.legendStore = new Ext.data.Store({
            storeId: this.getId() + '-legendStore',
            autoDestroy: true,
            fields: [
                'id', 'name', 'mark', 'disabled', 'series', 'index'
                // Adding my value field
                ,'value'
            ]
        });
        me.suspendLayout();

        // For whatever reason, AbstractChart doesn't want to call its superclass
        // (Ext.draw.Component) constructor and, by using callSuper, skips directly to
        // Ext.Container's one. So well... I respect, but I must do it old school since
        // callSuper would go to Ext.draw.Component from here.
        Ext.Container.prototype.constructor.apply(this, arguments);
        // me.callSuper(arguments);

        me.refreshLegendStore();
        me.getLegendStore().on('updaterecord', 'onUpdateLegendStore', me);
        me.resumeLayout();
    }
}, function() {
    // Post-create functions are not called for overrides in touch as they are 
    // in ExtJS? Hmm... That would have been the perfect place to issue a big
    //  warning in case the version has changed, but we'll live with it :(
});

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

/**
 * Overrides `provideLegendInfo` to add the value to the legend records.
 *
 * Here again, let us all cross our fingers very hard, hoping for Sencha's team to not decide
 * to add their own extra fields too soon...
 */
Ext.define(null, {
    override: 'Ext.chart.series.Pie'
    ,provideLegendInfo: function(target) {
        var store = this.getStore();
        if (store) {
            var items = store.getData().items,
                labelField = this.getLabelField(),
                field = this.getField(),
                hidden = this.getHidden();
            for (var i = 0; i < items.length; i++) {
                target.push({
                    name: labelField ? String(items[i].get(labelField))  : field + " " + i,
                    mark: this.getStyleByIndex(i).fillStyle || this.getStyleByIndex(i).strokeStyle || 'black',
                    disabled: hidden[i],
                    series: this.getId(),
                    index: i
                    // Providing actual data value to the legend record
                    ,value: items[i].get(field)
                });
            }
        }
    }
});

Подведем итоги. У нас есть два переопределения и собственный шаблон. Мы могли бы надеяться, что мы уже закончили. Но вот что мы получаем:

Пустая серия без CSS

Таким образом, DataView добавляет собственную разметку вокруг разметки itemTpl. Ну, ну, ну... На данный момент я устал следить за внутренностями Ext и, к счастью (на этот раз!), я представляю быстрый патч для этого. Так что я без колебаний добавляю это правило CSS:

.x-legend-item:empty {
    display: none;
}

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

В этой демонстрации есть «метрика четыре», которая имеет значение 0.

{
    'name': 'metric four',
    'data': 0
}

Но вы этого не увидите. Потому что в этом был смысл всего этого, не так ли?

person rixo    schedule 31.07.2013
comment
Удивительный! Я бы никогда не нашел это сам. Хорошая работа, и вы заслужили свои собственные поздравления! Я постараюсь сделать эту работу здесь и держать вас в курсе! - person Fawar; 31.07.2013
comment
Это сработало именно так, как вы сказали, вы действительно так хороши! Большое спасибо, я оставлю для вас ссылку на этот пост в коде, вы заработали! - person Fawar; 31.07.2013