Simply cannot make SignalR (asp.net mvc4) and require.js work together
Asked Answered
I

2

6

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
    });
Iroquoian answered 2/8, 2013 at 16:48 Comment(2)
Have you called MapHubs on the server? See question: #16235675, it's kind of the reverse of what you're running into but should answer your question.Frisbee
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.Iroquoian
T
11
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
});
Trenton answered 2/8, 2013 at 20:4 Comment(12)
Thanks. No dice. I think that much of it might be working. I have one more wrinkle though which I've added to my original post. I'm using this approach - Structuring scalable client side applications: (johndavidmathis.wordpress.com/2013/04/23/…). Second part in that series has me splitting my actual signalr code out into a separate Marionette chat module. I think that's where it's failing. Can't seem to get that second dependency, the autogenerated script, into that separate chat module. See my edit.Iroquoian
Regardless the client side code organization and technology you are using this should work. The tricky part here (I faced the same problem when integrating signalr) is the auto-generated file, which can be worked around by adding the ? at the end to prevent require.js to mess with it. I also tried noext plugin but I had no luck. The approach I suggested was suggested to me by another SO fellow. Require.js shims and paths are really powerful to describe dependencies between modules not written in the require.js way, and for your own modules you should be ok by just defining them properly.Trenton
I would try going from a simple scenario and introduce more complexity slowly so you get the idea about where things are failing. SignalR can work with Require.js, as a matter of fact I have both working in a project. I guess than answers your question.Trenton
Appreciate the responses. Do you have a code suggestion? I've laid my code out above. If you think it is not correct, can you be specific about what you'd change? You have SignalR and require.js working but I need SignalR, require.js and Marionette modules working together as prescribed in the links I've provided. If you've done that, could you give me some clues as to how you got the auto-generated dependency into the separate Marionette chat module? That does appear to be the precise location of the breakdown. Thanks again.Iroquoian
My bad. My dev evironment, VS 2012, was caching bad results. Kept getting undefined SignalR even after code above in place. Project clean and clear browser cache and SignalR loads. Code above is correct and works for me. My separate file containing my Marionette chat module does indeed pick up the dependency. Thanks much for the answer.Iroquoian
Actually have to amend here. Working in my dev environment, but not on my production server. Same code produces "404" there in browser console. Updated post above. Does anyone know how to get this autogenerated script thing happen on a real IIS server? How do I adjust the code above to account for the move from Visual Studio to an actual production server?Iroquoian
Try this: #8053729 If you got the setup above working, why not creating a new question instead of unaccepting this one? Not that I care too much about the points, but it was not nice.Trenton
Not meant to be not nice. The title of the post is "Simply cannot make SignalR (asp.net mvc4) and require.js work together". That is still the case. Your answer and any response is greatly appreciated. It takes time to respond. I don't know of anyone whose goal it is, however, to make Signalr work in their dev environment. I'm trying diligently to adjust your code to work. No luck. Original question still stands. If there is a way to make this work in a production environment, I've not seen it posted yet. If I find it, I will certainly post it for others. I've got lots of time in.Iroquoian
I'm pretty sure your problem is with something else in your setup or configuration. I have this approach working on production.Trenton
Final adjustment is change "signalr.hubs": "/signalr/hubs? in local dev to "signalr.hubs": "/virtualdirectory/signalr/hubs? on production server per answer 3 at #8053729. Final code above. Thanks much for your answer. I think this will help anyone trying to do this.Iroquoian
Can someone explain why a shim entry for jquery is required? If I leave it out, signalr doesn't work, if I leave it in it seems to cause problems with breeze.js loading q.js (although this may be a red herring).Evelynneven
You should use shims for scripts that do not express their dependencies via define().Trenton
V
2

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)

Venditti answered 14/5, 2015 at 21:40 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.