The symptom is an error in the browser console such as Cannot read properties of null (reading 'GetElementHandle') or ... of undefined, the first time your code touches a label, edit or button that is plainly on the form. The component is nil.
Each cause below was reproduced with a small TMS WEB Core project built with the command-line compiler and run in a browser. For each one: what happens, how to recognise it, and the fix.
A TMS WEB Core form that has an HTML template is loaded asynchronously: the HTML file is
fetched from the server, and only then are the components created and the DFM values applied.
CreateNew returns before that happens.
procedure TMainForm.WebFormCreate(Sender: TObject);
begin
FSecond := TSecondForm.CreateNew(@SecondLoaded);
// nil here - the form has not loaded yet
console.log('After CreateNew: ' + BoolToStr(Assigned(FSecond.lblSecond), True));
end;
procedure TMainForm.SecondLoaded(AForm: TObject);
begin
// assigned here
console.log('In callback: ' + BoolToStr(Assigned(TSecondForm(AForm).lblSecond), True));
end;
Output:
After CreateNew: False
In callback: True
Fix: do anything that touches the new form's components in the callback passed to
CreateNew, or in the form's own OnCreate, which runs after loading. Code
on the line after CreateNew runs too early. The callback is a method of the calling
form, passed with @.
If a form unit is reached only through another unit's uses clause and the search
path, the Pascal compiles and links, but the form is not treated as a form:
.html file is not copied to the output folder - the browser gets a
404 when the form is created;.dfm is not converted - the generated JavaScript does not contain the
form's component values at all;After CreateNew: False
Failed to load resource: the server responded with a status of 404 (File not found)
In callback: False
Fix: list every form in the .dpr with its form comment, which also adds it to
the .dproj:
uses
Vcl.Forms,
WEBLib.Forms,
Second_Form in 'Second_Form.pas' {SecondForm: TWebForm} {*.html},
Main_Form in 'Main_Form.pas' {MainForm: TWebForm} {*.html};
Look for a 404 on a form's .html file in the browser's network tab - it points
straight at this cause. If the project is open in the IDE while the .dpr or
.dproj is edited outside it, reload the project before building, or the IDE may
save its own copy over the change.
TMS WEB Core turns the .dfm into Pascal code - a LoadDFMValues method
that sets each property in turn. If one of those property setters raises an exception, loading
stops there. The test form had a label, then a component whose setter raised, then a second
label:
type
// A label whose property setter raises, standing in for any setter that fails at runtime
TFailingLabel = class(TWebLabel)
private
procedure SetFailOnLoad(const Value: Boolean);
published
property FailOnLoad: Boolean write SetFailOnLoad;
end;
procedure TFailingLabel.SetFailOnLoad(const Value: Boolean);
begin
if Value then
raise Exception.Create('Setter failed while the form was loading');
end;
With FailOnLoad = True in the .dfm:
OnCreate never ran;Fix: find the uncaught exception that happened while the form loaded - it is the first error in the console, before any nil errors - and correct the property value that caused it. Every nil error after it is a consequence.
Not every bad value raises. In testing, a TWebComboBox with
ItemIndex = 5 and no items, and a TWebStringGrid with
FixedCols larger than ColCount, both loaded without error.
A property in the .dfm that the component class does not have, or a value that is
not valid for its type, does not fail at runtime. Because the .dfm is converted to
Pascal, it is a compile error:
Test.Rights.Web_Main.pas (60, 13) Error: identifier not found "Fooable"
Two things make this easy to miss. The line number refers to the code generated from the
.dfm, not to your source - in the test, line 60 of a unit that has 41 lines. And a
failed build leaves the previous JavaScript in the output folder: in the test the
.js file kept its old timestamp. Run the page and you are running the last build
that worked. Anything you added since is not there, and code that depends on it fails in ways
that look like nil components.
This often happens with properties copied from a VCL form. A VCL TLabel has
properties that a TWebLabel does not, and the reverse.
Fix: check the build result before running, and remove or correct the property named in
the error. A build script that deletes the old .js files before compiling turns a
failed build into a page that does not load at all, which is far harder to overlook.
A component field declared in the form class, with no matching object in the
.dfm, compiles without a warning and is nil at runtime. It happens when a component is
deleted from the form while its field is kept, or the object is renamed in the .dfm
but not in the class.
OnCreate ran
lbl_Test assigned: True
lbl_Two assigned: False
The opposite - an object in the .dfm with no field of that name - is caught at
compile time: identifier not found "lbl_Second".
Fix: keep field and object names in step. When a field is nil and the form otherwise
loads normally, search the .dfm for its name first.
These were tested as well and did not leave components nil:
ElementID that does not exist in the form's HTML - TMS WEB Core created its
own element and the label showed normally..dfm saved with LF-only line endings - built with the command-line compiler,
the form loaded with all its components. The IDE's own build was not part of this test.ItemIndex and an invalid FixedCols, as noted
above..html loading, or getting a 404? If 404, add the form to the
.dpr.CreateNew?.dfm?
Please donate if helpful