Total.js tutorial: Simple Todo application (part 2)

louis_bertson_1124e9cdc59

Louis Bertson

Posted on March 19, 2023

Total.js tutorial: Simple Todo application (part 2)

Part 1 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 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

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:

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
}
Enter fullscreen mode Exit fullscreen mode

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:

<!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>
Enter fullscreen mode Exit fullscreen mode

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.

<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>
Enter fullscreen mode Exit fullscreen mode

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

You can download the source code of this tutorial here. 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.

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:

<script src="//cdn.componentator.com/spa.min@19.js"></script>
<link rel="stylesheet" href="//cdn.componentator.com/spa.min@19.css" />
Enter fullscreen mode Exit fullscreen mode

Once you have that CDN loaded into your HTML page, you have a bunch of amazing tools such as :

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

Image description
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. If you have some questions you can leave some comments here and/or check the Total.js Telegram community link below. Thank you!

💖 💪 🙅 🚩
louis_bertson_1124e9cdc59
Louis Bertson

Posted on March 19, 2023

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related