-
Notifications
You must be signed in to change notification settings - Fork 137
/
Copy pathexamples.html
426 lines (399 loc) · 39.2 KB
/
examples.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
<!DOCTYPE html><html><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1"><meta name="description" content="j2html - Fast and fluent Java HTML builder. Build type-safe HTML 5 with Java 8 expression!"><title>Examples of how to use j2html - Java HTML builder</title><link rel="icon" href="/img/favicon.svg"><link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Lato&family=Patua+One&display=swap"><style>/** * okaidia theme for JavaScript,CSS and HTML * Loosely based on Monokai textmate theme by http://www.monokai.nl/ * @author ocodia */code[class*="language-"],pre[class*="language-"]{color:#f8f8f2;direction:ltr;font-family:consolas,monaco,'Andale Mono',monospace;-webkit-hyphens:none;-moz-hyphens:none;-ms-hyphens:none;hyphens:none;line-height:1.5;-moz-tab-size:4;-o-tab-size:4;tab-size:4;text-align:left;text-shadow:0 1px rgba(0,0,0,0.3);white-space:pre;word-break:normal;word-spacing:normal}pre[class*="language-"]{border-radius:0.3em;margin:.5em 0;overflow:auto;padding:1em}:not(pre)>code[class*="language-"],pre[class*="language-"]{background:#272822}:not(pre)>code[class*="language-"]{border-radius:.3em;padding:.1em}.token.comment,.token.prolog,.token.doctype,.token.cdata{color:#708090}.token.punctuation{color:#f8f8f2}.namespace{opacity:.7}.token.property,.token.tag,.token.constant,.token.symbol,.token.deleted{color:#f92672}.token.boolean,.token.number{color:#ae81ff}.token.selector,.token.attr-name,.token.string,.token.char,.token.builtin,.token.inserted{color:#a6e22e}.token.operator,.token.entity,.token.url,.language-css .token.string,.style .token.string,.token.variable{color:#f8f8f2}.token.atrule,.token.attr-value,.token.function{color:#e6db74}.token.keyword{color:#66d9ef}.token.regex,.token.important{color:#fd971f}.token.important,.token.bold{font-weight:700}.token.italic{font-style:italic}.token.entity{cursor:help}</style><style>*{box-sizing:border-box}html,body{margin:0}html{background:#fafafa;font-family:arial,sans-serif;min-height:100%;overflow-y:scroll;position:relative}body{height:100%;margin-bottom:50px}h1,h2,h3,nav a{font-family:'Patua One',cursive;font-weight:400}h1,h2,h3,h4{margin-bottom:0.2em;margin-top:1.5em}h1+p,h2+p,h3+p{margin-top:0}h2,h3{color:rgba(0,0,0,0.7)}pre+p{margin-top:2em}#news h2:not(:first-of-type){margin-top:2em}a{color:#1272bf;text-decoration:none}p,li{color:rgba(0,0,0,0.7);font-family:"lato",arial,sans-serif;font-size:17px;line-height:1.5}li{margin:5px 0}dt{font-family:monospace;font-size:14px;font-weight:700;margin-top:10px}dd{font-size:13px;margin:0}.width-limit{margin:0 auto;max-width:940px;padding:0 20px}#github-stars{height:30px;position:absolute;right:27px;top:10px;width:100px}#logo{font-size:20px;font-style:italic}#logo img{display:block;height:50px}.top-header{background:#fff}.top-header nav{align-items:center;display:flex;justify-content:space-between;padding:20px}.top-header ul{list-style:none;margin:0;padding:0}.top-header li{display:inline-block;margin:0 10px}.top-header ul li a{display:inline-block;font-size:22px;padding:8px}.banner{background:#1272bf;border-bottom:3px solid #0d4d80;border-top:3px solid #0d4d80;color:#fff;height:100px;line-height:100px;margin:0;padding:0 25px}.banner h1{font-size:40px;text-shadow:4px 4px 0 rgba(0,0,0,0.3)}p code,li code{background:rgba(30,177,204,0.1);border:1px solid rgba(26,142,167,0.49);border-radius:3px;font-family:monospace;font-size:85%;padding:1px 3px}footer{background:#f5f5f5;border-top:1px solid #ddd;bottom:0;font-size:13px;height:40px;left:0;line-height:40px;overflow:hidden;padding:0 20px;position:absolute;text-align:center;width:100%}.lols{color:#777;font-size:9px;margin:0;text-align:center}#table-example{background:#fff;border:1px solid #ddd;border-radius:5px;border-spacing:5px 20px;font-weight:700;text-align:center;width:100%}@media screen and (max-width: 1024px){#fork-me{display:none}.top-header{border-bottom:1px solid #ccc}}@media screen and (max-width: 768px){.banner{display:none}.top-header nav ul li{margin:0 5px}.top-header nav ul li a{font-size:16px}#logo{font-size:0}}@media screen and (max-width: 460px){#logo{display:none}.top-header nav ul li{margin:0 2px}.top-header nav ul li a{font-size:14px}}.code-compare ul{background:#272822;border-bottom:1px solid #464644;border-radius:5px 5px 0 0;height:28px;list-style:none;margin:0;padding:0}.code-compare ul li{color:#999;cursor:pointer;display:inline-block;font-size:13px;height:28px;line-height:28px;margin:0;padding:0 10px}.code-compare ul li+li{border-left:1px solid #464644}.code-compare pre{border-radius:0 0 5px 5px;display:none;margin-top:0}.code-compare.with .with-switch,.code-compare.nowith .nowith-switch{color:#eee}.code-compare.with .with-pre,.code-compare.nowith .nowith-pre{display:block}pre{background:#272822;border-radius:5px;padding:15px!important}pre::-webkit-scrollbar{height:5px;width:5px}pre::-webkit-scrollbar-thumb{background:#777;border-radius:5px}pre::-webkit-scrollbar-thumb:hover{background:#999}pre::-webkit-scrollbar-track{background:#333;border-radius:5px}pre::-webkit-scrollbar-button{height:8px;width:8px}#javalin-suggestion{background:#fff;border:2px solid #009ebe;bottom:30px;box-shadow:4px 4px 0 0 rgba(0,0,0,.2);display:none;font-size:17px;line-height:2;max-width:calc(100% - 60px);padding:20px 40px 20px 20px;position:fixed;right:30px;z-index:1}#javalin-suggestion .close{cursor:pointer;position:absolute;right:15px;top:11px}#javalin-suggestion a{font-weight:700}</style></head><body><noscript><iframe src="//www.googletagmanager.com/ns.html?id=GTM-W4XBCK"
height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
<script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
'//www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','GTM-W4XBCK');</script>
<a id="fork-me" href="https://github.com/tipsy/j2html" target="_blank">
<img style="position:fixed;top:0;right:0;border:0;z-index:99999" src="data:image/png;base64,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" alt="Fork me on GitHub">
</a>
<header class="top-header"><nav class="width-limit"><a id="logo" href="/"><span><img src="/img/logo.svg" alt="j2html logo"></span></a><ul><li><a href="/">Home</a></li><li><a href="/download.html">Download</a></li><li><a href="/examples.html">Examples</a></li><li><a href="/news.html">News</a></li></ul></nav></header><header class="banner"><h1 class="width-limit">Reclaim control of your HTML</h1></header><main class="width-limit"><section id="examples"><h2 id="basic-example">Basic example</h2><p>Creating a basic HTML structure in j2html is pretty similar to plain HTML. This Java code:</p><div class="code-compare nowith"><ul><li class="nowith-switch active">version 1.0.0 +</li><li class="with-switch">earlier versions</li></ul><pre class="nowith-pre"><code class="language-java">html(
head(
title("Title"),
link().withRel("stylesheet").withHref("/css/main.css")
),
body(
main(attrs("#main.content"),
h1("Heading!")
)
)
)
</code></pre><pre class="with-pre"><code class="language-java">html().with(
head().with(
title("Title"),
link().withRel("stylesheet").withHref("/css/main.css")
),
body().with(
main().withId("main").withClass("content").with(
h1("Heading!")
)
)
)
</code></pre></div><p>Becomes this HTML:</p><pre><code class="language-markup"><html>
<head>
<title>Title</title>
<link rel="stylesheet" href="/css/main.css">
</head>
<body>
<main id="main" class="content">
<h1>Heading!</h1>
</main>
</body>
<html>
</code></pre><p>It's literally impossible to forget to close a div, mistype an attribute name, or forget an attribute quote! Remember to include the Java wrapping code though, j2html is not a template language, all files are .java. To see how the wrapping code could look, check out the <a href="/">getting started example</a>.</p><h2 id="core-concepts">Core concepts</h2><pre><code class="language-java">TagCreator.class // Static utility class for creating all tags
import static j2html.TagCreator.*; // Use static star import
Config.class // Holds all configuration. Offers global configuration or customizable instances
Config.closeEmptyTags = true // Global options are public, static and mutable.
Config.global() // Copy all static Config fields into an instance. Instances are immutable
Config.defaults() // A Config with defaults that are independent of global options
Config.global().withEmptyTagsClosed(true) // A Config that is different from the global options
Config.defaults().withEmptyTagsClosed(true) // A Config that is different from the default options
TagCreator.join() // Method for joining small snippets, like:
p(join("This paragraph has", b("bold"), "and", i("italic"), "text."))
TagCreator.iff(boolean condition, T ifValue) // If-expression for use in method calls
div().withClasses("menu-element", iff(isActive, "active"))
TagCreator.iffElse(boolean condition, T ifValue, T elseValue) // If/else-expression for use in method calls
div().withClasses("menu-element", iffElse(isActive, "active", "not-active"))
Tag.class // Is extended by ContainerTag (ex <div></div> and EmptyTag (ex <br>)
Tag.attr(String attribute, Object value) // Set an attribute on the tag
Tag.withXyz(String value) // Calls attr with predefined attribute (ex .withId, .withClass, etc.)
Tag.render(HtmlBuilder builder) // Render HTML using the given builder.
Tag.render() // Shortcut for rendering flat HTML into a string using global Config.
ContainerTag.renderFormatted() // Shortcut for rendering indented HTML into a string using global Config.
HtmlBuilder.class // Interface for composing HTML. Implemented by FlatHtml and IndentedHtml
FlatHtml.into(Appendable) // Render into a stream, file, etc. without indentation or line breaks
FlatHtml.into(Appendable appendable, Config config) // Customize rendering of flat html
IndentedHtml.into(Appendable) // Render human-readable HTML into an stream, file, etc.
IndentedHtml.into(Appendable appendable, Config config) // Customize rendering of intended html
ul(li("one"), li("two")).render(IndentedHtml.inMemory()).toString() // Similar to renderFormatted()
ul(li("one"), li("two")).render(IndentedHtml.into(filewriter)) // Write HTML into a file
</code></pre><h2 id="loops">Loops, each() and filter()</h2><p>Using Java 8's lambda syntax, you can write loops (via streams) inside your HTML-builder:</p><div class="code-compare nowith"><ul><li class="nowith-switch active">version 1.0.0 +</li><li class="with-switch">earlier versions</li></ul><pre class="nowith-pre"><code class="language-java">body(
div(attrs("#employees"),
employees.stream().map(employee ->
div(attrs(".employee"),
h2(employee.getName()),
img().withSrc(employee.getImgPath()),
p(employee.getTitle())
)
).toArray(ContainerTag[]::new)
)
)
</code></pre><pre class="with-pre"><code class="language-java">body().with(
div().withId("employees").with(
employees.stream().map(employee ->
div().withClass("employee").with(
h2(employee.getName()),
img().withSrc(employee.getImgPath()),
p(employee.getTitle())
)
).toArray(ContainerTag[]::new)
)
)
</code></pre></div><p>j2html also offers a custom each method, which is slightly more powerful:</p><div class="code-compare nowith"><ul><li class="nowith-switch active">version 1.0.0 +</li><li class="with-switch">earlier versions</li></ul><pre class="nowith-pre"><code class="language-java">// each() lets you iterate through a collection and returns the generated HTML
// as a DomContent object, meaning you can add siblings, which is not possible
// using the stream api in the previous example
body(
div(attrs("#employees"),
p("Some sibling element"),
each(employees, employee ->
div(attrs(".employee"),
h2(employee.getName()),
img().withSrc(employee.getImgPath()),
p(employee.getTitle())
)
)
)
)
</code></pre><pre class="with-pre"><code class="language-java">// each() lets you iterate through a collection and returns the generated HTML
// as a DomContent object, meaning you can add siblings, which is not possible
// using the stream api in the previous example
body().with(
div().withId("employees").with(
p("Some sibling element"),
each(employees, employee ->
div().withClass("employee").with(
h2(employee.getName()),
img().withSrc(employee.getImgPath()),
p(employee.getTitle())
)
)
)
)
</code></pre></div><p>If you need to filter your collection, j2html has a built in filter function too:</p><div class="code-compare nowith"><ul><li class="nowith-switch active">version 1.0.0 +</li><li class="with-switch">earlier versions</li></ul><pre class="nowith-pre"><code class="language-java">// filter() is meant to be used with each(). It just calls the normal
// stream().filter() method, but hides all the boilerplate Java code
// to keep your j2html code neat
body(
div(attrs("#employees"),
p("Some sibling element"),
each(filter(employees, employee -> employee != null), employee ->
div(attrs(".employee"),
h2(employee.getName()),
img().withSrc(employee.getImgPath()),
p(employee.getTitle())
)
)
)
)
</code></pre><pre class="with-pre"><code class="language-java">// filter() is meant to be used with each(). It just calls the normal
// stream().filter() method, but hides all the boilerplate Java code
// to keep your j2html code neat
body().with(
div().withId("employees").with(
p("Some sibling element"),
each(filter(employees, employee -> employee != null), employee ->
div().withClass("employee").with(
h2(employee.getName()),
img().withSrc(employee.getImgPath()),
p(employee.getTitle())
)
)
)
)
</code></pre></div><p>Since this is pure Java, all the Employee methods (getName, getImgPath, getTitle) are available to you, and you get autocomplete suggestions and compile time errors.</p><p>Given three random employees, all the above approaches would give the same HTML:</p><pre><code class="language-markup"><body>
<div id="employees">
<div class="employee">
<h2>David</h2>
<img src="/img/david.png">
<p>Creator of Bad Libraries</p>
</div>
<div class="employee">
<h2>Christian</h2>
<img src="/img/christian.png">
<p>Fanboi of Jenkins</p>
</div>
<div class="employee">
<h2>Paul</h2>
<img src="/img/paul.png">
<p>Hater of Lambda Expressions</p>
</div>
</div>
</body>
</code></pre><h2 id="2d-table-example">Two dimensional table example</h2><div class="code-compare nowith"><ul><li class="nowith-switch active">version 1.0.0 +</li><li class="with-switch">earlier versions</li></ul><pre class="nowith-pre"><code class="language-java">List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 9, 10);
...
table(attr("#table-example"),
tbody(
each(numbers, i -> tr(
each(numbers, j -> td(
String.valueOf(i * j)
))
))
)
)</code></pre><pre class="with-pre"><code class="language-java">List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 9, 10);
...
table().withId("table-example").with(
tbody().with(
each(numbers, i -> tr().with(
each(numbers, j -> td().with(
String.valueOf(i * j)
))
))
)
)
</code></pre></div><p>The code above is generating this table:</p><table id="table-example"><tbody><tr><td>1</td><td>2</td><td>3</td><td>4</td><td>5</td><td>6</td><td>7</td><td>9</td><td>10</td></tr><tr><td>2</td><td>4</td><td>6</td><td>8</td><td>10</td><td>12</td><td>14</td><td>18</td><td>20</td></tr><tr><td>3</td><td>6</td><td>9</td><td>12</td><td>15</td><td>18</td><td>21</td><td>27</td><td>30</td></tr><tr><td>4</td><td>8</td><td>12</td><td>16</td><td>20</td><td>24</td><td>28</td><td>36</td><td>40</td></tr><tr><td>5</td><td>10</td><td>15</td><td>20</td><td>25</td><td>30</td><td>35</td><td>45</td><td>50</td></tr><tr><td>6</td><td>12</td><td>18</td><td>24</td><td>30</td><td>36</td><td>42</td><td>54</td><td>60</td></tr><tr><td>7</td><td>14</td><td>21</td><td>28</td><td>35</td><td>42</td><td>49</td><td>63</td><td>70</td></tr><tr><td>9</td><td>18</td><td>27</td><td>36</td><td>45</td><td>54</td><td>63</td><td>81</td><td>90</td></tr><tr><td>10</td><td>20</td><td>30</td><td>40</td><td>50</td><td>60</td><td>70</td><td>90</td><td>100</td></tr></tbody></table><h2 id="partials">Partials</h2><p>You can create partials for elements you use a lot:</p><div class="code-compare nowith"><ul><li class="nowith-switch active">version 1.0.0 +</li><li class="with-switch">earlier versions</li></ul><pre class="nowith-pre"><code class="language-java">public static Tag enterPasswordInput(String placeholder) {
return passwordInput("enterPassword", placeholder);
}
public static Tag choosePasswordInput(String placeholder) {
return passwordInput("choosePassword", placeholder);
}
public static Tag repeatPasswordInput(String placeholder) {
return passwordInput("repeatPassword", placeholder);
}
public static Tag passwordInput(String identifier, String placeholder) {
return input()
.withType("password")
.withId(identifier)
.withName(identifier)
.withPlaceholder(placeholder)
.isRequired();
}
public static Tag emailInput(String placeholder) {
return input()
.withType("email")
.withId("email")
.withName("email")
.withPlaceholder(placeholder)
.isRequired();
}
public static Tag submitButton(String text) {
return button(text).withType("submit");
}
</code></pre><pre class="with-pre"><code class="language-java">public static Tag enterPasswordInput(String placeholder) {
return passwordInput("enterPassword", placeholder);
}
public static Tag choosePasswordInput(String placeholder) {
return passwordInput("choosePassword", placeholder);
}
public static Tag repeatPasswordInput(String placeholder) {
return passwordInput("repeatPassword", placeholder);
}
public static Tag passwordInput(String identifier, String placeholder) {
return input()
.withType("password")
.withId(identifier)
.withName(identifier)
.withPlaceholder(placeholder)
.isRequired();
}
public static Tag emailInput(String placeholder) {
return input()
.withType("email")
.withId("email")
.withName("email")
.withPlaceholder(placeholder)
.isRequired();
}
public static Tag submitButton(String text) {
return button().withType("submit").withText(text);
}
</code></pre></div><p>The equivalent HTML would be:</p><pre><code class="language-markup"><input
type="password"
id="enterPassword"
name="enterPassword"
placeholder="Enter password"
required
>
<input
type="password"
id="choosePassword"
name="choosePassword"
placeholder="Choose password"
required
>
<input
type="password"
id="repeatPassword"
name="repeatPassword"
placeholder="Repeat password"
required
>
<input
type="email"
id="email"
name="email"
placeholder="Email"
required
>
<button type="submit">Text</button>
</code></pre><p>You can then use these partials, for example in a registration form:</p><div class="code-compare nowith"><ul><li class="nowith-switch active">version 1.0.0 +</li><li class="with-switch">earlier versions</li></ul><pre class="nowith-pre"><code class="language-java">h1("Please sign up"),
form().withMethod("post").with(
emailInput("Email address"),
choosePasswordInput("Choose Password"),
repeatPasswordInput("Repeat Password"),
submitButton("Sign up")
)
</code></pre><pre class="with-pre"><code class="language-java">h1("Please sign up"),
form().withMethod("post").with(
emailInput("Email address"),
choosePasswordInput("Choose Password"),
repeatPasswordInput("Repeat Password"),
submitButton("Sign up")
)
</code></pre></div><p>Pretty clean, right? The rendered HTML is more verbose:</p><pre><code class="language-markup"><h1>Please sign up</h1>
<form method="post">
<input type="email" id="email" name="email" placeholder="Email address" required>
<input type="password" id="choosePassword" name="choosePassword" placeholder="Choose password" required>
<input type="password" id="repeatPassword" name="repeatPassword" placeholder="Repeat password" required>
<button type="submit">Sign up</button>
</form>
</code></pre><p>Imagine if you wanted labels in addition. The Java snippet would look almost identical: You could create a partial called<em> passwordAndLabel() </em>and nothing but the method name would change. The resulting HTML however, would be twice or thrice as big, depending on whether or not you wrapped the input and label in another tag.</p><h2 id="dynamic-views">Dynamic views</h2><p>Once you've set up partials, you can call them from wherever, which greatly reduces potential errors. Let's say we want to include the form from the partials-example in our webpage. We want a header above and a footer below. A lot of templating languages make you do this: </p><pre><code class="language-java">#include("/path/to/header")
#setTitle("Signup page")
<h1>Please sign up</h1>
<form>
...
</form>
#include("/path/to/footer")
</code></pre><p>This is a pain to work with. You have no idea what the header and footer expects, and you have no way to affect how they treat your content. You can easily break the site by forgetting to close divs, or by forgetting to include either the header or the footer in one of your views. In j2html you can specify the context in which a view is rendered, and supply the rendering method with type safe parameters! If we want to insert our form in a header/footer frame, we simply create a MainView and make it take our view as an argument:</p><div class="code-compare nowith"><ul><li class="nowith-switch active">version 1.0.0 +</li><li class="with-switch">earlier versions</li></ul><pre class="nowith-pre"><code class="language-java">public class MainView {
public static String render(String pageTitle, Tag... tags) {
return document(
html(
head(
title(pageTitle)
),
body(
header(
...
),
main(
tags //the view from the partials example
),
footer(
...
)
)
)
);
}
}
MainView.render(
"Signup page",
h1("Please sign up"),
form().withMethod("post").with(
emailInput("Email address"),
choosePasswordInput("Choose Password"),
repeatPasswordInput("Repeat Password"),
submitButton("Sign up")
)
);
</code></pre><pre class="with-pre"><code class="language-java">public class MainView {
public static String render(String pageTitle, Tag... tags) {
return document().render() +
html().with(
head().with(
title(pageTitle)
),
body().with(
header().with(
...
),
main().with(
tags //the view from the partials example
),
footer().with(
...
)
)
).render();
}
}
MainView.render(
"Signup page",
h1("Please sign up"),
form().withMethod("post").with(
emailInput("Email address"),
choosePasswordInput("Choose Password"),
repeatPasswordInput("Repeat Password"),
submitButton("Sign up")
)
);
</code></pre></div><p>Which will result in the rendered HTML:</p><pre><code class="language-markup"><html>
<head>
<title>Signup page</title>
</head>
<body>
<header>
...
</header>
<main>
<h1>Please sign up</h1>
<form method="post">
<input type="email" id="email" name="email" placeholder="Email address" required>
<input type="password" id="choosePassword" name="choosePassword" placeholder="Choose password" required>
<input type="password" id="repeatPassword" name="repeatPassword" placeholder="Repeat password" required>
<button type="submit">Sign up</button>
</form>
</main>
<footer>
...
</footer>
</body>
</html>
</code></pre><p>We would now get a compilation error if we forgot to include a title, and there is 0 chance of forgetting either header or footer, mistyping paths, forgetting to close divs, or anything else.</p></section></main><div id="javalin-suggestion"><span class="close">✖</span> Want a simple and modern web framework? <br> Try our other project: <a href="https://javalin.io?from=j2html">https://javalin.io</a></div><footer>This page was created using <a href="https://github.com/tipsy/j2html" target="_blank">j2html</a> and <a href="https://javalin.io/" target="_blank">Javalin</a>. Webpage source on <a href="https://github.com/j2html/j2html-webpage" target="_blank">Github</a>. <br> <p class="lols">A static page generator or a template engine would be better suited than a HTML builder for creating this page, but we had to do it.</p></footer><script>/* http://prismjs.com/download.html?themes=prism-okaidia&languages=markup+css+clike+javascript+java */
self="undefined"!=typeof window?window:"undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope?self:{};var Prism=function(){var e=/\blang(?:uage)?-(?!\*)(\w+)\b/i,t=self.Prism={util:{encode:function(e){return e instanceof n?new n(e.type,t.util.encode(e.content),e.alias):"Array"===t.util.type(e)?e.map(t.util.encode):e.replace(/&/g,"&").replace(/</g,"<").replace(/\u00a0/g," ")},type:function(e){return Object.prototype.toString.call(e).match(/\[object (\w+)\]/)[1]},clone:function(e){var n=t.util.type(e);switch(n){case"Object":var a={};for(var r in e)e.hasOwnProperty(r)&&(a[r]=t.util.clone(e[r]));return a;case"Array":return e.map(function(e){return t.util.clone(e)})}return e}},languages:{extend:function(e,n){var a=t.util.clone(t.languages[e]);for(var r in n)a[r]=n[r];return a},insertBefore:function(e,n,a,r){r=r||t.languages;var i=r[e];if(2==arguments.length){a=arguments[1];for(var l in a)a.hasOwnProperty(l)&&(i[l]=a[l]);return i}var s={};for(var o in i)if(i.hasOwnProperty(o)){if(o==n)for(var l in a)a.hasOwnProperty(l)&&(s[l]=a[l]);s[o]=i[o]}return t.languages.DFS(t.languages,function(t,n){n===r[e]&&t!=e&&(this[t]=s)}),r[e]=s},DFS:function(e,n,a){for(var r in e)e.hasOwnProperty(r)&&(n.call(e,r,e[r],a||r),"Object"===t.util.type(e[r])?t.languages.DFS(e[r],n):"Array"===t.util.type(e[r])&&t.languages.DFS(e[r],n,r))}},highlightAll:function(e,n){for(var a,r=document.querySelectorAll('code[class*="language-"], [class*="language-"] code, code[class*="lang-"], [class*="lang-"] code'),i=0;a=r[i++];)t.highlightElement(a,e===!0,n)},highlightElement:function(a,r,i){for(var l,s,o=a;o&&!e.test(o.className);)o=o.parentNode;if(o&&(l=(o.className.match(e)||[,""])[1],s=t.languages[l]),s){a.className=a.className.replace(e,"").replace(/\s+/g," ")+" language-"+l,o=a.parentNode,/pre/i.test(o.nodeName)&&(o.className=o.className.replace(e,"").replace(/\s+/g," ")+" language-"+l);var u=a.textContent;if(u){u=u.replace(/^(?:\r?\n|\r)/,"");var g={element:a,language:l,grammar:s,code:u};if(t.hooks.run("before-highlight",g),r&&self.Worker){var c=new Worker(t.filename);c.onmessage=function(e){g.highlightedCode=n.stringify(JSON.parse(e.data),l),t.hooks.run("before-insert",g),g.element.innerHTML=g.highlightedCode,i&&i.call(g.element),t.hooks.run("after-highlight",g)},c.postMessage(JSON.stringify({language:g.language,code:g.code}))}else g.highlightedCode=t.highlight(g.code,g.grammar,g.language),t.hooks.run("before-insert",g),g.element.innerHTML=g.highlightedCode,i&&i.call(a),t.hooks.run("after-highlight",g)}}},highlight:function(e,a,r){var i=t.tokenize(e,a);return n.stringify(t.util.encode(i),r)},tokenize:function(e,n){var a=t.Token,r=[e],i=n.rest;if(i){for(var l in i)n[l]=i[l];delete n.rest}e:for(var l in n)if(n.hasOwnProperty(l)&&n[l]){var s=n[l];s="Array"===t.util.type(s)?s:[s];for(var o=0;o<s.length;++o){var u=s[o],g=u.inside,c=!!u.lookbehind,f=0,h=u.alias;u=u.pattern||u;for(var p=0;p<r.length;p++){var d=r[p];if(r.length>e.length)break e;if(!(d instanceof a)){u.lastIndex=0;var m=u.exec(d);if(m){c&&(f=m[1].length);var y=m.index-1+f,m=m[0].slice(f),v=m.length,k=y+v,b=d.slice(0,y+1),w=d.slice(k+1),N=[p,1];b&&N.push(b);var O=new a(l,g?t.tokenize(m,g):m,h);N.push(O),w&&N.push(w),Array.prototype.splice.apply(r,N)}}}}}return r},hooks:{all:{},add:function(e,n){var a=t.hooks.all;a[e]=a[e]||[],a[e].push(n)},run:function(e,n){var a=t.hooks.all[e];if(a&&a.length)for(var r,i=0;r=a[i++];)r(n)}}},n=t.Token=function(e,t,n){this.type=e,this.content=t,this.alias=n};if(n.stringify=function(e,a,r){if("string"==typeof e)return e;if("Array"===t.util.type(e))return e.map(function(t){return n.stringify(t,a,e)}).join("");var i={type:e.type,content:n.stringify(e.content,a,r),tag:"span",classes:["token",e.type],attributes:{},language:a,parent:r};if("comment"==i.type&&(i.attributes.spellcheck="true"),e.alias){var l="Array"===t.util.type(e.alias)?e.alias:[e.alias];Array.prototype.push.apply(i.classes,l)}t.hooks.run("wrap",i);var s="";for(var o in i.attributes)s+=o+'="'+(i.attributes[o]||"")+'"';return"<"+i.tag+' class="'+i.classes.join(" ")+'" '+s+">"+i.content+"</"+i.tag+">"},!self.document)return self.addEventListener?(self.addEventListener("message",function(e){var n=JSON.parse(e.data),a=n.language,r=n.code;self.postMessage(JSON.stringify(t.util.encode(t.tokenize(r,t.languages[a])))),self.close()},!1),self.Prism):self.Prism;var a=document.getElementsByTagName("script");return a=a[a.length-1],a&&(t.filename=a.src,document.addEventListener&&!a.hasAttribute("data-manual")&&document.addEventListener("DOMContentLoaded",t.highlightAll)),self.Prism}();"undefined"!=typeof module&&module.exports&&(module.exports=Prism);;
Prism.languages.markup={comment:/<!--[\w\W]*?-->/,prolog:/<\?.+?\?>/,doctype:/<!DOCTYPE.+?>/,cdata:/<!\[CDATA\[[\w\W]*?]]>/i,tag:{pattern:/<\/?[\w:-]+\s*(?:\s+[\w:-]+(?:=(?:("|')(\\?[\w\W])*?\1|[^\s'">=]+))?\s*)*\/?>/i,inside:{tag:{pattern:/^<\/?[\w:-]+/i,inside:{punctuation:/^<\/?/,namespace:/^[\w-]+?:/}},"attr-value":{pattern:/=(?:('|")[\w\W]*?(\1)|[^\s>]+)/i,inside:{punctuation:/=|>|"/}},punctuation:/\/?>/,"attr-name":{pattern:/[\w:-]+/,inside:{namespace:/^[\w-]+?:/}}}},entity:/&#?[\da-z]{1,8};/i},Prism.hooks.add("wrap",function(t){"entity"===t.type&&(t.attributes.title=t.content.replace(/&/,"&"))});;
Prism.languages.css={comment:/\/\*[\w\W]*?\*\//,atrule:{pattern:/@[\w-]+?.*?(;|(?=\s*\{))/i,inside:{punctuation:/[;:]/}},url:/url\((?:(["'])(\\\n|\\?.)*?\1|.*?)\)/i,selector:/[^\{\}\s][^\{\};]*(?=\s*\{)/,string:/("|')(\\\n|\\?.)*?\1/,property:/(\b|\B)[\w-]+(?=\s*:)/i,important:/\B!important\b/i,punctuation:/[\{\};:]/,"function":/[-a-z0-9]+(?=\()/i},Prism.languages.markup&&(Prism.languages.insertBefore("markup","tag",{style:{pattern:/<style[\w\W]*?>[\w\W]*?<\/style>/i,inside:{tag:{pattern:/<style[\w\W]*?>|<\/style>/i,inside:Prism.languages.markup.tag.inside},rest:Prism.languages.css},alias:"language-css"}}),Prism.languages.insertBefore("inside","attr-value",{"style-attr":{pattern:/\s*style=("|').*?\1/i,inside:{"attr-name":{pattern:/^\s*style/i,inside:Prism.languages.markup.tag.inside},punctuation:/^\s*=\s*['"]|['"]\s*$/,"attr-value":{pattern:/.+/i,inside:Prism.languages.css}},alias:"language-css"}},Prism.languages.markup.tag));;
Prism.languages.clike={comment:[{pattern:/(^|[^\\])\/\*[\w\W]*?\*\//,lookbehind:!0},{pattern:/(^|[^\\:])\/\/.*/,lookbehind:!0}],string:/("|')(\\\n|\\?.)*?\1/,"class-name":{pattern:/((?:(?:class|interface|extends|implements|trait|instanceof|new)\s+)|(?:catch\s+\())[a-z0-9_\.\\]+/i,lookbehind:!0,inside:{punctuation:/(\.|\\)/}},keyword:/\b(if|else|while|do|for|return|in|instanceof|function|new|try|throw|catch|finally|null|break|continue)\b/,"boolean":/\b(true|false)\b/,"function":{pattern:/[a-z0-9_]+\(/i,inside:{punctuation:/\(/}},number:/\b-?(0x[\dA-Fa-f]+|\d*\.?\d+([Ee]-?\d+)?)\b/,operator:/[-+]{1,2}|!|<=?|>=?|={1,3}|&{1,2}|\|?\||\?|\*|\/|~|\^|%/,ignore:/&(lt|gt|amp);/i,punctuation:/[{}[\];(),.:]/};;
Prism.languages.javascript=Prism.languages.extend("clike",{keyword:/\b(break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|false|finally|for|function|get|if|implements|import|in|instanceof|interface|let|new|null|package|private|protected|public|return|set|static|super|switch|this|throw|true|try|typeof|var|void|while|with|yield)\b/,number:/\b-?(0x[\dA-Fa-f]+|\d*\.?\d+([Ee][+-]?\d+)?|NaN|-?Infinity)\b/,"function":/(?!\d)[a-z0-9_$]+(?=\()/i}),Prism.languages.insertBefore("javascript","keyword",{regex:{pattern:/(^|[^/])\/(?!\/)(\[.+?]|\\.|[^/\r\n])+\/[gim]{0,3}(?=\s*($|[\r\n,.;})]))/,lookbehind:!0}}),Prism.languages.markup&&Prism.languages.insertBefore("markup","tag",{script:{pattern:/<script[\w\W]*?>[\w\W]*?<\/script>/i,inside:{tag:{pattern:/<script[\w\W]*?>|<\/script>/i,inside:Prism.languages.markup.tag.inside},rest:Prism.languages.javascript},alias:"language-javascript"}});;
Prism.languages.java=Prism.languages.extend("clike",{keyword:/\b(abstract|continue|for|new|switch|assert|default|goto|package|synchronized|boolean|do|if|private|this|break|double|implements|protected|throw|byte|else|import|public|throws|case|enum|instanceof|return|transient|catch|extends|int|short|try|char|final|interface|static|void|class|finally|long|strictfp|volatile|const|float|native|super|while)\b/,number:/\b0b[01]+\b|\b0x[\da-f]*\.?[\da-fp\-]+\b|\b\d*\.?\d+[e]?[\d]*[df]\b|\b\d*\.?\d+\b/i,operator:{pattern:/(^|[^\.])(?:\+=|\+\+?|-=|--?|!=?|<{1,2}=?|>{1,3}=?|==?|&=|&&?|\|=|\|\|?|\?|\*=?|\/=?|%=?|\^=?|:|~)/m,lookbehind:!0}});;
</script><script>document.addEventListener("click",function(e){var classList=e.target.classList;var codeCompare=e.target.parentElement.parentElement;if(classList.contains("nowith-switch")){codeCompare.classList.add("nowith");codeCompare.classList.remove("with");}
if(classList.contains("with-switch")){codeCompare.classList.add("with");codeCompare.classList.remove("nowith");}});</script><script>(function(){var javalinSuggestion=document.querySelector("#javalin-suggestion");if(localStorage.getItem("javalin-suggestion-dismissed")!=="true"){javalinSuggestion.style.display="block";javalinSuggestion.querySelector(".close").addEventListener("click",function(){javalinSuggestion.style.display="none";localStorage.setItem("javalin-suggestion-dismissed","true");});}})();</script></body></html>