Blog post tutorial with total.js (part 2)

Blog post tutorial with total.js (part 2)

·

3 min read

Table of contents

No heading

No headings in the article.

Welcome to the second part of our tutorial series on building a CRUD (Create, Read, Update, Delete) application using Total.js. In the first part, we laid the foundation by setting up the backend functionality of our app. Now, we will focus on integrating a basic listing front-end to display the data fetched from the backend. This essential step will enhance the user experience and provide a visually appealing interface for viewing and managing the data. Let's dive in!

  • Prerequisites:

Before proceeding with this tutorial, make sure you have completed Part 1 of the series, where we set up the backend of our Total.js CRUD application. Additionally, you should have a basic understanding of HTML, CSS, and JavaScript.

  • Setting up the HTML structure

Create a new HTML file in the views folder, let's name it "index.html," and open it in your favorite text editor. Paste the following code inside.

@{layout('')}


<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8" />
        <meta http-equiv="X-UA-Compatible" content="IE=10" />
        <meta name="format-detection" content="telephone=no" />
        <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no, viewport-fit=cover" />
        <meta name="robots" content="all,follow" />
        <link rel="stylesheet" href="https://cdn.componentator.com/spa.min@19.css" />
        <script src="https://cdn.componentator.com/spa.min@19.js"></script>
        <style>
            .ui-tablegrid-bt, .ui-tablegrid-bl {
                border:0px !important;
            }
            .m { margin-bottom: 20px; }
            .bg { margin: 5px; background-color: #F0F0F0; }

        </style>

    </head>
    <body>

        <ui-plugin path="posts">
            <ui-component name="LAZY approve"></ui-component>
            <ui-component name="exec"></ui-component>
            <ui-component name="notify"></ui-component>
            <ui-component name="menu"></ui-component>

            <ui-component name="modal" path="common.form" config="if:formpost;submit:?/submit;width:800;title:Add/Edit a post" class="hidden">

                <div>
                    <span class="link cancel"><i class="ti ti-times"></i></span>
                    <label>@(Add/Edit post)</label>
                </div>
                <div class="padding npb">

                    <div class="row">
                        <div class="col-md-12 m">
                            <ui-component name="input" path="common.title" config="required:1;maxlength:148;innerlabel:1;placeholder:Enter the Title">@(Title)</ui-component>
                        </div>
                        <div class="col-md-12 m">
                            <ui-component name="input" path="common.excerpt" config="required:1;maxlength:256;type:multiline;innerlabel:1;placeholder:Enter the excerpt">@(Excerpt)</ui-component>
                        </div>
                        <div class="col-md-12 m">
                            <ui-component name="markdowneditor" path="common.content" config="markdown">@(Content)</ui-component>
                        </div>
                    </div>
                </div>
                <div>
                    <button class="link cancel">Cancel</button>
                    <button name="submit"><i class="ti ti-save"></i> Submit </button>
                </div>
            </ui-component>


            <div class="padding">

                <div class="row">
                    <div class="col-md-10 col-xs-10">

                        <h3>Posts</h3>
                    </div>

                    <div class="col-md-2 col-xs-2">
                        <ui-component name="button" config="icon:reader;exec:?/create" class="hidden"> @( Add Post )</ui-component>
                    </div>
                </div>

                <div class="row padding">

                        <ui-bind  path="items" config="template">

                        <script type="text/plain">
                            <div class="row">
                                {{ foreach m in value }}
                                    <div class="col-sm-3 m">
                                        <div class="row bg">
                                            <div class="col-sm-10">

                                                <h2>{{ m.title }}</h2>
                                                <p><i>{{ m.excerpt }}</i></p>
                                                <p> Publised on : {{ m.dtcreated | format('dd-MM-yyyy') }} </p>
                                            </div>
                                            <div class="col-sm-2">
                                                <a href="javascript:void(0)" data-id="{{m.id}}" class="menu options" style="color:black;font-size:20px"><i class="ti ti-ellipsis-v"></i></a>
                                            </div>
                                        </div>
                                    </div>
                                {{end}}
                            </div>

                            </script>
                </ui-bind>


                </div>

            </div>
        </ui-plugin>

        <script>

            var items = [];
            var common = {};

            PLUGIN('posts', function(exports) {

                DEF.api = '/api/';

                exports.reload = function() {
                    exports.refresh();
                };

                exports.refresh = function() {
                    exports.tapi('posts_list', function(response) {
                        SET('items', response.items);
                    });
                };

                exports.create = function() {
                    SET('common.form', 'formpost');
                };

                exports.submit = function(hide) {
                    var model = common;
                    delete model.form ;

                    exports.tapi((model.id ? ('posts_update/' + model.id) : 'posts_insert'), model, function(response) {
                        hide();
                        exports.refresh();
                        model.id ? SETTER('notify/success', 'Post Updated') : SETTER('notify/success', 'Post Added');
                    });
                };

            $(document).on('click', 'a.menu.options', function() {

                var opt = {};
                opt.items = ['Option', { name: 'Update', icon: 'print', value: 'update', note: 'Update content' }, { name: 'Delete', icon: 'trash', note: 'Delete that post', value: "remove" }, '-'];
                opt.align = 'center';
                opt.element = this;
                opt.callback = function(selected) {
                    var id = $(opt.element).attrd2('id');

                    switch( selected.value ) {

                        case 'remove':
                        SETTER(true, 'approve/show', '<i class="ti ti-question-circle"></i> Do you want to remove this post?', '"check-circle" OK', function() {
                            exports.tapi('posts_remove/' + id, function() {
                                exports.refresh();
                                SETTER('notify/success', 'Post Deleted successfully');

                            });
                        });
                        break;

                        case 'update':
                        exports.tapi('posts_read/'+ id, function( response ) {

                            SET('common', response);
                            SET('common.form', 'formpost');
                        });
                        break;
                    };
                };
                SETTER('menu/show', opt);
            });


                setTimeout(exports.reload, 200);

            });

        </script>
    </body>
</html>
  • Testing the implementation

Also, do not forget to map the root route in the default.js file in the controllers folder. Here is the content of the file

exports.install = function() {
    ROUTE('GET     /*');
};

In the next post we will be explaining every single line we write, so follow for more.