To Do Web Application

<< Click to Display Table of Contents >>

Navigation:  Languages > JavaScript > Tutorial >

To Do Web Application

The following guide walks you through the creation of a minimal yet functional To Do web application using IntraWeb 17. This app lets users manage tasks by organizing them across three distinct stages: To Do, In Progress, and Done, with manual controls for moving tasks between categories.

 

Before getting started, ensure your Apache Server is running and properly configured to serve IWML-based applications. Also, confirm that the basic setup steps for IntraWeb 17 development have been successfully completed.

 

Project Setup

 

First, navigate to the directory where your IntraWeb 17 projects are stored: C:\src\IW17\Demos.

 

Inside this folder, create a new directory named ToDo. This will be the root folder of your application.

 

Next, create the following three files inside the ToDo folder:

 

index.html – the main HTML entry point for the application.

Index.iwml – the IWML layout file describing the visual structure.

Index.js – the JavaScript logic file containing application behavior.

 

clip0140

 

Once these files are created, you can begin implementing the structure and logic of your To Do web application.

 

Editing index.html

 

Replace the contents of the index.html file with the following code:

 

<!DOCTYPE html>

<html lang="en">

<head>

 <title>To Do</title>

 <meta charset="utf-8" />

 <meta http-equiv="Pragma" content="no-cache">

 <meta http-equiv="Expires" content="-1">

 <meta http-equiv="CACHE-CONTROL" content="NO-CACHE">

 <script src="/IW17/IntraWeb/IntraWeb.js"></script>

</head>

<body>

 Loading IntraWeb...

</body>

</html>

 

This file sets up a basic HTML page and loads the core IntraWeb JavaScript framework, which is required to run IWML applications.

 

Editing Index.iwml

 

ACORN 1.0 IWML 1.0

 

# Binds the IWML layout to the logic defined in Index.js

ToDo.Index

 

# Top-level horizontal layout to contain three columns

 SimpleStack

   Orientation: Horizontal

 

  # --- To Do Column ---

   SimpleStack

     Text [b]To Do

 

    # Row containing task input field and button

     SimpleStack:InputRow

       Orientation: Horizontal

       Edit:TaskInput

         Hint: Enter task name

       Button:AddTaskButton Add Task

 

    # Container for dynamic list of tasks in "To Do"

     SimpleStack:ToDoList

 

   Gap 20

 

  # --- In Progress Column ---

   SimpleStack

     Text [b]In Progress

 

    # Container for dynamic list of tasks in "In Progress"

     SimpleStack:InProgressList

 

   Gap 20

 

  # --- Done Column ---

   SimpleStack

     Text [b]Done

 

    # Container for dynamic list of tasks in "Done"

     SimpleStack:DoneList

 

This layout defines the three columns used in the application: "To Do", "In Progress", and "Done". Each column uses a SimpleStack layout, and the "To Do" section includes a horizontal input row for entering new tasks.

 

Editing Index.js

 

// Declare the namespace for the application

var ToDo;

(function (ToDo) {

 

// Main page logic class, extends IntraWeb.Code

 class Index extends IntraWeb.Code {

   constructor() {

     super(...arguments);

 

   // Three arrays to store tasks by their current state

     this._ToDoItems = [];

     this._InProgressItems = [];

     this._DoneItems = [];

 

   // Field input visibility and references

     this._TaskInputVisible = false;

     this._TaskInputCell = null;

     this._TaskInputParent = null;

   }

 

  // Called once the page is fully loaded

   WhenPageHasLoaded() {

   // Detach the input field until it's needed

     if (this.TaskInput?.Cell?.Parent) {

       this._TaskInputCell = this.TaskInput.Cell;

       this._TaskInputParent = this.TaskInput.Cell.Parent;

       this._TaskInputParent.Cells.Remove(this.TaskInput);

       this._TaskInputParent.DoRender();

       this._TaskInputVisible = false;

 

     // Render initial lists (empty)

       this.RenderLists();

     }

   }

 

 // Handles clicks on the "Add Task" / "Save" button

   WhenAddTaskButtonClicked() {

    // If input is hidden, show it

     if (!this._TaskInputVisible) {

       this._TaskInputParent.Cells.Add(this.TaskInput, 0);     // insert before the button

       this.AddTaskButton.Text.Value = 'Save';                   // update button label

       this._TaskInputParent.DoRender();

       this._TaskInputVisible = true;

       this.TaskInput.Focus();                                  // focus input

     } else {

      // If input is visible, treat as Save action

       const xTaskText = this.TaskInput.Text.Value.trim();

       if (xTaskText) this._ToDoItems.push(xTaskText);          // add to To Do list

       this.TaskInput.Text.Value = '';                         // clear field

       this._TaskInputParent.Cells.Remove(this.TaskInput);       // hide input again

       this._TaskInputParent.DoRender();

       this._TaskInputVisible = false;

       this.AddTaskButton.Text.Value = 'Add Task';              // restore button label

       if (xTaskText) this.RenderLists();                      // refresh tasks

     }

   }

 

  // Moves a task from one list to another

   MoveTask(aTask, aFrom, aTo) {

     if (aFrom === aTo) return;

     const xFromList = this.GetList(aFrom);

     const xToList = this.GetList(aTo);

     const xIndex = xFromList.indexOf(aTask);

     if (xIndex >= 0) {

       xFromList.splice(xIndex, 1);   // remove from source

       xToList.push(aTask);           // add to target

       this.RenderLists();           // re-render

     }

   }

 

   // Returns the internal task array by its name

   GetList(aName) {

     switch (aName) {

       case 'todo': return this._ToDoItems;

       case 'inprogress': return this._InProgressItems;

       case 'done': return this._DoneItems;

       default: return [];

     }

   }

 

 // Triggers rendering of all three columns

   RenderLists() {

     this.RenderColumn(this.ToDoList, this._ToDoItems, 'todo');

     this.RenderColumn(this.InProgressList, this._InProgressItems, 'inprogress');

     this.RenderColumn(this.DoneList, this._DoneItems, 'done');

   }

 

   // Renders a single task list column

   RenderColumn(aContainer, aItems, aFrom) {

     aContainer.Cells.Clear(); // remove existing children

 

     for (const xItem of aItems) {

       const xStack = IntraWeb.Controls.SimpleStack.Create();

       xStack.Orientation.Value = IntraWeb.LayoutOrientationEnum.Horizontal;

       aContainer.Cells.Add(xStack); // required before populating children

 

       const xText = IntraWeb.Controls.Text.Create();

       xText.Text.Value = xItem;

 

      // Add task and buttons based on list type

       if (aFrom === 'todo') {

         xStack.Cells.Add(xText);

         xStack.Cells.Add(this.MakeButton('→', 'inprogress', xItem, aFrom));

       } else if (aFrom === 'inprogress') {

         xStack.Cells.Add(this.MakeButton('←', 'todo', xItem, aFrom));

         xStack.Cells.Add(xText);

         xStack.Cells.Add(this.MakeButton('→', 'done', xItem, aFrom));

       } else if (aFrom === 'done') {

         xStack.Cells.Add(this.MakeButton('←', 'inprogress', xItem, aFrom));

         xStack.Cells.Add(xText);

       }

     }

 

     aContainer.DoRender(); // apply layout updates

   }

 

   // Helper to create a button that moves a task between lists

   MakeButton(aLabel, aTarget, aTask, aFrom) {

     const xButton = IntraWeb.Controls.Button.Create();

     xButton.Text.Value = aLabel;

     xButton.Clicked.Attach(() => this.MoveTask(aTask, aFrom, aTarget));

     return xButton;

   }

 }

 

 // Register the class inside the namespace

 ToDo.Index = Index;

})(ToDo || (ToDo = {}));

 

Running the Application

 

Now that the web application is ready, launch the Apache server and navigate to: http://127.0.0.1/demo/ToDo/.

 

This URL will display the fully functional version of your To Do application.

 

clip0143