javascriptjquerymodule-pattern

javascript DOM object (window) extended property is null on page reload after being idle


I have a custom extended property attached to the window object in JavaScript as follows:

Community.js

(function (window, document, $) {
    'use strict';
    var containerScrollPositionOnHideList = [];
    var scrollToTopOnShowContainerList = [];
    var userProfileInfo = {};

    window.Community = $.extend({
        //init
        init: function () {
            var Site = window.Site;
            Site.run();
            this.enableHideShowEventTrigger();
        },
    setUserInfo: function (userObj) {
            if (UtilModule.allowDebug) { debugger; }
            window.localStorage.setItem('userInfo', JSON.stringify(userObj));
            var d = new $.Deferred();
            $.when(this.initUserProfile(userObj.UserId)).done(function () {
                d.resolve("ok");
            });
        },
    getUserInfo: function () {
            var userJson = window.localStorage.getItem('userInfo');
            var userObj = JSON.parse(userJson);
            return userObj;
        },  
})(window, document, jQuery);   

The problem is that this extension property window.Community is null in certian scenarios when i refresh the page which i am going to describe below along with flow of code.

and here is a module in JavaScript to force reload scripts even if they are cached every time the page is refreshed as my code heavily depends on javascript calls so I just enabled it to make sure while I am still writing the code page reloads every time, the code is below as follows:

Util.js

var UtilModule = (function () {
    var allowDebug = false;
    var currentVersion = 0;
    var properlyLoadScript = function (scriptPath, callBackAfterLoadScript) {
        //get the number of `<script>` elements that have the correct `src` attribute
        //debugger;
        var d = $.Deferred();
        $('script').each(function () {
            console.log($(this).attr('src'));
        });
        if (typeof window.Community == 'undefined') {
            //debugger;
            console.log('community was undefined till this point');
            //the flag was not found, so the code has not run
            $.when(forceReloadScript(scriptPath)).done(function () {
                callBackAfterLoadScript();
                d.resolve("ok");
            });
        }
        else {
            console.log('Community loaded already and running now : ' + scriptPath);
            callBackAfterLoadScript();
        }
        return d.promise();
    };
    var forceReloadScript = function (scriptPath) {
        if (UtilModule.allowDebug) { debugger; }
        var d = $.Deferred();
        initCurrentVersion();
        var JSLink = scriptPath + "?version=" + currentVersion;
        var JSElement = document.createElement('script');
        JSElement.src = JSLink;
        JSElement.onload = customCallBack;
        document.getElementsByTagName('head')[0].appendChild(JSElement);
        function customCallBack() {
            d.resolve("ok");
        }
        return d.promise();
    };
    var enableDebugger = function () {
        allowDebug = true;
    };
    var disableDebugger = function () {
        allowDebug = false;
    };
    var debugBreakPoint = function () {
        if (allowDebug) {
                    }
    };
    var initCurrentVersion = function () {
        if (currentVersion == 0) {
            var dt = new Date();
            var ttime = dt.getTime();
            currentVersion = ttime;
        }
    };
    var getCurrentVersion = function () {
        return currentVersion;
    };
    return {
        forceReloadScript,
        properlyLoadScript,
        enableDebugger,
        disableDebugger,
        debugBreakPoint,
        allowDebug,
        getCurrentVersion
    };

})();

Note: I have made deferred objects to resolve only when the JSElement.onload has been called successfully. This step was taken just for testing purpose to make sure that I am not missing something before reaching a point to call the method where I am getting an error.

After that the code where I load scripts using UtilModule in my layout file look like as below:

_Layout.cshtml

<script src = "~/Scripts/Application/Modules/Util.js" ></script>
<script>

$.when(
    UtilModule.properlyLoadScript('/Scripts/Application/Community.js', () => {
    // Community.init() was supposed to be called here but i was still getting the error so i implemented this using promise that is returned from properlyLoadScript and call Community.init() further in .done callback to make sure that script is properly loading till this point.
    //window.Community.init();
    })
).done(function() {
    window.Community.init();
});
</script>
@RenderSection("scripts", required: false)

Now coming to my main file where My index file is executing having (_layout.chsmtl) as parent layout

is

Index.cshtml

@{
    ViewBag.Title = "My Blog";
    Layout = "~/Views/Shared/_Layout.cshtml";
}

<article id="BlogPage" style="margin: 5px;">
</article>
@section scripts{


  <script type="text/javascript">
    $(document).ready(function () {
      $.when(UtilModule.properlyLoadScript('/Scripts/Application/Modules/Blog.js', () => {
      })).done(function () {
        BlogModule.init();
      });
    });

    //});
  </script>
}

from what I know is that @section scripts is executed only once all the scripts in the layout page are loaded first so seems like a safe place to initialize the code which is dependent on some script in _Layout.HTML file and further enclosed with $(document).ready() for testing just to make sure that this script loads after everything else is loaded already.

Note: I am running all this code in in-cognito mode in chrome so nothing is cached while this code is running

now my Blog.js file looks like as below

var BlogModule = (function () {
    var moduleReference = this;
    var PageId = "#BlogPage ";
    var currentUser;
    var BlogPostList = [];
    var blogPostInfo = {};

    //init
    var init = function () {
        if (UtilModule.allowDebug) { debugger; }
        //This is where the problem happens
        console.log(window.Community);
        console.log(window.Community.getUserInfo());
        currentUser = window.Community.getUserInfo();
        initBlogInformation();
        //window.Community.registerModule(BlogModule);
        if (Object.keys(window.Community.getUserProfileObject()) <= 0) {
            $.when(window.Community.initUserProfile(currentUser.UserId)).then(function () {
                $.when(initBlogInformation()).done(function () {
                    //debugger;
                    console.log(BlogPostList);
                    window.WidgetManager.populateWidget(PageId, moduleReference);
                    loadBlogPostWidget();
                    loadBlogViewWidget();
                    loadBlogCommentsWidget();
                });
            });
        }
        else {
            $.when(initBlogInformation()).done(function () {
                window.WidgetManager.populateWidget(PageId, moduleReference);
                loadBlogPostWidget();
                loadBlogViewWidget();
                loadBlogCommentsWidget();
            });
        }
    };
    var loadBlogIndexMenuWidget = function () {
        if (UtilModule.allowDebug) { debugger; }
    };
    var loadBlogPostWidget = function () {
        var widgetOptions = {};
        widgetOptions.type = "BlogPostWidget";
        widgetOptions.container = PageId + "#BlogPostWidgetContainer";

        var settings = {};
        settings.UserId = 1;
        widgetOptions.settings = settings;

        window.WidgetManager.loadWidget(widgetOptions);
    }
    var loadBlogViewWidget = function () {
        var widgetOptions = {};
        widgetOptions.type = "BlogViewWidget";
        widgetOptions.container = PageId + "#BlogViewWidgetContainer";

        var settings = {};
        settings.UserId = 1;
        widgetOptions.settings = settings;

        window.WidgetManager.loadWidget(widgetOptions);
    };

    var loadBlogCommentsWidget = function () {
        var widgetOptions = {};
        widgetOptions.type = "BlogCommentsWidget";
        widgetOptions.container = PageId + "#BlogCommentsWidgetContainer";

        var settings = {};
        settings.UserId = 1;
        widgetOptions.settings = settings;

        window.WidgetManager.loadWidget(widgetOptions);
    };

    var initBlogList = function () {
        $.when(getBlogPosts()).then(function (results) {
            if (UtilModule.allowDebug) { debugger; }
            BlogPostList = results.Record;
            console.log(BlogPostList);
        });
    };
    var getBlogPosts = function () {
        if (UtilModule.allowDebug) { debugger; }
        var d = new $.Deferred();

        var uri = '/Blog/GetBlogPosts?userId=' + currentUser.UserId;

        $.post(uri).done(function (returnData) {
            if (UtilModule.allowDebug) { debugger; }
            if (returnData.Status == "OK") {
                BlogPostList = returnData.Record;
                BlogPostList.map(x => {
                    if (UtilModule.allowDebug) { debugger; }
                    x.UserName = window.Community.getUserProfileObject().UserName;
                    if (x.Comments != null) {
                        x.CommentsObject = JSON.parse(x.Comments);
                        x.CommentsCount = x.CommentsObject.length;
                    }
                });
                console.log(returnData.Record);
                d.resolve("ok");
            } else {
                window.Community.showNotification("Error", returnData.Record, "error");
                d.resolve("error");
            }

        });

        return d.promise();
    };
    var initBlogInformation = function () {
        //debugger;
        var d = $.Deferred();
        getBlogPosts().then(getBlogModelTemplate()).then(function () {
            d.resolve("ok");
        });
        return d.promise();
    };
    //Get Blog Model
    var getBlogModelTemplate = function () {
        var d = new $.Deferred();

        var uri = '/Blog/GetBlogModel';

        $.post(uri).done(function (returnData) {
            blogPostInfo = returnData.Record;
            d.resolve("ok");
        });
        return d.promise();
    };


    return {
        init: init,
    };

})();

The error I have highlighted below

so the problem is in init function of BlogModule which is BlogModule.init() the page is idle for too long and I reload it I get the following error: cannot call window.Community.getUserInfo() of undefined implying that community is undefied after couple of refreshes its fine and the issue doesn't happen unless I change reasonable portion of code for js files to be recompiled again by browser or the browser is idle for too long and I am not able to understand what is triggering this issue.

below is log from console

enter image description here

enter image description here

p.s. error occurs more repeatedly if i refresh page with f5 but happens rarely if i refresh page with ctrl + f5

Please any help would be of great value


Solution

  • Answering my own question, took a while to figure it out but it was a small mistake on my end just fixing the following function in Util.js fixed it for me

      var properlyLoadScript = function(scriptPath, callBackAfterLoadScript) {
          //get the number of `<script>` elements that have the correct `src` attribute
          //debugger;
          var d = $.Deferred();
          $('script').each(function() {
              console.log($(this).attr('src'));
          });
          if (typeof window.Community == 'undefined') {
              //debugger;
              console.log('community was undefined till this point');
              //the flag was not found, so the code has not run
              $.when(forceReloadScript('/Scripts/Application/Community.js')).done(function() {
                  //debugger;
                  $.when(forceReloadScript(scriptPath)).done(function() {
                      callBackAfterLoadScript();
                  });
                  d.resolve("ok");
              });
          } else {
              console.log('Community loaded already and running now : ' + scriptPath);
              $.when(forceReloadScript(scriptPath)).done(function() {
                  callBackAfterLoadScript();
              });
          }
          return d.promise();
      };