mirror of
				https://github.com/gristlabs/grist-core.git
				synced 2025-06-13 20:53:59 +00:00 
			
		
		
		
	(core) Refactor more value parsing code into common
Summary: Following discussion in https://phab.getgrist.com/D3164: - Change createParser to accept docData and one or two metadata row IDs and let it extract the metadata, so it's more easily usable in the server. - Change ViewFieldRec.valueParser observable to a function createValueParser. Test Plan: Existing tests. Reviewers: dsagal Reviewed By: dsagal Differential Revision: https://phab.getgrist.com/D3172
This commit is contained in:
		
							parent
							
								
									4164d89b84
								
							
						
					
					
						commit
						6b448567c9
					
				@ -372,7 +372,7 @@ BaseView.prototype._parsePasteForView = function(data, fields) {
 | 
				
			|||||||
  });
 | 
					  });
 | 
				
			||||||
  const updateColIds = updateCols.map(c => c && c.colId());
 | 
					  const updateColIds = updateCols.map(c => c && c.colId());
 | 
				
			||||||
  const updateColTypes = updateCols.map(c => c && c.type());
 | 
					  const updateColTypes = updateCols.map(c => c && c.type());
 | 
				
			||||||
  const parsers = fields.map(field => field && field.valueParser() || (x => x));
 | 
					  const parsers = fields.map(field => field && field.createValueParser() || (x => x));
 | 
				
			||||||
  const docIdHash = tableUtil.getDocIdHash();
 | 
					  const docIdHash = tableUtil.getDocIdHash();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const richData = data.map((col, idx) => {
 | 
					  const richData = data.map((col, idx) => {
 | 
				
			||||||
 | 
				
			|||||||
@ -17,9 +17,6 @@ export class ReferenceUtils {
 | 
				
			|||||||
  public readonly isRefList: boolean;
 | 
					  public readonly isRefList: boolean;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  constructor(public readonly field: ViewFieldRec, docData: DocData) {
 | 
					  constructor(public readonly field: ViewFieldRec, docData: DocData) {
 | 
				
			||||||
    // Note that this constructor is called inside ViewFieldRec.valueParser, a ko.pureComputed,
 | 
					 | 
				
			||||||
    // and there are several observables here which get used and become dependencies.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    const colType = field.column().type();
 | 
					    const colType = field.column().type();
 | 
				
			||||||
    const refTableId = getReferencedTableId(colType);
 | 
					    const refTableId = getReferencedTableId(colType);
 | 
				
			||||||
    if (!refTableId) {
 | 
					    if (!refTableId) {
 | 
				
			||||||
 | 
				
			|||||||
@ -2,7 +2,6 @@ import {ColumnRec, DocModel, IRowModel, refRecord, ViewSectionRec} from 'app/cli
 | 
				
			|||||||
import * as modelUtil from 'app/client/models/modelUtil';
 | 
					import * as modelUtil from 'app/client/models/modelUtil';
 | 
				
			||||||
import * as UserType from 'app/client/widgets/UserType';
 | 
					import * as UserType from 'app/client/widgets/UserType';
 | 
				
			||||||
import {DocumentSettings} from 'app/common/DocumentSettings';
 | 
					import {DocumentSettings} from 'app/common/DocumentSettings';
 | 
				
			||||||
import {getReferencedTableId, isFullReferencingType} from 'app/common/gristTypes';
 | 
					 | 
				
			||||||
import {BaseFormatter, createFormatter} from 'app/common/ValueFormatter';
 | 
					import {BaseFormatter, createFormatter} from 'app/common/ValueFormatter';
 | 
				
			||||||
import {createParser} from 'app/common/ValueParser';
 | 
					import {createParser} from 'app/common/ValueParser';
 | 
				
			||||||
import * as ko from 'knockout';
 | 
					import * as ko from 'knockout';
 | 
				
			||||||
@ -70,7 +69,7 @@ export interface ViewFieldRec extends IRowModel<"_grist_Views_section_field"> {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  documentSettings: ko.PureComputed<DocumentSettings>;
 | 
					  documentSettings: ko.PureComputed<DocumentSettings>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  valueParser: ko.Computed<(value: string) => any>;
 | 
					  createValueParser(): (value: string) => any;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // Helper which adds/removes/updates field's displayCol to match the formula.
 | 
					  // Helper which adds/removes/updates field's displayCol to match the formula.
 | 
				
			||||||
  saveDisplayFormula(formula: string): Promise<void>|undefined;
 | 
					  saveDisplayFormula(formula: string): Promise<void>|undefined;
 | 
				
			||||||
@ -174,20 +173,10 @@ export function createViewFieldRec(this: ViewFieldRec, docModel: DocModel): void
 | 
				
			|||||||
      createFormatter(this.column().type(), this.widgetOptionsJson(), this.documentSettings());
 | 
					      createFormatter(this.column().type(), this.widgetOptionsJson(), this.documentSettings());
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  this.valueParser = ko.pureComputed(() => {
 | 
					  this.createValueParser = function() {
 | 
				
			||||||
    const docSettings = this.documentSettings();
 | 
					    const fieldRef = this.useColOptions.peek() ? undefined : this.id.peek();
 | 
				
			||||||
    const type = this.column().type();
 | 
					    return createParser(docModel.docData, this.colRef.peek(), fieldRef);
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
    const widgetOpts = this.widgetOptionsJson();
 | 
					 | 
				
			||||||
    if (isFullReferencingType(type)) {
 | 
					 | 
				
			||||||
      const vcol = this.visibleColModel();
 | 
					 | 
				
			||||||
      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.
 | 
					  // The widgetOptions to read and write: either the column's or the field's own.
 | 
				
			||||||
  this._widgetOptionsStr = modelUtil.savingComputed({
 | 
					  this._widgetOptionsStr = modelUtil.savingComputed({
 | 
				
			||||||
 | 
				
			|||||||
@ -76,7 +76,7 @@ export class NTextEditor extends NewBaseEditor {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public getCellValue(): CellValue {
 | 
					  public getCellValue(): CellValue {
 | 
				
			||||||
    const valueParser = this.options.field.valueParser.peek();
 | 
					    const valueParser = this.options.field.createValueParser();
 | 
				
			||||||
    return valueParser(this.getTextValue());
 | 
					    return valueParser(this.getTextValue());
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -1,12 +1,14 @@
 | 
				
			|||||||
import {csvDecodeRow} from 'app/common/csvFormat';
 | 
					import {csvDecodeRow} from 'app/common/csvFormat';
 | 
				
			||||||
 | 
					import {DocData} from 'app/common/DocData';
 | 
				
			||||||
import {DocumentSettings} from 'app/common/DocumentSettings';
 | 
					import {DocumentSettings} from 'app/common/DocumentSettings';
 | 
				
			||||||
 | 
					import {getReferencedTableId, isFullReferencingType} from 'app/common/gristTypes';
 | 
				
			||||||
import * as gristTypes from 'app/common/gristTypes';
 | 
					import * as gristTypes from 'app/common/gristTypes';
 | 
				
			||||||
import * as gutil from 'app/common/gutil';
 | 
					import * as gutil from 'app/common/gutil';
 | 
				
			||||||
import {safeJsonParse} from 'app/common/gutil';
 | 
					import {safeJsonParse} from 'app/common/gutil';
 | 
				
			||||||
import {getCurrency, NumberFormatOptions} from 'app/common/NumberFormat';
 | 
					import {getCurrency, NumberFormatOptions} from 'app/common/NumberFormat';
 | 
				
			||||||
import NumberParse from 'app/common/NumberParse';
 | 
					import NumberParse from 'app/common/NumberParse';
 | 
				
			||||||
import {parseDateStrict, parseDateTime} from 'app/common/parseDate';
 | 
					import {parseDateStrict, parseDateTime} from 'app/common/parseDate';
 | 
				
			||||||
import {TableData} from 'app/common/TableData';
 | 
					import {MetaRowRecord, TableData} from 'app/common/TableData';
 | 
				
			||||||
import {DateFormatOptions, DateTimeFormatOptions, formatDecoded, FormatOptions} from 'app/common/ValueFormatter';
 | 
					import {DateFormatOptions, DateTimeFormatOptions, formatDecoded, FormatOptions} from 'app/common/ValueFormatter';
 | 
				
			||||||
import flatMap = require('lodash/flatMap');
 | 
					import flatMap = require('lodash/flatMap');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -99,7 +101,7 @@ class ChoiceListParser extends ValueParser {
 | 
				
			|||||||
/**
 | 
					/**
 | 
				
			||||||
 * This is different from other widget options which are simple JSON
 | 
					 * This is different from other widget options which are simple JSON
 | 
				
			||||||
 * stored on the field. These have to be specially derived
 | 
					 * stored on the field. These have to be specially derived
 | 
				
			||||||
 * for referencing columns. See ViewFieldRec.valueParser for an example.
 | 
					 * for referencing columns. See createParser.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
interface ReferenceParsingOptions {
 | 
					interface ReferenceParsingOptions {
 | 
				
			||||||
  visibleColId: string;
 | 
					  visibleColId: string;
 | 
				
			||||||
@ -116,7 +118,7 @@ export class ReferenceParser extends ValueParser {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  protected _visibleColId = this.widgetOpts.visibleColId;
 | 
					  protected _visibleColId = this.widgetOpts.visibleColId;
 | 
				
			||||||
  protected _tableData = this.widgetOpts.tableData;
 | 
					  protected _tableData = this.widgetOpts.tableData;
 | 
				
			||||||
  protected _visibleColParser = createParser(
 | 
					  protected _visibleColParser = createParserRaw(
 | 
				
			||||||
    this.widgetOpts.visibleColType,
 | 
					    this.widgetOpts.visibleColType,
 | 
				
			||||||
    this.widgetOpts.visibleColWidgetOpts,
 | 
					    this.widgetOpts.visibleColWidgetOpts,
 | 
				
			||||||
    this.docSettings,
 | 
					    this.docSettings,
 | 
				
			||||||
@ -218,7 +220,7 @@ export const valueParserClasses: { [type: string]: typeof ValueParser } = {
 | 
				
			|||||||
 * widgetOpts is usually the field/column's widgetOptions JSON
 | 
					 * widgetOpts is usually the field/column's widgetOptions JSON
 | 
				
			||||||
 * but referencing columns need more than that, see ReferenceParsingOptions above.
 | 
					 * but referencing columns need more than that, see ReferenceParsingOptions above.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
export function createParser(
 | 
					export function createParserRaw(
 | 
				
			||||||
  type: string, widgetOpts: FormatOptions, docSettings: DocumentSettings
 | 
					  type: string, widgetOpts: FormatOptions, docSettings: DocumentSettings
 | 
				
			||||||
): (value: string) => any {
 | 
					): (value: string) => any {
 | 
				
			||||||
  const cls = valueParserClasses[gristTypes.extractTypeFromColType(type)];
 | 
					  const cls = valueParserClasses[gristTypes.extractTypeFromColType(type)];
 | 
				
			||||||
@ -228,3 +230,43 @@ export function createParser(
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
  return value => value;
 | 
					  return value => value;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Returns a function which can parse strings into values appropriate for
 | 
				
			||||||
 | 
					 * a specific widget field or table column.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Pass fieldRef (a row ID of _grist_Views_section_field) to use the settings of that view field
 | 
				
			||||||
 | 
					 * instead of the table column.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					export function createParser(
 | 
				
			||||||
 | 
					  docData: DocData,
 | 
				
			||||||
 | 
					  colRef: number,
 | 
				
			||||||
 | 
					  fieldRef?: number,
 | 
				
			||||||
 | 
					): (value: string) => any {
 | 
				
			||||||
 | 
					  const columnsTable = docData.getMetaTable('_grist_Tables_column');
 | 
				
			||||||
 | 
					  const fieldsTable = docData.getMetaTable('_grist_Views_section_field');
 | 
				
			||||||
 | 
					  const docInfoTable = docData.getMetaTable('_grist_DocInfo');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const col = columnsTable.getRecord(colRef)!;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  let fieldOrCol: MetaRowRecord<'_grist_Tables_column' | '_grist_Views_section_field'> = col;
 | 
				
			||||||
 | 
					  if (fieldRef) {
 | 
				
			||||||
 | 
					    fieldOrCol = fieldsTable.getRecord(fieldRef) || col;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const widgetOpts = safeJsonParse(fieldOrCol.widgetOptions, {});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const type = col.type;
 | 
				
			||||||
 | 
					  if (isFullReferencingType(type)) {
 | 
				
			||||||
 | 
					    const vcol = columnsTable.getRecord(fieldOrCol.visibleCol);
 | 
				
			||||||
 | 
					    widgetOpts.visibleColId = vcol?.colId || 'id';
 | 
				
			||||||
 | 
					    widgetOpts.visibleColType = vcol?.type;
 | 
				
			||||||
 | 
					    widgetOpts.visibleColWidgetOpts = safeJsonParse(vcol?.widgetOptions || '', {});
 | 
				
			||||||
 | 
					    widgetOpts.tableData = docData.getTable(getReferencedTableId(type)!);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const docInfo = docInfoTable.getRecord(1);
 | 
				
			||||||
 | 
					  const docSettings = safeJsonParse(docInfo!.documentSettings, {}) as DocumentSettings;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return createParserRaw(type, widgetOpts, docSettings);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user