Icon View Thread

The following is the text of the current message along with any replies.
Messages 1 to 6 of 6 total
Thread Problem calling a module
Wed, Jul 19 2017 7:08 AMPermanent Link

Paul Coshott

Avatar

Hi All,

I am currently trying to write a module that will accept a field name and then using that field return the next ID for use in my EWB app.

For example, if I'm adding a new client, before saving the new client, I'll make a call to the module using 'ClientId' as the field name. The (delphi) module itself is fine, but I am not even able to call it, as I'm doing something wrong in my EWB app.

I have a form that is never shown, it is just used for common functions and procedures to be called from anywhere in the app. The following is the form's code. I have indicated in the code below where it fails, and the error is:

Object expected
Line: 1

Any ideas what I'm doing wrong?

Thanks,
Paul


--------------------------------------------------------------------------------------------------------------------------

unit AppProcedures;

interface

uses WebCore, WebUI, WebForms, WebCtrls, WebHTTP;

type
  TNextSystemId = class(TPersistent)
  private
     FNextId: integer;
  published
     property NextId: integer read FNextId write FNextId;
  end;

  TfrmAppProcedures = class(TForm)
     srNextId: TServerRequest;
     procedure srNextIdComplete(Request: TServerRequest);
  private
     { Private declarations }
     Reader: TReader;
  public
     { Public declarations }
     NextSystemId : TNextSystemId;
  end;

var
  frmAppProcedures: TfrmAppProcedures;

//functions

  function GetNextSystemId(sIdField : string) : integer;

implementation

uses Main;

function GetNextSystemId(sIdField : string) : integer;
begin                                       
 Result := -1;                                                       <----------- THIS IS OK
 with frmAppProcedures do begin                          <----------- FAILS ON THIS LINE
   with srNextId do begin
     Method := rmPost;
     URL := '/modules/rs_sysids';
     RequestHeaders.Clear;
     RequestHeaders.Values['Content-Type'] := 'text/plain';
     RequestContent.Clear;
     RequestContent.Values['IdField'] := sIdField;
     Execute;
   end;
 end;
end;

procedure TfrmAppProcedures.srNextIdComplete(Request: TServerRequest);
begin
 Reader := TReader.Create;
 NextSystemId := TNextSystemId.Create;
 try
   if (Request.StatusCode <> HTTP_OK) then begin
     //an error occured, so set global next id to -1
     gblNextId := -1;
   end else begin
     Reader.Initialize(Request.ResponseContent.Text);
     NextSystemId.Load(Reader);
     gblNextId := NextSystemId.NextId;
   end;
 finally
   Reader.Free;
   NextSystemId.Free;
 end;
end;

end.
Wed, Jul 19 2017 8:51 AMPermanent Link

Raul

Team Elevate Team Elevate

On 7/19/2017 7:08 AM, Paul Coshott wrote:
> I am currently trying to write a module that will accept a field name and then using that field return the next ID for use in my EWB app.
> For example, if I'm adding a new client, before saving the new client, I'll make a call to the module using 'ClientId' as the field name. The (delphi) module itself is fine, but I am not even able to call it, as I'm doing something wrong in my EWB app.
> I have a form that is never shown, it is just used for common functions and procedures to be called from anywhere in the app. The following is the form's code. I have indicated in the code below where it fails, and the error is:

Is the actual form created either by you or IDE ?

In project options is the frmAppProcedures listed under "Auto-create
Forms and Databases" (Forms and Databases Tab).

If not then you need to either add it there or create it in your code on
app startup (i.e. frmAppProcedures := TfrmAppProcedures.Create... ).

easiest way to do latter would be to create it dynamically if needed.


function GetNextSystemId(sIdField : string) : integer;
begin
  Result := -1;

  if not assigned(frmAppProcedures ) then
      frmAppProcedures := TfrmAppProcedures.Create(Application);

  with frmAppProcedures do begin
    with srNextId do begin
      Method := rmPost;
      ...


Raul
Wed, Jul 19 2017 9:18 AMPermanent Link

Paul Coshott

Avatar

Raul wrote:

> Is the actual form created either by you or IDE ?

Hi Raul,

Got it in one! Thanks so much for the help. I added it to the Auto Create list and worked like a charm.

Now I know it's working, I have one other question. I want to call a function that returns the next id. But the function itself doesn't ever actually know the result. That happens in the OnComplete event of the TServerRequest.

Is there a way to handle this. So the calling form can get the result returned to it?

Hope this makes sense,

Thanks,
Paul
Wed, Jul 19 2017 10:08 AMPermanent Link

Matthew Jones

Paul Coshott wrote:

> So the calling form can get the result returned to it?

The OnComplete is usually a procedure on the form that wants it.

--

Matthew Jones
Wed, Jul 19 2017 10:59 AMPermanent Link

Tim Young [Elevate Software]

Elevate Software, Inc.

Avatar

Email timyoung@elevatesoft.com

Paul,

<< Is there a way to handle this. So the calling form can get the result returned to it? >>

You can do this by assigning an OnComplete event handler defined on the calling form directly to the TServerRequest.OnComplete event property, or you can chain the event handlers by adding a new OnComplete property to your TfrmAppProcedures form, like this:

 TfrmAppProcedures = class(TForm)
     srNextId: TServerRequest;
     procedure srNextIdComplete(Request: TServerRequest);
  private
     { Private declarations }
     Reader: TReader;
    FOnComplete: TServerRequestEvent;  <<<<<<<<<<<<<
  public
     { Public declarations }
     NextSystemId : TNextSystemId;
    OnComplete: TServerRequestEvent read FOnComplete write FOnComplete;  <<<<<<<<<<<<<
  end;

You need to fix your calling method, though.  It should look like this:

procedure GetNextSystemId(sIdField : string) ;
begin                                       
 with frmAppProcedures do begin                          <----------- FAILS ON THIS LINE
   with srNextId do begin
     Method := rmPost;
     URL := '/modules/rs_sysids';
     RequestHeaders.Clear;
     RequestHeaders.Values['Content-Type'] := 'text/plain';
     RequestContent.Clear;
     RequestContent.Values['IdField'] := sIdField;
     Execute;
   end;
 end;
end;

You can't return a result from this method yet because there isn't a result yet (it's returned in the OnComplete event handler).

procedure TfrmAppProcedures.srNextIdComplete(Request: TServerRequest);
begin
 Reader := TReader.Create;
 NextSystemId := TNextSystemId.Create;
 try
   if (Request.StatusCode <> HTTP_OK) then begin
     //an error occured, so set global next id to -1
     gblNextId := -1;
   end else begin
     Reader.Initialize(Request.ResponseContent.Text);
     NextSystemId.Load(Reader);
     gblNextId := NextSystemId.NextId;
   end;
 finally
   Reader.Free;
   NextSystemId.Free;
  if Assigned(FOnComplete) then  <<<<<<<<<<<<<
     FOnComplete(Request);  <<<<<<<<<<<<<
 end;
end;

And then you would call it like this:

frmAppProcedures.OnComplete:=MyFormOnCompleteEventHandler;
frmAppProcedures.GetNextSystemId('MyField');

The MyFormOnCompleteEventHandler event handler needs to be defined manually as a method of the calling form, and it needs to match the signature of the OnComplete event:

procedure MyFormOnCompleteEventHandler(Request: TServerRequest);
begin
  ShowMessage('The next ID is: '+IntToStr(gblNextId));   
end;

Of course, the *best* way to deal with all of this is to just make your frmAppProcedures form into a component instead of a form and install it on the component palette.  Then you won't need to mess around with chaining event handlers for the forms and can just drop the component directly on the form where you're using it.  There *is* a little more work required on the component creation side, but it's balanced by less work required on the usage side.  You can also defined your own event handler type that returns the next ID in the event handler instead of using a global variable, and use event handler type instead of the same TServerRequestEvent event type.

Please note that this technique works for *any* type of event handler chaining that is required due to async event triggering: server requests, form/dialog close events, timers, etc.

Tim Young
Elevate Software
www.elevatesoft.com
Thu, Jul 20 2017 9:25 AMPermanent Link

Paul Coshott

Avatar

Tim Young [Elevate Software] wrote:

>> You can do this by assigning an OnComplete event handler defined on the calling form directly to the
>> TServerRequest.OnComplete event property, or you can chain the event handlers by adding a new OnComplete
>> property to your TfrmAppProcedures form, like this:

Hi Tim,

Thanks so much for the detailed help. I implemented your suggestions and it's all working great now. Thanks heaps.

Cheers,
Paul
Image