mirror of
https://github.com/danieleteti/delphimvcframework.git
synced 2024-11-15 07:45:54 +01:00
Updated book search sample
Some checks are pending
TOC Generator / TOC Generator (push) Waiting to run
Some checks are pending
TOC Generator / TOC Generator (push) Waiting to run
This commit is contained in:
parent
6817fc1ce5
commit
a692a5a37e
@ -1,24 +1,94 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>Books Search</title>
|
<title>Books Search</title>
|
||||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.4/css/bulma.min.css" />
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.4/css/bulma.min.css" />
|
||||||
<script src="https://cdn.jsdelivr.net/npm/htmx.org/dist/htmx.min.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/htmx.org/dist/htmx.min.js"></script>
|
||||||
{{block "headers"}}{{endblock}}
|
<style>
|
||||||
|
.smooth {
|
||||||
|
transition: all 3s ease-in;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<style>
|
||||||
|
|
||||||
|
div {
|
||||||
|
padding: 0.2rem !important;
|
||||||
|
padding-left: 0.3rem !important;
|
||||||
|
}
|
||||||
|
div.blueTable {
|
||||||
|
font-family: Tahoma, Geneva, sans-serif;
|
||||||
|
border: 1px solid #1C6EA4;
|
||||||
|
background-color: #EEEEEE;
|
||||||
|
width: 100%;
|
||||||
|
text-align: left;
|
||||||
|
border-collapse: collapse;
|
||||||
|
}
|
||||||
|
.divTable.blueTable .divTableCell, .divTable.blueTable .divTableHead {
|
||||||
|
padding: 3px 2px;
|
||||||
|
}
|
||||||
|
.divTable.blueTable .divTableBody .divTableCell {
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
.divTable.blueTable .divTableRow:nth-child(even) {
|
||||||
|
background: #D0E4F5;
|
||||||
|
}
|
||||||
|
.divTable.blueTable .divTableHeading {
|
||||||
|
background: #1C6EA4;
|
||||||
|
border-bottom: 2px solid #444444;
|
||||||
|
}
|
||||||
|
.divTable.blueTable .divTableHeading .divTableHead {
|
||||||
|
font-size: 18px;
|
||||||
|
color: #FFFFFF;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
.blueTable .tableFootStyle {
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
.blueTable .tableFootStyle .links {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
.blueTable .tableFootStyle .links a{
|
||||||
|
display: inline-block;
|
||||||
|
background: #1C6EA4;
|
||||||
|
color: #FFFFFF;
|
||||||
|
padding: 2px 8px;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
.blueTable.outerTableFooter {
|
||||||
|
border-top: none;
|
||||||
|
}
|
||||||
|
.blueTable.outerTableFooter .tableFootStyle {
|
||||||
|
padding: 3px 5px;
|
||||||
|
}
|
||||||
|
/* HTMLtable.com */
|
||||||
|
.divTable{ display: table; }
|
||||||
|
.divTableRow { display: table-row; }
|
||||||
|
.divTableHeading { display: table-header-group;}
|
||||||
|
.divTableCell, .divTableHead { display: table-cell; }
|
||||||
|
.divTableHeading { display: table-header-group;}
|
||||||
|
.divTableFoot { display: table-footer-group;}
|
||||||
|
.divTableBody { display: table-row-group;}
|
||||||
|
</style>
|
||||||
|
{{block "headers"}}{{endblock}}
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body hx-indicator="#spinner">
|
<body hx-indicator="#spinner">
|
||||||
<section class="section" >
|
<section class="section">
|
||||||
{{block "body"}}{{endblock}}
|
{{block "body"}}{{endblock}}
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<div class="content has-text-centered">
|
<div class="content has-text-centered">
|
||||||
<p>
|
<p>
|
||||||
<strong><a href="https://github.com/danieleteti/templatepro">TemplatePRO</a> {{""|version}}</strong> by <a href="https://www.danieleteti.it">Daniele Teti</a>.
|
<strong><a href="https://github.com/danieleteti/templatepro">TemplatePRO</a> {{:|version}}</strong> by <a
|
||||||
|
href="https://www.danieleteti.it">Daniele Teti</a>.
|
||||||
The source code is licensed <a href="https://github.com/danieleteti/templatepro?tab=Apache-2.0-1-ov-file#readme">Apache 2.0</a>.
|
|
||||||
</p>
|
The source code is licensed <a
|
||||||
</div>
|
href="https://github.com/danieleteti/templatepro?tab=Apache-2.0-1-ov-file#readme">Apache 2.0</a>.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
@ -1,15 +1,19 @@
|
|||||||
{{extends "baselayout.html"}}
|
{{extends "baselayout.html"}}
|
||||||
{{block "body"}}
|
{{block "body"}}
|
||||||
<div class="columns">
|
<div class="columns">
|
||||||
<div class="column is-one-third is-offset-one-third">
|
<div class="column is-offset-one-third is-one-third">
|
||||||
<input type="text" class="input" placeholder="Search" name="q" hx-get="/search"
|
<h1 style="text-align: center; font-weight: 500; font-size: 170%">Instant Search Demo</h1>
|
||||||
value="{{""|fromquery,"q"}}"
|
<h2 style="text-align: center; font-size: 130%; color: #a0a0a0">DMVCFramework + TemplatePro + HTMX</h2>
|
||||||
hx-trigger="load, keyup changed delay:500ms" hx-target="#results"/>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="column is-one-third">
|
<div class="columns">
|
||||||
<p class="subtitle is-6">⭐ DMVCFramework + HTMX :: Instant Search Demo ⭐</p>
|
<div class="column is-one-third is-offset-one-third">
|
||||||
|
<input type="text" class="input" placeholder="Search" name="q" hx-get="/search"
|
||||||
|
value="{{:|fromquery,"q"}}"
|
||||||
|
hx-trigger="load, keyup changed delay:500ms"
|
||||||
|
hx-target="#results"
|
||||||
|
hx-swap="transition:true"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<progress id="spinner" class="htmx-indicator progress is-large is-info" max="100">ciao</progress>
|
|
||||||
<div id="results"></div>
|
<div id="results"></div>
|
||||||
{{endblock}}
|
{{endblock}}
|
@ -1,47 +1,37 @@
|
|||||||
<p>{{:books|count}} book/s found</p>
|
<p>{{:books|count}} book/s found</p>
|
||||||
<table class="table is-fullwidth is-hoverable">
|
|
||||||
<thead>
|
<div class="divTable blueTable">
|
||||||
<tr>
|
<div class="divTableHeading">
|
||||||
<th>ID</th>
|
<div class="divTableRow">
|
||||||
<th>Book Title</th>
|
<div class="divTableHead">ID</div>
|
||||||
<th>Book Author</th>
|
<div class="divTableHead">Book Title</div>
|
||||||
<th>Rating</th>
|
<div class="divTableHead">Book Author</div>
|
||||||
<th>Genre</th>
|
<div class="divTableHead">Rating</div>
|
||||||
</tr>
|
<div class="divTableHead">Genre</div>
|
||||||
</thead>
|
</div>
|
||||||
<tbody>
|
</div>
|
||||||
{{if !books}}
|
<div class="divTableBody">
|
||||||
<tr>
|
{{if !books}}
|
||||||
<td colspan="4">
|
<div class="divTableRow">
|
||||||
<span>No books found</span>
|
<span>No books found</span>
|
||||||
</td>
|
</div>
|
||||||
</tr>
|
{{endif}}
|
||||||
{{endif}}
|
{{for book in books}}
|
||||||
{{for book in books}}
|
<div class="divTableRow {{if book.genre|eq,"Horror"}}has-background-danger-light{{endif}}">
|
||||||
<tr class=
|
<div class="divTableCell">{{:book.id|lpad,8,"0"}}</div>
|
||||||
{{if book.genre|eq,"Horror"}}
|
<div class="divTableCell">{{:book.book_name}}</div>
|
||||||
"has-background-danger-light"
|
<div class="divTableCell">{{:book.author_name}}</div>
|
||||||
{{else}} {{if book.genre|eq,"Thriller"}}
|
<div class="divTableCell">
|
||||||
"has-background-success-light"
|
{{if book.rating|ge,1}}⭐{{endif}}
|
||||||
{{else}} {{if book.genre|eq,"Classic"}}
|
{{if book.rating|ge,2}}⭐{{endif}}
|
||||||
"has-background-primary-light"
|
{{if book.rating|ge,3}}⭐{{endif}}
|
||||||
{{endif}}
|
{{if book.rating|ge,4}}⭐{{endif}}
|
||||||
{{endif}}
|
{{if book.rating|ge,5}}⭐{{endif}}
|
||||||
{{endif}}>
|
</div>
|
||||||
<td>{{:book.id|lpad,8,"0"}}</td>
|
<div class="divTableCell">
|
||||||
<td>{{:book.book_name}}</td>
|
{{:book.genre|uppercase}}
|
||||||
<td>{{:book.author_name}}</td>
|
</div>
|
||||||
<td>
|
</div>
|
||||||
{{if book.rating|ge,1}}⭐{{endif}}
|
{{endfor}}
|
||||||
{{if book.rating|ge,2}}⭐{{endif}}
|
</div>
|
||||||
{{if book.rating|ge,3}}⭐{{endif}}
|
</div>
|
||||||
{{if book.rating|ge,4}}⭐{{endif}}
|
|
||||||
{{if book.rating|ge,5}}⭐{{endif}}
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
{{:book.genre|uppercase}}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
{{endfor}}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
|
@ -179,13 +179,17 @@ begin
|
|||||||
lCompiledTemplate.AddFilter('fromquery',
|
lCompiledTemplate.AddFilter('fromquery',
|
||||||
function (const aValue: TValue; const aParameters: TArray<string>): TValue
|
function (const aValue: TValue; const aParameters: TArray<string>): TValue
|
||||||
begin
|
begin
|
||||||
|
if not aValue.IsEmpty then
|
||||||
|
begin
|
||||||
|
raise ETProRenderException.Create('Filter "fromquery" cannot be applied to a value [HINT] Use {{:|fromquery,"parname"}}');
|
||||||
|
end;
|
||||||
if Length(aParameters) = 1 then
|
if Length(aParameters) = 1 then
|
||||||
begin
|
begin
|
||||||
Result := Self.WebContext.Request.QueryStringParam(aParameters[0]);
|
Result := Self.WebContext.Request.QueryStringParam(aParameters[0]);
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
begin
|
begin
|
||||||
Result := '(Error: Expected 1 param, got ' + Length(aParameters).ToString + ')';
|
raise ETProRenderException.Create('Expected 1 param for filter "fromquery", got ' + Length(aParameters).ToString);
|
||||||
end;
|
end;
|
||||||
end);
|
end);
|
||||||
if Assigned(FBeforeRenderCallback) then
|
if Assigned(FBeforeRenderCallback) then
|
||||||
|
@ -35,7 +35,7 @@ uses
|
|||||||
System.RTTI;
|
System.RTTI;
|
||||||
|
|
||||||
const
|
const
|
||||||
TEMPLATEPRO_VERSION = '0.7.0';
|
TEMPLATEPRO_VERSION = '0.7.1';
|
||||||
|
|
||||||
type
|
type
|
||||||
ETProException = class(Exception)
|
ETProException = class(Exception)
|
||||||
@ -241,6 +241,7 @@ type
|
|||||||
procedure ProcessJumps(const aTokens: TList<TToken>);
|
procedure ProcessJumps(const aTokens: TList<TToken>);
|
||||||
procedure Compile(const aTemplate: string; const aTokens: TList<TToken>; const aFileNameRefPath: String); overload;
|
procedure Compile(const aTemplate: string; const aTokens: TList<TToken>; const aFileNameRefPath: String); overload;
|
||||||
constructor Create(const aEncoding: TEncoding; const aOptions: TTProCompilerOptions = []); overload;
|
constructor Create(const aEncoding: TEncoding; const aOptions: TTProCompilerOptions = []); overload;
|
||||||
|
procedure MatchFilter(lVarName: string; var lFuncName: string; var lFuncParamsCount: Integer; var lFuncParams: TArray<String>);
|
||||||
public
|
public
|
||||||
function Compile(const aTemplate: string; const aFileNameRefPath: String = ''): ITProCompiledTemplate; overload;
|
function Compile(const aTemplate: string; const aFileNameRefPath: String = ''): ITProCompiledTemplate; overload;
|
||||||
constructor Create(aEncoding: TEncoding = nil); overload;
|
constructor Create(aEncoding: TEncoding = nil); overload;
|
||||||
@ -333,7 +334,7 @@ end;
|
|||||||
|
|
||||||
procedure FunctionError(const aFunctionName, aErrMessage: string);
|
procedure FunctionError(const aFunctionName, aErrMessage: string);
|
||||||
begin
|
begin
|
||||||
raise ETProRenderException.Create(Format('%s in function %s', [aErrMessage, aFunctionName])) at ReturnAddress;
|
raise ETProRenderException.Create(Format('[%1:s] %0:s (error in filter call for function [%1:s])', [aErrMessage, aFunctionName])) at ReturnAddress;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function _Comparand(const aComparandType: TComparandType; const aValue: TValue; const aParameters: TArray<String>; const aLocaleFormatSettings: TFormatSettings): TValue;
|
function _Comparand(const aComparandType: TComparandType; const aValue: TValue; const aParameters: TArray<String>; const aLocaleFormatSettings: TFormatSettings): TValue;
|
||||||
@ -701,6 +702,17 @@ begin
|
|||||||
Create(aEncoding, []);
|
Create(aEncoding, []);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
procedure TTProCompiler.MatchFilter(lVarName: string; var lFuncName: string; var lFuncParamsCount: Integer; var lFuncParams: TArray<String>);
|
||||||
|
begin
|
||||||
|
MatchSpace;
|
||||||
|
if not MatchVariable(lFuncName) then
|
||||||
|
Error('Invalid function name applied to variable ' + lVarName);
|
||||||
|
MatchSpace;
|
||||||
|
lFuncParams := GetFunctionParameters;
|
||||||
|
lFuncParamsCount := Length(lFuncParams);
|
||||||
|
MatchSpace;
|
||||||
|
end;
|
||||||
|
|
||||||
function TTProCompiler.CurrentChar: Char;
|
function TTProCompiler.CurrentChar: Char;
|
||||||
begin
|
begin
|
||||||
Result := fInputString.Chars[fCharIndex]
|
Result := fInputString.Chars[fCharIndex]
|
||||||
@ -899,6 +911,8 @@ var
|
|||||||
lContentOnThisLine: Integer;
|
lContentOnThisLine: Integer;
|
||||||
lStrVerbatim: string;
|
lStrVerbatim: string;
|
||||||
lLayoutFound: Boolean;
|
lLayoutFound: Boolean;
|
||||||
|
lFoundVar: Boolean;
|
||||||
|
lFoundFilter: Boolean;
|
||||||
begin
|
begin
|
||||||
aTokens.Add(TToken.Create(ttSystemVersion, TEMPLATEPRO_VERSION, ''));
|
aTokens.Add(TToken.Create(ttSystemVersion, TEMPLATEPRO_VERSION, ''));
|
||||||
lLastToken := ttEOF;
|
lLastToken := ttEOF;
|
||||||
@ -978,9 +992,12 @@ begin
|
|||||||
|
|
||||||
if CurrentChar = ':' then // variable
|
if CurrentChar = ':' then // variable
|
||||||
begin
|
begin
|
||||||
|
lFoundVar := False;
|
||||||
|
lFoundFilter := False;
|
||||||
Step;
|
Step;
|
||||||
if MatchVariable(lVarName) then { variable }
|
if MatchVariable(lVarName) then { variable }
|
||||||
begin
|
begin
|
||||||
|
lFoundVar := True;
|
||||||
if lVarName.IsEmpty then
|
if lVarName.IsEmpty then
|
||||||
Error('Invalid variable name');
|
Error('Invalid variable name');
|
||||||
lFuncName := '';
|
lFuncName := '';
|
||||||
@ -990,15 +1007,17 @@ begin
|
|||||||
MatchSpace;
|
MatchSpace;
|
||||||
if MatchSymbol('|') then
|
if MatchSymbol('|') then
|
||||||
begin
|
begin
|
||||||
MatchSpace;
|
MatchFilter(lVarName, lFuncName, lFuncParamsCount, lFuncParams);
|
||||||
if not MatchVariable(lFuncName) then
|
|
||||||
Error('Invalid function name applied to variable ' + lVarName);
|
|
||||||
MatchSpace;
|
|
||||||
lFuncParams := GetFunctionParameters;
|
|
||||||
lFuncParamsCount := Length(lFuncParams);
|
|
||||||
MatchSpace;
|
|
||||||
end;
|
end;
|
||||||
|
end
|
||||||
|
else if MatchSymbol('|') then
|
||||||
|
begin
|
||||||
|
lFoundFilter := True;
|
||||||
|
MatchFilter(lVarName, lFuncName, lFuncParamsCount, lFuncParams);
|
||||||
|
end;
|
||||||
|
|
||||||
|
if lFoundVar or lFoundFilter then
|
||||||
|
begin
|
||||||
if not MatchEndTag then
|
if not MatchEndTag then
|
||||||
begin
|
begin
|
||||||
Error('Expected end tag "' + END_TAG + '" near ' + GetSubsequentText);
|
Error('Expected end tag "' + END_TAG + '" near ' + GetSubsequentText);
|
||||||
@ -1020,7 +1039,11 @@ begin
|
|||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
end; // matchvariable
|
end
|
||||||
|
else
|
||||||
|
begin
|
||||||
|
Error('Expected variable or filter near ' + GetSubsequentText);
|
||||||
|
end;
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
begin
|
begin
|
||||||
@ -1631,15 +1654,42 @@ begin
|
|||||||
end
|
end
|
||||||
else if SameText(aFunctionName, 'uppercase') then
|
else if SameText(aFunctionName, 'uppercase') then
|
||||||
begin
|
begin
|
||||||
Result := UpperCase(aValue.AsString);
|
if not aValue.IsEmpty then
|
||||||
|
begin
|
||||||
|
CheckParNumber(0, aParameters);
|
||||||
|
Result := UpperCase(aValue.AsString);
|
||||||
|
end
|
||||||
|
else
|
||||||
|
begin
|
||||||
|
CheckParNumber(1, aParameters);
|
||||||
|
Result := UpperCase(aParameters[0]);
|
||||||
|
end;
|
||||||
end
|
end
|
||||||
else if SameText(aFunctionName, 'lowercase') then
|
else if SameText(aFunctionName, 'lowercase') then
|
||||||
begin
|
begin
|
||||||
Result := lowercase(aValue.AsString);
|
if not aValue.IsEmpty then
|
||||||
|
begin
|
||||||
|
CheckParNumber(0, aParameters);
|
||||||
|
Result := lowercase(aValue.AsString);
|
||||||
|
end
|
||||||
|
else
|
||||||
|
begin
|
||||||
|
CheckParNumber(1, aParameters);
|
||||||
|
Result := lowercase(aParameters[0]);
|
||||||
|
end;
|
||||||
end
|
end
|
||||||
else if SameText(aFunctionName, 'capitalize') then
|
else if SameText(aFunctionName, 'capitalize') then
|
||||||
begin
|
begin
|
||||||
Result := CapitalizeString(aValue.AsString, True);
|
if not aValue.IsEmpty then
|
||||||
|
begin
|
||||||
|
CheckParNumber(0, aParameters);
|
||||||
|
Result := CapitalizeString(aValue.AsString, True);
|
||||||
|
end
|
||||||
|
else
|
||||||
|
begin
|
||||||
|
CheckParNumber(1, aParameters);
|
||||||
|
Result := CapitalizeString(aParameters[0], True);
|
||||||
|
end;
|
||||||
end
|
end
|
||||||
else if SameText(aFunctionName, 'trunc') then
|
else if SameText(aFunctionName, 'trunc') then
|
||||||
begin
|
begin
|
||||||
@ -1760,6 +1810,10 @@ begin
|
|||||||
end
|
end
|
||||||
else if SameText(aFunctionName, 'version') then
|
else if SameText(aFunctionName, 'version') then
|
||||||
begin
|
begin
|
||||||
|
if not aValue.IsEmpty then
|
||||||
|
begin
|
||||||
|
FunctionError(aFunctionName, 'cannot be applied to a value - [HINT] Use {{:|' + aFunctionName + '}}');
|
||||||
|
end;
|
||||||
CheckParNumber(0, aParameters);
|
CheckParNumber(0, aParameters);
|
||||||
Result := TEMPLATEPRO_VERSION;
|
Result := TEMPLATEPRO_VERSION;
|
||||||
end
|
end
|
||||||
|
Loading…
Reference in New Issue
Block a user