6
votes

I've seen similar posts around the web and nothing anyone has suggested works for me. I'm really faced with the choice of dumping one or the other it seems at this point.

This "Getting Started with SignalR and MVC 4 tutorial":

http://www.asp.net/signalr/overview/getting-started/tutorial-getting-started-with-signalr-and-mvc-4

says you need two script includes to make signalR work:

    <!--Reference the SignalR library. -->
    <script src="~/Scripts/jquery.signalR-1.0.1.js"></script>
    <!--Reference the autogenerated SignalR hub script. -->
    <script src="~/signalr/hubs"></script>

I'm at a loss as to how to make the second one, the autogenerated SignalR hub script, happen in require.js. Unless I'm missing something there just doesn't seem to be a viable require.js syntax for inclusion of autogenerated scripts. Without it you get this error at line 159 of jquery.signalR-1.1.2.js:

"JavaScript runtime error: SignalR: Error loading hubs. Ensure your hubs reference is correct, e.g. "

The code at that point in jquery.signalR is doing this:

    signalR.hub = {
            start: function () {
                // This will get replaced with the real hub connection start method when hubs is referenced correctly
                throw new Error("SignalR: Error loading hubs. Ensure your hubs reference is correct, e.g. <script src='/signalr/hubs'></script>.");
        }
    };

Has anyone actually made this autogenerated script thing happen via require.js?

Been studying this a bit more. Let me add some detail:

I'm using this approach - Structuring scalable client side applications: (http://johndavidmathis.wordpress.com/2013/04/23/structuring-scalable-client-side-applications/) to make a more scalable structure. Second part in that series "Permit modules to utilize multiple files and a logical folder structure" http://johndavidmathis.wordpress.com/2013/04/23/structuring-scalable-client-side-applications/ has me splitting my actual signalr code out into a separate Marionette chat module (separate from my main app.js file) to achieve a better file structure. I really like this approach. The rest of my project is set up like this now and it really is showing benefits when it comes to finding code. I think that extra split is where I'm stuck. Can't seem to get that second dependency, the autogenerated script, into that separate chat module file. I'm still studying this but it looks like this to me at this point. require.js gets the dependency into my Marionette app:

    require(["marionette","handlebars", "signalr", "signalr.hubs"], function (Marionette) {
        window.App = new Marionette.Application();

        App.addRegions({
            headerRegion: "#header",
            contentRegion: "#content",
            footerRegion: "#footer",
        });            

        require(["modules/main/loader", "modules/chat/loader"], function () {
            App.start();
        });
    })

If I want chat that dependency to make its way further into the app, into the chat module in another file?

Something like?

    define(dependencies,
        function () {
            App.module("ChatModule", function (ChatModule, App, Backbone, Marionette, $, _, "signalr.hubs", "signalr.hubs") {

            // SignalR Proxy created on the fly
                var chat = $.connection.chatHub;

                // Start the connection
                $.connection.hub.start();

    //more chat code...

An update:

The answer below does work in my dev environment. But it does not work when I publish the code to a real production server.

When the code is published to a real production server (IIS 6.1 on Windows Server Enterprise 2008 R2) the browser console once again shows a "404" for the autogenerated reference.

Specifically, the console shows the "?" is being added into the reference path before ".js", like this...

http://mydomain.com/myapp/Scripts/application/signalr/hubs?.js...

Tried taking the "?" out but then it removes my app name from the path, like this...

http://mydomain.com/signalr/hubs.js.

I think what would get me there is the first one, without the "?", like...

http://mydomain.com/myapp/Scripts/application/signalr/hubs.js

I'm just not seeing how to make that happen.

FINAL UPDATE:

Final piece of the puzzle for production server is the site's virtual directory. Here's final code that worked for me. Thanks Raciel R for your help:

    requirejs.config({        
        paths: {
            //core
            "jquery": "jquery-1.9.1",

            "signalr": "jquery.signalR-1.1.2",
            "signalr.hubs": "/productionservervirtualdirectory/signalr/hubs?"
        },
        shim: {
            "jquery": {exports: "$"},            
            "signalr": { deps: ["jquery"] },
            "signalr.hubs": { deps: ["signalr"] }
        });
    //Then all you have to do is to make signalr.hubs required in your modules. Ie:

    require(["signalr.hubs"], function(){
         //your code here
    });
2
Have you called MapHubs on the server? See question: stackoverflow.com/questions/16235175/…, it's kind of the reverse of what you're running into but should answer your question.N. Taylor Mullen
Yes. Did that. No dice. The problem appears to be very specifically the lack of a require.js syntax to make that second reference - the one to the autogenerated SignalR hub script. I see people making various attempts at the syntax. I've tried them all but none of them have worked for me. The one below hasn't worked for me either.Robert

2 Answers

11
votes
requirejs.config({        
    paths: {
        //core
        "jquery": "jquery-1.9.1",

        "signalr": "jquery.signalR-1.1.2",
        "signalr.hubs": "/signalr/hubs?"
    },
    shim: {
        "jquery": {exports: "$"},            
        "signalr": { deps: ["jquery"] },
        "signalr.hubs": { deps: ["signalr"] }
    });

Then all you have to do is to make signalr.hubs required in your modules. Ie:

require(["signalr.hubs"], function(){
     //your code here
});
2
votes

I set up RequireJS successfully using @raciel-r's solution but I was still having problems with other JavaScript modules like karma that were also confused by the dynamic proxy. I converted the signalr proxy to a static file and used that with RequireJS instead:

  1. Import Microsoft.AspNet.SignalR.Utils

  2. Run packages/Microsoft.AspNet.SignalR.Utils.2.X.X/tools/signalr.exe ghp /path:my/bin /o:path/to/scripts/server.js where /my/bin is the directory containing the assemblies with your SignalR Hubs.

  3. Replace your reference in to /signalr/hubs with server:

    requirejs.config({        
    paths: {
         // ...
         "signalr.hubs": "path/to/scripts/server"
    },
    //  ....
    
  4. If you are using the convenience methods of the generated proxy, you will also have to rewrite them (see How to create a physical file for the SignalR generated proxy)