Ben Ben - 4 months ago 14
ASP.NET (C#) Question

Why is a new page loaded when I try to load a partial view?

I've written an application in ASP.NET MVC5. On one of the pages, I want the user to be able to select a book title from a drop-down list and then have another drop-down list generate when they have chosen one. The second list will contain chapters that correspond to the book that was selected. The problem is that, after the book has been selected, a new blank webpage is generated with just the new drop-down list in it.

Here is the CSHTML code that is intended to generate the two drop-down lists.

@{//Loop through the authors and find the selected one to show the details of
}
@foreach (Author nextAuthor in Model)
{
//Ignore this author if it is not the selected one
if (nextAuthor.Selected == false)
{
continue;
}

<fieldset class="EditAuthor">
<div class="EditAuthorLeft">
@{//Ajax form to select book by the selected author
}
@using (Ajax.BeginForm("SelectBook", "Library", new AjaxOptions { HttpMethod = "POST", UpdateTargetId = "chapters" }))
{
@Html.ValidationSummary(true)

<div class="editor-label">
@Html.LabelFor(model => nextAuthor.BookID)
</div>
<div class="editor-field">
@Html.DropDownList("BookID", nextAuthor.BookList, "< Please select a book >", new { onchange = "$(this.form).submit();" })
@Html.ValidationMessageFor(model => nextAuthor.BookID)
</div>
}

@{//Ajax form to submit and save changes to the author
}
@using (Ajax.BeginForm("UpdateAuthor", "Library", new AjaxOptions { HttpMethod = "POST", UpdateTargetId = "authors" }))
{
@Html.ValidationSummary(true)

@Html.HiddenFor(model => nextAuthor.ID)
@Html.HiddenFor(model => nextAuthor.LibraryID)

//Chapter selection if there is a selected book
<div id="chapters">
@if (nextAuthor.BookID > 0)
{
@Html.Partial("_ChapterDropDown", nextAuthor)
}
</div>
@:</div>

<p>
<input type="submit" value="Update Author" />
</p>
}
</fieldset>
}


And here is the code for the second drop-down list in "_ChapterDropDown.cshtml".

@Html.HiddenFor(model => model.BookID)
<div class="editor-label">
@Html.LabelFor(model => model.ChapterID)
</div>
<div class="editor-field">
@Html.DropDownListFor(model => model.ChapterID, Model.ChapterList, "< Please select a chapter >")
@Html.ValidationMessageFor(model => model.ChapterID)
</div>


And finally, here is the C# code to open the second drop-down list.

[HttpPost]
public ActionResult SelectBook(int bookId)
{
Author author;

//Create the author VM with just the info needed for the chapter partial view
//Select the list of chapters from the database that we are connected to
author = new Author();
author.BookID = bookId;
author.ChapterList = new SelectList(db.Chapters.Where(c => c.BookID == bookId).OrderBy(c => c.Name).ToList(), "ID", "Name");
author.Selected = true;

return PartialView("_ChapterDropDown", author);
}


I can't understand why this code would generate a brand new page. Why does it not add the new drop-down list to the existing view? Any help on this topic would be greatly appreciated. And by the way, I have set "UnobtrusiveJavaScriptEnabled" to true in "Web.config" and have added the following lines of code to the head of "_Layout.cshtml".

<script src="@Url.Content("~/Scripts/jquery.unobtrusive-ajax.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.unobtrusive-ajax.min.js")" type="text/javascript"></script>

Answer

In all the cases I've encoutered where @Ajax functionality was displaying a partial view on a new page the problem was always that the jquery.unobtrusive library was not correctly referenced.

To confirm that this is the problem is very simple:

1)Temporarily remove the Layout page:

@{
    Layout = null;
}

2)At the top of your child view add these CDN references:

<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.3/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/jquery.ajax.unobtrusive/3.2.4/jquery.unobtrusive-ajax.min.js"></script>

If you confirm that it's working after making the changes above you can bring back the code for referencing the master page, remove the two js references and fix your script references in the master page.Follow these rules:

  1. Firstly you should add a reference to jQuery, right at the top of the master page.
  2. Add the reference to the jquery.unobtrusive library AFTER jQuery.
  3. You should only have one reference to the jquery.unobtrusive file.Don't have a minified and a standard version as references, you only need one.
  4. Do not re-add the javascript references to the child views.You either reference the scripts in the master\layout page or in the child views, not both.