Skip to content

Latest commit

 

History

History
90 lines (57 loc) · 5.37 KB

MODEL_CHANGES.md

File metadata and controls

90 lines (57 loc) · 5.37 KB

Model Changes

Motivation:

Two problems motivate the creation of this new model. The first is to make the editing experience faster. Our old model did expensive string operations on each edit. Our new model defers string operations until saving. The second is to avoid saving a whole copy of the string to the data store on each change. This would be prohibitively expensive for memory on large data sets.

Original Model:

How the original model works and what problems it poses

Our model originally was based on directly changing the data of the csv file on every change to the grid. The data is stored as a string, so if you had an editor open that looked like this image

then the string would look like "1,2,3\n4,5,6\n7,8,9". If you added a row 1, we make this change appear by modifing the string so that it looked like ",,,\n1,2,3\n4,5,6\n7,8,9". Seems simple enough, but what happens if you want to undo the change you made? We have to options. One is to save a copy of the previous string in our data store (which is what we did). This means that the memory requirement for the program is roughly nm, where n is the number of changes and m is the size of the string. You can see how this blows up incredibly fast, even for relatively small data sets. The other option is to actually do an inverse operation on the original string when the undo command is executed. This is what we intially tried to do when we were first considering how to implement undoing. While it does fix the memory problem, it is very tricky to implement and doesn't fix the problem of string operations a long time.

The New Model:

How the new model works and the benefits of it

Our new model doesn't directly modify this string until you save the file. To understand what the new model is doing we have to dig a bit deeper into how the grid displays the correct values in each cell. The grid asks a class called DSVModel what is at each row/column position by calling a one of DSVModel's methods called data. So, the DataGrid would first call this._model.data('body', 0, 0) to figure out what should be in the first row and first column (the body argument specifies what region the DataGrid is looking at). This method would return 1, since 1 is the value in the first row, first column cell.

Our new model is called EditorModel and it sits between The data grid and the model. When The data grid calls data, it is actually calling EditorModel's data method. EditorModel's data function In turn calls the DSVModel's data function. But first we do some pre-processing. We map the requested row and column to the row and column That corresponds to where the field lies. We do this with two arrays, which we call the rowMap and the columnMap. Here is a visual of what this looks like.

init-state

When the grid queries for the top left body cell, it does data('body', 0, 0). Our code does something like this.

data(region, row, column) {
  const row = rowMap[row]; // rowMap[0] = 0;
  const column = columnMap[column]; // columnMap[0] = 0;
  return this._model.data(region, row, column) // data('body', 0, 0) = 1.
}

Now, let's say we added a column. The new picture looks like this.

column-added

We'll cover why the 3 is negative. First, notice that when the datgrid asks for what is in, say, row 0 column 2, our function passes on to the DSVModel's data method the question "what is in row 0 column 1". This data method then returns the exact value that was in row 0 column 1 before the change. But what about if the grid asks for a value in the B column? Here is where the negative comes in. We can add a little if statement to our data function.

const row = rowMap[row];
const column = columnMap[column];

if (row < 0 || column < 0) {
  return '';
}

This is sufficient to handle moving, deleting, or adding rows/columns. But what if the user inputs data directly into one of the cells. Suppose in the previous example, we input a value hello world into the first entry of the B column (corresponding to row 0 column 1). How would we know not to just return '' when this value is queried for? What we do is set up a dictionary (called a MapField in DataStore terminology) that records specific values. So when the user put in hello world, we would add the following key, value pair to what we call our valueMap.

{ '0,1': 'hello world' }

And we add the following if statement to our data function.

const row = rowMap[row];
const column = columnMap[column];

if (valueMap[`${row},${column}`]) {
  return valueMap[`${row},${column}`]
}

if (row < 0 || column < 0) {
  return '';
}
...

That is essentially how the new model works. Here is a visual that sums it up pretty well. New Model with Example

TODOs:

  • editing single cell working
  • adding/removing/moving rows columns working
  • cut/copy/paste working
  • undo/redo working
  • saving working
  • S & R working
  • clearing working