3

I followed a now deleted blog (see all my code below for what I did) to create a basic RequireJS structure in MVC. While in IIS Express everything works as expected. When in IIS the core module is not found. The only difference between the two (IIS and IIS Express) is that the Map Paths shows wwwroot for IIS and the actual file path where I am developing for IIS Express. Here is the code I am referring to that builds the require script.

        if (File.Exists(helper.ViewContext.HttpContext.Server.MapPath(Path.Combine(jsLocation, module + ".js"))))
        {
            require.AppendLine("require( [ \"" + jsLocation + core + "\" ], function() {");
            require.AppendLine("    require( [ \"" + module + "\"] );");
            require.AppendLine("});");
        }

SOLUTION

Replace the above AppendLines that have dependency names with the following structure. The VirtualPathUtility is what was needed.

require.AppendLine("require( [ \"" + VirtualPathUtility.ToAbsolute("~/" + jsLocation + core) + "\" ], function() {");

END SOLUTION

Here is my file structure (root project MVCTesting)

Scripts
   require.js
   -AMD
      core.js
      -Views
         -JQuery
          AMDRequire.js

If my layout.cshtml contains the require in the following format (Note: for some reason it is allowing me to put the .js in the module name, not sure on that one).

    <script type="text/javascript">
        require( [ "/Scripts/AMD/core.js" ], function() {});
    </script>

What should the format of the dependency name ("/Scripts/AMD/core.js") be to correctly load while in MVC and using IIS? My project name is MVCTesting, so I have tried "/MVCTesting/Scripts/AMD/core.js" and various combinations with the forward slash manually without any luck.

UPDATE

Code in AMDRequire.js

alert("HELLO AMD WORLD!");

IIS Express

Full URL: http://localhost:55916/jquery/amdrequire
Project Url (Project Properties): http://localhost:55916/
Results: Displays alert message HELLO AMD WORLD!

IIS

Full URL: http://localhost/MVCTesting/jquery/amdrequire
Project URL (Project Properties): http://localhost/MVCTesting
Results: No display message, I am assuming because the js file was not file

Final Code (Note, I recommend using AngularJS over this)

    #region DOJO AMD Loader
    /// <summary>
    /// Create the javascript required to use DOJO while following AMD specifications
    /// 
    /// Just place @Html.DOJOAMDLoader() in your Layout or other cshtml that you want to use 
    /// this and it will populate the dojoConfig, the script include for dojo, and the current 
    /// page's AMD JavaScript file.
    /// </summary>
    /// <param name="helper"></param>
    /// <param name="bsndm"></param>
    /// <param name="btsRoot"></param>
    /// <returns></returns>
    public static MvcHtmlString DOJOAMDLoader(this HtmlHelper helper)
    {
        var action = helper.ViewContext.RouteData.Values["action"].ToString().ToLower();
        var controller = helper.ViewContext.RouteData.Values["controller"].ToString().ToLower();

        return helper.CreateDOJORequire(controller, action,
            BuildInitJsLogging(ConfigurationManager.AppSettings["JavascriptLoggingLevel"],
                               ConfigurationManager.AppSettings["JavascriptLoggingAllowDisplayOverride"]));
    }

    /// <summary>
    /// Kick off the custom js logging, display is handled inside the logging JS
    /// </summary>
    /// <param name="logLevel">The state of the js log (expects Off, On, File)</param>
    /// <param name="displayOverride">For failure to log to files, display to screen override</param>
    /// <returns></returns>
    private static string BuildInitJsLogging(string logLevel, string displayOverride)
    {
        //This is not used for non system pages or test pages
        if (logLevel == null)
        {
            return null;
        }

        var updateVariable = new StringBuilder();

        updateVariable.AppendLine("require(['logging', 'dojo/domReady!'], function (logging) {");
        updateVariable.AppendFormat("logging.initJsLogging('{0}','{1}');", logLevel, displayOverride);
        updateVariable.AppendLine("});");

        return updateVariable.ToString();
    }

    /// <summary>
    /// This builds the script that will be placed on the page for DOJOAMDLoader
    /// 
    /// Included scripts will be the dojoConfig, the script include for dojo, and the currnet
    /// page's AMD JavaScript file.
    /// </summary>
    /// <param name="helper"></param>
    /// <param name="controller"></param>
    /// <param name="action"></param>
    /// <returns></returns>
    private static MvcHtmlString CreateDOJORequire(this HtmlHelper helper, string controller
        , string action, string javascriptLogging)
    {
        //Don't build require string if there is not an amd script
        if (!File.Exists(helper.ViewContext.HttpContext.Server.MapPath(
            GetAbsolutePath(Path.Combine("Scripts", "AMD", "Views", controller, action + ".js")))))
        {
            return null;
        }

        //TODO:HP:At the end of the project before going live, merge these
        //into as few AppendLine statements as possible.

        var require = new StringBuilder();

        require.AppendLine("<script>");
        require.AppendLine("dojoConfig = {");
        require.AppendLine("    async: true,");
        require.AppendLine("    packages: [");
        require.AppendLine(BuildPackageScriptText("jquery", "Scripts/ThirdParty/jQuery", "jquery-1.9.1", true));
        require.AppendLine(BuildPackageScriptText("jquery-ui", "Scripts/ThirdParty/jQuery", "jquery-ui-1.10.2"));
        require.Append(BuildPackageScriptText("jquery-loadmask", "Scripts/ThirdParty/jQuery", "jquery.loadmask"));
        require.AppendLine(BuildPackageScriptText("jsfunctions", "Scripts/Common", "jsfunctions"));
        require.AppendLine(BuildPackageScriptText("logging", "Scripts/TJPackages", "JsLogging"));
        require.AppendLine(BuildPackageScriptText(action, string.Format("Scripts/AMD/Views/{0}", controller), action));
        require.AppendLine("    ]");
        require.AppendLine("};");
        require.AppendLine("</script>");
        require.AppendLine("<script src='" + GetAbsolutePath("Scripts/ThirdParty/ESRI/init.js") + "'></script>");
        require.AppendLine("<script>");
        require.AppendLine(javascriptLogging);
        require.AppendFormat("require(['{0}', 'dojo/domReady!'],function({0})", action);
        require.AppendLine("{");
        //require.AppendLine("debugger;");
        require.AppendFormat("    {0}.init();", action);
        require.AppendLine("});");
        require.AppendLine("</script>");

        return new MvcHtmlString(require.ToString());
    }

    /// <summary>
    /// Common format for packages in the dojoConfig
    /// </summary>
    /// <param name="name"></param>
    /// <param name="location"></param>
    /// <param name="main"></param>
    /// <param name="isFirst">Set to true for the first package and the comma will NOT be appended</param>
    /// <param name="isCDN">Set to true if using a CDN location for your javascript</param>
    /// <returns></returns>
    private static string BuildPackageScriptText(string name, string location, string main, bool isFirst = false, bool isCDN = false)
    {
        var sb = new StringBuilder();

        if (!isFirst)
        {
            sb.Append(",");
        }

        sb.Append("{");
        sb.AppendFormat("name: '{0}', location: '{1}', main: '{2}'", name, isCDN ? location : GetAbsolutePath(location), main);
        sb.Append("}");

        return sb.ToString();
    }
    #endregion

    /// <summary>
    /// Convert the path to work in IIS for MVC
    /// </summary>
    /// <param name="path"></param>
    /// <returns></returns>
    private static string GetAbsolutePath(string path)
    {
        return VirtualPathUtility.ToAbsolute("~/" + path);
    }
JabberwockyDecompiler
  • 3,318
  • 2
  • 42
  • 54

1 Answers1

3

I think you need to rewrite the C# code to use the Url.Content() function:

Converts a virtual (relative) path to an application absolute path.

But to be sure you should test if it does the job for applications in a subfolder (i.e. MVCTesting). In your code (e.g. the _layout file) just write:

@Url.Content("/Scripts/AMD/core.js")

On localhost the output should be something like /Scripts/AMD/core.js, but in IIS it should be /MVCTesting/Scripts/AMD/core.js. Let me know if this is the case.

Marthijn
  • 3,292
  • 2
  • 31
  • 48
  • Ahh, talk about brain fart on my part. I used something slightly different because I was using a helper to create it. I will modify the code above to have the correct answer but basically I used this answer to help (http://stackoverflow.com/questions/351937/in-asp-net-mvc-how-can-use-i-url-content-from-code) – JabberwockyDecompiler Jan 17 '14 at 17:23
  • 1
    Im glad you fixed it! :) – Marthijn Jan 17 '14 at 17:45
  • 1
    @JabberwockyDecompiler yep... this will work beautifully in IIS: `Url.Content("~/Scripts/AMD/core.js");` Note the tilde in front of everything. – Leniel Maccaferri Apr 17 '15 at 01:34
  • @LenielMacaferi Yes, you are correct, that is the link that I added in my comment for what I formatted and I should have mentioned that instead of just providing a link, basically the answer was missing the `~`. – JabberwockyDecompiler Apr 17 '15 at 14:03