tui-grid
Version:
TOAST UI Grid : Powerful data grid control supported by TOAST UI
963 lines (819 loc) • 30.6 kB
JavaScript
/**
* @fileoverview Rendering 모델
* @author NHN. FE Development Lab <dl_javascript@nhn.com>
*/
'use strict';
var _ = require('underscore');
var snippet = require('tui-code-snippet');
var Model = require('../base/model');
var Row = require('./row');
var RowList = require('./rowList');
var renderStateMap = require('../common/constMap').renderState;
var CELL_BORDER_WIDTH = require('../common/constMap').dimension.CELL_BORDER_WIDTH;
var DATA_LENGTH_FOR_LOADING = 1000;
/**
* View 에서 Rendering 시 사용할 객체
* @module model/renderer
* @extends module:base/model
* @param {Object} attrs - Attributes
* @param {Object} options - Options
* @ignore
*/
var Renderer = Model.extend(/** @lends module:model/renderer.prototype */{
initialize: function(attrs, options) {
var rowListOptions;
var partialLside, partialRside;
_.assign(this, {
dataModel: options.dataModel,
columnModel: options.columnModel,
focusModel: options.focusModel,
dimensionModel: options.dimensionModel,
coordRowModel: options.coordRowModel,
coordColumnModel: options.coordColumnModel
});
rowListOptions = {
dataModel: this.dataModel,
columnModel: this.columnModel,
focusModel: this.focusModel
};
partialLside = new RowList([], rowListOptions);
partialRside = new RowList([], rowListOptions);
this.set({
lside: [],
rside: [],
partialLside: partialLside,
partialRside: partialRside
});
this.listenTo(this.columnModel, 'columnModelChange change', this._onColumnModelChange)
.listenTo(this.dataModel, 'reset', this._initializeScrollValues)
.listenTo(this.dataModel, 'sort reset', this._onDataModelChange)
.listenTo(this.dataModel, 'deleteRange', this._onRangeDataModelChange)
.listenTo(this.dataModel, 'add', this._onAddDataModelChange)
.listenTo(this.dataModel, 'remove', this._onRemoveDataModelChange)
.listenTo(this.dataModel, 'beforeReset', this._onBeforeResetData)
.listenTo(this.dataModel, 'expanded ', this._onExpanded)
.listenTo(this.dataModel, 'collapsed ', this._onCollapsed)
.listenTo(this.focusModel, 'change:editingAddress', this._onEditingAddressChange)
.listenTo(partialLside, 'valueChange', this._executeRelation)
.listenTo(partialRside, 'valueChange', this._executeRelation)
.listenTo(this.coordRowModel, 'reset', this._onChangeRowHeights)
.listenTo(this.dimensionModel, 'columnWidthChanged', this.finishEditing)
.listenTo(this.dimensionModel, 'change:width', this._updateMaxScrollLeft)
.listenTo(this.dimensionModel, 'change:totalRowHeight change:scrollBarSize change:bodyHeight',
this._updateMaxScrollTop);
if (this.get('showDummyRows')) {
this.listenTo(this.dimensionModel, 'change:bodyHeight change:totalRowHeight', this._resetDummyRowCount);
this.on('change:dummyRowCount', this._resetDummyRows);
}
this.on('change', this._onChangeIndex, this);
this._onChangeLayoutBound = _.bind(this._onChangeLayout, this);
},
defaults: {
top: 0,
bottom: 0,
scrollTop: 0,
scrollLeft: 0,
maxScrollLeft: 0,
maxScrollTop: 0,
startIndex: -1,
endIndex: -1,
startNumber: 1,
lside: null,
rside: null,
partialLside: null,
partialRside: null,
showDummyRows: false,
dummyRowCount: 0,
// text that will be shown if no data to render (custom value set by user)
emptyMessage: null,
// constMap.renderState
state: renderStateMap.DONE
},
/**
* Event handler for 'expanded' event on dataModel using tree
* @param {object} ev - Event object
* @private
*/
_onExpanded: function(ev) {
var rowKeys = ev.descendantRowKeys;
var dataModel = this.dataModel;
var columnNamesMap = this._getColumnNamesOfEachSide();
var height, viewData, rowNum, viewModel, index, row;
_.each(rowKeys, function(rowKey) {
index = dataModel.indexOfRowKey(rowKey);
row = dataModel.at(index);
height = this.coordRowModel.getHeightAt(index);
_.each(['lside', 'rside'], function(attrName) {
rowNum = index + 1;
viewData = this._createViewDataFromDataModel(
row, columnNamesMap[attrName], height, rowNum);
viewModel = this._createRowModel(viewData, true);
this.get(attrName)[index] = viewModel;
}, this);
}, this);
this._setRenderingRange();
this.refresh({
type: 'add',
dataListChanged: true
});
},
/**
* Event handler for 'collapsed' event on dataModel using tree
* @param {object} ev - Event object
* @private
*/
_onCollapsed: function(ev) {
var rowKeys = ev.descendantRowKeys;
_.each(rowKeys, function(rowKey) {
var index = this.dataModel.indexOfRowKey(rowKey);
_.each(['lside', 'rside'], function(attrName) {
delete this.get(attrName)[index];
}, this);
}, this);
this._setRenderingRange();
this.refresh({
type: 'deleteRange',
dataListChanged: true
});
},
/**
* Event handler for change:scrollTop and change:scrollLeft.
* @private
*/
_onChangeLayout: function() {
this.focusModel.finishEditing();
this.focusModel.focusClipboard();
},
/**
* Event handler for changing startIndex or endIndex.
* @param {Object} model - Renderer model fired event
* @private
*/
_onChangeIndex: function(model) {
var changedData = model.changed;
var changedStartIndex = _.has(changedData, 'startIndex');
var changedEndIndex = _.has(changedData, 'endIndex');
if (changedStartIndex || changedEndIndex) {
this.refresh();
}
},
/**
* Event handler for 'reset' event on coordRowModel
* @private
*/
_onChangeRowHeights: function() {
var lside = this.get('partialLside');
var rside = this.get('partialRside');
var i = 0;
var len = lside.length;
var rowKey, height;
for (; i < len; i += 1) {
rowKey = lside.at(i).get('rowKey');
height = this.coordRowModel.getHeight(rowKey);
lside.at(i).set('height', height);
rside.at(i).set('height', height);
}
},
/**
* Event handler for 'change:width' event on Dimension.
* @private
*/
_updateMaxScrollLeft: function() {
var dimension = this.dimensionModel;
var maxScrollLeft = this.coordColumnModel.getFrameWidth('R') - dimension.get('rsideWidth') +
dimension.getScrollYWidth();
this.set('maxScrollLeft', maxScrollLeft);
},
/**
* Event handler to reset 'maxScrollTop' attribute.
* @private
*/
_updateMaxScrollTop: function() {
var dimension = this.dimensionModel;
var maxScrollTop = dimension.get('totalRowHeight') - dimension.get('bodyHeight') +
dimension.getScrollXHeight();
this.set('maxScrollTop', maxScrollTop);
},
/**
* Event handler for 'beforeReset' event on dataModel
* @param {number} dataLength - the length of data
* @private
*/
_onBeforeResetData: function(dataLength) {
if (dataLength > DATA_LENGTH_FOR_LOADING) {
this.set('state', renderStateMap.LOADING);
}
},
/**
* Event handler for 'change:editingAddress' event on focusModel
* @param {module:model/focus} focusModel - focus model
* @param {{rowKey: Number, columnName: String}} address - address
* @private
*/
_onEditingAddressChange: function(focusModel, address) {
var target = address;
var editing = true;
var self = this;
if (!address) {
target = focusModel.previous('editingAddress');
editing = false;
}
this._updateCellData(target.rowKey, target.columnName, {
editing: editing
});
this._triggerEditingStateChanged(target.rowKey, target.columnName);
// defered call to prevent 'change:scrollLeft' or 'change:scrollTop' event
// triggered by module:view/layout/body._onScroll()
// when module:model/focus.scrollToFocus() method is called.
_.defer(function() {
self._toggleChangeLayoutEventHandlers(editing);
});
},
/**
* Toggle event handler for change:scrollTop and change:scrollLeft event.
* @param {Boolean} editing - whether currently editing
* @private
*/
_toggleChangeLayoutEventHandlers: function(editing) {
var renderEventName = 'change:scrollTop change:scrollLeft';
var dimensionEventName = 'columnWidthChanged';
if (editing) {
this.listenToOnce(this.dimensionModel, dimensionEventName, this._onChangeLayoutBound);
this.once(renderEventName, this._onChangeLayoutBound);
} else {
this.stopListening(this.dimensionModel, dimensionEventName, this._onChangeLayoutBound);
this.off(renderEventName, this._onChangeLayoutBound);
}
},
/**
* Triggers the 'editingStateChanged' event if the cell data identified by
* given row key and column name has the useViewMode:true option.
* @param {String} rowKey - row key
* @param {String} columnName - column name
* @private
*/
_triggerEditingStateChanged: function(rowKey, columnName) {
var cellData = this.getCellData(rowKey, columnName);
if (snippet.pick(cellData, 'columnModel', 'editOptions', 'useViewMode') !== false &&
cellData.convertedHTML === null) {
this.trigger('editingStateChanged', cellData);
}
},
/**
* Updates the view-data of the cell identified by given rowKey and columnName.
* @param {(String|Number)} rowKey - row key
* @param {String} columnName - column name
* @param {Object} cellData - cell data
* @private
*/
_updateCellData: function(rowKey, columnName, cellData) {
var rowModel = this._getRowModel(rowKey, columnName);
if (rowModel) {
rowModel.setCell(columnName, cellData);
}
},
/**
* Update data of tree-cell
* @param {number} rowKey - row key
* @private
*/
_updateTreeCellData: function(rowKey) {
var columnName = this.columnModel.getTreeColumnName();
var rowModel = this._getRowModel(rowKey, columnName);
if (rowModel) {
rowModel.setCell(columnName, {
hasChildren: this.dataModel.get(rowKey).hasTreeChildren()
});
}
},
/**
* Initializes own properties.
* (called by module:addon/net)
*/
initializeVariables: function() {
this.set({
top: 0,
scrollTop: 0,
scrollLeft: 0,
startNumber: 1
});
},
/**
* Initializes values of the scroll
* @private
*/
_initializeScrollValues: function() {
this.set({
scrollTop: 0,
scrollLeft: 0
});
},
/**
* 열고정 영역 또는 열고정이 아닌 영역에 대한 Render Collection 을 반환한다.
* @param {String} [whichSide='R'] 어느 영역인지 여부. 'L|R' 중에 하나의 값을 넘긴다.
* @returns {Object} collection 해당 영역의 랜더 데이터 콜랙션
*/
getCollection: function(whichSide) {
var attrName = this._getPartialWhichSideType(whichSide);
return this.get(attrName);
},
/**
* Get string of partial which side type
* @param {string} whichSide - Type of which side (L|R)
* @returns {string} String of appened prefix value 'partial'
* @private
*/
_getPartialWhichSideType: function(whichSide) {
return snippet.isString(whichSide) ? 'partial' + whichSide + 'side' : 'partialRside';
},
/**
* Event handler for regenerating left and right side frames when the Data.ColumnModel is changed
* @private
*/
_onColumnModelChange: function() {
var scrollLeftBeforeChange = this.get('scrollLeft');
var scrollTopBeforeChange = this.get('scrollTop');
this.set({
scrollLeft: 0,
scrollTop: 0
}, {silent: true});
this._resetViewModelList();
this._setRenderingRange(true);
this.refresh({
columnModelChanged: true
});
this._updateMaxScrollLeft();
this.set({
scrollLeft: scrollLeftBeforeChange,
scrollTop: scrollTopBeforeChange
});
},
/**
* Event handler for changing data list
* @private
*/
_onDataModelChange: function() {
this._resetViewModelList();
this._setRenderingRange(true);
this.refresh({
type: 'reset',
dataListChanged: true
});
},
/**
* Event handler for adding data list
* @param {array} rowList - List of added item
* @param {object} options - Info of added item
* @private
*/
_onAddDataModelChange: function(rowList, options) {
var columnNamesMap = this._getColumnNamesOfEachSide();
var at = options.at;
var height, viewData, rowNum;
var viewModel;
_.each(rowList, function(row, index) {
height = this.coordRowModel.getHeightAt(index);
_.each(['lside', 'rside'], function(attrName) {
rowNum = at + index + 1;
viewData = this._createViewDataFromDataModel(
row, columnNamesMap[attrName], height, rowNum);
viewModel = this._createRowModel(viewData, true);
this.get(attrName).splice(at + index, 0, viewModel);
}, this);
}, this);
this._updateTreeCellData(options.parentRowKey);
this._setRenderingRange(true);
this.refresh({
type: 'add',
dataListChanged: true
});
if (options.focus) {
this.focusModel.focusAt(options.at, 0);
}
},
/**
* Event handler for removing data list
* @param {number|string} rowKey - rowKey of the removed row
* @param {number} removedIndex - Index of the removed row
* @param {Array.<number>} [descendantRowKeys] - All descendants key of the removed when using tree
* @param {number} [parentRowKey] - Parent key of the removed row when using tree
* @private
*/
_onRemoveDataModelChange: function(rowKey, removedIndex, descendantRowKeys, parentRowKey) {
var removedRowsCnt = descendantRowKeys ? descendantRowKeys.length : 1;
_.each(['lside', 'rside'], function(attrName) {
this.get(attrName).splice(removedIndex, removedRowsCnt);
}, this);
this._updateTreeCellData(parentRowKey);
this._setRenderingRange(true);
this.refresh({
dataListChanged: true
});
},
/**
* Event handler for deleting cell data
* @param {GridEvent} ev - event object when "delRange" event is fired
* @private
*/
_onRangeDataModelChange: function(ev) {
var columnModel = this.columnModel;
var rowKeys = ev.rowKeys;
var columnNames = ev.columnNames;
this._setRenderingRange(true);
_.each(['partialLside', 'partialRside'], function(attrName) {
_.each(this.get(attrName).models, function(model) {
var rowKey = model.get('rowKey');
var changedRow = _.contains(rowKeys, rowKey);
if (changedRow) {
_.each(columnNames, function(columnName) {
if (columnModel.getColumnModel(columnName).editOptions) {
this._updateCellData(rowKey, columnName, {
value: '',
formattedValue: ''
});
}
}, this);
}
}, this);
}, this);
this.refresh({
type: 'deleteRange',
dataListChanged: true
});
},
/**
* Resets dummy rows and trigger 'dataListChanged' event.
* @private
*/
_resetDummyRows: function() {
this._clearDummyRows();
this._fillDummyRows();
this.trigger('rowListChanged');
},
/**
* Set index-range to render
* @param {boolean} silent - whether set attributes silently
* @private
*/
_setRenderingRange: function(silent) {
var dataLength = this.dataModel.length;
this.set({
startIndex: dataLength ? 0 : -1,
endIndex: dataLength - 1
}, {
silent: silent
});
},
/**
* Returns the new data object for rendering based on rowDataModel and specified column names.
* @param {Object} rowDataModel - Instance of module:model/data/row
* @param {Array.<String>} columnNames - Column names
* @param {Number} height - the height of the row
* @param {Number} rowNum - Row number
* @returns {Object} - view data object
* @private
*/
_createViewDataFromDataModel: function(rowDataModel, columnNames, height, rowNum) {
var viewData = {
rowNum: rowNum,
height: height,
rowKey: rowDataModel.get('rowKey'),
_extraData: rowDataModel.get('_extraData')
};
_.each(columnNames, function(columnName) {
var value = rowDataModel.get(columnName);
if (columnName === '_number' && !_.isNumber(value)) {
value = rowNum;
}
viewData[columnName] = value;
});
return viewData;
},
/**
* Returns the object that contains two array of column names splitted by frozenCount.
* @returns {{lside: Array, rside: Array}} - Column names map
* @private
*/
_getColumnNamesOfEachSide: function() {
var frozenCount = this.columnModel.getVisibleFrozenCount(true);
var columnModels = this.columnModel.getVisibleColumns(null, true);
var columnNames = _.pluck(columnModels, 'name');
return {
lside: columnNames.slice(0, frozenCount),
rside: columnNames.slice(frozenCount)
};
},
/**
* Add view model list by range
* @param {number} startIndex - Index of start row
* @param {number} endIndex - Index of end row
* @private
*/
_addViewModelListWithRange: function(startIndex, endIndex) {
var dataModel = this.dataModel;
var columnNamesMap = this._getColumnNamesOfEachSide();
var index, row, height;
if (startIndex < 0 || endIndex < 0) {
return;
}
for (index = startIndex; index < endIndex + 1; index += 1) {
row = dataModel.at(index);
height = this.coordRowModel.getHeightAt(index);
if (dataModel.isVisibleRow(row.get('rowKey'))) {
this._addViewModelList(row, columnNamesMap, height, index);
}
}
},
/**
* Add view model list on each side
* @param {object} rowDataModel - Data model of row
* @param {object} columnNamesMap - Map of column names
* @param {number} height - Height of row
* @param {number} index - Index of row
* @private
*/
_addViewModelList: function(rowDataModel, columnNamesMap, height, index) {
_.each(['lside', 'rside'], function(attrName) {
var viewData;
if (!this.get(attrName)[index]) {
viewData = this._createViewDataFromDataModel(
rowDataModel, columnNamesMap[attrName], height, index + 1);
this.get(attrName)[index] = this._createRowModel(viewData, true);
}
}, this);
},
/**
* Update the row number
* @param {number} startIndex - Start index
* @param {number} endIndex - End index
* @private
*/
_updateRowNumber: function(startIndex, endIndex) {
var collection = this.get('lside');
var index = startIndex;
var currentModel, rowNum, newRowNum;
for (; index <= endIndex; index += 1) {
currentModel = collection[index];
newRowNum = index + 1;
if (currentModel) {
rowNum = currentModel.get('rowNum');
newRowNum = index + this.get('startNumber');
if (rowNum !== newRowNum) {
currentModel.set({
rowNum: newRowNum
}, {
silent: true
});
currentModel.setCell('_number', {
formattedValue: newRowNum,
value: newRowNum
});
}
}
}
},
/**
* Reset partial view model list
* @param {number} startIndex - Index of start row
* @param {number} endIndex - Index of end row
* @private
*/
_resetPartialViewModelList: function(startIndex, endIndex) {
var originalWhichSide, partialWhichSide;
var viewModelList, partialViewModelList;
_.each(['L', 'R'], function(whichSide) {
partialViewModelList = [];
originalWhichSide = whichSide.toLowerCase() + 'side';
partialWhichSide = this._getPartialWhichSideType(whichSide);
viewModelList = this.get(originalWhichSide);
partialViewModelList = this._getPartialViewModelList(viewModelList, startIndex, endIndex);
this.get(partialWhichSide).reset(partialViewModelList);
}, this);
},
/**
* Get partial view model list
* @param {Array.<obejct>} viewModelList - List of view model
* @param {number} startIndex - Index of start row
* @param {number} endIndex - Index of end row
* @returns {Array.<module:model/data/row>}>} List of partial view model
* @private
*/
_getPartialViewModelList: function(viewModelList, startIndex, endIndex) {
var index = startIndex;
var len = endIndex + 1;
var partialViewModelList = [];
var viewModel;
for (; index < len; index += 1) {
viewModel = viewModelList[index];
if (viewModel && this.dataModel.isVisibleRow(viewModel.get('rowKey'))) {
partialViewModelList.push(viewModel);
}
}
return partialViewModelList;
},
/**
* Returns the count of rows (except dummy rows) in view model list
* @returns {Number} Count of rows
* @private
*/
_getActualRowCount: function() {
return this.get('endIndex') - this.get('startIndex') + 1;
},
/**
* Removes all dummy rows in the view model list.
* @private
*/
_clearDummyRows: function() {
var dataRowCount = this.get('endIndex') - this.get('startIndex') + 1;
_.each(['lside', 'rside'], function(attrName) {
var rowList = this.get(attrName);
while (rowList.length > dataRowCount) {
rowList.pop();
}
}, this);
},
/**
* Calculate required count of dummy rows and set the 'dummyRowCount' attribute.
* @param {boolean} silent - whether sets the dummyRowCount silently
* @private
*/
_resetDummyRowCount: function() {
var dimensionModel = this.dimensionModel;
var totalRowHeight = dimensionModel.get('totalRowHeight');
var rowHeight = dimensionModel.get('rowHeight') + CELL_BORDER_WIDTH;
var bodyHeight = dimensionModel.get('bodyHeight') - dimensionModel.getScrollXHeight();
var dummyRowCount = 0;
if (totalRowHeight < bodyHeight) {
dummyRowCount = Math.ceil((bodyHeight - totalRowHeight) / rowHeight);
}
this.set('dummyRowCount', dummyRowCount);
},
/**
* fills the empty area with dummy rows.
* @private
*/
_fillDummyRows: function() {
var dummyRowCount = this.get('dummyRowCount');
var rowNum, rowHeight;
if (dummyRowCount) {
rowNum = this.get('startNumber') + this.get('endIndex') + 1;
rowHeight = this.dimensionModel.get('rowHeight');
_.times(dummyRowCount, function() {
_.each(['partialLside', 'partialRside'], function(listName) {
this.get(listName).push(this._createRowModel({
height: rowHeight,
rowNum: rowNum
}));
}, this);
rowNum += 1;
}, this);
}
},
/* eslint-disable complexity */
/**
* Refreshes the rendering range and the list of view models, and triggers events.
* @param {object} options - options
* @param {boolean} [options.columnModelChanged] - The boolean value whether columnModel has changed
* @param {boolean} [options.dataListChanged] - The boolean value whether dataModel has changed
* @param {string} [options.type] - Event type (reset|add|remove)
*/
refresh: function(options) {
var columnModelChanged = !!options && options.columnModelChanged;
var dataListChanged = !!options && options.dataListChanged;
var eventType = !!options && options.type;
var startIndex, endIndex, i;
startIndex = this.get('startIndex');
endIndex = this.get('endIndex');
if (eventType !== 'add' && eventType !== 'deleteRange') {
this._addViewModelListWithRange(startIndex, endIndex);
}
if (eventType !== 'deleteRange') {
this._updateRowNumber(startIndex, endIndex);
}
this._resetPartialViewModelList(startIndex, endIndex);
this._fillDummyRows();
if (startIndex >= 0 && endIndex >= 0) {
for (i = startIndex; i <= endIndex; i += 1) {
this._executeRelation(i);
}
}
if (columnModelChanged) {
this.trigger('columnModelChanged');
} else {
this.trigger('rowListChanged', dataListChanged);
if (dataListChanged) {
this.coordRowModel.syncWithDom();
}
}
this._refreshState();
},
/* eslint-enable complexity */
/**
* Set state value based on the DataModel.length
* @private
*/
_refreshState: function() {
if (this.dataModel.length) {
this.set('state', renderStateMap.DONE);
} else {
this.set('state', renderStateMap.EMPTY);
}
},
/**
* columnName 으로 lside 와 rside rendering collection 중 하나를 반환한다.
* @param {String} columnName 컬럼명
* @returns {Collection} 컬럼명에 해당하는 영역의 콜랙션
* @private
*/
_getCollectionByColumnName: function(columnName) {
var lside = this.get('partialLside');
var collection;
if (lside.at(0) && lside.at(0).get(columnName)) {
collection = lside;
} else {
collection = this.get('partialRside');
}
return collection;
},
/**
* Returns the specified row model.
* @param {(Number|String)} rowKey - row key
* @param {String} columnName - column name
* @returns {module:model/row}
* @private
*/
_getRowModel: function(rowKey, columnName) {
var collection = this._getCollectionByColumnName(columnName);
return collection.get(rowKey);
},
/**
* 셀 데이터를 반환한다.
* @param {number} rowKey 데이터의 키값
* @param {String} columnName 컬럼명
* @returns {object} cellData 셀 데이터
* @example
{
rowKey: rowKey,
columnName: columnName,
value: value,
rowSpan: rowSpanData.count,
isMainRow: rowSpanData.isMainRow,
mainRowKey: rowSpanData.mainRowKey,
editable: editable,
disabled: disabled,
listItems: [],
className: row.getClassNameList(columnName).join(' '),
changed: [] //names of changed properties
}
*/
getCellData: function(rowKey, columnName) {
var row = this._getRowModel(rowKey, columnName);
var cellData = null;
if (row) {
cellData = row.get(columnName);
}
return cellData;
},
/**
* Executes the relation of the row identified by rowIndex
* @param {Number} rowIndex - Row index
* @private
*/
_executeRelation: function(rowIndex) {
var row = this.dataModel.at(rowIndex);
var renderIdx = rowIndex - this.get('startIndex');
var rowModel, relationResult;
relationResult = row.executeRelationCallbacksAll();
_.each(relationResult, function(changes, columnName) {
rowModel = this._getCollectionByColumnName(columnName).at(renderIdx);
if (rowModel) {
rowModel.setCell(columnName, changes);
}
}, this);
},
/**
* Create row model
* @param {object} attrs - Attributes to create
* @param {boolean} parse - Whether calling parse or not
* @returns {object} Row model
* @private
*/
_createRowModel: function(attrs, parse) {
return new Row(attrs, {
parse: parse,
dataModel: this.dataModel,
columnModel: this.columnModel,
focusModel: this.focusModel
});
},
/**
* Reset view models when value of columModel or dataModel is changed
* @private
*/
_resetViewModelList: function() {
_.each(['lside', 'rside'], function(attrName) {
this.set(attrName, new Array(this.dataModel.length));
}, this);
}
});
module.exports = Renderer;