(core) Move most of the reference parsing code into common so that the server can use it

Summary: Refactoring in preparation for parsing strings from the API. The plan is that the API code will only need to do a server-side version of the code in ViewFieldRec.valueParser (minus ReferenceUtils) which is quite minimal.

Test Plan: Nothing extra here, I don't think it's needed. This stuff will get tested more in a future diff which changes the API.

Reviewers: dsagal

Reviewed By: dsagal

Differential Revision: https://phab.getgrist.com/D3164
This commit is contained in:
Alex Hall
2021-12-06 14:07:52 +02:00
parent 7f08934cf0
commit 116fb15eda
5 changed files with 130 additions and 137 deletions

View File

@@ -10,8 +10,6 @@ import { countIf } from 'app/common/gutil';
import { TableData as BaseTableData, ColTypeMap } from 'app/common/TableData';
import { Emitter } from 'grainjs';
export type SearchFunc = (value: any) => boolean;
/**
* TableData class to maintain a single table's data.
*/
@@ -60,33 +58,6 @@ export class TableData extends BaseTableData {
this.dataLoadedEmitter.emit(rowIds, []);
}
/**
* Given a colId and a search function, returns a list of matching row IDs, optionally limiting their number.
* @param {String} colId: identifies the column to search.
* @param {Function} searchFunc: A function which, given a column value, returns whether to include it.
* @param [Number] optMaxResults: if given, limit the number of returned results to this.
* @returns Array[Number] array of row IDs.
*/
public columnSearch(colId: string, searchFunc: SearchFunc, optMaxResults?: number) {
const maxResults = optMaxResults || Number.POSITIVE_INFINITY;
const rowIds = this.getRowIds();
const valColumn = this.getColValues(colId);
const ret = [];
if (!valColumn) {
// tslint:disable-next-line:no-console
console.warn(`TableData.columnSearch called on invalid column ${this.tableId}.${colId}`);
} else {
for (let i = 0; i < rowIds.length && ret.length < maxResults; i++) {
const value = valColumn[i];
if (value && searchFunc(value)) {
ret.push(rowIds[i]);
}
}
}
return ret;
}
/**
* Counts and returns the number of error values in the given column. The count is cached to
* keep it faster for large tables, and the cache is cleared as needed on changes to the table.

View File

@@ -1,10 +1,8 @@
import {ReferenceUtils} from 'app/client/lib/ReferenceUtils';
import {ColumnRec, DocModel, IRowModel, refRecord, ViewSectionRec} from 'app/client/models/DocModel';
import * as modelUtil from 'app/client/models/modelUtil';
import * as UserType from 'app/client/widgets/UserType';
import {csvDecodeRow} from 'app/common/csvFormat';
import {DocumentSettings} from 'app/common/DocumentSettings';
import {isFullReferencingType} from 'app/common/gristTypes';
import {getReferencedTableId, isFullReferencingType} from 'app/common/gristTypes';
import {BaseFormatter, createFormatter} from 'app/common/ValueFormatter';
import {createParser} from 'app/common/ValueParser';
import * as ko from 'knockout';
@@ -180,31 +178,15 @@ export function createViewFieldRec(this: ViewFieldRec, docModel: DocModel): void
const docSettings = this.documentSettings();
const type = this.column().type();
if (!isFullReferencingType(type)) {
return createParser(type, this.widgetOptionsJson(), docSettings);
} else {
const widgetOpts = this.widgetOptionsJson();
if (isFullReferencingType(type)) {
const vcol = this.visibleColModel();
const vcolParser = createParser(vcol.type(), vcol.widgetOptionsJson(), docSettings);
const refUtils = new ReferenceUtils(this, docModel.docData); // uses several more observables immediately
if (!refUtils.isRefList) {
return (s: string) => refUtils.parseReference(s, vcolParser(s));
} else {
return (s: string) => {
let values: any[] | null;
try {
values = JSON.parse(s);
} catch {
values = null;
}
if (!Array.isArray(values)) {
// csvDecodeRow should never raise an exception
values = csvDecodeRow(s);
}
values = values.map(v => typeof v === "string" ? vcolParser(v) : v);
return refUtils.parseReferenceList(s, values);
};
}
widgetOpts.visibleColId = vcol.colId() || 'id';
widgetOpts.visibleColType = vcol.type();
widgetOpts.visibleColWidgetOpts = vcol.widgetOptionsJson();
widgetOpts.tableData = docModel.docData.getTable(getReferencedTableId(type)!);
}
return createParser(type, widgetOpts, docSettings);
});
// The widgetOptions to read and write: either the column's or the field's own.