Customer use case: bringing an FMX app to the web with TMS WEB Core

After years of marketing a Delphi Windows/macOS version of my Star Analyzer program, I found that many clients wanted a web-based program which would run on any device, especially a tablet. They also wanted their data stored in the cloud so that several people could use the program at the same time. The macOS version produced by Delphi was often plagued with macOS incompatibilities, and neither version would run on a tablet. So I decided to take the leap to the web.

Another deciding factor was the library of 85 Object Pascal code files that I developed over the years for use with all my programs. I definitely did not want to rewrite my library from scratch. I am rather fond of it. My Star Analyzer program included another 100 Object Pascal code files which all use my library files.

So far I have not needed to write a single line of JavaScript or HTML code, and about 80% my existing Delphi code works nicely with the WEB Core components. Some of 20% of new code I had to write was relatively straight forward, and some was a bit tricky. I’ll talk about the tricky parts a bit later in in this Blog.

Converting my existing FMX forms to TMS WEB Core

As we all know, the components on the forms talk to your code through their event handlers. Theoretically then all I would have to do is create new forms, drop FNC and WEB Core components on the new forms, and use hook them up to most of my existing code.  Not too much work, or so I thought, but it turns out it was not quite that simple.

If you look at Star Analyzer for the web you’ll see that it is built around tab controls. Click Data Entry and Data Analysis button and you’ll find there are 15 tab controls on the main form and 18 tab controls on the tabs on the main form. The original program was built around one large and complicated main form that held all the components for all 33 tabs, but the program worked nicely, and was easy to code and trouble shoot.

In converting my program to WEB Core, I first duplicated this tab structure using the TTMSFNCPageControl. The new layout looked just like the old layout, and was easy to work with in programming, but it would not compile. It turns out that you can’t compile a program with thousands of components and 33 tabs on one main form. It chokes the compiler.

Using Hosted Forms to reduce the complexity of browser pages

The solution was to use a TTMSFNCTabSet (which has no pages, just tabs) and to use a hosted form as shown in the TMSDemosWebBasicsFormhosting demo provide by TMS. The text and number of tabs on the main TabSet are changed in code, and the controls that used to be on the tabs are now on a Hosted Form.

In the image below the area outside the red box is the main form, while the area inside the red box shows one of the 23 different Hosted Forms. The advantage of Hosted Forms is that each web browser page has a small number of components and on it. 

TMS Software Delphi  Components

This Case statement below shows how the hosted forms are called and created by the program.

  case SubFm of  // there are 23 hosted forms that I think of as SubForms
    sfProp     : MF.MainSubFm := TmfPropSubForm.CreateNew(MF.pnlMainHost.ElementID, @AfterSubFmCreate);
    sfFinBuy  : MF.MainSubFm := TmfFinBuySubForm.CreateNew(MF.pnlMainHost.ElementID, @AfterSubFmCreate);
    sfFinSell  : MF.MainSubFm := TmfFinSellSubForm.CreateNew(MF.pnlMainHost.ElementID, @AfterSubFmCreate);
   end;

Creating Pop-up Forms

In addition to the Hosted Forms mentioned above, every program needs pop-up forms as shown below. Forgive my wife and my cat in the images. This is just a demo after all. You can import photos and even take photos on your iPad with the TMS components. See the TMSDemosWebBasicsMultiform demo for details on creating pop-up forms. 

TMS Software Delphi  Components
Why I chose Google Firestore as my online database

TMS Software Delphi  Components
My original Star Analyzer program used a local SQL database and the FMS FNC Cloud Pack to allow users to backup and restore their data to Dropbox so that they could use multiple computers, one at a time, with their data. This confused a lot of users. Not a good solution.
After spending many days looking at all the cloud databases available, and after playing with various TMS cloud data storage solutions, I ended up choosing the Google Firestore and the TMS WebFirestoreClientDataset as my cloud data storage solution. 

Without the TMS Firestore components I never would have chosen Firestore. If you go to https://firebase.google.com/

and begin reading the hundreds of pages of Firestore documents you may find it overwhelming as I did. Or perhaps not. But I wanted a relatively simple solution, and did not want to learn another programming language. I know that you can write Java Script routines that work with Firestore, but as I said, Java Script is incompatible with my mind.

Beyond ease of use, I was looking for an inexpensive cloud database solution. Look at https://firebase.google.com/pricing

you’ll see that Firestore is free at first, and then inexpensive as your needs increase. You can write 20,000 documents and read 50,000 documents per day for no charge, and after that it is only $0.06 per 100,000 reads. Given my customer database, and the fact that each customer will never have more than 100 documents in their database, I’ll probably never have to spend a penny on cloud storage.

Learning to use the TMS TWebFirestoreClientDataset

It is probably best to start by reading the TMS WEB Core PDF help file, which is well written and fairly comprehensive. Next you should look at the TMS Blog entries on Firestore. And finally, you can look at the DemosWebDBBackendFirestore as a simple example to learn the basics. 

At first you may find the lack of SQL query capability in Firestore to be a bit disturbing, but you’ll find that there are ways around most of the limitations. One limitation of Firestore is that the maximum document size is 1 Meg, so if you are storing photographs you’ll need to shrink them a bit. And that brings me to my next topic, hints and tricks in using WEB Core.

TMS WEB Core Hints and Tips

TMS Software Delphi  Components
As you work with TMS WEB Core you’ll find little tricks that are not emphasized in the documentation. Here are a couple that I’ve found useful.

Tip 1: Selecting and Resizing Images

There are two images controls that I use with WEB Core, the TTMSFNCImage and the WebImageControl. The TTMSFNCImage can show images in their proper Height and Width Ratio (which I like), while in the TWebImageControl the image always takes the shape of the control on the form (which I don’t like). 

If you click the button or the image on the main form, a pop-up Image Select Form is shown. See the TMSDemosWebBasicsMultiform demo for more information on pop-up forms.

On the image select form, you can select a JPG if you are on a computer, and you can even take a photo if you are in an iPad. The image select form uses both an FNCImage (to see the image in actual height/width ratio) and an TWebImageControl to allow me to shrink JPG’s that are larger than 1 Meg. The image resizing is done during the TWebImage OnLoaded event. 

Curiously, the length of the resized images depends on the physical size of the TWebImageControl on the page. See the code in the zip file for details. I spent days trying to find a useable image resizing routine that did not distort the height/width ratio, but finally gave up and resorted to the TWebImageControl resizing. If anyone has a nice resizing routine, I would appreciate a copy.

TMS Software Delphi  Components

TMS Software Delphi  Components

Tip 2: Handling asynchronous behavior

I started writing the Star Analyzer for the web before the many async/await functions were introduced in TMS WEB Core and I’m eager to investigate and start using these. But well, when writing applications running applications in the browser, one cannot change the fact there are many asynchronous processes happening in the browser all for the sake of keeping the application at all times responsive. This is also the case in Star Analyzer when opening databases. Some delays occur when reading and writing to Firestore databases. In Star Analyzer we have to open three databases when the program starts up: Agents, Images, and Properties. This takes around seven seconds. This is how I handle the startup.

  1. Show the WaitMessage
  2. Call OpenServiceFilter for each database
  3. Start the WebTimer
  4. Wait for the WebTimer OnTimer event
  5. Check to see if all databases are open
  6. Keep looping with the timer until all are open
  7. Stop the WebTimer

The code will look something like this:

{----------------------------------------------}
procedure TMF.EnableWaitMsgAndStartupTimer;
{----------------------------------------------}
  procedure dbOpenDBSvcFilterStr(CDS: TWebFirestoreClientDataset;
                                 const FldName, Condition, Data: string);
  begin
    dbClose(CDS);
    CDS.ClearSortFieldDefs;
    CDS.ClearServiceFilters;
    CDS.AddServiceFilterCondition(FldName, Condition, Data);
    dbOpen(CDS);
  end; {dbOpenDBSvcFilterStr}

{--- EnableWaitMsgAndStartupTimer ---}
begin
  WaitMsg.Show;

  {--- agent ---}
  dbOpenDBSvcFilterStrEq(FAgent.CDS, xagAgentIDStr, cfv.SelectedAgentIDStr);

  {--- agent photo and logo ---}
  dbOpenDBSvcFilterStrEq(DM.imCDS, ximAgentIDStr, cfv.SelectedAgentIDStr);

  {--- agent properties ---}
  dbOpenDBSvcFilterStrEq(FProp.CDS, xprAgentIDStr, cfv.SelectedAgentIDStr);

  StartupTimer.Enabled := true;
end; 

We now loop while waiting for the three databases to all be open. Once all are open, we stop the timer, hide the wait message, and continue with the program.

{----------------------------------------------}
procedure TMF.mfStartupTimerUp(Sender: TObject);
begin
  //Note: DM.IsOpen uses the CDSAfterOpen event to set values true

  if not DM.IsOpen(Agents.CDS) then exit;
  if not DM.IsOpen(Images.CDS)   then exit;
  if not DM.IsOpen(Properties.CDS)  then exit;

  {--- stop timer and hide wait message ---}
  StartupTimer.Enabled := false;
  WaitMsg.Hide;

  //All datasets are open. Proceed with code
end; 


Conclusion

Well, that’s probably enough for now. Bottom line, my Star Analyzer program now runs perfectly in any web browser on most any device, my users don’t need to download and install program updates and fight antivirus programs, and their data is available from any computer they or their assistants wish to use. I’m happy. They are happy.

Feel free to contact me at Howard@StarAnalyzer.com if you have questions that I can answer, or hints to share with me about programming with TMS WEB Core. Or more, I’m also available for remote development services for your projects and can apply my expertise with TMS WEB Core for your developments. And again, it has been fun working with you! Thanks for all the time you’ve given me.

Your story here?

Did you develop equally exciting projects meanwhile with TMS WEB Core? Your fellow Delphi developers for sure will want to hear & learn from it! Be in touch and we can work on bringing your success story here.