WebStencils support in IDE Wizard and code generator

This commit is contained in:
Daniele Teti 2024-10-14 09:21:56 +02:00
parent 92c4bf9f0c
commit 787daee3b2
22 changed files with 439 additions and 207 deletions

View File

@ -176,6 +176,19 @@ type
); override;
end;
TUnitWebStencilsHelpersDeclarationCommand = class(TCustomCommand)
public
procedure ExecuteInterface(
Section: TStringBuilder;
Model: TJSONObject
); override;
procedure ExecuteImplementation(
Section: TStringBuilder;
Model: TJsonObject
); override;
end;
TUnitFooterCommand = class(TCustomCommand)
@ -319,21 +332,18 @@ begin
.AppendLine(' MVCFramework.Commons,')
.AppendLine(' MVCFramework.Serializer.Commons,');
if Model.B[TConfigKey.program_ssv_templatepro] then
begin
Section
.AppendLine(' MVCFramework.View.Renderers.TemplatePro,');
end;
if Model.B[TConfigKey.program_ssv_mustache] then
begin
{The Mustache units are required in the "program" because the mustache helpers are declared there.
Should we create an external unit as we do in templatepro and webstencils }
Section
.AppendLine(' MVCFramework.View.Renderers.Mustache,')
.AppendLine(' mormot.core.mustache,');
.AppendLine(' MVCFramework.View.Renderers.Mustache,')
.AppendLine(' mormot.core.mustache,');
end;
if Model.B[TConfigKey.program_service_container_generate] then
begin
Section
.AppendLine(' MVCFramework.Container,')
.AppendLine(' MVCFramework.Container,')
end;
Section
.AppendLine(' MVCFramework.Signal;')
@ -786,6 +796,13 @@ begin
.AppendLine(' MVCFramework.View.Renderers.TemplatePro,')
end;
if Model.B[TConfigKey.program_ssv_webstencils] then
begin
Section
.AppendLine(' MVCFramework.View.Renderers.WebStencils,')
end;
if Model.B[TConfigKey.program_ssv_mustache] then
begin
Section
@ -846,6 +863,15 @@ begin
.AppendLine;
end;
if Model.B[TConfigKey.program_ssv_webstencils] then
begin
Section
.AppendLine(' // Server Side View')
.AppendLine(' FMVC.SetViewEngine(TMVCWebStencilsViewEngine);')
.AppendLine(' // Server Side View - END')
.AppendLine;
end;
if Model.B[TConfigKey.program_ssv_mustache] then
begin
Section
@ -1097,6 +1123,18 @@ begin
.AppendLine('{$ENDIF}')
.AppendLine;
if Model.B[TConfigKey.program_ssv_templatepro] then
begin
Section
.AppendLine(' TemplateProContextConfigure;');
end;
if Model.B[TConfigKey.program_ssv_webstencils] then
begin
Section
.AppendLine(' WebStencilsProcessorConfigure;');
end;
if Model.B[TConfigKey.program_ssv_mustache] then
begin
Section
@ -1175,7 +1213,7 @@ begin
.AppendLine(' LServer.MaxConnections := dotEnv.Env(''dmvc.webbroker.max_connections'', 0);')
.AppendLine(' LServer.ListenQueue := dotEnv.Env(''dmvc.indy.listen_queue'', 500);')
.AppendLine(' LServer.Active := True;')
.AppendLine(' LogI(''Listening on port '' + APort.ToString);')
.AppendLine(' LogI(''Listening on http://localhost:'' + APort.ToString);')
.AppendLine(' LogI(''Application started. Press Ctrl+C to shut down.'');')
.AppendLine(' WaitForTerminationSignal;')
.AppendLine(' EnterInShutdownState;')
@ -1442,7 +1480,7 @@ procedure TUnitTemplateProHelpersDeclarationCommand.ExecuteInterface(
Section: TStringBuilder; Model: TJSONObject);
begin
inherited;
CheckFor(TConfigKey.program_ssv_mustache, Model);
CheckFor(TConfigKey.program_ssv_templatepro, Model);
CheckFor(TConfigKey.templatepro_helpers_unit_name, Model);
Section
.AppendLine('unit ' + Model[TConfigKey.templatepro_helpers_unit_name] + ';')
@ -1460,4 +1498,77 @@ begin
.AppendLine;
end;
{ TUnitWebStencilsHelpersDeclarationCommand }
procedure TUnitWebStencilsHelpersDeclarationCommand.ExecuteImplementation(Section: TStringBuilder; Model: TJsonObject);
begin
inherited;
Section
.AppendLine('implementation')
.AppendLine('')
.AppendLine('uses')
.AppendLine(' System.SysUtils, MVCFramework.View.Renderers.WebStencils, System.Bindings.Methods, Web.Stencils;')
.AppendLine('')
.AppendLine('')
.AppendLine('function MyHelper1(const Parameters: TArray<IValue>): TValue;')
.AppendLine('begin')
.AppendLine(' Result := Parameters[0].GetValue.ToString + '' (I''''m The MyHelper1)'';')
.AppendLine('end;')
.AppendLine('')
.AppendLine('function MyHelper2(const Parameters: TArray<IValue>): TValue;')
.AppendLine('begin')
.AppendLine(' Result := Parameters[0].GetValue.ToString + '' (I''''m The MyHelper2)'';')
.AppendLine('end;')
.AppendLine('')
.AppendLine('procedure WebStencilsProcessorConfigure;')
.AppendLine('begin')
.AppendLine(' TBindingMethodsFactory.RegisterMethod(')
.AppendLine(' TMethodDescription.Create(')
.AppendLine(' MakeInvokable(function(Args: TArray<IValue>): IValue')
.AppendLine(' begin')
.AppendLine(' Result := TValueWrapper.Create(MyHelper1(Args));')
.AppendLine(' end),')
.AppendLine(' ''MyHelper1'', ''MyHelper1'', '''', True, ''MyHelper1 is just a sample'', nil));')
.AppendLine('')
.AppendLine('')
.AppendLine(' TBindingMethodsFactory.RegisterMethod(')
.AppendLine(' TMethodDescription.Create(')
.AppendLine(' MakeInvokable(function(Args: TArray<IValue>): IValue')
.AppendLine(' begin')
.AppendLine(' Result := TValueWrapper.Create(MyHelper2(Args));')
.AppendLine(' end),')
.AppendLine(' ''MyHelper2'', ''MyHelper2'', '''', True, ''MyHelper2 is just a sample'', nil));')
.AppendLine('')
.AppendLine(' TMVCWebStencilsConfiguration.OnProcessorConfiguration :=')
.AppendLine(' procedure(const WebStencilsProcessor: TWebStencilsProcessor)')
.AppendLine(' begin')
.AppendLine(' //custom configuration for TWebStencilsProcessor (executed for each view)')
.AppendLine(' end;')
.AppendLine('')
.AppendLine('end;')
.AppendLine('')
.AppendLine('end.')
end;
procedure TUnitWebStencilsHelpersDeclarationCommand.ExecuteInterface(Section: TStringBuilder; Model: TJSONObject);
begin
inherited;
CheckFor(TConfigKey.program_ssv_webstencils, Model);
CheckFor(TConfigKey.webstencils_helpers_unit_name, Model);
Section
.AppendLine('unit ' + Model[TConfigKey.webstencils_helpers_unit_name] + ';')
.AppendLine
.AppendLine('interface')
.AppendLine
.AppendLine('uses')
.AppendLine(' System.Rtti, System.Bindings.EvalProtocol;')
.AppendLine
.AppendLine('function MyHelper1(const Parameters: TArray<IValue>): TValue;')
.AppendLine('function MyHelper2(const Parameters: TArray<IValue>): TValue;')
.AppendLine
.AppendLine
.AppendLine('procedure WebStencilsProcessorConfigure;')
.AppendLine;
end;
end.

View File

@ -35,6 +35,7 @@ procedure FillWebModuleTemplates(Gen: TMVCCodeGenerator);
procedure FillWebModuleDFMTemplates(Gen: TMVCCodeGenerator);
procedure FillJSONRPCTemplates(Gen: TMVCCodeGenerator);
procedure FillTemplateProTemplates(Gen: TMVCCodeGenerator);
procedure FillWebStencilsTemplates(Gen: TMVCCodeGenerator);
procedure FillMustacheTemplates(Gen: TMVCCodeGenerator);
procedure FillEntitiesTemplates(Gen: TMVCCodeGenerator);
procedure FillServicesTemplates(Gen: TMVCCodeGenerator);
@ -98,6 +99,14 @@ begin
]);
end;
procedure FillWebStencilsTemplates(Gen: TMVCCodeGenerator);
begin
Gen.Commands.AddRange([
TUnitWebStencilsHelpersDeclarationCommand.Create
]);
end;
procedure FillServicesTemplates(Gen: TMVCCodeGenerator);
begin
Gen.Commands.AddRange([

View File

@ -59,11 +59,13 @@ type
program_sqids='program.sqids';
program_dotenv='program.dotenv';
program_ssv_templatepro='program.ssv.templatepro';
program_ssv_webstencils='program.ssv.webstencils';
program_ssv_mustache='program.ssv.mustache';
program_service_container_generate = 'program.service.container.generate';
program_service_container_unit_name = 'program.service.container.unit_name';
mustache_helpers_unit_name = 'mustache.helpers_unit_name';
templatepro_helpers_unit_name = 'templatepro.helpers_unit_name';
webstencils_helpers_unit_name = 'webstencils.helpers_unit_name';
controller_unit_name='controller.unit_name';
controller_classname= 'controller.classname';
controller_index_methods_generate= 'controller.index_methods.generate';

View File

@ -3,7 +3,7 @@ object frmDMVCNewProject: TfrmDMVCNewProject
Top = 0
BorderStyle = bsDialog
Caption = 'DelphiMVCFramework :: New Project Wizard'
ClientHeight = 615
ClientHeight = 631
ClientWidth = 729
Color = clBtnFace
Constraints.MinHeight = 145
@ -18,7 +18,7 @@ object frmDMVCNewProject: TfrmDMVCNewProject
OnDestroy = FormDestroy
DesignSize = (
729
615)
631)
TextHeight = 13
object Shape1: TShape
Left = 0
@ -400,7 +400,7 @@ object frmDMVCNewProject: TfrmDMVCNewProject
object lblBook: TLabel
AlignWithMargins = True
Left = 10
Top = 590
Top = 606
Width = 259
Height = 16
Cursor = crHandPoint
@ -419,6 +419,7 @@ object frmDMVCNewProject: TfrmDMVCNewProject
OnClick = lblBookClick
OnMouseEnter = lblBookMouseEnter
OnMouseLeave = lblBookMouseLeave
ExplicitTop = 590
end
object lblCopyRight: TLabel
Left = 24
@ -434,12 +435,13 @@ object frmDMVCNewProject: TfrmDMVCNewProject
ParentFont = False
end
object lblPATREON: TLabel
Left = 452
Top = 549
Left = 461
Top = 572
Width = 257
Height = 16
Cursor = crHandPoint
Alignment = taRightJustify
Anchors = [akRight, akBottom]
AutoSize = False
Caption = 'Support DMVCFramework on PATREON'
Color = clBtnFace
@ -453,10 +455,11 @@ object frmDMVCNewProject: TfrmDMVCNewProject
OnClick = lblPATREONClick
OnMouseEnter = lblPATREONMouseEnter
OnMouseLeave = lblPATREONMouseLeave
ExplicitTop = 549
end
object btnOK: TButton
Left = 561
Top = 580
Top = 596
Width = 77
Height = 27
Anchors = [akRight, akBottom]
@ -468,7 +471,7 @@ object frmDMVCNewProject: TfrmDMVCNewProject
end
object btnCancel: TButton
Left = 644
Top = 580
Top = 596
Width = 77
Height = 27
Anchors = [akRight, akBottom]
@ -504,9 +507,9 @@ object frmDMVCNewProject: TfrmDMVCNewProject
end
object Panel2: TPanel
Left = 0
Top = 400
Top = 423
Width = 308
Height = 189
Height = 182
Anchors = [akLeft, akBottom]
BevelOuter = bvNone
Caption = 'Panel2'
@ -516,21 +519,21 @@ object frmDMVCNewProject: TfrmDMVCNewProject
TabOrder = 5
DesignSize = (
308
189)
182)
object gbControllerUnitOptions: TGroupBox
Left = 12
Top = 0
Top = 13
Width = 296
Height = 184
Height = 165
Anchors = [akLeft, akTop, akRight, akBottom]
Caption = 'Controller Unit Options'
TabOrder = 0
DesignSize = (
296
184)
165)
object lblClassName: TLabel
Left = 16
Top = 126
Top = 115
Width = 105
Height = 13
Caption = 'Controller Class Name'
@ -555,7 +558,7 @@ object frmDMVCNewProject: TfrmDMVCNewProject
end
object edtControllerClassName: TEdit
Left = 16
Top = 146
Top = 135
Width = 264
Height = 19
Anchors = [akLeft, akTop, akRight]
@ -597,29 +600,29 @@ object frmDMVCNewProject: TfrmDMVCNewProject
Left = 314
Top = 135
Width = 407
Height = 210
Height = 221
Caption = 'Middlewares'
TabOrder = 6
DesignSize = (
407
210)
221)
object Label4: TLabel
Left = 161
Top = 112
Top = 125
Width = 144
Height = 13
Caption = 'FireDAC Connections filename'
end
object Bevel1: TBevel
Left = 11
Top = 102
Top = 115
Width = 383
Height = 3
Shape = bsTopLine
end
object Label5: TLabel
Left = 161
Top = 156
Top = 169
Width = 101
Height = 13
Caption = 'ConnectionDef Name'
@ -681,7 +684,7 @@ object frmDMVCNewProject: TfrmDMVCNewProject
end
object chkActiveRecord: TCheckBox
Left = 27
Top = 111
Top = 124
Width = 135
Height = 17
Anchors = [akTop]
@ -690,7 +693,7 @@ object frmDMVCNewProject: TfrmDMVCNewProject
end
object EdtFDConnDefFileName: TEdit
Left = 161
Top = 129
Top = 142
Width = 230
Height = 21
Anchors = [akLeft, akTop, akRight]
@ -699,7 +702,7 @@ object frmDMVCNewProject: TfrmDMVCNewProject
end
object EdtConnDefName: TEdit
Left = 161
Top = 175
Top = 188
Width = 230
Height = 21
Anchors = [akLeft, akTop, akRight]
@ -709,7 +712,7 @@ object frmDMVCNewProject: TfrmDMVCNewProject
end
object GroupBoxJSONRPC: TGroupBox
Left = 314
Top = 438
Top = 436
Width = 407
Height = 105
Anchors = [akLeft, akRight, akBottom]
@ -748,7 +751,7 @@ object frmDMVCNewProject: TfrmDMVCNewProject
end
object chkMSHeap: TCheckBox
Left = 24
Top = 292
Top = 283
Width = 225
Height = 17
Anchors = [akLeft, akRight, akBottom]
@ -757,50 +760,40 @@ object frmDMVCNewProject: TfrmDMVCNewProject
end
object chkCustomConfigDotEnv: TCheckBox
Left = 24
Top = 273
Top = 264
Width = 225
Height = 17
Anchors = [akLeft, akRight, akBottom]
Caption = 'Generate custom .env configuration'
TabOrder = 9
end
object chkMustache: TCheckBox
Left = 24
Top = 331
Width = 225
Height = 17
Anchors = [akLeft, akRight, akBottom]
Caption = 'Use Mustache as Server Side View engine'
TabOrder = 10
OnClick = chkMustacheClick
end
object chkServicesContainer: TCheckBox
Left = 24
Top = 351
Top = 303
Width = 225
Height = 17
Anchors = [akLeft, akRight, akBottom]
Caption = 'Use Services Container'
Checked = True
State = cbChecked
TabOrder = 11
TabOrder = 10
end
object chkSqids: TCheckBox
Left = 24
Top = 371
Top = 323
Width = 225
Height = 17
Anchors = [akLeft, akRight, akBottom]
Caption = 'Use Sqids'
Checked = True
State = cbChecked
TabOrder = 12
TabOrder = 11
end
object rgNameCase: TRadioGroup
Left = 314
Top = 351
Top = 362
Width = 407
Height = 81
Height = 63
Caption = 'Default style for serialized property names (MVCNameCaseDefault)'
Columns = 3
ItemIndex = 2
@ -811,17 +804,23 @@ object frmDMVCNewProject: TfrmDMVCNewProject
'CamelCase (fooBar)'
'PascalCase (FooBar)'
'SnakeCase (foo_bar)')
TabOrder = 13
TabOrder = 12
end
object chkTemplatePro: TCheckBox
Left = 24
Top = 312
Width = 245
Height = 17
Anchors = [akLeft, akRight, akBottom]
Caption = 'Use TemplatePro as Server Side View engine'
TabOrder = 14
OnClick = chkTemplateProClick
object rgSSV: TRadioGroup
Left = 12
Top = 362
Width = 296
Height = 63
Caption = 'Server Side Views Template Engine'
Columns = 2
ItemIndex = 0
Items.Strings = (
'None'
'TemplatePro'
'WebStencils'
'Mustache')
TabOrder = 13
OnClick = rgSSVClick
end
object ApplicationEvents: TApplicationEvents
OnIdle = ApplicationEventsIdle

View File

@ -32,6 +32,8 @@
unit DMVC.Expert.Forms.NewProjectWizard;
{$I dmvcframework.inc}
interface
uses
@ -97,11 +99,10 @@ type
chkCustomConfigDotEnv: TCheckBox;
chkProfileActions: TCheckBox;
lblPATREON: TLabel;
chkMustache: TCheckBox;
chkServicesContainer: TCheckBox;
chkSqids: TCheckBox;
rgNameCase: TRadioGroup;
chkTemplatePro: TCheckBox;
rgSSV: TRadioGroup;
procedure FormCreate(Sender: TObject);
procedure Image1Click(Sender: TObject);
procedure lblBookMouseEnter(Sender: TObject);
@ -116,8 +117,7 @@ type
procedure lblPATREONClick(Sender: TObject);
procedure lblPATREONMouseEnter(Sender: TObject);
procedure lblPATREONMouseLeave(Sender: TObject);
procedure chkMustacheClick(Sender: TObject);
procedure chkTemplateProClick(Sender: TObject);
procedure rgSSVClick(Sender: TObject);
private
{ Private declarations }
fModel: TJsonObject;
@ -177,18 +177,6 @@ begin
end;
end;
procedure TfrmDMVCNewProject.chkMustacheClick(Sender: TObject);
begin
if chkMustache.Checked then
chkTemplatePro.Checked := False;
end;
procedure TfrmDMVCNewProject.chkTemplateProClick(Sender: TObject);
begin
if chkTemplatePro.Checked then
chkMustache.Checked := False;
end;
procedure TfrmDMVCNewProject.FormCreate(Sender: TObject);
begin
edtControllerClassName.TextHint := TDefaultValues.sDefaultControllerName;
@ -287,6 +275,18 @@ begin
lblPATREON.Font.Style := lblPATREON.Font.Style - [fsUnderline];
end;
procedure TfrmDMVCNewProject.rgSSVClick(Sender: TObject);
begin
{$if not Defined(WEBSTENCILS)}
if SameText(rgSSV.Items[rgSSV.ItemIndex], 'webstencils') then
begin
ShowMessage('This Delphi version doesn''t support WebStencils, so DMVCFramework cannot use it.' +
sLineBreak + 'Consider to use TemplatePro.');
rgSSV.ItemIndex := 1;
end;
{$endif}
end;
procedure TfrmDMVCNewProject.lblBookClick(Sender: TObject);
begin
ShellExecute(0, PChar('open'),
@ -348,8 +348,9 @@ begin
fModel.B[TConfigKey.program_msheap] := chkMSHeap.Checked;
fModel.B[TConfigKey.program_sqids] := chkSqids.Checked;
fModel.B[TConfigKey.program_dotenv] := chkCustomConfigDotEnv.Checked;
fModel.B[TConfigKey.program_ssv_mustache] := chkMustache.Checked;
fModel.B[TConfigKey.program_ssv_templatepro] := chkTemplatePro.Checked;
fModel.B[TConfigKey.program_ssv_templatepro] := SameText(rgSSV.Items[rgSSV.ItemIndex], 'templatepro');
fModel.B[TConfigKey.program_ssv_webstencils] := SameText(rgSSV.Items[rgSSV.ItemIndex], 'webstencils');
fModel.B[TConfigKey.program_ssv_mustache] := SameText(rgSSV.Items[rgSSV.ItemIndex], 'mustache');
fModel.B[TConfigKey.program_service_container_generate] := chkServicesContainer.Checked;
fModel.S[TConfigKey.program_service_container_unit_name] := 'TBA';
fModel.S[TConfigKey.controller_unit_name] := 'TBA';

View File

@ -110,6 +110,8 @@ begin
lTemplateProHelpersUnitName: string;
lEntityUnitName: string;
EntityUnit: IOTAModule;
lWebStencilsHelpersUnitName: string;
WebStencilsHelperUnit: IOTAModule;
begin
WizardForm := TfrmDMVCNewProject.Create(Application);
try
@ -203,7 +205,7 @@ begin
end;
end;
{********** SERVER SIDE VIEWS TEMPLATE ENGINE CONFIGURATION **************}
lMustacheHelpersUnitName := '';
// Create Mustache Helpers Unit
@ -243,6 +245,26 @@ begin
end;
end;
lWebStencilsHelpersUnitName := '';
// Create WebStencils Helpers Unit
if lJSON.B[TConfigKey.program_ssv_webstencils] then
begin
HelpersUnitCreator := TNewGenericUnitFromTemplate.Create(
lJSON,
FillWebStencilsTemplates,
TConfigKey.webstencils_helpers_unit_name,
APersonality);
WebStencilsHelperUnit := ModuleServices.CreateModule(HelpersUnitCreator);
ChangeIOTAModuleFileNamePrefix(WebStencilsHelperUnit, 'WebStencilsHelpers');
lWebStencilsHelpersUnitName := GetUnitName(WebStencilsHelperUnit.FileName);
lJSON.S[TConfigKey.webstencils_helpers_unit_name] := lWebStencilsHelpersUnitName;
if Project <> nil then
begin
Project.AddFile(WebStencilsHelperUnit.FileName, True);
end;
end;
{******** END - SERVER SIDE VIEWS TEMPLATE ENGINE CONFIGURATION ************}
// Create Webmodule Unit
WebModuleCreator := TNewWebModuleUnitEx.Create(

View File

@ -33,7 +33,8 @@ type
implementation
uses
System.StrUtils, System.SysUtils, MVCFramework.Logger, MVCFramework.HTMX, RandomUtilsU;
System.StrUtils, System.SysUtils, MVCFramework.Logger, MVCFramework.HTMX, RandomUtilsU,
TemplatePro;
function TMyController.Customers: String;
begin
@ -63,7 +64,7 @@ begin
var lPosts := GetPosts(20);
try
ViewData['posts'] := lPosts;
Result := Page(['pages/posts']);
Result := Page('pages/posts');
finally
lPosts.Free;
end;
@ -74,7 +75,11 @@ begin
var lUsers := GetUsers();
try
ViewData['users'] := lUsers;
Result := Page(['pages/users']);
Result := Page('pages/users', False,
procedure (const Tmpl: TObject)
begin
(Tmpl as TTProCompiledTemplate).SetData('var1', 1234);
end);
finally
lUsers.Free;
end;

View File

@ -2,30 +2,59 @@ unit HelpersU;
interface
uses
System.Rtti;
type
TMyMustacheHelpers = class sealed
public
class procedure MyHelper1(const Value: variant; out Result: variant);
class procedure MyHelper2(const Value: variant; out Result: variant);
end;
function MyHelper1(const Value: TValue; const Parameters: TArray<string>): TValue;
function MyHelper2(const Value: TValue; const Parameters: TArray<string>): TValue;
procedure TemplateProContextConfigure;
implementation
uses
MVCFramework.View.Renderers.Mustache, System.SysUtils;
TemplatePro, System.SysUtils;
{ TMyMustacheHelpers }
class procedure TMyMustacheHelpers.MyHelper1(const Value: variant; out Result: variant);
function MyHelper1(const Value: TValue; const Parameters: TArray<string>): TValue;
begin
Result := Value + ' (I''m The MyHelper1)';
Result := Value.ToString + ' (I''m The MyHelper1)';
end;
class procedure TMyMustacheHelpers.MyHelper2(const Value: variant; out Result: variant);
function MyHelper2(const Value: TValue; const Parameters: TArray<string>): TValue;
begin
Result := Value + ' (I''m The MyHelper2)';
Result := Value.ToString + ' (I''m The MyHelper2)';
end;
procedure TemplateProContextConfigure;
begin
TTProConfiguration.OnContextConfiguration := procedure(const CompiledTemplate: ITProCompiledTemplate)
begin
// These filters will be available to the TemplatePro views as if they were the standard ones
CompiledTemplate.AddFilter('MyHelper1', MyHelper1);
CompiledTemplate.AddFilter('MyHelper2', MyHelper2);
CompiledTemplate.OnGetValue :=
procedure(const DataSource, Members: string; var Value: TValue; var Handled: Boolean)
begin
if SameText(DataSource, 'ext1') then
begin
if Members.IsEmpty then
begin
Value := 'External Value Ext1'
end
else
begin
Value := 'Reading ext1.' + Members;
end;
Handled := True;
end;
end
end;
end;
end.

View File

@ -56,7 +56,7 @@ begin
//view path
Config[TMVCConfigKey.ViewPath] := dotEnv.Env('dmvc.view_path', 'templates');
//use cache for server side views (use "false" in debug and "true" in production for faster performances
Config[TMVCConfigKey.ViewCache] := dotEnv.Env('dmvc.view_cache', 'false');
Config[TMVCConfigKey.ViewCache] := dotEnv.Env('dmvc.view_cache', 'true');
//Max Record Count for automatic Entities CRUD
Config[TMVCConfigKey.MaxEntitiesRecordCount] := dotEnv.Env('dmvc.max_entities_record_count', IntToStr(TMVCConstants.MAX_RECORD_COUNT));
//Enable Server Signature in response

View File

@ -1,4 +1,4 @@
program htmx_website;
program htmx_website_with_templatepro;
{$APPTYPE CONSOLE}
@ -17,7 +17,8 @@ uses
ControllerU in 'ControllerU.pas',
WebModuleU in 'WebModuleU.pas' {MyWebModule: TWebModule},
RandomUtilsU in '..\commons\RandomUtilsU.pas',
TemplatePro in '..\..\sources\TemplatePro.pas';
TemplatePro in '..\..\sources\TemplatePro.pas',
HelpersU in 'HelpersU.pas';
{$R *.res}
@ -68,7 +69,7 @@ begin
Profiler.WarningThreshold := dotEnv.Env('dmvc.profiler.warning_threshold', 2000);
end;
{$ENDIF}
TemplateProContextConfigure;
RunServer(dotEnv.Env('dmvc.server.port', 8080));
except
on E: Exception do

View File

@ -6,10 +6,10 @@
<Base>True</Base>
<Config Condition="'$(Config)'==''">Debug</Config>
<Platform Condition="'$(Platform)'==''">Win32</Platform>
<ProjectName Condition="'$(ProjectName)'==''">htmx_website</ProjectName>
<ProjectName Condition="'$(ProjectName)'==''">htmx_website_with_templatepro</ProjectName>
<TargetedPlatforms>1</TargetedPlatforms>
<AppType>Console</AppType>
<MainSource>htmx_website.dpr</MainSource>
<MainSource>htmx_website_with_templatepro.dpr</MainSource>
</PropertyGroup>
<PropertyGroup Condition="'$(Config)'=='Base' or '$(Base)'!=''">
<Base>true</Base>
@ -69,7 +69,7 @@
<Icns_MainIcns>$(BDS)\bin\delphi_PROJECTICNS.icns</Icns_MainIcns>
<DCC_UnitSearchPath>$(DMVC);$(DCC_UnitSearchPath)</DCC_UnitSearchPath>
<DCC_Framework>FMX;$(DCC_Framework)</DCC_Framework>
<SanitizedProjectName>htmx_website</SanitizedProjectName>
<SanitizedProjectName>htmx_website_with_templatepro</SanitizedProjectName>
<VerInfo_Locale>1040</VerInfo_Locale>
<VerInfo_Keys>CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=</VerInfo_Keys>
</PropertyGroup>
@ -128,6 +128,7 @@
</DCCReference>
<DCCReference Include="..\commons\RandomUtilsU.pas"/>
<DCCReference Include="..\..\sources\TemplatePro.pas"/>
<DCCReference Include="HelpersU.pas"/>
<BuildConfiguration Include="Base">
<Key>Base</Key>
</BuildConfiguration>
@ -146,7 +147,7 @@
<BorlandProject>
<Delphi.Personality>
<Source>
<Source Name="MainSource">htmx_website.dpr</Source>
<Source Name="MainSource">htmx_website_with_templatepro.dpr</Source>
</Source>
<Excluded_Packages>
<Excluded_Packages Name="$(BDSBIN)\dcloffice2k290.bpl">Microsoft Office 2000 Sample Automation Server Wrapper Components</Excluded_Packages>
@ -172,9 +173,9 @@
</Platform>
</DeployFile>
<DeployFile LocalName="bin\htmx_website.exe" Configuration="Debug" Class="ProjectOutput"/>
<DeployFile LocalName="bin\htmx_website.exe" Configuration="Debug" Class="ProjectOutput">
<DeployFile LocalName="bin\htmx_website_with_templatepro.exe" Configuration="Debug" Class="ProjectOutput">
<Platform Name="Win32">
<RemoteName>htmx_website.exe</RemoteName>
<RemoteName>htmx_website_with_templatepro.exe</RemoteName>
<Overwrite>true</Overwrite>
</Platform>
</DeployFile>

View File

@ -4,7 +4,7 @@ interface
uses
MVCFramework, MVCFramework.Commons, MVCFramework.Serializer.Commons,
System.Generics.Collections;
System.Generics.Collections, Web.Stencils;
type
[MVCPath]
@ -53,7 +53,7 @@ begin
var lPosts := GetPosts(20);
try
ViewData['posts'] := lPosts;
Result := Page('posts' + IfThen(Context.Request.IsHTMX, '_body'))
Result := Page('posts' + IfThen(Context.Request.IsHTMX, '_body'));
finally
lPosts.Free;
end;

View File

@ -2,30 +2,57 @@ unit HelpersU;
interface
uses
System.Rtti, System.Bindings.EvalProtocol;
type
TMyMustacheHelpers = class sealed
public
class procedure MyHelper1(const Value: variant; out Result: variant);
class procedure MyHelper2(const Value: variant; out Result: variant);
end;
function MyHelper1(const Parameters: TArray<IValue>): TValue;
function MyHelper2(const Parameters: TArray<IValue>): TValue;
procedure WebStencilsProcessorConfigure;
implementation
uses
MVCFramework.View.Renderers.Mustache, System.SysUtils;
System.SysUtils, MVCFramework.View.Renderers.WebStencils, System.Bindings.Methods, Web.Stencils;
{ TMyMustacheHelpers }
class procedure TMyMustacheHelpers.MyHelper1(const Value: variant; out Result: variant);
function MyHelper1(const Parameters: TArray<IValue>): TValue;
begin
Result := Value + ' (I''m The MyHelper1)';
Result := Parameters[0].GetValue.ToString + ' (I''m The MyHelper1)';
end;
class procedure TMyMustacheHelpers.MyHelper2(const Value: variant; out Result: variant);
function MyHelper2(const Parameters: TArray<IValue>): TValue;
begin
Result := Value + ' (I''m The MyHelper2)';
Result := Parameters[0].GetValue.ToString + ' (I''m The MyHelper2)';
end;
procedure WebStencilsProcessorConfigure;
begin
TBindingMethodsFactory.RegisterMethod(
TMethodDescription.Create(
MakeInvokable(function(Args: TArray<IValue>): IValue
begin
Result := TValueWrapper.Create(MyHelper1(Args));
end),
'MyHelper1', 'MyHelper1', '', True, 'MyHelper1 is just a sample', nil));
TBindingMethodsFactory.RegisterMethod(
TMethodDescription.Create(
MakeInvokable(function(Args: TArray<IValue>): IValue
begin
Result := TValueWrapper.Create(MyHelper2(Args));
end),
'MyHelper2', 'MyHelper2', '', True, 'MyHelper2 is just a sample', nil));
TMVCWebStencilsConfiguration.OnProcessorConfiguration :=
procedure(const WebStencilsProcessor: TWebStencilsProcessor)
begin
//custom configuration for TWebStencilsProcessor (executed for each view)
end;
end;
end.

View File

@ -3,6 +3,7 @@
<p>
<article style="padding: 0.6rem">
<header style="font-size: 140%">
@(MyHelper2("ciao mondo"))
@(uppercase(p.title))
</header>
@p.abstract

View File

@ -17,7 +17,8 @@ uses
MVCFramework.View.Renderers.WebStencils,
ControllerU in 'ControllerU.pas',
WebModuleU in 'WebModuleU.pas' {MyWebModule: TWebModule},
RandomUtilsU in '..\commons\RandomUtilsU.pas';
RandomUtilsU in '..\commons\RandomUtilsU.pas',
HelpersU in 'HelpersU.pas';
{$R *.res}
@ -68,7 +69,7 @@ begin
Profiler.WarningThreshold := dotEnv.Env('dmvc.profiler.warning_threshold', 2000);
end;
{$ENDIF}
WebStencilsProcessorConfigure;
RunServer(dotEnv.Env('dmvc.server.port', 8080));
except
on E: Exception do

View File

@ -126,6 +126,7 @@
<DesignClass>TWebModule</DesignClass>
</DCCReference>
<DCCReference Include="..\commons\RandomUtilsU.pas"/>
<DCCReference Include="HelpersU.pas"/>
<BuildConfiguration Include="Base">
<Key>Base</Key>
</BuildConfiguration>

View File

@ -419,7 +419,7 @@ type
{ public declarations }
end;
EMVCFrameworkViewException = class(EMVCException)
EMVCSSVException = class(EMVCException)
private
{ private declarations }
protected

View File

@ -54,6 +54,7 @@ type
public
procedure Execute(const ViewName: string; const Builder: TStringBuilder); override;
constructor Create(const AEngine: TMVCEngine; const AWebContext: TWebContext;
const AController: TMVCController;
const AViewModel: TMVCViewDataObject;
const AContentType: string); override;
class destructor Destroy;
@ -98,8 +99,8 @@ var
gHelpersLoaded : Boolean = False;
constructor TMVCMustacheViewEngine.Create(const AEngine: TMVCEngine;
const AWebContext: TWebContext; const AViewModel: TMVCViewDataObject;
const AContentType: string);
const AWebContext: TWebContext; const AController: TMVCController;
const AViewModel: TMVCViewDataObject; const AContentType: string);
begin
inherited;
fModelPrepared := False;
@ -137,7 +138,7 @@ begin
PrepareModels;
lViewFileName := GetRealFileName(ViewName);
if lViewFileName.IsEmpty then
raise EMVCFrameworkViewException.CreateFmt('View [%s] not found', [ViewName]);
raise EMVCSSVException.CreateFmt('View [%s] not found', [ViewName]);
lViewTemplate := StringToUTF8(TFile.ReadAllText(lViewFileName, TEncoding.UTF8));
lViewEngine := TSynMustache.Parse(lViewTemplate);
Builder.Append(UTF8Tostring(RenderJSON(lViewEngine, FJSONModelAsString, fPartials, fHelpers, nil, false)));

View File

@ -128,7 +128,7 @@ begin
lUseCompiledVersion := False;
lViewFileName := GetRealFileName(ViewName);
if lViewFileName.IsEmpty then
raise EMVCFrameworkViewException.CreateFmt('View [%s] not found', [ViewName]);
raise EMVCSSVException.CreateFmt('View [%s] not found', [ViewName]);
if FUseViewCache then
begin
lCacheDir := TPath.Combine(TPath.GetDirectoryName(lViewFileName), '__cache__');
@ -137,7 +137,7 @@ begin
if not FileAge(lViewFileName, lActualFileTimeStamp) then
begin
raise EMVCFrameworkViewException.CreateFmt('View [%s] not found',
raise EMVCSSVException.CreateFmt('View [%s] not found',
[ViewName]);
end;
@ -188,6 +188,10 @@ begin
Result := '(Error: Expected 1 param, got ' + Length(aParameters).ToString + ')';
end;
end);
if Assigned(FBeforeRenderCallback) then
begin
FBeforeRenderCallback(TObject(lCompiledTemplate));
end;
Builder.Append(lCompiledTemplate.Render);
except
on E: ETProException do

View File

@ -24,30 +24,48 @@
unit MVCFramework.View.Renderers.WebStencils;
{$I dmvcframework.inc}
interface
{$if Defined(WEBSTENCILS)}
//This unit is usable from Delphi 12.2+
uses
MVCFramework, System.Generics.Collections, System.SysUtils,
MVCFramework.Commons, System.IOUtils, System.Classes;
MVCFramework.Commons, System.IOUtils, System.Classes, Web.Stencils;
type
TMVCWebStencilsEvent = reference to procedure(const WebStencilsProcessor: TWebStencilsProcessor);
{ This class implements the WebStencils view engine for server side views }
TMVCWebStencilsViewEngine = class(TMVCBaseViewEngine)
protected
procedure OnFile(Sender: TObject; const AFilename: string; var AText: string; var AHandled: Boolean);
public
procedure Execute(const ViewName: string; const Builder: TStringBuilder); override;
end;
TMVCWebStencilsConfiguration = class sealed
private
class var fOnProcessorConfiguration: TMVCWebStencilsEvent;
public
class property OnProcessorConfiguration: TMVCWebStencilsEvent
read fOnProcessorConfiguration
write fOnProcessorConfiguration;
end;
{$endif}
implementation
{$if Defined(WEBSTENCILS)}
uses
MVCFramework.Serializer.Defaults,
MVCFramework.Serializer.Intf,
MVCFramework.DuckTyping,
System.Bindings.EvalProtocol,
System.Bindings.Methods,
Web.Stencils,
MVCFramework.Cache,
Data.DB,
System.Rtti,
@ -121,6 +139,14 @@ begin
end;
function MakeMethodJSON: IInvokable;
begin
Result := MakeInvokable(function(Args: TArray<IValue>): IValue
begin
Result := TValueWrapper.Create(DumpAsJSONString(Args[0].GetValue.AsObject, []));
end)
end;
procedure RegisterWSFunctions(WSProcessor: TWebStencilsProcessor);
begin
if gFunctionInitialized then Exit;
@ -128,13 +154,20 @@ begin
try
if gFunctionInitialized then Exit;
gFunctionInitialized := True;
TBindingMethodsFactory.RegisterMethod(
TMethodDescription.Create(
MakeInvokable(function(Args: TArray<IValue>): IValue
begin
if Length(Args) <> 1 then
begin
raise EMVCSSVException.Create(500, 'Expected 1 parameter in "JSON" function, got ' + Length(Args).ToString);
end;
Result := TValueWrapper.Create(DumpAsJSONString(Args[0].GetValue.AsObject, []));
end),
end) as IInvokable,
'json', 'json', '', True, 'Serialize an object to JSON', nil));
finally
TMonitor.Exit(gWSLock);
end;
@ -143,45 +176,24 @@ end;
procedure TMVCWebStencilsViewEngine.Execute(const ViewName: string; const Builder: TStringBuilder);
var
lViewFileName: string;
lViewTemplate: String;
lWebStencilsProcessor: TWebStencilsProcessor;
lPair: TPair<String, TValue>;
lActualFileTimeStamp: TDateTime;
lCompiledViewFileName: string;
lActualCompiledFileTimeStamp: TDateTime;
lUseCompiledVersion: Boolean;
lCacheDir: string;
begin
lUseCompiledVersion := False;
lViewFileName := GetRealFileName(ViewName);
if lViewFileName.IsEmpty then
raise EMVCFrameworkViewException.CreateFmt('View [%s] not found', [ViewName]);
// if FUseViewCache then
// begin
// lCacheDir := TPath.Combine(TPath.GetDirectoryName(lViewFileName), '__cache__');
// TDirectory.CreateDirectory(lCacheDir);
// lCompiledViewFileName := TPath.Combine(lCacheDir, TPath.ChangeExtension(TPath.GetFileName(lViewFileName), '.' + WebStencils_VERSION + '.tpcu'));
//
// if not FileAge(lViewFileName, lActualFileTimeStamp) then
// begin
// raise EMVCFrameworkViewException.CreateFmt('View [%s] not found',
// [ViewName]);
// end;
//
// if FileAge(lCompiledViewFileName, lActualCompiledFileTimeStamp) then
// begin
// lUseCompiledVersion := lActualFileTimeStamp < lActualCompiledFileTimeStamp;
// end;
// end;
raise EMVCSSVException.CreateFmt('View [%s] not found', [ViewName]);
lWebStencilsProcessor := TWebStencilsProcessor.Create(nil);
try
RegisterWSFunctions(lWebStencilsProcessor);
try
//lWebStencilsProcessor.OnFile := Self.OnFile;
if Assigned(TMVCWebStencilsConfiguration.fOnProcessorConfiguration) then
begin
TMVCWebStencilsConfiguration.OnProcessorConfiguration(lWebStencilsProcessor);
end;
//lWebStencilsProcessor.OnFile := Self.OnFile; {12.2, any filename starting with ..\ is not read correctly by the parser. Is it a feature? }
lWebStencilsProcessor.InputFileName := lViewFileName;
lWebStencilsProcessor.PathTemplate := TPath.GetDirectoryName(lViewFileName);
lWebStencilsProcessor.PathTemplate := Config[TMVCConfigKey.ViewPath];
if Assigned(ViewModel) then
begin
for lPair in ViewModel do
@ -190,6 +202,10 @@ begin
lWebStencilsProcessor.AddVar(lPair.Key, ViewModel[lPair.Key].AsObject, False);
end;
end;
if Assigned(FBeforeRenderCallback) then
begin
FBeforeRenderCallback(lWebStencilsProcessor);
end;
Builder.Append(lWebStencilsProcessor.Content);
except
on E: EWebStencilsException do
@ -203,28 +219,6 @@ begin
end;
end;
procedure TMVCWebStencilsViewEngine.OnFile(Sender: TObject; const AFilename: string; var AText: string;
var AHandled: Boolean);
var
lFName: String;
begin
lFName := AFilename;
if TPath.GetExtension(lFName).IsEmpty then
begin
lFName := lFName + '.' + Config[TMVCConfigKey.DefaultViewFileExtension];
end;
if not TFile.Exists(lFName) then
begin
lFName := TPath.Combine(Config[TMVCConfigKey.ViewPath], lFName);
if not TFile.Exists(lFName) then
begin
lFName := TPath.Combine(AppPath, lFName);
end;
end;
AText := TFile.ReadAllText(lFName);
AHandled := True;
end;
initialization
gWSLock := TObject.Create;
@ -233,5 +227,6 @@ finalization
FreeAndNil(gWSLock);
{$endif}
end.

View File

@ -883,6 +883,8 @@ type
const Retry: Integer = TMVCConstants.SSE_RETRY_DEFAULT);
end;
TMVCSSVBeforeRenderCallback = reference to procedure(const TemplateRenderInstance: TObject);
TMVCController = class(TMVCRenderer)
private
FViewModel: TMVCViewDataObject;
@ -904,8 +906,8 @@ type
function GetClientId: string;
function GetCurrentWebModule: TWebModule;
function GetViewModel: TMVCViewDataObject;
function GetRenderedView(const AViewNames: TArray<string>): string; overload; virtual;
function GetRenderedView(const AViewNames: TArray<string>; const JSONModel: TJSONObject): string; overload; virtual;
function GetRenderedView(const AViewNames: TArray<string>; const OnBeforeRenderCallback: TMVCSSVBeforeRenderCallback = nil): string; overload; virtual;
function GetRenderedView(const AViewNames: TArray<string>; const JSONModel: TJSONObject; const OnBeforeRenderCallback: TMVCSSVBeforeRenderCallback = nil): string; overload; virtual;
/// <summary>
/// Normally used in OnBeforeControllerAction to define view headers automatically used by the Page method.
@ -922,27 +924,28 @@ type
/// Page method just concatenate -> commonheader_header_views + views + commonfooter_views
/// PageFragment ignore header and footer views
/// </summary>
function Page(const AViewNames: TArray<string>; const UseCommonHeadersAndFooters: Boolean = True): string; overload; inline;
function Page(const AViewName: string; const UseCommonHeadersAndFooters: Boolean = True): string; overload; inline;
function Page(const AViewNames: TArray<string>; const UseCommonHeadersAndFooters: Boolean = True; const OnBeforeRenderCallback: TMVCSSVBeforeRenderCallback = nil): string; overload; inline;
function Page(const AViewName: string; const UseCommonHeadersAndFooters: Boolean = True; const OnBeforeRenderCallback: TMVCSSVBeforeRenderCallback = nil): string; overload; inline;
/// <summary>
/// Page calls GetRenderedView with sensible defaults.
/// Page method with UseCommonHeadersAndFooters = True (default) concatenates
// commonheader_header_views + views + commonfooter_views
/// </summary>
function Page(const AViewNames: TArray<string>; const JSONModel: TJSONObject; const UseCommonHeadersAndFooters: Boolean = True): string; overload; inline;
function Page(const AViewNames: TArray<string>; const JSONModel: TJSONObject;
const UseCommonHeadersAndFooters: Boolean = True; const OnBeforeRenderCallback: TMVCSSVBeforeRenderCallback = nil): string; overload; inline;
/// <summary>
/// PageFragment calls GetRenderedView.
/// PageFragment ignore header and footer views.
/// </summary>
function PageFragment(const AViewNames: TArray<string>): string; overload; inline;
function PageFragment(const AViewNames: TArray<string>; const OnBeforeRenderCallback: TMVCSSVBeforeRenderCallback = nil): string; overload; inline;
/// <summary>
/// PageFragment calls GetRenderedView.
/// PageFragment ignore header and footer views.
/// </summary>
function PageFragment(const AViewNames: TArray<string>; const JSONModel: TJSONObject): string; overload; inline;
function PageFragment(const AViewNames: TArray<string>; const JSONModel: TJSONObject; const OnBeforeRenderCallback: TMVCSSVBeforeRenderCallback = nil): string; overload; inline;
/// <summary>
/// Load mustache view located in TMVCConfigKey.ViewsPath
@ -1277,18 +1280,24 @@ type
FViewModel: TMVCViewDataObject;
FContentType: string;
FOutput: string;
FController: TMVCController;
protected
FUseViewCache: Boolean;
FJSONModel: TJSONObject;
FBeforeRenderCallback: TMVCSSVBeforeRenderCallback;
function GetRealFileName(const AViewName: string): string; virtual;
function IsCompiledVersionUpToDate(const AFileName, ACompiledFileName: string): Boolean; virtual; abstract;
public
constructor Create(const AEngine: TMVCEngine; const AWebContext: TWebContext;
constructor Create(
const AEngine: TMVCEngine;
const AWebContext: TWebContext;
const AController: TMVCController;
const AViewModel: TMVCViewDataObject;
const AContentType: string); overload; virtual;
constructor Create(
const AEngine: TMVCEngine;
const AWebContext: TWebContext;
const AController: TMVCController;
const AViewModel: TMVCViewDataObject;
const AJSONModel: TJSONObject;
const AContentType: string); overload; virtual;
@ -4188,14 +4197,15 @@ begin
Result := Context.Request.GetHeader('If-None-Match');
end;
function TMVCController.GetRenderedView(const AViewNames: TArray<string>; const JSONModel: TJSONObject): string;
function TMVCController.GetRenderedView(const AViewNames: TArray<string>; const JSONModel: TJSONObject; const OnBeforeRenderCallback: TMVCSSVBeforeRenderCallback): string;
var
lView: TMVCBaseViewEngine; lViewName: string; lStrStream: TStringBuilder;
begin
lStrStream := TStringBuilder.Create;
try
lView := FEngine.ViewEngineClass.Create(Engine, Context, FViewModel, JSONModel, ContentType);
lView := FEngine.ViewEngineClass.Create(Engine, Context, Self, FViewModel, JSONModel, ContentType);
try
lView.FBeforeRenderCallback := OnBeforeRenderCallback;
for lViewName in AViewNames do
begin
lView.Execute(lViewName, lStrStream);
@ -4385,36 +4395,36 @@ begin
end;
function TMVCController.Page(const AViewNames: TArray<string>;
const JSONModel: TJSONObject; const UseCommonHeadersAndFooters: Boolean): string;
const JSONModel: TJSONObject; const UseCommonHeadersAndFooters: Boolean; const OnBeforeRenderCallback: TMVCSSVBeforeRenderCallback): string;
begin
if UseCommonHeadersAndFooters then
Result := GetRenderedView(fPageHeaders + AViewNames + fPageFooters, JSONModel)
Result := GetRenderedView(fPageHeaders + AViewNames + fPageFooters, JSONModel, OnBeforeRenderCallback)
else
Result := GetRenderedView(AViewNames, JSONModel)
Result := GetRenderedView(AViewNames, JSONModel, OnBeforeRenderCallback)
end;
function TMVCController.Page(const AViewName: string; const UseCommonHeadersAndFooters: Boolean): string;
function TMVCController.Page(const AViewName: string; const UseCommonHeadersAndFooters: Boolean; const OnBeforeRenderCallback: TMVCSSVBeforeRenderCallback): string;
begin
Result := Page([AViewName], UseCommonHeadersAndFooters);
Result := Page([AViewName], UseCommonHeadersAndFooters, OnBeforeRenderCallback);
end;
function TMVCController.PageFragment(const AViewNames: TArray<string>;
const JSONModel: TJSONObject): string;
const JSONModel: TJSONObject; const OnBeforeRenderCallback: TMVCSSVBeforeRenderCallback): string;
begin
Result := Page(AViewNames, JSONModel, False);
Result := Page(AViewNames, JSONModel, False, OnBeforeRenderCallback);
end;
function TMVCController.PageFragment(const AViewNames: TArray<string>): string;
function TMVCController.PageFragment(const AViewNames: TArray<string>; const OnBeforeRenderCallback: TMVCSSVBeforeRenderCallback): string;
begin
Result := Page(AViewNames, nil, False);
end;
function TMVCController.Page(const AViewNames: TArray<string>; const UseCommonHeadersAndFooters: Boolean): string;
function TMVCController.Page(const AViewNames: TArray<string>; const UseCommonHeadersAndFooters: Boolean; const OnBeforeRenderCallback: TMVCSSVBeforeRenderCallback): string;
begin
if UseCommonHeadersAndFooters then
Result := GetRenderedView(fPageHeaders + AViewNames + fPageFooters)
Result := GetRenderedView(fPageHeaders + AViewNames + fPageFooters, OnBeforeRenderCallback)
else
Result := GetRenderedView(AViewNames);
Result := GetRenderedView(AViewNames, OnBeforeRenderCallback);
end;
procedure TMVCController.PushObjectToView(const aModelName: string; const AModel: TObject);
@ -4835,7 +4845,7 @@ begin
Render<T>(ACollection, AOwns, ASerializationAction);
end;
function TMVCController.GetRenderedView(const AViewNames: TArray<string>): string;
function TMVCController.GetRenderedView(const AViewNames: TArray<string>; const OnBeforeRenderCallback: TMVCSSVBeforeRenderCallback): string;
var
lView: TMVCBaseViewEngine;
lViewName: string;
@ -4844,10 +4854,13 @@ begin
lStrStream := TStringBuilder.Create;
try
lView := FEngine.ViewEngineClass.Create(
Engine, Context,
Engine,
Context,
Self,
FViewModel,
ContentType);
try
lView.FBeforeRenderCallback := OnBeforeRenderCallback;
for lViewName in AViewNames do
begin
lView.Execute(lViewName, lStrStream);
@ -5224,12 +5237,14 @@ end;
constructor TMVCBaseViewEngine.Create(
const AEngine: TMVCEngine;
const AWebContext: TWebContext;
const AController: TMVCController;
const AViewModel: TMVCViewDataObject;
const AContentType: string);
begin
inherited Create;
Engine := AEngine;
FWebContext := AWebContext;
FController := AController;
FViewModel := AViewModel;
FContentType := AContentType;
FOutput := EmptyStr;
@ -5239,11 +5254,12 @@ end;
constructor TMVCBaseViewEngine.Create(
const AEngine: TMVCEngine;
const AWebContext: TWebContext;
const AController: TMVCController;
const AViewModel: TMVCViewDataObject;
const AJSONModel: TJSONObject;
const AContentType: string);
begin
Create(AEngine, AWebContext, AViewModel, AContentType);
Create(AEngine, AWebContext, AController, AViewModel, AContentType);
fJSONModel := AJSONModel;
end;

View File

@ -104,3 +104,9 @@ DelphiMVCFramework is compatible with Delphi version XE7 or better
{$DEFINE CUSTOM_MANAGED_RECORDS}
{$ENDIF}
{$UNDEF WEBSTENCILS}
{$IF Defined(ATHENSORBETTER)}
{$IF declared(RTLVersion122) or (RTLVersion >= 37)}
{$DEFINE WEBSTENCILS}
{$ENDIF}
{$ENDIF}