Jump to content

communicating between two html files


Recommended Posts

In our maths tuition software we have a mainform that includes an iframe. The maths main form exercise that messages the infobar about the students actions.

The infobar along the bottom is in an iframe that is separate from the exercise. The huge benefit is that we can have hundreds of exercises and only one infobar to change. The two parts talk using messaging. Get an answer right mainform sends the infobar a "welldone" message and a green ball appears.

 

The latest update has changed the message system and while the code compiles it no longer works.

I have tried to get a new system working but to be honest I am getting horribly stuck. I am hoping someone can help me out.

I am looking for an example (using latest RTL) where one html file sends message to another html file. Simple string is all that is required.

messaging.jpg

Link to post
Share on other sites
  • Moderators

form with iframe element and button 

button sends message to iframe which echoes it to console

(substitute button with your main sender)

unit Form1;

interface

uses 
  System.Types,
  System.Types.Convert,
  System.Objects,
  System.Time,
  SmartCL.System,
  SmartCL.Time,
  SmartCL.Graphics,
  SmartCL.Components,
  SmartCL.FileUtils,
  SmartCL.Forms,
  SmartCL.Fonts,
  SmartCL.Theme,
  SmartCL.Borders,
  SmartCL.Application, SmartCL.Controls.Elements, SmartCL.Controls.Button;

type
  TForm1 = class(TW3Form)
    procedure W3Button1Click(Sender: TObject);
  private
    {$I 'Form1:intf'}
  protected
    procedure InitializeForm; override;
    procedure InitializeObject; override;
    procedure Resize; override;
  end;

implementation

{ TForm1 }

procedure TForm1.W3Button1Click(Sender: TObject);
begin
  W3IFrameHTMLElement1.handle.contentWindow.postMessage('success','*');
end;

procedure TForm1.InitializeForm;
begin
  inherited;
  // this is a good place to initialize components
end;

procedure TForm1.InitializeObject;
begin
  inherited;
  {$I 'Form1:impl'}

  W3IframeHTMLElement1.handle.onload := procedure ()
  begin
    W3IframeHTMLElement1.handle.contentWindow.addEventListener('message',procedure(evt:variant)
    begin
      //W3IframeHtmlElement1.handle.srcdoc := evt.data;  
      writeln(evt.data);
    end, false);
  end;
end;
 
procedure TForm1.Resize;
begin
  inherited;
end;
 
initialization
  Forms.RegisterForm({$I %FILE%}, TForm1);
end.

 

Link to post
Share on other sites

I am trying to understand how this works.

Is this code only placed in the mainform?  How does the form in the iframe listen?

 

In the mainform I do the following

w3IframeInfoBar.src := 'http://numberworks.com/cloud/infobarnew/index.html';

and when I send a message I assume it is
     w3IframeInfoBar.handle.contentWindow.postMessage('welldone','*');

 

But I am confused what how I set up a listener in the other html file (the one in the iframe)

 

 

Link to post
Share on other sites
  • Moderators

in the example i send you, the Iframe is your infobar and the postmessage is sent from the sender.

The sender (your main form ?) sends a postmessage with some payload ('success'), which is picked up by the eventlistener of the embedded window (contentWindow) within the infobar iFrame. The payload is contained in the data variable of the event (evt.data).

make sure you set up the eventlistener on a fully loaded element (hence the onload handler), or alternatively put it in a readyExecute section

(instead of 'eventlistener' an alternative syntax W3IframeHTMLElement1.handle.contentWindow.onmessage := procedure () begin ... end;    which is exactly the same)

hope this helps

Link to post
Share on other sites

ok, I am feeling a bit dumb. But the sender program has the iframe and it loads the infobar html page into it. The actual infobar code doesn't know it is in an iframe. So I am confused because it feels like the event listener is not inside the actual infobar code.

Sorry if I come across a bit slow :)

Link to post
Share on other sites
  • Moderators

not sure how to answer your post

I understand from your initial question that your current infobar iframe responds to a message, so it must have some code to handle that message (ie success = green ball)

The example sent sets an eventlistener on the infobar and handles the message, not really different I think

Maybe we both have a doh moment :) 

Link to post
Share on other sites

Thanks @lynkfs, I have it working - this is the current code that I am using and it works. There might be some redundant code in there.

 

In Main Form

Main Form

procedure TForm1.InitializeForm;
begin
  inherited;
  // Set teh iframe to the html page you want to communicate with
   w3IframeInfoBar.src := 'http://192.168.1.4:8092/index.html';
end;

Send a message

procedure TForm1.SendWelldone;
begin
   if assigned(w3IframeInfoBar.MessagePort) then
    begin
     writeln('Sent welldone to infobar');
     w3IframeInfoBar.handle.contentWindow.postMessage('welldone','*');
    end
     else
    writeln("No message-port has been assigned error");
end;

 

In the receiving program I did this


procedure TForm1.InitializeForm;
begin
  SetupSubscriber;
end;

procedure TForm1.SetupSubscriber;
begin
  BrowserAPI.WindowObject.addEventListener('message', procedure (Event: JEvent)
  var
    LMsg: JMessageEvent;
  begin
    LMsg := JMessageEvent(event);

    if (string(LMsg.data).contains('msg=')) or (string(LMsg.data).contains('message=')) then
      begin
       var s : string;
       s := LMsg.data;
       delete(s,1,4);
       // DoMessage(s);
      end
      else
    if LMsg.data = 'welldone' then
      begin
        DoGreenFeedbackBalls; // put a green ball on the screen and move all feedback balls along
      end
       else
    if LMsg.data = 'sorry' then
       begin
         DoBlackFeedbackBalls;
      end
       else
    if LMsg.data = 'finished' then
       begin
         // write away results etc
          IsFinished := true;
          lblMessage.handle.style["color"] := "#008000";
          lblMessage.Caption := 'You have finished';
          TW3Dispatch.Execute( procedure () begin TaskbarFinished(true); end, 1800);
      end;
    BrowserAPI.windowobject.removeEventListener('message', @HandleWelcomeMessage); // <------------ is this required?
   end);
end;

 

 

Link to post
Share on other sites
  • Moderators

the removeEventlistener is not required. 

LMsg is probably redundant. LMsg.data equals Event.data (for postmessaged messages)

I like your use of the iFrame.messagePort. Although not per se required, it limits the messages being delivered through pre-defined channels, rather than broadcasting them all over the place. (The same mechanism can be used in webworkers, see previous post)

Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...