Ciel
Ciel

Reputation: 4440

Creating a jQuery "object" that is available between _layout and views in ASP.NET MVC

I am coding in ASP.NET MVC 5.2, and using jQuery as my primary script library. I am having a bit of a problem though, with the disparity between _Layout and views that use that layout.

Essentially, it goes like this

  1. _Layout has some script that needs to run (initial wiring, progress bar, splash screen, etc)
  2. Inheriting View has some script that needs to run (unique to that view)
  3. _Layout has additional scripts that need to run after the view's unique scripts.

I have been trying a lot of ways to solve this, but it is actually proving to be a big problem. I have been frequently told that I should not create objects on the global namespace, so I am wondering if there are any other options to creating a script object that I can access in both views that isn't as damaging as global objects.

I have tried promises, and that is getting frustrating. I have tried events, and that doesn't really help because I cannot figure out what to attach the events to. I am told not to attach them to $(document), but that is really one of the only things that will be shared between the view and the layout.

I understand that global objects are not considered good in javascript, but at this point I'm not sure what other options I have to make sure things execute in the right order.

Update

The issue is more about "tooling" than it is about run time. It is true that when the actual view loads and runs, it is all pressed into one big happy page, and would work just fine. The issue is mostly that I have to split up the logic in the tooling (Visual Studio) to keep it from throwing errors and getting confused.

So I suppose it is more accurate to say it is a pseudo-problem.

I have attempted to split up the logic like this, but I think this is just another way of declaring a global object. I got the idea from the Q.js library.

Tasks.js

(function(definition) {
    // assign the task system
    tasks = definition();
})(function() {
    var list = [];

    function tasks() {

    };

    tasks.start = start;
    tasks.enqueue = enqueue;
    /*
     * start the task queue.
     */
    function start() {
        // make sure to raise a started event for things that need
        // to monitor it.
        $(this).trigger("started");
    };
    function enqueue(f) {
       // add the potential function to the queue to be processed later.
       list.push(f);
       $(this).trigger("enqueue", { item: f });
    };

    return tasks;

});

example usage

$(function(){
  $(tasks).on("started", function(){
    console.log("event called");
  });
  
  console.log("tasks", tasks);
  tasks.start();
});

Upvotes: 3

Views: 159

Answers (2)

StriplingWarrior
StriplingWarrior

Reputation: 156524

There are a number of ways you could go about this:

  1. Use RequireJs to define Tasks as a module, then:

    require(['tasks'], function(tasks){
      $(tasks).on("started", function(){
        console.log("event called");
      });
    
      console.log("tasks", tasks);
      tasks.start();
    });
    
  2. Use a global object, but namespace it:

    Ciel = Ciel || {};
    Ciel.tasks = Ciel.tasks || function(){
        var list = [];
        ...
    };
    
  3. Tie your data to a specific dom element:

    <div class="ciel-tasks"></div>
    
    ...
    
    $(function() { $('.ciel-tasks').each(function() {
        var tasks = $(this);
        ...
    });
    

Upvotes: 1

David
David

Reputation: 218847

It's not really clear what you're describing. From JavaScript's perspective there's no such thing as "_Layout" and "Inheriting View." There's only the resulting DOM delivered to the browser. Any JavaScript code within that DOM can operate on anything else in that DOM. So I'm not sure what any of this has to do with global namespace, events, $(document), etc. Perhaps you're overcomplicating the issue by assuming disparity between your views when, client side, no such disparity exists?

_Layout has additional scripts that need to run after the view's unique scripts.

This sounds like it's just a matter of providing callbacks for operations so that they internally execute in the correct order. For example, if the desired order is:

  • Layout executes initializeLayout()
  • View executes initializeView()
  • Layout executes completeLayout()

Then you can pass these to one another as callbacks and the functions can internally execute those callbacks. So in your Layout you might have something like this at the very top (such as in the header, as long as it's before the view is rendered):

<script type="text/javascript">
    function initializeView(){} // placeholder for view-specific initialization
</script>

Then at the bottom with the rest of your scripts:

initializeLayout();
initializeView(completeLayout);

What this does is provide your views with an opportunity to overwrite that initializeView function. If the view defines its own function called initializeView then that one will be executed instead of the placeholder one defined in the layout (remembering that the layout and the view are all one page to JavaScript).

(This also assumes you've elsewhere defined a completeLayout function, since that's what you want to execute after the view is initialized.)

Then in your view you can define that overwriting function:

function initializeView(callback) {
    // do some stuff...

    if (typeof callback == 'function') {
        callback();
    }
}

That will execute your view initialization code and then when it's complete will invoke the callback which was provided by the layout, so the layout will then execute its post-view-initialization code. (Naturally, if any of this "initialization" code is asynchronous, you'll want to invoke callbacks in response to those asynchronous callbacks, etc.)

Upvotes: 1

Related Questions