view.grid.js |
|||
---|---|---|---|
/*jshint multistr:true */
this.recline = this.recline || {};
this.recline.View = this.recline.View || {};
(function($, my) {
|
|||
(Data) Grid Dataset ViewProvides a tabular view on a Dataset. Initialize it with a |
my.Grid = Backbone.View.extend({
tagName: "div",
className: "recline-grid-container",
initialize: function(modelEtc) {
var self = this;
this.el = $(this.el);
_.bindAll(this, 'render', 'onHorizontalScroll');
this.model.records.bind('add', this.render);
this.model.records.bind('reset', this.render);
this.model.records.bind('remove', this.render);
this.tempState = {};
var state = _.extend({
hiddenFields: []
}, modelEtc.state
);
this.state = new recline.Model.ObjectState(state);
},
events: {
|
||
does not work here so done at end of render function 'scroll .recline-grid tbody': 'onHorizontalScroll' |
},
|
||
====================================================== Column and row menus |
setColumnSort: function(order) {
var sort = [{}];
sort[0][this.tempState.currentColumn] = {order: order};
this.model.query({sort: sort});
},
hideColumn: function() {
var hiddenFields = this.state.get('hiddenFields');
hiddenFields.push(this.tempState.currentColumn);
this.state.set({hiddenFields: hiddenFields});
|
||
change event not being triggered (because it is an array?) so trigger manually |
this.state.trigger('change');
this.render();
},
showColumn: function(e) {
var hiddenFields = _.without(this.state.get('hiddenFields'), $(e.target).data('column'));
this.state.set({hiddenFields: hiddenFields});
this.render();
},
onHorizontalScroll: function(e) {
var currentScroll = $(e.target).scrollLeft();
this.el.find('.recline-grid thead tr').scrollLeft(currentScroll);
},
|
||
====================================================== Templating |
template: ' \
|
||
TODO: move this sort of thing into a toTemplateJSON method on Dataset? |
modelData.fields = _.map(this.fields, function(field) {
return field.toJSON();
});
|
||
last header width = scroll bar - border (2px) */ |
modelData.lastHeaderWidth = this.scrollbarDimensions.width - 2;
return modelData;
},
render: function() {
var self = this;
this.fields = this.model.fields.filter(function(field) {
return _.indexOf(self.state.get('hiddenFields'), field.id) == -1;
});
this.scrollbarDimensions = this.scrollbarDimensions || this._scrollbarSize(); // skip measurement if already have dimensions
var numFields = this.fields.length;
|
||
compute field widths (-20 for first menu col + 10px for padding on each col and finally 16px for the scrollbar) |
var fullWidth = self.el.width() - 20 - 10 * numFields - this.scrollbarDimensions.width;
var width = parseInt(Math.max(50, fullWidth / numFields), 10);
|
||
if columns extend outside viewport then remainder is 0 |
var remainder = Math.max(fullWidth - numFields * width,0);
_.each(this.fields, function(field, idx) {
|
||
add the remainder to the first field width so we make up full col |
if (idx === 0) {
field.set({width: width+remainder});
} else {
field.set({width: width});
}
});
var htmls = Mustache.render(this.template, this.toTemplateJSON());
this.el.html(htmls);
this.model.records.forEach(function(doc) {
var tr = $('
|
||
hide extra header col if no scrollbar to avoid unsightly overhang |
var $tbody = this.el.find('tbody')[0];
if ($tbody.scrollHeight <= $tbody.offsetHeight) {
this.el.find('th.last-header').hide();
}
this.el.find('.recline-grid').toggleClass('no-hidden', (self.state.get('hiddenFields').length === 0));
this.el.find('.recline-grid tbody').scroll(this.onHorizontalScroll);
return this;
},
|
||
_scrollbarSizeMeasure width of a vertical scrollbar and height of a horizontal scrollbar. @return: { width: pixelWidth, height: pixelHeight } |
_scrollbarSize: function() {
var $c = $("").appendTo("body");
var dim = { width: $c.width() - $c[0].clientWidth + 1, height: $c.height() - $c[0].clientHeight };
$c.remove();
return dim;
}
});
|
||
GridRow View for rendering an individual record.Since we want this to update in place it is up to creator to provider the element to attach to. In addition you must pass in a FieldList in the constructor options. This should be list of fields for the Grid. Example: var row = new GridRow({ model: dataset-record, el: dom-element, fields: mydatasets.fields // a FieldList object }); |
my.GridRow = Backbone.View.extend({
initialize: function(initData) {
_.bindAll(this, 'render');
this._fields = initData.fields;
this.el = $(this.el);
this.model.bind('change', this.render);
},
template: ' \
{{#cells}} \
|
||
=================== Cell Editor methods |
cellEditorTemplate: ' \
\ ',
onEditClick: function(e) {
var editing = this.el.find('.data-table-cell-editor-editor');
if (editing.length > 0) {
editing.parents('.data-table-cell-value').html(editing.text()).siblings('.data-table-cell-edit').removeClass("hidden");
}
$(e.target).addClass("hidden");
var cell = $(e.target).siblings('.data-table-cell-value');
cell.data("previousContents", cell.text());
var templated = Mustache.render(this.cellEditorTemplate, {value: cell.text()});
cell.html(templated);
},
onEditorOK: function(e) {
var self = this;
var cell = $(e.target);
var rowId = cell.parents('tr').attr('data-id');
var field = cell.parents('td').attr('data-field');
var newValue = cell.parents('.data-table-cell-editor').find('.data-table-cell-editor-editor').val();
var newData = {};
newData[field] = newValue;
this.model.set(newData);
this.trigger('recline:flash', {message: "Updating row...", loader: true});
this.model.save().then(function(response) {
this.trigger('recline:flash', {message: "Row updated successfully", category: 'success'});
})
.fail(function() {
this.trigger('recline:flash', {
message: 'Error saving row',
category: 'error',
persist: true
});
});
},
onEditorCancel: function(e) {
var cell = $(e.target).parents('.data-table-cell-value');
cell.html(cell.data('previousContents')).siblings('.data-table-cell-edit').removeClass("hidden");
}
});
})(jQuery, recline.View);
|