Javascript module initialization

Today we gonna work with javascript modules. What is it and why do we need to use this approach.

Nothing special – it’s logically separated pieces of your javascript. Wait but I can add my javascript to views where they need it and it will be separated from other views where it’s useless. I don’t have so much JS in my app.

php and js spaghetti

You know… it sucks… it looks like spaghetti code. You mix javascript and php.  And say very big “thank you” for PhpStorm that allows you to separate and highlight javascript.

Ok, so let’s try to separate view from javascript. From the example below I still need to get some data from backend side($typeLink). Sometimes we need to pass some constant or URL. I’d suggest to forget about passing that from backend side. I’m a great supporter of “API First” approach. After my openmed project where I did only the API and we have separate frontend web application that interacted with API I must admin it’s extremely cool. You don’t need to take care about view you are concentrated only on logic. But this is another story. Perhaps I’ll write about that later. Ok we digress… so my piece of advise – try to separate js from your app as much as possible, try to make it independent.

At this point we can move all our javascript code to js files and include all of them. But here we face with some problems. First your javascript still is absolutely unstructured. The increasing of javascript will exacerbate this problem. So let’s add some logic to our javascript put logic related to posts to javascript post module.

Yii2 comes with updated javasctipt module structure. Check it out  read comments. You can use this approach for your own app.

// initially you create your main application module that will
// load all his child modules
myapp = (function ($) {
    'use strict';

    var pub = {
        initModule: function (module) {
            if (module.isActive === undefined || module.isActive) {
                if ($.isFunction(module.init)) {
                    module.init();
                }
                $.each(module, function () {
                    if ($.isPlainObject(this)) {
                        pub.initModule(this);
                    }
                });
            }
        },
        init: function () {
            // common for all you project javascript logic
            initLayoutComponents();
            // whatever you want
        }
    };
    // define private functions
    function initLayoutComponents() {
    }

    return pub;
})(jQuery);

jQuery(document).ready(function () {
    myapp.initModule(myapp);
});

// in other files you can define child modules
if (typeof myapp == "undefined" || !myapp) {
    var myapp = {};
}

myapp.post = (function ($) {

    var pub = {
        isActive: true,
        init: function () {
            initPostComments()
        },
    }

    function initPostComments() {
    }
    return pub;
})(jQuery);

OK now we have structured javascript. That located separately from our views. That a good point. But there other issue that we have is – have we gonna include this files.

  • We can include all javascript or even better concatenate it in one file and include once. It will be cached and work much faster. Did you notice isActive param for javasctipt modules. if all modules have it equals true it means that we gonna execute all our javascript. And in most cases we don’t need to do that. Module could init components that currently not in our page. Some page has map, some has comments widget etc. So we need to take care about dynamic module initialization.
  • OR we need to include specific javascript module (or file) for specific page. But this approach even worse since you still need to take care about dynamic includes but lost all profits from caching.

Then I’ll tell you about 2 approaches how you could do that. The easiest one is  just include what you need in your views.

$this->registerJs('myapp.post.isActive = true;', $this::POS_END);
$this->registerJs('myapp.author.isActive = true;', $this::POS_END);

The advantage of such approach that you have full control on what do you include. But I’m pretty sure that in most cases you don’t need such level of control. Using convention over configuration principle you can adjust that for all you controller you’ll have specific module for all contoler’s action you have the function inside your module or another module like controller_action.js. Using this assertion you can easily add some helper for yii and initiate modules base on current route.

Finally for a change I will add one more example. Where I will initiate module base on data-attributes of body. I have rails project so you’ll have coffee script 😉

###

###


'use strict'

appRunner =
  exec: (controller, action = "init") ->
    ns = window.app

    if controller != "" && ns[controller] && typeof ns[controller][action] == "function"
      ns[controller][action]()

  init: ()  ->
    body = document.body
    controller = body.getAttribute( "data-controller" )
    action = body.getAttribute( "data-action" )

    appRunner.exec("common")
    appRunner.exec(controller)
    appRunner.exec(controller, action)

window.app.common =
  init: () ->


$(document).ready(()->
  appRunner.init()
)

As you can see here you have some common for your app logic and module with methods per your actions.

Resume: don’t mix js and PHP/Ruby/Whatever. Keep your code clean. Thanks.

Leave a Reply

Your email address will not be published. Required fields are marked *