|
@@ -1,16 +1,18 @@
|
|
|
using Astralis;
|
|
using Astralis;
|
|
|
using Invercargill;
|
|
using Invercargill;
|
|
|
using Invercargill.DataStructures;
|
|
using Invercargill.DataStructures;
|
|
|
|
|
+using Inversion;
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
* DocumentBuilderTemplate Example
|
|
* DocumentBuilderTemplate Example
|
|
|
*
|
|
*
|
|
|
* Demonstrates loading an existing HTML template and modifying elements
|
|
* Demonstrates loading an existing HTML template and modifying elements
|
|
|
* using the DocumentModel classes. Shows how to:
|
|
* using the DocumentModel classes. Shows how to:
|
|
|
- * - Load HTML from a string template
|
|
|
|
|
|
|
+ * - Define a MarkupTemplate subclass with embedded HTML
|
|
|
|
|
+ * - Register the template as a singleton with WebApplication
|
|
|
|
|
+ * - Inject and use the template in endpoints
|
|
|
* - Use XPath selectors to find specific elements
|
|
* - Use XPath selectors to find specific elements
|
|
|
* - Modify element content, attributes, and classes
|
|
* - Modify element content, attributes, and classes
|
|
|
- * - Add new elements dynamically
|
|
|
|
|
* - Handle form POST to update the document state
|
|
* - Handle form POST to update the document state
|
|
|
*
|
|
*
|
|
|
* This example complements DocumentBuilder.vala by showing template-based
|
|
* This example complements DocumentBuilder.vala by showing template-based
|
|
@@ -55,12 +57,19 @@ class AppState : Object {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-// Global app state
|
|
|
|
|
-AppState app_state;
|
|
|
|
|
-
|
|
|
|
|
-// HTML template loaded as a constant
|
|
|
|
|
-private const string HTML_TEMPLATE = """
|
|
|
|
|
-<!DOCTYPE html>
|
|
|
|
|
|
|
+/**
|
|
|
|
|
+ * CounterTemplate - A cached HTML template for the counter page.
|
|
|
|
|
+ *
|
|
|
|
|
+ * This class extends MarkupTemplate to provide a reusable, cached template.
|
|
|
|
|
+ * The HTML is parsed once and cached; new_instance() returns efficient copies.
|
|
|
|
|
+ */
|
|
|
|
|
+class CounterTemplate : MarkupTemplate {
|
|
|
|
|
+ /// <summary>
|
|
|
|
|
+ /// Returns the HTML markup for this template.
|
|
|
|
|
+ /// This could also use read_file() to load from disk.
|
|
|
|
|
+ /// </summary>
|
|
|
|
|
+ protected override string get_markup() {
|
|
|
|
|
+ return """<!DOCTYPE html>
|
|
|
<html lang="en">
|
|
<html lang="en">
|
|
|
<head>
|
|
<head>
|
|
|
<meta charset="UTF-8"/>
|
|
<meta charset="UTF-8"/>
|
|
@@ -290,12 +299,17 @@ if (counter > 0) {
|
|
|
</body>
|
|
</body>
|
|
|
</html>
|
|
</html>
|
|
|
""";
|
|
""";
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
|
|
|
-// Main page endpoint - loads template and modifies it
|
|
|
|
|
|
|
+// Main page endpoint - uses field initializer injection
|
|
|
class HomePageEndpoint : Object, Endpoint {
|
|
class HomePageEndpoint : Object, Endpoint {
|
|
|
|
|
+ // Field initializer injection - template is injected by the DI container
|
|
|
|
|
+ private CounterTemplate template = Inversion.inject<CounterTemplate>();
|
|
|
|
|
+
|
|
|
public async HttpResult handle_request(HttpContext context, RouteContext route) throws Error {
|
|
public async HttpResult handle_request(HttpContext context, RouteContext route) throws Error {
|
|
|
- // Load the HTML template
|
|
|
|
|
- var doc = new MarkupDocument.from_string(HTML_TEMPLATE);
|
|
|
|
|
|
|
+ // Get a copy of the cached template
|
|
|
|
|
+ var doc = template.new_instance();
|
|
|
|
|
|
|
|
// Update the counter value
|
|
// Update the counter value
|
|
|
var counter_el = doc.select_one("//div[@id='counter-value']");
|
|
var counter_el = doc.select_one("//div[@id='counter-value']");
|
|
@@ -388,9 +402,12 @@ class ResetEndpoint : Object, Endpoint {
|
|
|
|
|
|
|
|
// Raw HTML endpoint - shows the unmodified template
|
|
// Raw HTML endpoint - shows the unmodified template
|
|
|
class RawHtmlEndpoint : Object, Endpoint {
|
|
class RawHtmlEndpoint : Object, Endpoint {
|
|
|
|
|
+ // Field initializer injection
|
|
|
|
|
+ private CounterTemplate template = Inversion.inject<CounterTemplate>();
|
|
|
|
|
+
|
|
|
public async HttpResult handle_request(HttpContext context, RouteContext route) throws Error {
|
|
public async HttpResult handle_request(HttpContext context, RouteContext route) throws Error {
|
|
|
- // Load template without modifications
|
|
|
|
|
- var doc = new MarkupDocument.from_string(HTML_TEMPLATE);
|
|
|
|
|
|
|
+ // Get a fresh copy of the template
|
|
|
|
|
+ var doc = template.new_instance();
|
|
|
|
|
|
|
|
// Add a notice at the top
|
|
// Add a notice at the top
|
|
|
var body = doc.body;
|
|
var body = doc.body;
|
|
@@ -409,8 +426,11 @@ class RawHtmlEndpoint : Object, Endpoint {
|
|
|
|
|
|
|
|
// API endpoint that demonstrates modifying multiple elements at once
|
|
// API endpoint that demonstrates modifying multiple elements at once
|
|
|
class BulkUpdateEndpoint : Object, Endpoint {
|
|
class BulkUpdateEndpoint : Object, Endpoint {
|
|
|
|
|
+ // Field initializer injection
|
|
|
|
|
+ private CounterTemplate template = Inversion.inject<CounterTemplate>();
|
|
|
|
|
+
|
|
|
public async HttpResult handle_request(HttpContext context, RouteContext route) throws Error {
|
|
public async HttpResult handle_request(HttpContext context, RouteContext route) throws Error {
|
|
|
- var doc = new MarkupDocument.from_string(HTML_TEMPLATE);
|
|
|
|
|
|
|
+ var doc = template.new_instance();
|
|
|
|
|
|
|
|
// Demonstrate selecting multiple elements
|
|
// Demonstrate selecting multiple elements
|
|
|
var all_info_values = doc.select("//div[contains(@class, 'info-value')]");
|
|
var all_info_values = doc.select("//div[contains(@class, 'info-value')]");
|
|
@@ -439,6 +459,9 @@ class BulkUpdateEndpoint : Object, Endpoint {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+// Global app state
|
|
|
|
|
+AppState app_state;
|
|
|
|
|
+
|
|
|
void main(string[] args) {
|
|
void main(string[] args) {
|
|
|
int port = args.length > 1 ? int.parse(args[1]) : 8080;
|
|
int port = args.length > 1 ? int.parse(args[1]) : 8080;
|
|
|
|
|
|
|
@@ -466,9 +489,12 @@ void main(string[] args) {
|
|
|
var application = new WebApplication(port);
|
|
var application = new WebApplication(port);
|
|
|
|
|
|
|
|
// Register compression components
|
|
// Register compression components
|
|
|
- application.container.register_singleton<GzipCompressor>(() => new GzipCompressor());
|
|
|
|
|
- application.container.register_singleton<ZstdCompressor>(() => new ZstdCompressor());
|
|
|
|
|
- application.container.register_singleton<BrotliCompressor>(() => new BrotliCompressor());
|
|
|
|
|
|
|
+ application.use_compression();
|
|
|
|
|
+
|
|
|
|
|
+ // Register the template as a singleton - it will be parsed once and cached
|
|
|
|
|
+ // Each request gets a copy via new_instance()
|
|
|
|
|
+ // Endpoints use field initializer injection with Inversion.inject<T>()
|
|
|
|
|
+ application.add_singleton<CounterTemplate>();
|
|
|
|
|
|
|
|
// Register endpoints
|
|
// Register endpoints
|
|
|
application.add_endpoint<HomePageEndpoint>(new EndpointRoute("/"));
|
|
application.add_endpoint<HomePageEndpoint>(new EndpointRoute("/"));
|