TypeScript Tutorial: Building a Simple Interactive Web-Based Poll Application

In today’s digital landscape, gathering feedback and opinions is crucial. Whether it’s for market research, understanding customer preferences, or simply gauging interest in a new idea, polls provide a quick and effective way to collect valuable data. This tutorial will guide you through creating a simple, interactive web-based poll application using TypeScript. We’ll explore the core concepts of TypeScript, build a functional poll, and learn how to handle user interactions and display results.

Why TypeScript?

TypeScript, a superset of JavaScript, offers several advantages for web development. It introduces static typing, which helps catch errors early in the development process, improving code quality and maintainability. TypeScript also provides better code completion and refactoring capabilities, making it easier to write and manage large codebases. For this project, TypeScript will help us structure our poll application, ensuring type safety and making it more robust.

Project Overview

Our poll application will allow users to:

  • Create a poll with a question and multiple choice options.
  • View the poll question and options.
  • Vote for their preferred option.
  • See the results of the poll, including the percentage of votes for each option.

We’ll use HTML for the structure, CSS for styling, and TypeScript for the logic. We’ll focus on the core functionality and keep the design simple for clarity.

Setting Up the Project

Before we start coding, let’s set up our project environment. You’ll need:

  • Node.js and npm (Node Package Manager) installed.
  • A code editor (e.g., Visual Studio Code, Sublime Text, Atom).

1. **Create a Project Directory:**

mkdir poll-app
cd poll-app

2. **Initialize npm:**

npm init -y

This command creates a `package.json` file in your project directory. This file will store information about your project and its dependencies.

3. **Install TypeScript:**

npm install typescript --save-dev

This command installs TypeScript as a development dependency. The `–save-dev` flag ensures that TypeScript is only used during development and not in the production environment.

4. **Create `tsconfig.json`:**

This file configures the TypeScript compiler. Create a file named `tsconfig.json` in your project directory with the following content:

{
  "compilerOptions": {
    "target": "es5",
    "module": "commonjs",
    "outDir": "./dist",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true
  },
  "include": ["src/**/*"]
}

* `target`: Specifies the JavaScript version to compile to (e.g., ES5, ES6). We’re using ES5 for broader browser compatibility.

* `module`: Specifies the module system to use (e.g., commonjs, es2015). We’re using `commonjs` for Node.js compatibility.

* `outDir`: Specifies the output directory for the compiled JavaScript files. We’re using `dist`.

* `strict`: Enables strict type checking, which helps catch more errors.

* `esModuleInterop`: Enables interoperability between CommonJS and ES modules.

* `skipLibCheck`: Skips type checking of declaration files.

* `forceConsistentCasingInFileNames`: Enforces consistent casing in file names.

* `include`: Specifies the files and directories to include in the compilation. We’re including all files in the `src` directory and its subdirectories.

5. **Create Project Structure:**

Create the following directory structure in your project:

poll-app/
├── src/
│   ├── index.ts
│   └── style.css
├── dist/
├── index.html
├── tsconfig.json
└── package.json

Coding the Poll Application

1. HTML Structure (`index.html`)

Let’s create the basic HTML structure for our poll application. This file will hold the question, the options, and the results.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Simple Poll</title>
    <link rel="stylesheet" href="src/style.css">
</head>
<body>
    <div class="poll-container">
        <h2 id="poll-question">What is your favorite color?</h2>
        <div id="poll-options">
            <button class="poll-button" data-option="red">Red</button>
            <button class="poll-button" data-option="blue">Blue</button>
            <button class="poll-button" data-option="green">Green</button>
        </div>
        <div id="poll-results">
            <p>Results:</p>
            <ul>
                <li>Red: <span id="red-votes">0</span> votes</li>
                <li>Blue: <span id="blue-votes">0</span> votes</li>
                <li>Green: <span id="green-votes">0</span> votes</li>
            </ul>
        </div>
    </div>
    <script src="dist/index.js"></script>
</body>
</html>

* We have a `poll-container` to hold everything.

* `poll-question` displays the question.

* `poll-options` contains buttons for each answer option, each with a `data-option` attribute that holds the option value.

* `poll-results` displays the vote counts for each option.

2. CSS Styling (`src/style.css`)

Next, let’s add some basic styling to make our poll look presentable. This is a very basic example; you can customize the styles to your liking.

.poll-container {
    width: 80%;
    margin: 20px auto;
    padding: 20px;
    border: 1px solid #ccc;
    border-radius: 5px;
    text-align: center;
}

.poll-button {
    margin: 10px;
    padding: 10px 20px;
    font-size: 16px;
    cursor: pointer;
    border: 1px solid #ddd;
    border-radius: 5px;
    background-color: #f0f0f0;
}

.poll-button:hover {
    background-color: #ddd;
}

3. TypeScript Logic (`src/index.ts`)

Now, let’s write the TypeScript code that will handle the poll logic. This is where the core functionality of our application resides.


// Define an interface for the poll options
interface PollOption {
    name: string;
    votes: number;
}

// Define the poll data
let pollQuestion: string = "What is your favorite color?";
let pollOptions: PollOption[] = [
    { name: "red", votes: 0 },
    { name: "blue", votes: 0 },
    { name: "green", votes: 0 },
];

// Get references to HTML elements
const questionElement = document.getElementById("poll-question") as HTMLHeadingElement;
const optionsContainer = document.getElementById("poll-options") as HTMLDivElement;
const resultsContainer = document.getElementById("poll-results") as HTMLDivElement;

// Function to render the poll
function renderPoll(): void {
    if (questionElement) {
        questionElement.textContent = pollQuestion;
    }

    if (optionsContainer) {
        optionsContainer.innerHTML = ""; // Clear existing buttons
        pollOptions.forEach(option => {
            const button = document.createElement("button") as HTMLButtonElement;
            button.textContent = option.name.charAt(0).toUpperCase() + option.name.slice(1); // Capitalize first letter
            button.className = "poll-button";
            button.dataset.option = option.name;
            button.addEventListener("click", handleVote);
            optionsContainer.appendChild(button);
        });
    }

    renderResults();
}

// Function to handle a vote
function handleVote(event: Event): void {
    const target = event.target as HTMLButtonElement;
    const selectedOption = target.dataset.option;

    if (!selectedOption) return; // Exit if no option is selected

    // Increment the vote count for the selected option
    pollOptions = pollOptions.map(option =>
        option.name === selectedOption ? { ...option, votes: option.votes + 1 } : option
    );

    renderResults();
}

// Function to render the results
function renderResults(): void {
    if (!resultsContainer) return; // Exit if results container is not found

    let resultsHTML = "<p>Results:</p><ul>";
    const totalVotes = pollOptions.reduce((sum, option) => sum + option.votes, 0);

    pollOptions.forEach(option => {
        const percentage = totalVotes === 0 ? 0 : ((option.votes / totalVotes) * 100).toFixed(2);
        resultsHTML += `<li>${option.name.charAt(0).toUpperCase() + option.name.slice(1)}: <span>${option.votes} votes (${percentage}%)</span></li>`;
    });

    resultsHTML += "</ul>";
    resultsContainer.innerHTML = resultsHTML;
}

// Initialize the poll
renderPoll();

* **Interfaces and Data:** We define a `PollOption` interface to represent each option in our poll, including its name and the number of votes it has. We then initialize the poll question and options. Note the use of `let` to allow the pollOptions array to be updated.

* **Element References:** We get references to the HTML elements we’ll be manipulating, using `document.getElementById()`. The `as HTMLHeadingElement`, `as HTMLDivElement`, and `as HTMLButtonElement` are *type assertions* and help TypeScript understand the types of the elements. This enables better autocompletion and error checking.

* **`renderPoll()` Function:** This function is responsible for rendering the poll question and options in the HTML. It dynamically creates the buttons for each option and attaches event listeners to them.

* **`handleVote()` Function:** This function is called when a user clicks an option. It increments the vote count for the selected option and then calls `renderResults()` to update the display.

* **`renderResults()` Function:** This function calculates the results (vote counts and percentages) and displays them in the HTML. It uses the `reduce()` method to calculate the total votes and the `toFixed(2)` method to format the percentage to two decimal places.

* **Initialization:** Finally, we call `renderPoll()` to initialize the poll when the page loads.

4. Compiling and Running the Application

To compile the TypeScript code, run the following command in your terminal:

tsc

This command will compile the `index.ts` file and generate a `index.js` file in the `dist` directory. Open `index.html` in your browser. You should see the poll, and when you click on the options, the results should update.

Step-by-Step Instructions

Let’s break down the process into smaller, more manageable steps:

  1. **Project Setup:** Follow the steps outlined in the “Setting Up the Project” section to create the project directory, initialize npm, install TypeScript, create `tsconfig.json`, and set up the project structure.
  2. **HTML Structure:** Create the `index.html` file and add the HTML structure for the poll, including the question, options (buttons), and results display.
  3. **CSS Styling:** Create the `src/style.css` file and add CSS rules to style the poll elements.
  4. **TypeScript Logic:** Create the `src/index.ts` file and add the TypeScript code for the poll logic, including the data structures, event listeners, and functions to render the poll and update the results.
  5. **Compilation:** Run the `tsc` command in your terminal to compile the TypeScript code.
  6. **Testing:** Open `index.html` in your web browser and test the poll functionality. Verify that you can click on the options and that the results update correctly.
  7. **Deployment (Optional):** You can deploy the application to a web server to make it accessible online.

Common Mistakes and How to Fix Them

Here are some common mistakes and how to fix them:

  • **Incorrect File Paths:** Double-check the file paths in your HTML (e.g., the path to your CSS file and the compiled JavaScript file). Make sure they are correct relative to the HTML file.
  • **Typos in HTML Element IDs:** Make sure the IDs you use in your TypeScript code to get references to HTML elements (e.g., `document.getElementById(“poll-question”)`) match the IDs in your HTML. Typos here will cause the program to fail to find the elements.
  • **Incorrect Event Listener Syntax:** Ensure that you are attaching event listeners correctly (e.g., `button.addEventListener(“click”, handleVote)`).
  • **Type Errors:** TypeScript’s type checking can help prevent errors. If you see type errors, carefully review the error messages and ensure that your variables and function parameters are of the correct types. Use type assertions (e.g., `as HTMLButtonElement`) when necessary.
  • **Not Compiling TypeScript:** Remember to compile your TypeScript code using the `tsc` command before refreshing the browser. Otherwise, the changes you make in the `.ts` file won’t be reflected in your application.
  • **Incorrect Data Handling:** Ensure that your data structures (e.g., the `pollOptions` array) are updated correctly when a user votes. Carefully review the logic in the `handleVote()` function.

Key Takeaways

  • **TypeScript for Structure:** TypeScript significantly improves code organization and maintainability.
  • **HTML for Structure:** HTML provides the basic structure and layout of the poll.
  • **CSS for Styling:** CSS makes our poll visually appealing.
  • **Event Handling:** Event listeners are crucial for handling user interactions, such as clicking on poll options.
  • **Dynamic Content:** The ability to dynamically update the content of our poll, such as the vote counts and percentages, is a key aspect of making it interactive.

FAQ

Here are some frequently asked questions:

  1. **Can I add more options to the poll?** Yes, you can easily add more options by adding more buttons to the HTML and adding corresponding entries to the `pollOptions` array in the TypeScript code.
  2. **How can I make the poll data persistent?** Currently, the poll data is stored in memory and will be lost when the page is refreshed. To make the data persistent, you would need to store the data in a database or local storage.
  3. **How can I style the poll differently?** You can customize the appearance of the poll by modifying the CSS styles in the `style.css` file.
  4. **Can I add validation to the poll?** Yes, you can add validation to ensure that users select an option before submitting their vote.

The creation of this simple poll application using TypeScript is more than just a coding exercise; it’s a gateway to understanding the fundamentals of web development. By mastering the concepts presented in this tutorial, you’ll be well-equipped to tackle more complex projects and create engaging user experiences. As you continue to build and experiment, remember that the most important aspect of learning is the willingness to try new things and embrace the challenges that come with them. The skills you’ve gained here will serve as a solid foundation for your journey into the world of web development.