- Left Section:
- Title (Careers Opportunities)
- Description (Some text about careers)
- CTA (View All Jobs)
- Right Section:
- Table with 3 columns:
- Job Title (Clickable anchor)
- Location (e.g., "United States")
- Posted Date (e.g., "2 days ago")
Implementation Plan
Back-End (Sling Model or Servlet)
- Fetch dynamic data from the Careers API.
- Process response and pass data to the front-end component.
Front-End (HTL & JavaScript)
- Use HTL (Sightly) for HTML structure.
- Use CSS for layout (left-right structure).
- Use JavaScript (AJAX) to call servlet and populate the table dynamically.
Caching Strategy
- Use Sling Dynamic Include (SDI) or Dispatcher caching if needed.
POC: AEM Component Code
Let's start with the AEM component implementation:
Backend - Sling Servlet to Fetch Careers API Data
We'll create a servlet to fetch jobs dynamically.
CareersServlet.java
java
package com.myproject.core.servlets; import com.google.gson.JsonElement; import com.google.gson.JsonParser; import org.apache.sling.api.SlingHttpServletRequest; import org.apache.sling.api.SlingHttpServletResponse; import org.apache.sling.api.servlets.SlingSafeMethodsServlet; import org.osgi.service.component.annotations.Component; import javax.servlet.Servlet; import java.io.BufferedReader; import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.URL; @component( service = Servlet.class, property = { "sling.servlet.paths=/bin/careers", "sling.servlet.methods=GET", "sling.servlet.extensions=json" } ) public class CareersServlet extends SlingSafeMethodsServlet { private static final String API_URL = "https://example.com/api/careers"; // Replace with actual API URL @Override protected void doGet(SlingHttpServletRequest request, SlingHttpServletResponse response) { try { URL url = new URL(API_URL); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("GET"); conn.setRequestProperty("Accept", "application/json"); if (conn.getResponseCode() != 200) { response.setStatus(HttpURLConnection.HTTP_BAD_REQUEST); response.getWriter().write("Failed to fetch careers data"); return; } BufferedReader br = new BufferedReader(new InputStreamReader((conn.getInputStream()))); StringBuilder result = new StringBuilder(); String output; while ((output = br.readLine()) != null) { result.append(output); } conn.disconnect(); // Parse and return JSON response JsonElement jsonResponse = JsonParser.parseString(result.toString()); response.setContentType("application/json"); response.getWriter().write(jsonResponse.toString()); } catch (Exception e) { response.setStatus(HttpURLConnection.HTTP_INTERNAL_ERROR); try { response.getWriter().write("Error: " + e.getMessage()); } catch (Exception ignored) { } } } }
Front-End - HTL & JavaScript
Now, let's create the HTL (Sightly) and JavaScript to display the careers component.
/apps/myproject/components/careers/careers.html
html
<div class="careers-container"> <!-- Left Side --> <div class="careers-info"> <h2>Career Opportunities</h2> <p>Explore exciting job openings and build your career with us.</p> <a href="/careers.html" class="btn-cta">View All Jobs</a> </div> <!-- Right Side (Table) --> <div class="careers-table"> <table> <thead> <tr> <th>Job Title</th> <th>Location</th> <th>Posted Date</th> </tr> </thead> <tbody id="careers-list"> <!-- Jobs will be populated here via JavaScript --> </tbody> </table> </div> </div> <sly data-sly-use.clientLib="/libs/granite/sightly/templates/clientlib.html"> <sly data-sly-call="${clientLib.js @ categories='myproject.careers'}" /> </sly>
JavaScript to Fetch API Data
We'll use JavaScript to fetch job data dynamically.
/apps/myproject/components/careers/clientlib-site/js/careers.js
javascript
document.addEventListener("DOMContentLoaded", function () { fetch('/bin/careers.json') // Call the servlet .then(response => response.json()) .then(data => { let careersList = document.getElementById("careers-list"); careersList.innerHTML = ""; // Clear previous data data.jobs.forEach(job => { let row = document.createElement("tr"); // Job Title (Anchor Tag) let jobTitle = document.createElement("td"); let jobLink = document.createElement("a"); jobLink.href = `/careers/${job.id}`; jobLink.textContent = job.title; jobTitle.appendChild(jobLink); // Location let location = document.createElement("td"); location.textContent = job.location; // Posted Date let postedDate = document.createElement("td"); postedDate.textContent = formatPostedDate(job.posted_date); row.appendChild(jobTitle); row.appendChild(location); row.appendChild(postedDate); careersList.appendChild(row); }); }) .catch(error => console.error("Error fetching careers:", error)); }); // Function to format date (convert "2024-02-01" to "2 days ago") function formatPostedDate(dateString) { let jobDate = new Date(dateString); let today = new Date(); let diffTime = Math.abs(today - jobDate); let diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24)); return diffDays === 1 ? "1 day ago" : `${diffDays} days ago`; }