Blob Field for notes - is there a better demo?

I've been using the Xdata web core demo for blobs to try and implement this (I'd have thought that this would be just a built in method for TXDataWebDataset - maybe I'll add a request).

Anyway:

my entity is:

  {$SCOPEDENUMS ON}
  [Enumeration(TEnumMappingType.emString, 'PlainText,MarkDown')]
  TsxeNoteFormat = (PlainText, MarkDown);
  {$SCOPEDENUMS OFF}

  [Entity]
  [AutoMapping]
  [Table('NOTES')]
  [DBIndex('IX_NOTE_ENTITY', 'ENTITY_CLASS_NAME,ENTITY_ID')]
  [Sequence('SEQ_NOTE', 1, 1)]
  [Id('FId', TIdGenerator.IdentityOrSequence)]
  TsxeNote = class(TSMXBaseEntity)
  private
    FId: Integer;
    FDateAdded: TDateTime;
    FAddedBy: Integer;
    FLastUpdatedBy: NullableInteger;
    FLastUpdated: NullableDateTime;

    [Column('ENTITY_CLASS_NAME', [], 64)]
    FEntityClassName: string;

    [Column('ENTITY_ID')]
    FEntityId: Integer;

    [Column('TITLE', [], 255)]
    FTitle: string;

    [Column('NOTE', [TColumnProp.Lazy])]
    [DBTypeWideMemo]
    FNote: TBlob;

    FNoteFormat: TsxeNoteFormat;
  public
    property Id: Integer read FId write FId;
    property DateAdded: TDateTime read FDateAdded write FDateAdded;
    property AddedBy: Integer read FAddedBy write FAddedBy;
    property LastUpdated: NullableDateTime read FLastUpdated write FLastUpdated;
    property LastUpdatedBy: NullableInteger read FLastUpdatedBy write FLastUpdatedBy;
    property EntityClassName: string read FEntityClassName write FEntityClassName;
    property EntityId: Integer read FEntityId write FEntityId;
    property Title: string read FTitle write FTitle;
    property Note: TBlob read FNote write FNote;
    property NoteFormat: TsxeNoteFormat read FNoteFormat write FNoteFormat;
  end;

On my Webform I have a TXDataWebDataset with all the fields retrieved from the server app at design time, except for the Note field which is added as

NotesDatasetNotexdataproxy: TMemoField;
FieldName = Note@xdata.proxy

In code, when a note is saved it first calls

[Async]
    procedure SaveNote;

The blob saving is pretty much a straight lift from the demo. NoteData is a TWebMemo control.

procedure TEssentialsNotesForm.SaveNote;
var
  xhr: TJSXmlHttpRequest;
  Info: TResolveResults;
  isNew: Boolean;
  updateStatus: TUpdateStatus;

  function strEncodeUTF16(str: string): TJSArrayBuffer;
  var
    BufView: TJSUInt16Array;
    I: Integer;
  begin
    Result := TJSArrayBuffer.new(Length(str) * 2);
    BufView := TJSUInt16Array.new(Result);
    for I := 0 to Length(str) - 1 do
      BufView[I] := TJSString(str).charCodeAt(I);
  end;

begin
  FIsSaving := True;
  try
    //Save the record
    if TXDataDSUtils.CheckIsModified(NotesDataset) then
    begin
      isNew := False;
      Info := Await(NotesDataset.ApplyUpdatesAsync);
      updateStatus := Info.Records[0].Status;
      if Info.Records[0].Status = TUpdateStatus.usInserted then
      begin
        isNew := True;
        TXDataDSUtils.UpdateId(NotesDataset, Info, 'Id', 0);
      end;
    end;

    //Now save the blob
    if (isNew and (NoteData.Text <> '')) or NoteData.Modified then
    begin
      Assert(NotesDatasetId.Value > 0);
      xhr := TJSXMLHttpRequest.new;
      xhr.open('PUT',
        NotesDataset.Connection.URL + '/' +
        string(TJSObject(NotesDataset.CurrentData)['Note@xdata.proxy'])
        );

      xhr.send(strEncodeUTF16(NoteData.Lines.Text));

    end;
  finally
    FIsSaving := False;
  end;

end;

The problem I have at the moment is that when a new record is saved (I haven't got as far as having a record in the dataset to edit it yet).

NotesDataset.ApplyUpdatesAsync()

but the server raises the exception

ExceptionMessage="Expected DoubleQuoted but was Null at path $.Note@xdata.proxy"
ExceptionName="EInvalidStateException"
ExceptionDisplayName="EInvalidStateException"

The demo doesn't seem to allow adding a new record, but that could just be me.

What do I need to do here?

Is this error happening in the ApplyUpdatesAsync call or xhr.send call?
It would help if you could inspect the full request in the browser to see what exactly is being sent to the server, the endpoint, headers, body content, etc., in the failed request.

on the ApplyUpdatesAsync

Payload:

{"Id":null,"DateAdded":"2025-10-17T10:59:16.096","AddedBy":null,"LastUpdatedBy":null,"LastUpdated":"2025-10-17T10:59:16.097","EntityClassName":"TEssentialDemo1","EntityId":1,"Title":"this title","NoteFormat":null,"Note@xdata.proxy":null}

Response:

{
    "error": {
        "code": "InvalidStateException",
        "message": "Expected DoubleQuoted but was Null at path $.Note@xdata.proxy"
    }
}

I tried setting the field to '', but then the server raised

ExceptionMessage="Reference "" not found"
ExceptionName="EReferenceNotFound"

You should indeed remove the xdata.proxy field from the JSON, it doesn't make sense to send it back to the server.

I was just copying the demo. I think I'll just do it through a service

1 Like

Service works a treat

1 Like