📖 Templating with JavaScript

Templating allows you to dynamically generate HTML content using JavaScript. By using templates, you can efficiently create and update the user interface without manually writing repetitive HTML code. This approach improves code maintainability and readability.

Core Principles

Definition and Purpose of Templating

Templating is the process of creating a structure for your HTML that can be reused with different data. This technique is essential for generating dynamic content on the fly, such as lists, forms, and other repeated structures. In JavaScript, you can create templates using template literals, which allow you to embed expressions and variables within a string. This approach provides a clean and efficient way to generate dynamic HTML content.

Template Literals

Template literals are string literals allowing embedded expressions. They are enclosed by backticks (` `) instead of single or double quotes. Template literals can contain placeholders, which are indicated by the ${expression} syntax. Here are some benefits, rules, and restrictions:

  • Multi-line strings: Template literals preserve the newline characters, making it easier to create multi-line strings.
  • Embedded expressions: You can embed any valid JavaScript expression inside a template literal using the ${expression} syntax.
  • Tagged templates: These provide advanced functionality by allowing you to parse template literals with a function.
  • String interpolation: Template literals simplify string concatenation and interpolation by embedding expressions directly within the string.

Coding Examples

Creating Reusable Components

In this example, we create a reusable component to display user information. The component is a function that takes user data as input and returns a string of HTML using template literals.


// Data
const users = [
    { name: 'John Doe', email: 'john.doe@example.com' },
    { name: 'Jane Smith', email: 'jane.smith@example.com' },
    { name: 'Bob Johnson', email: 'bob.johnson@example.com' }
];

// Component function
function UserCard(user) {
    return `
        <div class="card">
            <div class="card-body">
                <h5 class="card-title">${user.name}</h5>
                <p class="card-text">${user.email}</p>
            </div>
        </div>
    `;
}

// Using the component
const userCards = users.map(UserCard).join('');
document.getElementById('userContainer').innerHTML = userCards;
        

In this example, the UserCard function is a component that generates HTML for a user card. We then use the map method to create a user card for each user in the users array and join them into a single string of HTML. This string is then inserted into the userContainer element.

Task List with Components

In this example, we create a task list using reusable components. Each task item is rendered as a list item, and we use event delegation to handle click events on the task items.


// Data
const tasks = [
    { task: 'Buy groceries', completed: false },
    { task: 'Walk the dog', completed: true },
    { task: 'Do laundry', completed: false }
];

// Component function
function TaskItem(task) {
    return `
        <li class="list-group-item ${task.completed ? 'completed' : ''}">
            ${task.task}
        </li>
    `;
}

// Using the component
const taskItems = tasks.map(TaskItem).join('');
document.getElementById('taskList').innerHTML = taskItems;
        

The TaskItem function generates HTML for a task item. We use the map method to create a list item for each task in the tasks array and join them into a single string of HTML. This string is then inserted into the taskList element.

The Ternary Operator

The ternary operator shortens this if/else statement into a single statement. If the condition evaluates true, the content between the ? and : is applied. If the condition evaluates to false, the content after the : is applied.

(condition) ? 'something' : 'somethingelse';

In the TaskItem function, we use a ternary operator to conditionally apply a CSS class to the list item. The expression ${task.completed ? 'completed' : ''} checks if the task.completed property is true. If it is, it returns the string 'completed'; otherwise, it returns an empty string. This allows us to dynamically add the 'completed' class to tasks that are marked as completed.

Putting It Into Action

Create an HTML file with the following structure and include the provided script. This script demonstrates how to generate a list of tasks using a template function. The list items are dynamically created based on the data provided.


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Task List>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
</head>
<body>
    <div class="container mt-5">
        <h2>User List>
        <div id="userContainer" class="mb-5">>
        
        <h2>Task List</h2>
        <ul id="taskList" class="list-group">>
    >

    <script>
        // Data for users
        const users = [
            { name: 'John Doe', email: 'john.doe@example.com' },
            { name: 'Jane Smith', email: 'jane.smith@example.com' },
            { name: 'Bob Johnson', email: 'bob.johnson@example.com' }
        ];

        // Component function for users
        function UserCard(user) {
            return `
                <div class="card">
                    <div class="card-body">
                        <h5 class="card-title">${user.name}</h5>
                        <p class="card-text">${user.email}</p>
                    </div>
                </div>
            `;
        }

        // Using the component for users
        const userCards = users.map(UserCard).join('');
        document.getElementById('userContainer').innerHTML = userCards;

        // Data for tasks
        const tasks = [
            { task: 'Buy groceries', completed: false },
            { task: 'Walk the dog', completed: true },
            { task: 'Do laundry', completed: false }
        ];

        // Component function for tasks
        function TaskItem(task) {
            return `
                <li class="list-group-item ${task.completed ? 'completed' : ''}">
                    ${task.task}
                </li>
            `;
        }

        // Using the component for tasks
        const taskItems = tasks.map(TaskItem).join('');
        document.getElementById('taskList').innerHTML = taskItems;
    </script>
</body>
>
        

In this example, we create two sets of components: one for user cards and one for task items. The data for users and tasks is mapped to their respective components, and the generated HTML is inserted into the appropriate container elements.

Challenge

Modify the example to include a feature where clicking a task item toggles its completion status by marking through the text. Use event delegation to handle the click events.

In order to check your learning, you should attempt to create a solution before revealing the provided solution below.


// JavaScript Code
document.getElementById('taskList').addEventListener('click', function(event) {
    if (event.target.tagName === 'LI') {
        event.target.classList.toggle('completed');
    }
});
                        

References