Total.js tutorial: Simple Todo application (part 2)
Tags: totaljs, node, tutorial, todo
Published: 2023-03-19 17:34:31

[**Part 1**](https://dev.to/louis_bertson_1124e9cdc59/totaljs-tutorial-simple-todo-application-part-1-2gjg) of this tutorial has been a success! It was said to be simple, instructive and awesome. **Part 1** has shown how easy it was to make a todo application REST API. if you haven’t read it yet, just click [**here**](https://dev.to/louis_bertson_1124e9cdc59/totaljs-tutorial-simple-todo-application-part-1-2gjg) to read and play with it before you come to **Part 2**! Part 2 is about the UI interface for interacting with the API that we created in Part 1. You will appreciate how easy and lightweight it is to make UI using the Total.js client-side libraries. We will follow these steps:
- Add the UI page route.
    - UI preview.
    - Defining UI endpoint.
- Add the UI page.
    - Layout HTML file.
    - Index HTML file.
- Testing.
- Understanding the key concepts of the Total.js Client-side library.

Let us jump in and give a UI to our to-do application.

## Add the UI page route 

Adding a UI page will require route definition. In our case the `/` (root) endpoint is available, so we are using it for returning the user interface to application users.

**UI preview**
Before you move to the next step, let the following gif describes the result of this application.
![Ui preview Gif image](https://cdn.hashnode.com/res/hashnode/image/upload/v1679528725314/2574d36f-6abc-4291-97f0-891a43891cb1.gif?auto=format,compress&gif-q=60&format=webm)


Before you even ask it, let me tell you that this is neither **React** nor **Angular**, this is a SPA (Single Page Application) implementation using Total.js client side and everything is dynamic, fast and reactive. Total.js is amazing! Let us move to discover how to do that.

**Defining UI endpoint**
Create a new file in controllers, `/controllers/default.js` and add the following code:

```js
exports.install = function() {
    ROUTE('GET /', home);
}

function home() {
    var self = this;     // `This` is the controller instance
    self.view('index');  // self.view() will resolve views/index.html
}
```

## Add the UI page
The controller will be looking for `index.html` in the views folder, so we need to prepare that file and try to organize things to meet good practices.

**Add `layout` HTML file.**
Working with big HTML files is not easy. To avoid scrolling up and down in one file we will create a `layout.html` page in the views folder. This file will contain the HTML footer and most important the HTML head part which will hold CDN links and CSS style. Just create `/views/layout.html` file and insert the content below:
```html
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <link rel="stylesheet" href="//cdn.componentator.com/spa.min@19.css">
    <script src="//cdn.componentator.com/spa.min@19.js"></script>

    <style>
        html,
        body {
            margin: 0;
            padding: 0;
            background-color: #f0f0f0;
        }

        .todos.panel {
            width: 700px;
            height: 600px;
            background-color: #ffffff;
            margin: 0 auto;
            padding-top: 20px;
            border-radius: var(--radius);
            box-shadow: 0 0 80px rgba(0, 0, 0, 0.1);
        }

        .flex {
            display: flex;
            justify-content: space-between;
        }

        .flex header {
            font-size: 16px;
            font-weight: bold;
            margin: 0;
        }

        .flex button {
            border: 1px solid #E0E0E0;
            border-radius: var(--radius);
            font-size: 15px;
            height: 26px;
            padding: 0 15px;
            color: #000;
            min-width: 110px;
            background-color: #fff;
            line-height: 24px;
            text-align: center !important;
        }


        .list {
            border-radius: var(--radius);
            width: 100%;
        }

        .list .header {
            font-size: 16px;
            font-weight: bold;
            text-align: center;
            margin-bottom: 20px;
        }

        .list figure {
            display: flex;
            justify-content: space-between;
            padding: 8px 12px;
            font-size: 15px;
            line-height: 2.2;
            color: #666;
            background-color: #F8F8F8;
            border-radius: var(--radius);
            margin-bottom: 5px;
        }

        .list figure:first-child {
            margin-top: 10px;
        }

        .list figure:last-child {
            margin-bottom: 0;
        }

        .list figure .chekbox {
            width: 10%;
            text-align: left;
        }

        .list figure .name {
            width: 30%;
            text-align: left;
        }

        .list figure .description {
            width: 40%;
            text-align: left;
        }

        .list figure .edit {
            width: 10%;
            text-align: right;
        }

        .list figure .remove {
            width: 10%;
            text-align: right;
        }

        .edit {
            text-align: left !important;
        }
    </style>
</head>

<body>
    <ui-component name="exec"></ui-component>
    <ui-component name="edit"></ui-component>

    @{body}
</body>

</html>
```
**Good to know:** Notice the `@{ body }` expression in the body tag. This tag is important in including the other parts of the HTML. You should keep it there always.
**Add `index.html` HTML file.**
Now we can add the index.html file in the views and the framework will include it within the layout file thanks to `@{body}` expression.

```html
<ui-component name="viewbox" config="parent:window;centered:1">
    <div class="todos panel">
        <ui-plugin path="todos" class="padding npt">
            <div>
                <div class="flex" style="padding:15px 15px 0">
                    <div class="header"><i class="ti ti-check-square mr5"></i></div>
                    <button class="exec" data-exec="?/add"><i class="ti ti-plus mr5"></i>@(Add)</button>
                </div>
                <ui-bind class="list" path="?.items" config="template">
                    <script type="text/html">
                        <div class="header">
                            <div>@(Tasks)</div>
                        </div>
                          {{ foreach m in value }}
                            <figure data-id="{{ m.id }}">
                                <div class="checkbox" data-id="{{ m.id }}">
                                    <i class="ti ti-check {{ if m.isdone }} orange {{ else }} green {{ fi }} mr5 exec" data-exec="?/check" ></i><span></span>
                                </div>
                                <div class="edit2 name " data-edit="required:1;floating:1;exec:?/savename" style="text-decoration: {{ if m.isdone }} line-through {{ else }} none {{ fi }};">{{ m.name }}</div>
                                <div class="edit2 description" data-edit="floating:1;exec:?/save" style="text-decoration: {{ if m.isdone }} line-through {{ else }} none {{ fi }};">{{ m.description }}</div>
                                <div class="exec remove" data-exec="?/remove"><i class="ti ti-trash red mr5"></i><span></span></div>
                            </figure>
                          {{ end }}
                        </script>
                </ui-bind>
            </div>
        </ui-plugin>
    </div>
</ui-component>
<ui-component name="floatinginput" path="null" config="placeholder:Type task name ..."></ui-component>

<script>
    var common = {};
    common.form = '';
    PLUGIN('todos', function (exports) {

        var getid = function (element) {
            return $(element).parent().attrd2('id');
        };

        exports.refresh = function () {
            exports.ajax('GET /todos/list/', '?.items');
        };

        exports.add = function (element) {
            var opt = {};
            opt.element = element;
            opt.callback = function (value) {
                exports.ajax('POST /todos/create', { name: value, description: 'Description', isdone: false }, function (res, err) {
                    exports.refresh();
                });
            };

            SETTER('floatinginput/show', opt);
        };


        exports.savename = function (opt) {
            var id = getid(opt.element);
            exports.ajax('PUT /todos/update/' + id, { name: opt.value }, function (res, err) {
                exports.refresh();
            });
        };

        exports.save = function (opt) {
            var id = getid(opt.element);
            var items = GET('todos.items');
            var item = items.findItem('id', id);
            console.log(item);
            exports.ajax('PUT /todos/update/' + id, { name: item.name, description: opt.value }, function (res, err) {
                exports.refresh();

            });
        };

        exports.check = function (element) {
            var id = getid(element);
            exports.ajax('GET /todos/toggle/' + id, function (res, err) {
                console.log(res);
                exports.refresh();
            });
        };

        exports.remove = function (element) {
            var id = getid(element);
            exports.ajax('DELETE /todos/remove/' + id, function (res, err) {
                exports.refresh();
            });
        };
    
        setTimeout(exports.refresh,300);
    });
</script>
```

## Testing
To test it out, we simply have to run $ node index.js in the root of the project and then open `http://localhost:5000/` in the browser. You should see a simple page as follows:

![Ui preview Gif image](https://cdn.hashnode.com/res/hashnode/image/upload/v1679528725314/2574d36f-6abc-4291-97f0-891a43891cb1.gif?auto=format,compress&gif-q=60&format=webm)

You can download the source code of this tutorial [**here**](https://github.com/Will-create/totaljs-todoapp). I will also publish a video tutorial to explain every aspect of the components and scripts used in this UI. So consider subscribing to my Youtube channel so that I will notify you whenever I publish new videos.[ **Click here to subscribe.**](https://www.youtube.com/channel/UCjLm1RgVhdMbxawSLJ5V2ng?sub_confirmation=1)

## Key concepts of the Total.js Client-side library

The client-side library of Total.js is Jcomponent. it is a complete alternative to Angular, React and Vue.js. Its installation is very simple, you just need to import the library via a CDN in the head of your HTML document:

```html
<script src="//cdn.componentator.com/spa.min@19.js"></script>
<link rel="stylesheet" href="//cdn.componentator.com/spa.min@19.css" />
```
Once you have that CDN loaded into your HTML page, you have a bunch of amazing tools such as :
- [**Client-side Routing**](https://docs.totaljs.com/components/40d06003lm51c/#977dd001ch51c)
- [**Client-side Middleware systems**](https://docs.totaljs.com/components/40d06003lm51c/#61b97002yb50c)
- [**Web components**](https://docs.totaljs.com/components/q24m001me41d/)
- [**Font Icons**](https://componentator.com/icons/)
- [**Data-binding**](https://docs.totaljs.com/components/40d04001gw51c/)
- [**Javascript prototypes**](https://docs.totaljs.com/components/40d05001pj51c/)
- [**Tangular template engine**](https://docs.totaljs.com/components/40d06002lm50c/)
- [**Etc**](https://docs.totaljs.com/components/)

You can build some nice and modern UI with this library. Last year I tested some UI interfaces with JComponent. I am not sure it is impressive because I am not a very good front-end developer but look at the potential in the following WhatsApp web clone using JComponnent.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/os1ol31vryapf8s6xei6.png)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dgq9frz9v749m6k2pvl8.png)
I hope that this tutorial has been useful. Don’t forget to like share and subscribe.
I know that I did not explain very much and that is why a video version of this tutorial is coming soon. Just subscribe to my channel by clicking [**here**](https://www.youtube.com/channel/UCjLm1RgVhdMbxawSLJ5V2ng?sub_confirmation=1). If you have some questions you can leave some comments here and/or check the Total.js Telegram community link below. Thank you!

- [Total.js Framework official website](https://www.totaljs.com/)

- [Total.js Framework documentation](https://docs.totaljs.com/)

- [Make the Total.js Platform better, support it!](https://www.totaljs.com/support/)

- [Have any questions? Contact us](https://www.totaljs.com/contact/)

- [Join Total.js Telegram community](https://t.me/totaljs)