Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Defining the jQuery dependency for a module with Durandal in the HotTowel SPA

I am going round and round in circles trying to understand RequireJS and how to use it to load dependencies that DON'T show up as Undefined when I try and use them in Modules.

I started with John Papa's Pluralsight "SPA with HTML5" course. What I never understood was why there was a need to specify every library in a script tag that went into a bundle when I thought the whole point of AMD in requireJS was that you handed over responsibility to it and its data-main attribute to asynchronously load what was required. What was doubly confusing about the solution presented in that course was it didn't follow the whole data-main set up documented in the RequireJS documentation.

Move on a few months and we now have the "Hot Towel" template with Durandal, a sample "service" module, a set-up that follows the RequireJS documentation and a fairly easy to understand starter app. The only thing that sounds alarm bells is the idea that most of the libraries are in the usual "scripts" folder while Durandal is in a completely separate 'app' folder along with the application logic. If I've learnt one thing from the pluralsight SPA course and playing with requireJS it's that moving stuff out of a single folder things get very messy very quickly.

Anyway, the new template works fine. Module dependencies are working great, Views and ViewModels are binding and there's a sample logger module with a module dependency on a Durandal module (ie in the app folder) that works great. It should be simple to add another simple module that uses jQuery and mockJson based on the code that's in the Hot Towel template, right? Err, no, not really.

Here's a very simple declaration for the start of my module 'dataservice.js' inside the 'app/services' folder

define(['jquery'],
function ($) {
    var init - function....

I do of course get 'undefined' for $ when I try and access it within my code block. So after reading far too much confusing literature (much of which ends up with a comment to say 'This changed in jquery xxxx so isn't relevant any more') I have the following questions:

  1. When I monitor the network traffic I can see that the jQuery library is being downloaded correctly, as is mockJSON if I add it to the 'vendor' bundle defined in BundleConfig.cs So jQuery is there and waiting in my browser, even if I haven't given it a module definition myself. To get rid of the 'undefined' reference in my module documentation seemed to imply I need to add something like the following:

    require.config({
        paths: {
            'text': 'durandal/amd/text',
            jquery: '../Scripts/jquery-1.9.1'
        }
    });
    

    (the 'text' declaration was already there. I just added the jquery alias) This causes my module to have a function rather than 'undefined' which solves part of the problem except that it causes a second copy of the script I've already seen downloaded because of the Hot Towell templates 'vendor' bundle. I don't want to be downloading two copies of jQuery for obvious reasons so how do I fix this?

  2. I've added the mockJSON library to my 'vendor' bundle and this is normally referred to using $.mockJSON I can't do anything to make this valid. Even if I hack in a reference to the script using the same process I did for jquery with a 'require.config({paths:' declaration I'm getting undefined, not to mention the 'the library is downloaded twice' problem already mentioned. How can I get the library dependency defined so that my use of that library in a module will work?

  3. I'm guessing all this pain is something to do with the fact that requireJS is about downloading asynchronous modules but jQuery and mockjson are synchronous so I have to download them by other means (the bundling hard-coded reference) but that still means my requireJS modules need a way of stating them as dependencies and nothing I've tried is working. Is my assumption about this being a syncrhonoys/asynchronous issue correct? I had hoped to find at least one sample app in Durandal that used jQuery and ideally a jquery plug-in but all I can see is module code that uses Durandal modules rather than anything else. The fact that all these libraries are outside the main root for requireJS is probably exacerbating the problem as I'm having to resort to paths with '../' strings in them to get references. Does anybody know why this structure of 'all libraries except Durandal are in one folder, but Durandal is in a completeley separate one' exists?

  4. I understand that requireJS uses 'convention over configuration' on the define command so that if I miss out a module id then it assumes an id based on the modules source file name and path from the defined root folder specified using data-main. However Durandal refers to a module called 'entrance' in its code. This module is not in the root folder but in a folder called 'Durandal/transitions/entrance.js' so I am confused as to why any reference to it is 'entrance' rather than 'Durandal/transitions/entrance'. Where is it being aliased?

  5. Finally (hoorah) I haven't grasped the subtleties of the difference between specifying dependencies as the first argument (a string array) to a define statement vs omitting those dependencies but then in the module factory that becomes the first argument specifying something like var system = require('../system') - why would I use one form in preference to another. I see the two types being mixed across the sample application and don't understand why.

P.S.: When I edit this entry I see five questions numbered 1 to 5. When I view it I see it gets rendered as 1 and then 1 to 4. Some weird HTML editor vs rendering bug that no matter what formatting I try I haven't been able to fix, so please post any comments as if there are five questions numbered 1 to 5 rather than 2 numbered 1 and three numbered 2 to 4!)

like image 203
Ian Smith Avatar asked Oct 22 '22 16:10

Ian Smith


1 Answers

Wow that's a lot of questions!

Check out this post on the durandal google group: https://groups.google.com/forum/#!searchin/durandaljs/papa/durandaljs/Ku1gwuvqPQQ/gupgNqGIK1MJ

The interesting point from the thread is that John Papa, the author of the Hot Towel template, recommends loading all your third party libraries up front using script tags or bundles and then just using the require/AMD approach just for your own modules. Your own modules should then reference the global properties of jquery ($) and knockout (ko).

I think that if you load third party modules up front and then via require/AMD then you can get unpredictable bugs with some code using the global and some using the AMD version. (This is especially common with knockout, it seems).

The approach is not quite as architecturally clean I'll admit, but having been heads down on durandal for 5 days myself I am starting to prefer loading known third party libraries up front.

like image 166
Alexander Preston Avatar answered Oct 27 '22 10:10

Alexander Preston