0

I have a simple yet really annoying problem. I have an ASP.NET MVC3 application with Razor as view engine.

I have two scripts that I call from a partial view that work good if I put the whole code in the _layout page. If I put the scripts in external files and reference them, they do not work. How can it be? I already checked basic issues such as file location and syntax of the script tag.

Here is the JavaScript code:

$(document).ready(function () {
$('#Year').change(function () {
    var selectedYear = $(this).val();
    if (selectedYear != null && selectedYear != '') {
        $.getJSON('@Url.Action("Months", "Home")', { year: selectedYear }, function (months) {
            var monthsSelect = $('#Month');
            monthsSelect.empty();
            $.each(months, function (index, month) {
                monthsSelect.append($('<option/>', {
                    value: month.value,
                    text: month.text
                }));
            });
        });
    }
}).change();

});

And here the reference in the _Layout file:

<script src="../../Scripts/CascadeDropDownList.js" type="text/javascript"></script>

This is the action method giving back the Json result to the view:

public ActionResult Months(int year)
    {
        if (year == DateTime.Now.Year)
        {
            return Json(
                Enumerable.Range(1, (DateTime.Now.Month -1)).Select(m => new 
                {
                    value = m,
                    text = CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(m)
                }),
                JsonRequestBehavior.AllowGet
            );
        }
        return Json(
            Enumerable.Range(1, 12).Select(m => new 
            {
                value = m,
                text = CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(m) 
            }),
            JsonRequestBehavior.AllowGet
        );
    }

Stack:

[ArgumentException: Illegal characters in path.]
   System.IO.Path.CheckInvalidPathChars(String path) +126
   System.IO.Path.Combine(String path1, String path2) +38
   System.Web.Configuration.UserMapPath.GetPhysicalPathForPath(String path, VirtualDirectoryMapping mapping) +114
   System.Web.Configuration.UserMapPath.GetPathConfigFilename(String siteID, VirtualPath path, String&amp; directory, String&amp; baseName) +82
   System.Web.Configuration.UserMapPath.MapPath(String siteID, String path) +58
   System.Web.Hosting.HostingEnvironment.MapPathActual(VirtualPath virtualPath, Boolean permitNull) +301
   System.Web.Hosting.HostingEnvironment.MapPathInternal(VirtualPath virtualPath, Boolean permitNull) +51
   System.Web.CachedPathData.GetPhysicalPath(VirtualPath virtualPath) +39
   System.Web.CachedPathData.GetConfigPathData(String configPath) +704
   System.Web.CachedPathData.GetVirtualPathData(VirtualPath virtualPath, Boolean permitPathsOutsideApp) +110
   System.Web.HttpContext.GetFilePathData() +36
   System.Web.HttpContext.GetConfigurationPathData() +26
   System.Web.Configuration.RuntimeConfig.GetConfig(HttpContext context) +43
   System.Web.Configuration.CustomErrorsSection.GetSettings(HttpContext context, Boolean canThrow) +41
   System.Web.HttpResponse.ReportRuntimeError(Exception e, Boolean canThrow, Boolean localExecute) +101
   System.Web.HttpRuntime.FinishRequest(HttpWorkerRequest wr, HttpContext context, Exception e) +397
</pre></code>

Thanks in advance

2
  • 2
    What do you mean by "do not work?" Including the error and/or code would help. Commented Apr 4, 2011 at 9:13
  • It does not give any kind of error, it is just not executed. I am now trying to sort something out with Fiddler and in case post the information. Thanks for the help Commented Apr 4, 2011 at 9:31

2 Answers 2

1

Problem is that JS files are not handled by the razor parser, so THIS CODE IS RENDERED ON CLIENT, EXACTLY THIS ONE

$(document).ready(function () {
$('#Year').change(function () {
    var selectedYear = $(this).val();
    if (selectedYear != null && selectedYear != '') {
        $.getJSON('@Url.Action("Months", "Home")', { year: selectedYear }, function (months) {
            var monthsSelect = $('#Month');
            monthsSelect.empty();
            $.each(months, function (index, month) {
                monthsSelect.append($('<option/>', {
                    value: month.value,
                    text: month.text
                }));
            });
        });
    }
}).change();

Note, the request url is EXACTLY SAME ON CLIENT SIDE

@Url.Action("Months", "Home")

it is NOT transformed to real URL, because JS files are not handled by razor parser. Or, any other parser in fact. This's why, external file donesn't work. If you put it in razor view, the @Url.Action is processed by razor and it produces valid link on client side

Sign up to request clarification or add additional context in comments.

4 Comments

@Archil: thanks for your answer. This issue is getting more clear. Thanks to you I realized that none of the other js files in my application use the Url.Action method, because they do not call any action method on the controller. I also found a discussion on the web where one guy was blaming the double quotes (%20) as the reason of illegal characters in path. Is there any other way to call one action method from a javascript/jQuery ? Thanks
@Archil: do you know if this is a Razor problem or it is typical for all the ASP.NET applications? How do you usually call an action method from a javascript?
This is not problem, this's typical for MVC applications. Case is simple - if you want your code to be generated and not static, you should not write it in js files.
I'm sorry but I really think is a problem. I think that filling your _Layout page with Javascript code is not an elegant thing (not DRY at all) and it is against the principles of reusability that are the base of MVC.If I would like to use a Javascript that interacts with an action method in another application, I have to copy and paste all the js code instead of simply referencing it?
1

Never hardcode urls like this:

<script src="../../Scripts/CascadeDropDownList.js" type="text/javascript"></script>

Always use Url helpers when dealing with urls:

<script src="@Url.Content("~/Scripts/CascadeDropDownList.js")" type="text/javascript"></script>

Also make sure that your controller action doesn't throw an exception and because of the JSON part ensure it allows GET requests:

public ActionResult Months(int year) 
{
    var someModel = ...
    return Json(someModel, JsonRequestBehavior.AllowGet);
}

Finally ensure that the model returned from this action is a collection and that each element of this collection possesses text and value properties (case sensitive).

Also what are you trying to do with the .change() method call at the end of your script? If you want to populate this dropdown when the page is loaded I would more than strongly recommend you doing this on the server side (in the corresponding controller action rendering this view).

5 Comments

Dear Darin, in my application I already take care about the AllowGet in the Json request and the element passed/return to/by the action method. The script actually works if I write it directly in the _Layout page without referencing it to an external file. I have some other scripts and they work perfectly if referenced. I run Fiddler and I obtain "<title>Illegal characters in path.</title> " when calling the script
@Francesco, What script are you running? How does your controller action look like?
I attached in the main post script and action method. Thanks
@Francesco, did you try including the script as I showed in my answer? Using Url.Content helper instead of hardcoding urls?
@Dimitri: Yes I did. Actually at the beginning I was using the Url.Content to include the script and I thought this was the reason why the script was not executed. That's why I changed to the "classic" way

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.