Published on December 15, 2022.
Over the past several months, I've developed several Web applications using Xojo and the Web 2.0 framework. I've primarily been using it on projects where a Web app is needed that integrates with NetSuite.
While working on those projects, there were several times when I wanted to use code that I had previously written for non-Xojo projects - things like Bootstrap components, complex HTML forms, and so on. However, I didn't want to use the Web Control SDK to create custom contols in order to re-use the code. Instead, I wanted to be able to create and modify the code as quickly as possible.
It occurred to me that I could easily add code to Xojo Web pages by using the WebHTMLViewer. However, there are two big issues with that approach.
First, while the HTML and CSS that you load into a WebHTMLViewer will be reflected in the Web page, any Javascript that you load will not work. The code will be added to the document, and you can even see it if you view the page's source. (To get injected JavaScript to work, you have to add it using the browser's Range interface, and specifically using its createContextualFragment() method. Doing so will cause the browser to parse the added code and properly add it to the document.)
The other issue with using an WebHTMLViewer to add code to a Xojo Web page is that the Xojo app doesn't really know anything about the code that was added. For example, suppose you add a button via a WebHTMLViewer. There's no way to add a "Pressed" event handler (or any other event handler) to the injected button.
To resolve these issues, I developed a Xojo module that I've been calling "CodeInjector." It includes a couple of classes, and a handful of methods, that make injecting code a little easier.
The primary class is InjectedControl. It's a subclass of WebHTMLViewer, and you can use it to inject code into a Web page. The code can consist of HTML, CSS, and/or Javascript.
The control appears in the IDE just as a normal WebHTMLViewer does. The only difference is that it includes a "Code" property, and that's what you use to specify the code that you want to inject.
When a Web page loads and the control is displayed (i.e. the "Shown" event fires), the code is injected into the page. It essentially replaces the code that the control originally consisted of. (If you're interested in learning how the injection works, take a look at the InjectedControl control's "InjectCode" method.)
To resolve the issue where Xojo doesn't know about events that occur when users interact with the injected code, the CodeInjector module includes support for a special, and relatively simple, API. The API is implemented directly in the Xojo Web app.
Injected controls use Javascript to send HTTP POST requests to the API. The requests are received by the app's HandleURL event handler, and then passed to one of the CodeInjector methods (the "HandleAPIRequest" method). The method evaluates the request, determines the session, Web page, and control that sent the request - and then sends the request to the control's APIRequestReceived method for handling. (It does a bunch of that stuff by making use of Xojo's support for Introspection.)
Controls that are embedded in containers (specifically in WebContainers) pose other challenges.
First, when a request is sent to the CodeInjector API, simply scanning the page from which the request originated, and looking for the control that initiated it, won't work because the control is actually in the container (duh!). Therefore, CodeInjector has to also look in any WebContainers on the page, and also take into account WebContainers within WebContainers, and so on.
I'm happy to report that CodeInjector does take this into account. To get it to work, instead of the standard WebContainer class, you'll need to use CodeInjector's InjectedContainer class (which is a subclass of WebContainer).
Another issue with controls embedded in WebContainers is that when a page is redisplayed, controls in a container aren't "re-shown." As a result, code injected via instances of the InjectedControl class won't redisplay properly. (Instead, the app will send the original, empty HTMLViewers back down to the browser.)
To resolve this issue, the InjectedContainer's redisplay any injected controls that they contain. So you can display injected controls in an InjectedContainer, display another page, then redisplay the original page, and the injected controls will redisplay properly.
Here's a short preview of the CodeInjector demo project.
Click the image to view a larger version.
Being able to dynamically inject code - and especially custom controls - into Xojo Web apps gives us a lot of possibilities.
By subclassing the InjectedControl, we can share special-purpose controls with other Xojo developers - controls with applicable properties and methods. For example, imagine an injected control that gets data from a Web API and displays it in a DataTable. The class might present the developer with properties to specify the Web API's address, credentials, query parameters, and so on.
Also, keep in mind that an injected control can be as simple or as complex as you need it to be. It could be as simple as a specially formatted button or link, an entire container full of custom HTML code, or as complex as an entire Web page. (As an example, see the LoginPage in the demo project.) In addition, an injected control can get its code from an external file. (See the ExternalPage in the demo project to see how this might work.)
Injected controls aren't without challenges of their own. As I mentioned earlier, they appear in the IDE just as WebHTMLViewer do. There's no preview of what they will really look like when the page is rendered. This can make positioning and sizing them challenging.
Another challenge with injected controls is that, in order to create them, you to need to know at least the basics regarding HTML, CSS, and JavaScript. Xojo is a low-code development platform, but injected controls do require some coding. That being said, injected controls, the CodeInjector module, and the techniques that I've discussed in this post aren't going to appeal to all Xojo developers.
If you're interested in learning about this technique, I encourage you to download the CodeInjector project file and check it out. In it, you'll find a variety of injected code examples (login pages, navbars, Bootstrap controls, etc) and, of course, the CodeInjector module. (By the way, if you run the app, the sample Login page will appear. Login using email address "test@test.com" and enter "secret" as the password.)
I'd also like to invite you to join members of the Xojo team, Xojo developers, and myself, next Monday (December 19, 2022) for a year-end Xojo Hangout. I'll be giving a quick demo of the CodeInjector project, and can answer any questions that come up. I hope you'll join us.
Hello, I'm Tim Dietrich. I develop custom software for businesses that are running on NetSuite, including mobile apps, Web portals, Web APIs, and more.
I'm the developer of several popular NetSuite open source solutions, including the SuiteQL Query Tool, SuiteAPI, and more.
I founded SuiteStep, a NetSuite development studio, to provide custom software and AI solutions - and continue pushing the boundaries of what's possible on the NetSuite platform.
Copyright © 2025 Tim Dietrich.