Closed edwinyzh closed 1 year ago
I think it depends on how you are outputting stuff. Ideally, there should not be any issue. If you are following the model of passing data into the template rather than using functions to fetch data - there should be no issue. if there is an exception during the data fetching phase, well, you know about it and can then use the error handling template however.
if you want to do something like you suggest, I think you would have to have something like: <% errorCode := getErrorCode(); %> <% errorCode %>
if there is no errorCode, it can be blank...
you probably know this already, but fundamentally, the template engine is not trying to be something like php... otherwise it would have all of the features and be a general purpose programming language. If data and computation is done before template processing takes place, there is less of a dependency on the template engine helper functions to have to have concerns about threading and access to shared sate, etc... as it can just query the data structure it has been presented with...
actually, I was reviewing what you said again about nested templates...
The template engine uses 'stack frames' for storing variables. I can't recall offhand, but it may be that when going across template boundaries, a new stack frame is created. However, the way variable resolution works is by traversing the various stack frames until the relevant variable is found. if not found, it is added to the current stack frame.
with this in mind, what I'm thinking you could try... in the parent template, create a variable <% errorCode := '' %>
then in the child template, if you assign <% errorCode := 'some value' %>, it should be visible in the parent template... I can't say 100% for sure, but it might work.
Sorry for not being clear enough for the suggestion. I hope the following example explains the issue better. Assume we have a html template file:
<html>
...many lines here
// the end user writes the template code and have written it wrongly:
<% CallCustomFunction(Anything might go wrong here for non-techies) %>
...many lines here
</html>
With the current implementation, Template.Eval
will raise exception and the template processing will be halted when if there is a parsing error, so the Eval result will be zero or full.
My suggestion is not to raise exception (thus stopping the processing) but insert the error message in where the error happened. Take the above as an example, the processed output should be something like:
<html>
...many lines here
// the end user writes the template code and have written it wrongly:
<% Template error here, error code: 1, error message: 'blablabla', original code: 'CallCustomFunction(...)' %>
...many lines here
</html>
I think I see what you are getting it...
It is more about recovery during the parsing process.
I think the demo app demonstrates how to get error line and position, but does not render anything.
If you have a server app, it may be more ideal to parse the templates on startup, and then 'eval' the parsed template ... which should be a little faster. this might not be the case for you right now though, but just mentioning... if there is a problem, it is an application issue and should just be fixed in my opinion. ;) but if I recall, in your scenario you are working on an editor, so recovery during preview may be nicer...
Recovery and injection of an error message may be a good extension. thank you for the suggestion.
Recovery can be a tricky problem, but I'm just thinking about how it can be done...
So I think I'd add an option to support recovery, as it might not always be automatically desirable.
As an error essentially takes place within the <% %> type script tags, the recovery process would log the error (there may be some configuration as to a prefix/suffix around the error - e.g. so it fits into html, etc) and ignores everything until it finds the next closing %> type tag at which point it just, where it switches state back into text mode and resumes normal parsing.
There is a new option on the context - eoDebug. When this is enabled, you can also leverage the new property on the context: DebugMessageFormat.
If you want to use html output, simply do something like:
DebugMessageFormat := '<b>Error:</b><i>%s</i>';
procedure TTestTemplate.TestException;
var
LContext: ITemplateContext;
Functions: ITemplateFunctions;
begin
Functions := CreateTemplateFunctions;
Functions.RegisterDefaults;
Functions.AddFunctions(TMyExceptProc);
LContext := Template.Context([eoEmbedException]);
LContext.Functions := Functions;
Assert.AreEqual(#$D#$A#$D#$A'ERROR: (Line 1, Column 16) test'#$D#$A#$D#$A, Template.Eval(LContext, '<% RaiseExcept(''test'') %>'));
LContext.DebugErrorFormat := '<b>Error:</b><i>%s</i>';
Assert.AreEqual('<b>Error:</b><i> (Line 1, Column 16) test2</i>', Template.Eval(LContext, '<% RaiseExcept(''test2'') %>'));
end;
Let's say a template is consists of tens or hundreds of lines. Assume most of the template code are OK, but one of them has syntax error :
<% errorCodeHere %>
It'll be great if the compilation goes on and insert the Exception ClassName and Error Message in-place into somewhere in between
<% errorCodeHere %>
in the result string.Hope it makes sense.