Updated book search sample
Some checks are pending
TOC Generator / TOC Generator (push) Waiting to run

This commit is contained in:
Daniele Teti 2024-10-17 17:04:03 +02:00
parent 6817fc1ce5
commit a692a5a37e
5 changed files with 200 additions and 78 deletions

View File

@ -1,24 +1,94 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Books Search</title>
<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>
{{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>
<body hx-indicator="#spinner">
<section class="section" >
<section class="section">
{{block "body"}}{{endblock}}
</section>
<div class="content has-text-centered">
<p>
<strong><a href="https://github.com/danieleteti/templatepro">TemplatePRO</a> {{""|version}}</strong> by <a href="https://www.danieleteti.it">Daniele Teti</a>.
<div class="content has-text-centered">
<p>
<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>
</div>
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>
</div>
</body>
</html>

View File

@ -1,15 +1,19 @@
{{extends "baselayout.html"}}
{{block "body"}}
<div class="columns">
<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"/>
</div>
<div class="column is-one-third">
<p class="subtitle is-6">⭐ DMVCFramework + HTMX :: Instant Search Demo ⭐</p>
<div class="column is-offset-one-third is-one-third">
<h1 style="text-align: center; font-weight: 500; font-size: 170%">Instant Search Demo</h1>
<h2 style="text-align: center; font-size: 130%; color: #a0a0a0">DMVCFramework + TemplatePro + HTMX</h2>
</div>
</div>
<div class="columns">
<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>
<progress id="spinner" class="htmx-indicator progress is-large is-info" max="100">ciao</progress>
<div id="results"></div>
{{endblock}}

View File

@ -1,47 +1,37 @@
<p>{{:books|count}} book/s found</p>
<table class="table is-fullwidth is-hoverable">
<thead>
<tr>
<th>ID</th>
<th>Book Title</th>
<th>Book Author</th>
<th>Rating</th>
<th>Genre</th>
</tr>
</thead>
<tbody>
{{if !books}}
<tr>
<td colspan="4">
<span>No books found</span>
</td>
</tr>
{{endif}}
{{for book in books}}
<tr class=
{{if book.genre|eq,"Horror"}}
"has-background-danger-light"
{{else}} {{if book.genre|eq,"Thriller"}}
"has-background-success-light"
{{else}} {{if book.genre|eq,"Classic"}}
"has-background-primary-light"
{{endif}}
<div class="divTable blueTable">
<div class="divTableHeading">
<div class="divTableRow">
<div class="divTableHead">ID</div>
<div class="divTableHead">Book Title</div>
<div class="divTableHead">Book Author</div>
<div class="divTableHead">Rating</div>
<div class="divTableHead">Genre</div>
</div>
</div>
<div class="divTableBody">
{{if !books}}
<div class="divTableRow">
<span>No books found</span>
</div>
{{endif}}
{{endif}}>
<td>{{:book.id|lpad,8,"0"}}</td>
<td>{{:book.book_name}}</td>
<td>{{:book.author_name}}</td>
<td>
{{if book.rating|ge,1}}⭐{{endif}}
{{if book.rating|ge,2}}⭐{{endif}}
{{if book.rating|ge,3}}⭐{{endif}}
{{if book.rating|ge,4}}⭐{{endif}}
{{if book.rating|ge,5}}⭐{{endif}}
</td>
<td>
{{:book.genre|uppercase}}
</td>
</tr>
{{endfor}}
</tbody>
</table>
{{for book in books}}
<div class="divTableRow {{if book.genre|eq,"Horror"}}has-background-danger-light{{endif}}">
<div class="divTableCell">{{:book.id|lpad,8,"0"}}</div>
<div class="divTableCell">{{:book.book_name}}</div>
<div class="divTableCell">{{:book.author_name}}</div>
<div class="divTableCell">
{{if book.rating|ge,1}}⭐{{endif}}
{{if book.rating|ge,2}}⭐{{endif}}
{{if book.rating|ge,3}}⭐{{endif}}
{{if book.rating|ge,4}}⭐{{endif}}
{{if book.rating|ge,5}}⭐{{endif}}
</div>
<div class="divTableCell">
{{:book.genre|uppercase}}
</div>
</div>
{{endfor}}
</div>
</div>

View File

@ -179,13 +179,17 @@ begin
lCompiledTemplate.AddFilter('fromquery',
function (const aValue: TValue; const aParameters: TArray<string>): TValue
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
begin
Result := Self.WebContext.Request.QueryStringParam(aParameters[0]);
end
else
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);
if Assigned(FBeforeRenderCallback) then

View File

@ -35,7 +35,7 @@ uses
System.RTTI;
const
TEMPLATEPRO_VERSION = '0.7.0';
TEMPLATEPRO_VERSION = '0.7.1';
type
ETProException = class(Exception)
@ -241,6 +241,7 @@ type
procedure ProcessJumps(const aTokens: TList<TToken>);
procedure Compile(const aTemplate: string; const aTokens: TList<TToken>; const aFileNameRefPath: String); 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
function Compile(const aTemplate: string; const aFileNameRefPath: String = ''): ITProCompiledTemplate; overload;
constructor Create(aEncoding: TEncoding = nil); overload;
@ -333,7 +334,7 @@ end;
procedure FunctionError(const aFunctionName, aErrMessage: string);
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;
function _Comparand(const aComparandType: TComparandType; const aValue: TValue; const aParameters: TArray<String>; const aLocaleFormatSettings: TFormatSettings): TValue;
@ -701,6 +702,17 @@ begin
Create(aEncoding, []);
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;
begin
Result := fInputString.Chars[fCharIndex]
@ -899,6 +911,8 @@ var
lContentOnThisLine: Integer;
lStrVerbatim: string;
lLayoutFound: Boolean;
lFoundVar: Boolean;
lFoundFilter: Boolean;
begin
aTokens.Add(TToken.Create(ttSystemVersion, TEMPLATEPRO_VERSION, ''));
lLastToken := ttEOF;
@ -978,9 +992,12 @@ begin
if CurrentChar = ':' then // variable
begin
lFoundVar := False;
lFoundFilter := False;
Step;
if MatchVariable(lVarName) then { variable }
begin
lFoundVar := True;
if lVarName.IsEmpty then
Error('Invalid variable name');
lFuncName := '';
@ -990,15 +1007,17 @@ begin
MatchSpace;
if MatchSymbol('|') then
begin
MatchSpace;
if not MatchVariable(lFuncName) then
Error('Invalid function name applied to variable ' + lVarName);
MatchSpace;
lFuncParams := GetFunctionParameters;
lFuncParamsCount := Length(lFuncParams);
MatchSpace;
MatchFilter(lVarName, lFuncName, lFuncParamsCount, lFuncParams);
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
begin
Error('Expected end tag "' + END_TAG + '" near ' + GetSubsequentText);
@ -1020,7 +1039,11 @@ begin
end;
end;
end;
end; // matchvariable
end
else
begin
Error('Expected variable or filter near ' + GetSubsequentText);
end;
end
else
begin
@ -1631,15 +1654,42 @@ begin
end
else if SameText(aFunctionName, 'uppercase') then
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
else if SameText(aFunctionName, 'lowercase') then
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
else if SameText(aFunctionName, 'capitalize') then
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
else if SameText(aFunctionName, 'trunc') then
begin
@ -1760,6 +1810,10 @@ begin
end
else if SameText(aFunctionName, 'version') then
begin
if not aValue.IsEmpty then
begin
FunctionError(aFunctionName, 'cannot be applied to a value - [HINT] Use {{:|' + aFunctionName + '}}');
end;
CheckParNumber(0, aParameters);
Result := TEMPLATEPRO_VERSION;
end