[#19,routing][m]: introduce Backbone routing and use it for navigation between grid and graph views.
Some substantial other related changes: * Switched DataExplorer view to have element it attaches to passed in rather than being created internally (this was important for FlotGraph to set up correctly). * Also refactored DataExplorer setup somewhat (e.g. got rid of draw function which wasn't needed -- merging necessary parts with initialize) * Substantial refactoring of FlotGraph so that we correctly work around flot's delicateness regarding rendering (element must not be hidden etc -- see comments in code) * Also generally cleaner code
This commit is contained in:
@@ -4,9 +4,9 @@ $(function() {
|
|||||||
window.$container = $('.data-explorer-here');
|
window.$container = $('.data-explorer-here');
|
||||||
var dataset = demoDataset();
|
var dataset = demoDataset();
|
||||||
window.dataExplorer = new recline.View.DataExplorer({
|
window.dataExplorer = new recline.View.DataExplorer({
|
||||||
model: dataset
|
el: window.$container
|
||||||
|
, model: dataset
|
||||||
});
|
});
|
||||||
window.$container.append(window.dataExplorer.el);
|
|
||||||
setupLoadFromWebstore(function(dataset) {
|
setupLoadFromWebstore(function(dataset) {
|
||||||
window.dataExplorer.remove();
|
window.dataExplorer.remove();
|
||||||
window.dataExplorer = null;
|
window.dataExplorer = null;
|
||||||
|
|||||||
146
src/view.js
146
src/view.js
@@ -7,6 +7,10 @@ var my = {};
|
|||||||
|
|
||||||
// The primary view for the entire application.
|
// The primary view for the entire application.
|
||||||
//
|
//
|
||||||
|
// It should be initialized with a recline.Model.Dataset object and an existing
|
||||||
|
// dom element to attach to (the existing DOM element is important for
|
||||||
|
// rendering of FlotGraph subview).
|
||||||
|
//
|
||||||
// To pass in configuration options use the config key in initialization hash
|
// To pass in configuration options use the config key in initialization hash
|
||||||
// e.g.
|
// e.g.
|
||||||
//
|
//
|
||||||
@@ -22,12 +26,11 @@ var my = {};
|
|||||||
//
|
//
|
||||||
// All other views as contained in this one.
|
// All other views as contained in this one.
|
||||||
my.DataExplorer = Backbone.View.extend({
|
my.DataExplorer = Backbone.View.extend({
|
||||||
tagName: 'div',
|
|
||||||
className: 'data-explorer',
|
|
||||||
template: ' \
|
template: ' \
|
||||||
|
<div class="data-explorer"> \
|
||||||
<div class="header"> \
|
<div class="header"> \
|
||||||
<ul class="navigation"> \
|
<ul class="navigation"> \
|
||||||
<li class="active"><a href="#datatable" class="btn">Grid</a> \
|
<li class="active"><a href="#grid" class="btn">Grid</a> \
|
||||||
<li><a href="#graph" class="btn">Graph</a></li> \
|
<li><a href="#graph" class="btn">Graph</a></li> \
|
||||||
</ul> \
|
</ul> \
|
||||||
<div class="pagination"> \
|
<div class="pagination"> \
|
||||||
@@ -48,14 +51,15 @@ my.DataExplorer = Backbone.View.extend({
|
|||||||
<img src="images/small-spinner.gif" class="notification-loader"><span class="notification-message">Loading...</span> \
|
<img src="images/small-spinner.gif" class="notification-loader"><span class="notification-message">Loading...</span> \
|
||||||
</div> \
|
</div> \
|
||||||
</div> \
|
</div> \
|
||||||
|
</div> \
|
||||||
',
|
',
|
||||||
|
|
||||||
events: {
|
events: {
|
||||||
'click .navigation li a': 'navChange',
|
|
||||||
'submit form.display-count': 'onDisplayCountUpdate'
|
'submit form.display-count': 'onDisplayCountUpdate'
|
||||||
},
|
},
|
||||||
|
|
||||||
initialize: function(options) {
|
initialize: function(options) {
|
||||||
|
var self = this;
|
||||||
this.el = $(this.el);
|
this.el = $(this.el);
|
||||||
this.config = options.config || {};
|
this.config = options.config || {};
|
||||||
_.extend(this.config, {
|
_.extend(this.config, {
|
||||||
@@ -65,35 +69,34 @@ my.DataExplorer = Backbone.View.extend({
|
|||||||
if (this.config.readOnly) {
|
if (this.config.readOnly) {
|
||||||
this.setReadOnly();
|
this.setReadOnly();
|
||||||
}
|
}
|
||||||
this.draw();
|
// Hash of 'page' views (i.e. those for whole page) keyed by page name
|
||||||
|
this.pageViews = {
|
||||||
|
grid: new my.DataTable({
|
||||||
|
model: this.model
|
||||||
|
})
|
||||||
|
, graph: new my.FlotGraph({
|
||||||
|
model: this.model
|
||||||
|
})
|
||||||
|
};
|
||||||
|
// this must be called after pageViews are created
|
||||||
|
this.render();
|
||||||
|
|
||||||
|
this.router = new Backbone.Router();
|
||||||
|
this.setupRouting();
|
||||||
|
Backbone.history.start();
|
||||||
|
|
||||||
|
// retrieve basic data like headers etc
|
||||||
|
// note this.model and dataset returned are the same
|
||||||
|
this.model.fetch().then(function(dataset) {
|
||||||
|
// initialize of dataTable calls render
|
||||||
|
self.model.getDocuments(self.config.displayCount);
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
onDisplayCountUpdate: function(e) {
|
onDisplayCountUpdate: function(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
this.config.displayCount = parseInt(this.el.find('input[name="displayCount"]').val());
|
this.config.displayCount = parseInt(this.el.find('input[name="displayCount"]').val());
|
||||||
this.draw();
|
this.model.getDocuments(this.config.displayCount);
|
||||||
},
|
|
||||||
|
|
||||||
draw: function() {
|
|
||||||
var self = this;
|
|
||||||
this.el.empty();
|
|
||||||
// retrieve basic data like headers etc
|
|
||||||
// note this.model and dataset returned are the same
|
|
||||||
this.model.fetch().then(function(dataset) {
|
|
||||||
self.render();
|
|
||||||
self.$dataViewContainer = self.el.find('.data-view-container');
|
|
||||||
// initialize of dataTable calls render
|
|
||||||
self.dataTable = new my.DataTable({
|
|
||||||
model: dataset
|
|
||||||
});
|
|
||||||
self.flotGraph = new my.FlotGraph({
|
|
||||||
model: dataset
|
|
||||||
});
|
|
||||||
self.flotGraph.el.hide();
|
|
||||||
self.$dataViewContainer.append(self.dataTable.el)
|
|
||||||
self.$dataViewContainer.append(self.flotGraph.el);
|
|
||||||
self.model.getDocuments(self.config.displayCount);
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
|
|
||||||
setReadOnly: function() {
|
setReadOnly: function() {
|
||||||
@@ -105,25 +108,40 @@ my.DataExplorer = Backbone.View.extend({
|
|||||||
tmplData.displayCount = this.config.displayCount;
|
tmplData.displayCount = this.config.displayCount;
|
||||||
var template = $.mustache(this.template, tmplData);
|
var template = $.mustache(this.template, tmplData);
|
||||||
$(this.el).html(template);
|
$(this.el).html(template);
|
||||||
|
var $dataViewContainer = this.el.find('.data-view-container');
|
||||||
|
_.each(this.pageViews, function(view, pageName) {
|
||||||
|
$dataViewContainer.append(view.el)
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
navChange: function(e) {
|
setupRouting: function() {
|
||||||
// TODO: really ugly and will not scale to more widgets ...
|
var self = this;
|
||||||
var widgetToShow = $(e.target).attr('href').slice(1);
|
this.router.route('', 'grid', function() {
|
||||||
|
self.updateNav('grid');
|
||||||
|
});
|
||||||
|
this.router.route('grid', 'grid', function() {
|
||||||
|
self.updateNav('grid');
|
||||||
|
});
|
||||||
|
this.router.route('graph', 'graph', function() {
|
||||||
|
self.updateNav('graph');
|
||||||
|
// we have to call here due to fact plot cannot draw if hidden
|
||||||
|
// see comments in FlotGraph.redraw
|
||||||
|
self.pageViews['graph'].redraw();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
updateNav: function(pageName) {
|
||||||
this.el.find('.navigation li').removeClass('active');
|
this.el.find('.navigation li').removeClass('active');
|
||||||
$(e.target).parent().addClass('active');
|
var $el = this.el.find('.navigation li a[href=#' + pageName + ']');
|
||||||
if (widgetToShow == 'datatable') {
|
$el.parent().addClass('active');
|
||||||
this.flotGraph.el.hide();
|
// show the specific page
|
||||||
this.dataTable.el.show();
|
_.each(this.pageViews, function(view, pageViewName) {
|
||||||
} else if (widgetToShow == 'graph') {
|
if (pageViewName === pageName) {
|
||||||
this.flotGraph.el.show();
|
view.el.show();
|
||||||
this.dataTable.el.hide();
|
} else {
|
||||||
// Have to call this here
|
view.el.hide();
|
||||||
// If you attempt to render with flot when graph is hidden / invisible flot will complain with
|
|
||||||
// Invalid dimensions for plot, width = 0, height = 0
|
|
||||||
// (Could hack this by moving plot left -1000 or similar ...)
|
|
||||||
this.flotGraph.createPlot();
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -641,15 +659,18 @@ my.FlotGraph = Backbone.View.extend({
|
|||||||
initialize: function(options, chart) {
|
initialize: function(options, chart) {
|
||||||
var self = this;
|
var self = this;
|
||||||
this.el = $(this.el);
|
this.el = $(this.el);
|
||||||
_.bindAll(this, 'render');
|
_.bindAll(this, 'render', 'redraw');
|
||||||
this.model.currentDocuments.bind('add', this.render);
|
// we need the model.headers to render properly
|
||||||
this.model.currentDocuments.bind('reset', this.render);
|
this.model.bind('change', this.render);
|
||||||
|
this.model.currentDocuments.bind('add', this.redraw);
|
||||||
|
this.model.currentDocuments.bind('reset', this.redraw);
|
||||||
this.chart = chart;
|
this.chart = chart;
|
||||||
this.chartConfig = {
|
this.chartConfig = {
|
||||||
group: null,
|
group: null,
|
||||||
series: [],
|
series: [],
|
||||||
graphType: 'line'
|
graphType: 'line'
|
||||||
};
|
};
|
||||||
|
this.render();
|
||||||
},
|
},
|
||||||
|
|
||||||
toTemplateJSON: function() {
|
toTemplateJSON: function() {
|
||||||
@@ -671,6 +692,29 @@ my.FlotGraph = Backbone.View.extend({
|
|||||||
onEditorSubmit: function(e) {
|
onEditorSubmit: function(e) {
|
||||||
var select = this.el.find('.editor-group select');
|
var select = this.el.find('.editor-group select');
|
||||||
this._getEditorData();
|
this._getEditorData();
|
||||||
|
this.redraw();
|
||||||
|
},
|
||||||
|
|
||||||
|
redraw: function() {
|
||||||
|
// There appear to be issues generating a Flot graph if either:
|
||||||
|
|
||||||
|
// * The relevant div that graph attaches to his hidden at the moment of creating the plot -- Flot will complain with
|
||||||
|
//
|
||||||
|
// Uncaught Invalid dimensions for plot, width = 0, height = 0
|
||||||
|
// * There is no data for the plot -- either same error or may have issues later with errors like 'non-existent node-value'
|
||||||
|
var areWeVisible = !jQuery.expr.filters.hidden(this.el[0]);
|
||||||
|
if (!this.plot && (!areWeVisible || this.model.currentDocuments.length == 0)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// create this.plot and cache it
|
||||||
|
if (!this.plot) {
|
||||||
|
// only lines for the present
|
||||||
|
options = {
|
||||||
|
id: 'line',
|
||||||
|
name: 'Line Chart'
|
||||||
|
};
|
||||||
|
this.plot = $.plot(this.$graph, this.createSeries(), options);
|
||||||
|
}
|
||||||
this.plot.setData(this.createSeries());
|
this.plot.setData(this.createSeries());
|
||||||
this.plot.resize();
|
this.plot.resize();
|
||||||
this.plot.setupGrid();
|
this.plot.setupGrid();
|
||||||
@@ -686,16 +730,6 @@ my.FlotGraph = Backbone.View.extend({
|
|||||||
this.chartConfig.group = this.el.find('.editor-group select').val();
|
this.chartConfig.group = this.el.find('.editor-group select').val();
|
||||||
},
|
},
|
||||||
|
|
||||||
createPlot: function () {
|
|
||||||
// only lines for the present
|
|
||||||
options = {
|
|
||||||
id: 'line',
|
|
||||||
name: 'Line Chart'
|
|
||||||
};
|
|
||||||
this.plot = $.plot(this.$graph, this.createSeries(), options);
|
|
||||||
return this;
|
|
||||||
},
|
|
||||||
|
|
||||||
createSeries: function () {
|
createSeries: function () {
|
||||||
var self = this;
|
var self = this;
|
||||||
var series = [];
|
var series = [];
|
||||||
|
|||||||
Reference in New Issue
Block a user