The dashboard

We have the application infrastructure in place, so it's time to build out the components that are going to rest on this infrastructure. First up is the dashboard, something that requires us to carefully consider how to implement the rest of our application.

The dashboard consists of two live charts and two historical charts. The live charts are very similar to each other, as are the historical charts (just some minor formatting and binding configuration between them). However, in order to build a chart, you also have to build the axes and the series being drawn on it, which results in a fairly long-winded configuration object. Here's what the historical log chart from the dashboard will look like:

{ 
    xtype: 'cartesian',
    title: 'Last 30 Days',
    margin: 10, flex: 1
    bind: '{historicalWebLogs}'
    axes: [{
        type: 'numeric',
        position: 'left',
        fields: ['value'],
        title: {
            text: 'Avg. Response 
Time (ms)',
            fontSize: 15
        },
        grid: true,
        minimum: 0,
        maximum: 20
    }, {
        type: 'time',
        fields: 'time',
        dateFormat: 'd M'
    }],
    series: {
        type: 'line',
        xField: 'time',
        yField: 'value',
        style: { 'stroke': 'red' }
    }
}

Excellent! We have configured a chart with a line series that has a numeric left axis and a time-base bottom axis. This is exactly what we need for the dashboard, so where's the problem?

Duplication is the problem. The majority of this configuration object would be duplicated, one copy for the web logs and one copy for the SQL logs. We've mentioned before in this chapter that we'd like to battle duplication where possible, so in this case, we will create a new class that we can reuse in places where we need a chart to plot historical requests from the logs.

Here's the class:

// app/ux/chart/HistoricalRequestChart.js
Ext.define('Instrumatics.ux.chart.HistoricalRequestChart', {
    extend: 'Ext.chart.CartesianChart',
    xtype: 'historical-request-chart',
    frame: true,
    axes: [{
        type: 'numeric',
        position: 'left',
        fields: ['value'],
        title: {
            text: 'Avg. Response 
Time (ms)',
            fontSize: 15
        },
        grid: true,
        minimum: 0,
        maximum: 20
    }, {
        type: 'time',
        position: 'bottom',
        fields: ['time'],
        dateFormat: 'd M'
        style: {
            axisLine: false
        }
    }],

    series: {
        type: 'line',
        xField: 'time',
        yField: 'value',
        style: { 'stroke': 'red' }
    }
});

It combines the basic definition of the chart, such as the axes and series, with things we know we'll reuse across the application, such as the title on the left axis.

Note that we're putting this class in a different location from anything we've seen so far in our example applications, but one that we discussed in Chapter 3, Application Structure. The ux namespace and corresponding directory is a fairly standard location for reusable classes in the Ext JS community, so we will follow this convention here.

We will create another reusable class, this time for the live request chart:

// app/ux/chart/LiveRequestChart.js
Ext.define('Instrumatics.ux.chart.LiveRequestChart', {
    extend: 'Ext.chart.CartesianChart',
    xtype: 'live-request-chart',
    redrawCounter: 0,
    frame: true,
    axes: [{
        type: 'numeric',
        position: 'left',
        fields: ['value'],
        title: {
            text: 'Avg. Response 
Time (ms)',
            fontSize: 15
        },
        grid: true,
        minimum: 0,
        maximum: 20
    }, {
        type: 'time',
        position: 'bottom',
        step: [Ext.Date.SECOND, 1],
        fields: ['time'],
        dateFormat: 'H:i:s',
        fromDate: new Date(new Date().setMinutes( new Date().getMinutes() - 1)).setSeconds(0),
        toDate: new Date(new Date().setMinutes( new Date().getMinutes() + 5)).setSeconds(0)
    }],
    series: {
        type: 'line',
        xField: 'time',
        yField: 'value',
        style: {
            'stroke-width': 2
        }
    },


    constructor: function() {
        this.callParent(arguments);

        this.on('redraw', this.onRedraw, this);
    },


    onRedraw: function() {
        this.redrawCounter++;

        if(this.redrawCounter > 15) {
            this.redrawCounter = 0;

            var timeAxis = this.getAxes()[1],
                oldFrom = new Date(timeAxis.getFromDate()),
                oldTo = new Date(timeAxis.getToDate()),
                newFrom = Ext.Date.add(oldFrom, Ext.Date.SECOND, 15),
                newTo = Ext.Date.add(oldTo, Ext.Date.SECOND, 15);

            timeAxis.setFromDate(newFrom);
            timeAxis.setToDate(newTo);
        }
    }
});

You'll notice that this code has a lot in common with the code from our code investigation earlier in the chapter. It's just that we've wrapped it up into a reusable class.

With these new classes ready to go, creating the dashboard is just a matter of piecing together what we've already written, as shown in the following code:

// app/view/dashboard/Dashboard.js
Ext.define("Instrumatics.view.dashboard.Dashboard", {
    extend: "Ext.panel.Panel",
    xtype: 'app-dashboard',
    title: 'hello',
    requires: [
        'Instrumatics.ux.chart.LiveRequestChart',
        'Instrumatics.ux.chart.HistoricalRequestChart'
    ],
    viewModel: {
        type: 'dashboard-dashboard'
    },
    controller: 'dashboard-dashboard',
    layout: {
        type: 'vbox',
        align: 'stretch'
    },
    items: [
        {
            xtype: 'container',
            flex: 1,
            layout: {
                type: 'hbox',
                align: 'stretch'
            },
            items: [
                {
                    xtype: 'live-request-chart',
                    title: 'Live Web Requests', bind: '{webLogs}',
                    series: { style: { 'stroke': 'red' } },
                    margin: '10 5 0 10', flex: 1
                },
                {
                    xtype: 'live-request-chart',
                    title: 'Live SQL Requests', bind: '{sqlLogs}',
                    series: { style: { 'stroke': 'green' } },
                    margin: '10 10 0 5', flex: 1
                }
            ]
        },
        {
            xtype: 'container',
            flex: 1,
            layout: {
                type: 'hbox',
                align: 'stretch'
            },
            items: [
                {
                    xtype: 'historical-request-chart',
                    title: 'Last 30 Days', bind: '{historicalWebLogs}',
                    series: { style: { 'stroke': 'red' } },
                    margin: '10 5 10 10', flex: 1
                },
                {
                    xtype: 'historical-request-chart',
                    title: 'Last 30 Days', bind: '{historicalSqlLogs}',
                    series: { style: { 'stroke': 'green' } },
                    margin: '10 10 10 5', flex: 1
                }
            ]
        }

    ]
});

Rather than duplication of the configuration that forms the charts, we'll just put together the containers—which use the vbox and hbox layouts—and set up titles, formatting, and bindings.

This is great. Rather than duplicating code and having lots of unnecessary configuration in the dashboard view itself, we've moved this code to a more logical location, promoting reuse and making for a tidier code base.

Constant evaluation

At this point, let's look at what we've built in comparison with our design. It matches our implementation so far, but there wasn't any mention of these reusable classes. There are two ways of looking at this:

  • We didn't go far enough in our design and missed an opportunity to spell out exactly what we needed to build
  • We saw an opportunity to refactor our code after we found ourselves duplicating something we'd already written

There's something to be said for both these viewpoints; however, in Chapter 5, Practical – a CMS Application, we discussed YAGNI—you aren't going to need it—which dictates that there's little point in planning for reuse if the component in question is never going to be reused.

In this case, the implementation process revealed that we'd have some duplication, so we refactored. While taking careful consideration at the design stage is important, re-evaluating our decisions and code should be an ongoing process, so it's critical to realize that even if there's a feeling that something should have been noticed in the design phase, nothing is set in stone. We can always change things as long as we understand why they need to change and use it as a learning experience.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset