TJSXmlHttpRequest responseType is ReadOnly, can we change this, or ...

Hello Wagner,

Web Core Project

could you change the field "responseType" to read/write in the next update. It also works without it, but an error is always displayed.
See code. ( // The error is displayed here / Read-Only property)

procedure TdmSngWebModel.GetBlobURL( AXDataBlobValue: JSValue; ALoadDataProc: TLoadDataProc; ARequestId: String = '');
var
  xhr: TJSXmlHttpRequest;

  procedure _Load;
  begin
    ALoadDataProc( ARequestId, TJSURL.createObjectURL(xhr.response));
  end;

begin
  xhr := TJSXMLHttpRequest.new;
  xhr.open('GET', XConnection.URL + '/' + string( AXDataBlobValue));
  xhr.setRequestHeader( 'Authorization', 'Bearer ' + Env.Token);
  xhr.responseType := 'blob';  // The error is displayed here / Read-Only property
  xhr.addEventListener('load', @_Load);
  xhr.send;
end;

Another question about this.

I am in the customer master data.
A customer can have multiple images.
I bring up a popup. The first image data set should be displayed there. (FDatasetImages Fields = id,title,date,image)
I want to scroll forward and back, insert, edit and save.
The easiest way is via a dataset. (without XDataClient, but I have)

   FDatasetImages := TXDataWebDataSet.Create( Self);
   FDatasetImages.Connection := XConnection;
   FDatasetImages.EntitySetName := 'PersonImage';
   FDatasetImages.QueryString := '$filter=Person/PersonID eq '+APersonID+'&'+'$orderby=sorting asc';
   FDatasetImages.Load;

I can now navigate and edit. With Post and ApplyUpdates I can work with the data.
But what about the images? I now have completely separate functions for loading and saving. Isn't it possible to integrate this in XDataClient/XDataConnextion/XDataWebDataset?
Or is there a better, simpler solution?
Now I need to catch "DataSource.DataChanged" and am having some trouble.

Thomas

Hi Thomas,

Regarding the XML request, I will check with TMS Web Core team (that's part of the core of the product, not TMS XData related).

About the datasets, I still believe the best approach is handle images separately, as they need special treatment and depends if they are images, memo, binary files (like PDF), etc.

What is the exact problem you are having? what is the code you are using?

I had some problems with synchronization between dataset editing (using TXDataWebDataSet) and manual editing. I have now switched everything back to manual. Without using TXDataWebDataSet.
The only thing that still bothers me is that I can't work with XData for the images. But the way I did it now, it should work.

Here is my base class for the XData communication with the individual servers. This works well and I have centralized editing of headers, errors, etc.

Below are the functions that work with the TJSXmlHttpRequest.
Please take a look at the load and save image functions.

  TdmSngWebModel = class(TWebDataModule)
    XDataWebDataSet: TXDataWebDataSet;
  private
    FXConnection         : TXDataWebConnection;
    FXClient             : TXDataWebClient;
    FIsConnected         : Boolean;
    FModelname           : String;
    FTempToken           : String;
    FExpandString        : String;
    FSelectString        : String;
    FOrderByString       : String;
    FAddQueryString      : Boolean;
    FUnauthorizedProc    : TUnauthorizedProc;
    FConnectionErrorProc : TConnectionErrorProc;
    //FWaitFrameElementId  : String;
    function GetQueryString: String;

  protected
    FLastConnectionError : TErrorRec;
    FLastError           : TErrorRec;
    FLastSngResultHeader : TSngResultHeader;
    FConnectedModel      : String;

    procedure XConnectionRequest(Args: TXDataWebConnectionRequest); virtual;
    procedure XConnectionResponse(Args: TXDataWebConnectionResponse); virtual;
    procedure XConnectionError(Error: TXDataWebConnectionError); virtual;
    procedure XConnectionConnect(Sender: TObject); virtual;
    procedure XClientRequest(Request: TXDataClientRequest); virtual;
    procedure XClientError(Error: TXDataClientError); virtual;
    procedure XClientLoad(Response: TXDataClientResponse); virtual;

  public  // Modulfunktionen und Properties
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;

    /// SetConnection in Fehlerfall
    ///   Die Rückmeldung 401:Unauthorized erzeugt in der Connect Routine KEIN Error, sodern durchläuft
    ///   nur das Event XDataWebConnectionResponse. Daher wird zwischen "Unauthorized" und "Error" unterschieden
    procedure SetConnection( const AServicePath, AToken, AModel: String;
                             ASuccess            : TSuccessProc;
                             AUnauthorized       : TUnauthorizedProc = nil;
                             AConnectionError    : TConnectionErrorProc = nil ); virtual;
    function MakeSngParameter( AFunction, AVersion: String; APageSize, APageIndex: Integer; AData: TJSObject): TJSObject; overload;
    function MakeSngParameter( AFunction, AVersion: String; AData: TJSObject): TJSObject; overload;

    function MakeFilter( AFilter: TsngFilterGroup; APageSize, APageIndex: Integer): String; overload;
    function MakeFilter( AFilter: TsngFilterGroup): String; overload;

    /// Setzt den QueryString. ASelect ist die Selectliste (kommagetrennt), AExpand die Eypandliste (kommagetrennt)
    procedure SetQueryString( ASelect, AExpand, AOrderBy: String); overload;
    procedure SetQueryString( ASelect, AExpand: String); overload;
    procedure SetQueryOrder( AOrderBy: String);
    function GetResultValue           ( AResultValue: JSValue)                 : JSValue;
    function GetResultValueAsArray    ( AResultValue: JSValue)                 : TJSArray;
    function GetSngResultAsObject     ( AResultValue: JSValue)                 : TJSObject;
    function GetSngResultDataAsObject ( AResultValue: JSValue)                 : TJSObject;
    function GetSngResultDataField    ( AResultValue: JSValue; AField: String) : JSValue;

    procedure GetBlobURL( AXDataBlobValue: JSValue; ALoadDataProc: TLoadDataProc; ARequestId: String = ''); overload;
    procedure GetBlobURL( AJSObject: TJSObject; AXDataBlobFieldname: String; ALoadDataProc: TLoadDataProc; ARequestId: String = ''); overload;
    procedure LoadImage( const AWebImageControl: TWebImageControl; AJSObject: TJSObject; AXDataBlobFieldname: String);
    procedure SaveImage( const ABase64Image: String; AJSObject: TJSObject; AXDataBlobFieldname: String);

    property XConnection: TXDataWebConnection read FXConnection;
    property XClient: TXDataWebClient read FXClient;

    property LastConnectionError : TErrorRec        read FLastConnectionError;
    property LastSngResultHeader : TSngResultHeader read FLastSngResultHeader;

    property IsConnected        : Boolean read FIsConnected;
    property ConnectedModel     : String read FConnectedModel;
    property QueryString        : String read GetQueryString;
    property AddQueryString     : Boolean read FAddQueryString write FAddQueryString;
    //property WaitFrameElementId : String read FWaitFrameElementId write FWaitFrameElementId;

  public // direkte Aufrufe der sng REST-API
    //function Callback         ( const CallbackFunction: string; const Param: TJSONObject): TJSONObject;
//    function CloseModule     ( const AKill: Boolean = false)       : Boolean;
    [async] function Signal                                                : String;
    [async] function Version         ( out AVersion: String)               : Boolean; overload;
    [async] function Version                                               : String; overload;
//    function DBVersion                                             : String;
//    function InstallDB       ( out AMessage: String)               : Boolean;
//    function RegisterService ( out AMessage: String)               : Boolean;
//    function Uninstall       ( out AMessage: String)               : Boolean;
//    function Update          ( out AMessage: String)               : Boolean;
//    function Backup          ( out AMessage: String)               : Boolean;

    [async] function GetSettingList  ( const AFilter: TJSObject)                        : TJSArray;
    [async] function GetSetting      ( const AData: TJSObject)                          : String; overload;
    [async] function GetSettingDef   ( const AData: TJSObject; ADefault: String)        : String; overload;
    [async] function GetSetting      ( const AGroup, ASubgroup, AName: String)          : String; overload;
    [async] function GetSettingDef   ( const AGroup, ASubgroup, AName, ADefault: String): String; overload;
    [async] function GetSettingJSON  ( const AData: TJSObject)                          : TJSObject;
    [async] function SetSetting      ( const AData: TJSObject)                          : Boolean; overload;
    [async] function SetSetting      ( const AGroup, ASubgroup, AName, AValue: String)  : Boolean; overload;
    [async] function SetSetting      ( const AGroup, ASubgroup, AName: String; AValue: TJSObject) : Boolean; overload;

    [async] function GetLookupDic                                                       : TJSArray;
    [async] function GetLookupList   ( const AName: String)                             : TJSArray;
    [async] function AddLookup       ( const AName, AValue: String)                     : String;
    [async] function ChangeLookup    ( const ALupID, AValue: String)                    : Boolean;
    [async] function DelLookup       ( const ALupID: String)                            : Boolean;
    [async] function DelLookupAll    ( const AName: String)                             : Boolean;

  end;

Here is the code for load and save. Please check this out.

{-----------------------------------------------------------------------------}
{        TdmSngWebModel                GetBlobURL                             }
{-----------------------------------------------------------------------------}
procedure TdmSngWebModel.GetBlobURL( AXDataBlobValue: JSValue; ALoadDataProc: TLoadDataProc; ARequestId: String = '');
var
  xhr: TJSXmlHttpRequest;

  procedure _Load;
  begin
    if xhr.Status = _http_200_Ok then
      ALoadDataProc( ARequestId, TJSURL.createObjectURL(xhr.response));
  end;

begin
  xhr := TJSXMLHttpRequest.new;
  xhr.open('GET', XConnection.URL + '/' + string( AXDataBlobValue));
  xhr.setRequestHeader( 'Authorization', 'Bearer ' + Env.Token);
  xhr.responseType := 'blob';
  xhr.addEventListener('load', @_Load);
  xhr.send;
end;

procedure TdmSngWebModel.GetBlobURL( AJSObject: TJSObject; AXDataBlobFieldname: String; ALoadDataProc: TLoadDataProc; ARequestId: String = '');
begin
  GetBlobURL( AJSObject[ AXDataBlobFieldname+'@xdata.proxy'], ALoadDataProc, ARequestId);
end;

procedure TdmSngWebModel.LoadImage( const AWebImageControl: TWebImageControl; AJSObject: TJSObject; AXDataBlobFieldname: String);

  procedure OnLoad( ARequestId: String; AData: JSValue);
  begin
    AWebImageControl.URL := JS.toString( AData);
  end;

begin
  GetBlobURL( AJSObject, AXDataBlobFieldname, TLoadDataProc( @OnLoad));
end;

procedure TdmSngWebModel.SaveImage( const ABase64Image: String; AJSObject: TJSObject; AXDataBlobFieldname: String);
var
  xhr: TJSXmlHttpRequest;

  function Base64ToArrayBuffer(str: string): TJSArrayBuffer;
  var
    BufView: TJSUInt8Array;
    BinaryString: string;
    I: Integer;
  begin
    BinaryString := window.atob(str);
    Result := TJSArrayBuffer.new(Length(BinaryString));
    BufView := TJSUInt8Array.new(Result);
    for I := 0 to Length(BinaryString) - 1 do
      BufView[I] := TJSString(BinaryString).charCodeAt(I);
  end;

begin
  xhr := TJSXMLHttpRequest.new;
  xhr.open( 'PUT', XConnection.URL + '/' + string( AJSObject[ AXDataBlobFieldname+'@xdata.proxy']));
  xhr.setRequestHeader( 'Authorization', 'Bearer ' + Env.Token);
  xhr.send( Base64ToArrayBuffer( ABase64Image));
end;

1 Like