Skip to content

Latest commit

 

History

History
381 lines (287 loc) · 7.26 KB

File metadata and controls

381 lines (287 loc) · 7.26 KB

Context & Variables

Introduction

The Context object is the bridge between your Java code and JHP templates. It holds all the data that will be available to your templates during rendering.

Creating Context

Create a new context instance:

Context ctx = new Context();

Adding Variables

Add variables to the context using the add() method:

ctx.add("variableName", value);

Simple Example

Context ctx = new Context();
ctx.add("title", "My Page");
ctx.add("count", 42);
ctx.add("active", true);

Template:

<h1>{{ title }}</h1>
<p>Count: {{ count }}</p>
<p>Active: {{ active }}</p>

Data Types

Context supports all common Java data types.

Strings

ctx.add("name", "Alice");
ctx.add("email", "alice@example.com");

Numbers

ctx.add("age", 25);              // Integer
ctx.add("price", 19.99);         // Double
ctx.add("count", 100L);          // Long
ctx.add("rating", 4.5f);         // Float

Booleans

ctx.add("isActive", true);
ctx.add("verified", false);

Null Values

ctx.add("optional", null);

Template:

{{ optional }}
<!-- Renders as empty string -->

Lists and Collections

List<String> fruits = Arrays.asList("Apple", "Banana", "Cherry");
ctx.add("fruits", fruits);

Set<Integer> numbers = new HashSet<>(Arrays.asList(1, 2, 3));
ctx.add("numbers", numbers);

Template:

{% foreach (fruit in fruits) %}
    <li>{{ fruit }}</li>
{% endforeach %}

Arrays

String[] colors = {"Red", "Green", "Blue"};
ctx.add("colors", colors);

int[] scores = {85, 90, 78, 92};
ctx.add("scores", scores);

Template:

{{ colors[0] }}
{{ scores[1] }}

Maps

Map<String, Object> user = new HashMap<>();
user.put("name", "Bob");
user.put("age", 30);
user.put("email", "bob@example.com");
ctx.add("user", user);

Template:

{{ user.name }}
{{ user.age }}
{{ user["email"] }}

POJO Conversion

Context automatically converts Plain Old Java Objects (POJOs) to Maps.

Basic POJO

class User {
    public String name;
    public int age;
    public String email;
    
    public User(String name, int age, String email) {
        this.name = name;
        this.age = age;
        this.email = email;
    }
}

User user = new User("Alice", 25, "alice@example.com");
ctx.add("user", user);

Template:

<h2>{{ user.name }}</h2>
<p>Age: {{ user.age }}</p>
<p>Email: {{ user.email }}</p>

POJO Conversion Rules

  1. Only public fields are converted
  2. Nested POJOs are skipped (only primitives, strings, collections, and arrays)
  3. Collections and arrays within POJOs are converted recursively

Example with Collections

class Product {
    public String name;
    public double price;
    public List<String> tags;
    
    public Product(String name, double price, List<String> tags) {
        this.name = name;
        this.price = price;
        this.tags = tags;
    }
}

Product product = new Product("Laptop", 999.99, Arrays.asList("electronics", "computers"));
ctx.add("product", product);

Template:

<h3>{{ product.name }}</h3>
<p>${{ product.price }}</p>
<ul>
{% foreach (tag in product.tags) %}
    <li>{{ tag }}</li>
{% endforeach %}
</ul>

Nested Objects Limitation

class Address {
    public String city;
    public String country;
}

class User {
    public String name;
    public Address address;  // This will be skipped
}

Workaround: Convert nested objects to Maps manually:

User user = new User();
user.name = "Alice";

Map<String, Object> userMap = new HashMap<>();
userMap.put("name", user.name);

Map<String, String> addressMap = new HashMap<>();
addressMap.put("city", user.address.city);
addressMap.put("country", user.address.country);
userMap.put("address", addressMap);

ctx.add("user", userMap);

Variable Scope

Global Scope

Variables added to the context are available throughout the template:

ctx.add("siteName", "My Website");

Template:

<header>{{ siteName }}</header>
<main>{{ siteName }}</main>
<footer>{{ siteName }}</footer>

Loop Scope

Loop variables are scoped to the loop:

{% for (i = 0; i < 5; i++) %}
    {{ i }}  <!-- i is available here -->
{% endfor %}
{{ i }}  <!-- i is NOT available here -->

Include Scope

Included templates have access to the parent context:

main.jhp:

{% include 'header.jhp' %}

header.jhp:

<h1>{{ title }}</h1>  <!-- Can access title from main context -->

Best Practices

1. Use Descriptive Names

// Good
ctx.add("userName", "Alice");
ctx.add("productList", products);

// Avoid
ctx.add("x", "Alice");
ctx.add("data", products);

2. Prefer Maps Over POJOs for Complex Data

// Better for templates
Map<String, Object> user = new HashMap<>();
user.put("name", "Alice");
user.put("profile", profileMap);
ctx.add("user", user);

3. Prepare Data in Java, Not Templates

// Good - calculate in Java
int total = items.stream().mapToInt(Item::getPrice).sum();
ctx.add("total", total);

// Avoid - complex logic in template
ctx.add("items", items);
// Then doing: {{ items[0].price + items[1].price + ... }}

4. Use Null-Safe Defaults

ctx.add("title", title != null ? title : "Untitled");
ctx.add("items", items != null ? items : Collections.emptyList());

5. Group Related Data

Map<String, Object> page = new HashMap<>();
page.put("title", "Home");
page.put("description", "Welcome");
page.put("keywords", Arrays.asList("home", "welcome"));
ctx.add("page", page);

Template:

<title>{{ page.title }}</title>
<meta name="description" content="{{ page.description }}">

6. Reuse Context Objects

// Create a base context with common variables
Context baseContext = new Context();
baseContext.add("siteName", "My Site");
baseContext.add("year", 2025);

// For each page, create a new context with page-specific data
Context pageContext = new Context();
// Copy base context data
pageContext.getContext().putAll(baseContext.getContext());
pageContext.add("title", "Page Title");

Getting Context Data

Access the underlying map if needed:

Context ctx = new Context();
ctx.add("name", "Alice");

Map<String, Object> data = ctx.getContext();
System.out.println(data.get("name")); // Alice

Immutability Considerations

The Context class doesn't enforce immutability. Variables can be modified during rendering (e.g., in loops). If you need immutable data, consider using immutable collections:

List<String> items = List.of("A", "B", "C");  // Immutable list
ctx.add("items", items);

Next Steps