0

I have a question concerning updating a partial view from another partial view where the first view is contained.

I have 4 dropdowns that are populated based on the previous selections, then the user may submit their selections and a database is queried and a table is populated based on their selections. I should note that I am very new to asp.net mvc and it's all still quite confusing to me.

Below is my code:

<form action="/Home/LoadRelease" method="post" style="text-align: center;">
    @*Headers*@
    <div id="BusinessAreaLabel" class="inline" style="width:14em;">Business Area</div>
    <div id="GenericProjectLabel" class="inline" style="width:13em;">Generic Project</div>
    <div id="ProjectLabel" class="inline" style="width:17em;">Project</div>
    <div id="ReleaseLabel" class="inline" style="width:13em;">Release</div>

    <br />

    @*Dropdowns*@
    <select id="BusinessAreaDropDown" name="BusinessArea" onchange="javascript: FillGenericProject(); FillProject(); FillReleases();" style="width: 13em;">
    @Html.Partial(@"Dropdowns\_BusinessArea", Model.ProjectViewModels);
    </select>
    <select id="GenericProjectDropDown" name="GenericProject" onchange="javascript: FillProject(); FillReleases();" style="width: 13em;"></select>
    <select id="ProjectDropDown" name="Project" style="width: 17em;" onchange="javascript: FillReleases();"></select>
    <select id="ReleaseDropDown" name="Release" style="width: 13em;"></select>
    <input type="submit" id="GoButton" style="visibility:hidden;" value="Go" />

</form>
<form id="ReleaseTableBody" style="text-align:center;">
    @Html.Partial("_TableBody", Model.OpenCloseViewModels)  //I want to update this.
</form>
<br />

and Home/LoadRelease:

    [HttpPost]
    public ActionResult LoadRelease(string Project, string Release)
    {
        var ProjectID = _ProblemReportsDB.ProjectMaps
             .Where(r => r.Project == Project)
             .Select(r => r.ID).FirstOrDefault();

        ViewBag.Project = Project;

        var Releases = from row in _ProblemReportsDB.PlannedOpenCloses
                       where (row.Project == ProjectID)
                       select row;

        return PartialView("_TableBody", Releases.ToList());
    }

The above loads the partial view "_TableBody", but actually directs to the page containing only the contents of _TableBody.

Ideally, I would remain on the page displaying and only update the _TableBody section of the page. I think I understand why it is currently failing, I'm telling it to run the action /Home/LoadRelease, which returns the _TableBody partial view, which it loads.

I'm having trouble figuring out how to make it only update the _TableBody partial view.

Thanks for any help you can offer.

EDIT:

Attempting Jasens method I have begun using an ajax function: Still loads to another page instead of updating the partial:

Code:

    @using (Html.BeginForm("LoadRelease", "Home", FormMethod.Post, new { id = "DropDownForm", style="" }))
{ 
    @*Headers*@
    <div id="BusinessAreaLabel" class="inline" style="width:14em;">Business Area</div>
    <div id="GenericProjectLabel" class="inline" style="width:13em;">Generic Project</div>
    <div id="ProjectLabel" class="inline" style="width:17em;">Project</div>
    <div id="ReleaseLabel" class="inline" style="width:13em;">Release</div>

    <br />

    @*Dropdowns*@
    <select id="BusinessAreaDropDown" name="BusinessArea" onchange="javascript: FillGenericProject(); FillProject(); FillReleases();" style="width: 13em;">
        @Html.Partial(@"Dropdowns\_BusinessArea", Model.ProjectViewModels);
    </select>
    <select id="GenericProjectDropDown" name="GenericProject" onchange="javascript: FillProject(); FillReleases();" style="width: 13em;"></select>
    <select id="ProjectDropDown" name="Project" style="width: 17em;" onchange="javascript: FillReleases();"></select>
    <select id="ReleaseDropDown" name="Release" style="width: 13em;"></select>
    <button type="submit" id="GoButton" style="visibility:hidden;">Go</button>
}
@*</form>*@

<form id="ReleaseTableBody" style="text-align:center;">
    @Html.Partial("_TableBody", Model.OpenCloseViewModels)
</form>
<br />

In index: (Parent of _DropDownBody):

<script src="~/Scripts/jquery-1.10.2.js">
    $(document).ready(function () {
        $("#DropDownForm").on("submit", function (event) {
            event.preventDefault();

            var form = $(this);
            var Project = $('#ProjectDropDown').val();
            var Release = $('#ReleaseDropDown').val();
            alert(Project);
            $.ajax({
                url: form.attr("action"),
                method: form.attr("method"),
                data: form.serialize()
            })
            .done(function (result) {
                $("#ReleaseTableBody").html(result);
            });
        });

    });
</script>

Using A. Burak Erbora's method produces the same issue as well. Am I missing something?

Final edit: Jasen's answer worked and allowed me to update a partial view without redirecting. Still having issues getting the partial to show my content, but as far as the question goes - Jasen's answer works!

Aserian
  • 1,047
  • 1
  • 15
  • 31
  • So you change the drop down values, then press *Go* to submit the form, then you intend to update the partial `_TableBody`? – Jasen Oct 19 '15 at 18:21
  • A form submission will cause the browser to navigate away from the page. You'll need to trap the submission event e.g. `event.preventDefault()` so you can update the partial view. This will require AJAX to get the partial view then update the `ReleaseTableBody` form. – Jasen Oct 19 '15 at 18:31
  • @Jasen I'm very new to mvc and ajax and the like, is there any way that you can show me an example of this? – Aserian Oct 19 '15 at 18:35
  • Here's a [basic AJAX example](http://stackoverflow.com/a/19410973/2030565). You can pre-load your partial where I have `` in the answer. Give me some time to type up a more specific answer (or if I find another existing example). – Jasen Oct 19 '15 at 18:41
  • @Jasen Thanks! I have put the script in my code, but am getting an odd error, it says "$ is undefined", do I need to reference ajax somewhere? – Aserian Oct 19 '15 at 19:19
  • `$` is shorthand for `jQuery`. You'll need to include _jquery.js_ (usually in your _Layout.cshtml). – Jasen Oct 19 '15 at 19:22
  • @Jasen Thanks! compiles now, but it appears that it is never running the script. Threw an alert in the script and a breakpoint on the C# code and neither are hit.. I'll update my question with the code I have – Aserian Oct 19 '15 at 19:29
  • That's nearly it. You need to add to document ready: `$("form").on("submit", function(event) { event.preventDefault(); });`. Add an id to the form so you can specify which form to prevent this behavior. – Jasen Oct 19 '15 at 19:36
  • @Jasen It is now reloading the partial from which I am calling it (instead of the partial I am attempting to reload: $(document).ready(function () { $("#DropDownForm").on("submit", function (event) { event.preventDefault(); }); $("#GoButton").on("submit", function () { ...} – Aserian Oct 19 '15 at 19:48
  • You cannot drastically change your question making the answers and comments useless. In future, append new code you have tried or ask a new question. I have rolled back your changes. –  Oct 19 '15 at 20:36
  • @StephenMuecke Okay thanks, I'll append – Aserian Oct 19 '15 at 20:37

2 Answers2

3

Submitting a form will cause navigation. Since you want to stay on the same page you'll need to trap the submission event and use AJAX to update your page.

Main View

@using(Html.BeginForm("LoadRelease", "Home", FormMethod.Post, new { id = "DropDownForm", style = "" })
{
    <!-- your drop down inputs -->
    <button type="submit">Go</button>
}
<form id="ReleaseTableBody" style="text-align:center;">
    @Html.Partial("_TableBody", Model.OpenCloseViewModels)  //I want to update this.
</form>

Then the page script (don't forget to load jquery.js before this). Also note if you are embedding partial views you need to move this script "up" to the parent since @section will not render in partials.

<script src="jquery.js"></script>
<script>
$(document).ready(function() {
    $("#DropDownForm").on("submit", function(e) {
        // prevent default submission
        e.preventDefault();

        // do AJAX post instead
        var form = $(this);
        $.ajax({
            url: form.attr("action"),
            method: form.attr("method"),
            data: form.serialize()
        })
        .done(function(result) {
            // replace content
            $("#ReleaseTableBody").html(result);
        });
    });
}
</script>

Controller action unchanged

[HttpPost]
public ActionResult LoadRelease(string Project, string Release)
{
    // search
    return PartialView("_TableBody", results);
}
Jasen
  • 14,030
  • 3
  • 51
  • 68
  • If you'll go this way you can also use `onsubmit` as the form action, like: `@using(Html.BeginForm("LoadRelease", "Home", FormMethod.Post, new { id = "DropDownForm", style = "", onsubmit = "return SubmitFunction()" }));` then have a normal `function SubmitFunction(){...}` – A. Burak Erbora Oct 19 '15 at 20:19
  • @A.BurakErbora you still need `jQuery.ajax()` or `XMLHttpRequest` to stay on the same page. – Jasen Oct 19 '15 at 20:21
  • not necessarily, take a look at: [http://stackoverflow.com/questions/8567114/how-to-make-an-ajax-call-without-jquery](How to make an AJAX call without jQuery?). But of course using jQuery is way simpler ;-) – A. Burak Erbora Oct 19 '15 at 20:25
  • ah you mentioned XMLHttpRequest as well, nevermind ;-P – A. Burak Erbora Oct 19 '15 at 20:26
  • @jasen This *still* seems to redirect my page D: I used – Aserian Oct 19 '15 at 20:36
  • yes, that is the only external script you need. Strip everything but the one form and submit handler. You shouldn't be navigating. Then add the rest. – Jasen Oct 19 '15 at 20:43
  • Your example needs to separate the script tags – Jasen Oct 19 '15 at 20:52
  • @Jasen Ah! I see! It updated the partial view instead of redirecting, which is a huge step forward! However, the table is now just blank >_< (no headers or anything). – Aserian Oct 19 '15 at 20:55
  • @Jasen So close I can taste it... I've done an alert(result), and it is indeed returning the proper html, however it displays it as nothing (and the table no longer appears empty on first loadup). – Aserian Oct 19 '15 at 21:04
  • ok, now you need to start debugging on your own as I can't see your actual code and runtime values. You'll need to debug stop in the post action to see if you are getting the values and search results. Also, check your original model has values, or if you want it empty initially, just do `@Html.Partial("_TableBody", new OpenCloseViewModel())`. This is also staring to creep into an entirely new question so you may need to make a new post for better help. – Jasen Oct 19 '15 at 21:12
  • @Jasen thanks very much for your help! I'll mark your answer as having answered my question as it did stop from redirecting! – Aserian Oct 19 '15 at 21:15
1

First off, I'd recommend you use html helpers. What you seem to need here is an ajax call instead of a standard form post. Instead of

<form action="/Home/LoadRelease" method="post" style="text-align: center;">

you can use

@using (Ajax.BeginForm("LoadRelease", "Home", options)){

        @*Headers*@
        <div id="BusinessAreaLabel" class="inline" style="width:14em;">Business Area</div>
        <div id="GenericProjectLabel" class="inline" style="width:13em;">Generic Project</div>
        <div id="ProjectLabel" class="inline" style="width:17em;">Project</div>
        <div id="ReleaseLabel" class="inline" style="width:13em;">Release</div>

        <br />

        @*Dropdowns*@
        <select id="BusinessAreaDropDown" name="BusinessArea" onchange="javascript: FillGenericProject(); FillProject(); FillReleases();" style="width: 13em;">
        @Html.Partial(@"Dropdowns\_BusinessArea", Model.ProjectViewModels);
        </select>
        <select id="GenericProjectDropDown" name="GenericProject" onchange="javascript: FillProject(); FillReleases();" style="width: 13em;"></select>
        <select id="ProjectDropDown" name="Project" style="width: 17em;" onchange="javascript: FillReleases();"></select>
        <select id="ReleaseDropDown" name="Release" style="width: 13em;"></select>
        <input type="submit" id="GoButton" style="visibility:hidden;" value="Go" />

    }

and somewhere in your html you have:

<div id="ReleaseTableBody">
    @Html.Partial(_TableBody", Model.OpenCloseViewModels)
</div>

you will need to define the options object for the Ajax helper like:

var options = new AjaxOptions
{
    HttpMethod = "POST",
    UpdateTargetId = "ReleaseTableBody",
    OnBegin = "OnCallbackBegin",
    OnFailure = "OnCallbackFailure",
    OnSuccess = "OnCallbackSuccess",
    LoadingElementId = "loading"
};

you can also use the event callback methods if you define their JavaScript functions like:

<script>

function OnCallbackBegin() {
    $(".btn-loading-icon").show();
    $(".btn-loading-text").hide();
}
function OnCallbackSuccess(data) {
    //alert("onSuccess: result = " + data.result);
    $(".btn-loading-icon").hide();
    $(".btn-loading-text").show();
    SomeOtherFunction();
}

I would also advise using the @Html.DropdownFor helper for your dropdowns.

A. Burak Erbora
  • 1,054
  • 2
  • 12
  • 26
  • Thanks for your input... Although this seems more like formatting than a solution to the issue? Using your code produces the same error, the partial view loads as its own page instead of updating. – Aserian Oct 19 '15 at 19:18
  • 1
    This method requires the [Unobtrusive Ajax scripts](http://stackoverflow.com/questions/23895918/mvc5-ajaxhelper-and-the-correct-scripts-load-order). – Jasen Oct 19 '15 at 19:25
  • Make sure you're using the `Ajax.Beginform` helper and your `
    ` (mind you, not form but div) should be outside the scope of your ajax form. so when the form ajax posts, your div is found by the id and replaced by the partial view. If the page refreshes, you're not using ajax.
    – A. Burak Erbora Oct 19 '15 at 19:31
  • @A.BurakErbora I attempted to use the Ajax.Beginform("LoadRelease", "Home", options)) as you had suggested, then changed the form on my partial to a div, but it still sends my page to the partial itself instead of updating. Edit: The page isn't refreshing, but actually redirecting to the partial page html itself. Aka localhost/Home/LoadRelease – Aserian Oct 19 '15 at 19:39
  • @Aserian try loading the Unobtrusive Ajax scripts from nuget: type this in the nuget packet manager console: `Install-Package Microsoft.jQuery.Unobtrusive.Ajax` – A. Burak Erbora Oct 19 '15 at 19:44
  • you also need to include these scripts in your code. either bundle or directly add "~/Scripts/jquery.unobtrusive-ajax.min.js" – A. Burak Erbora Oct 19 '15 at 19:48
  • @A.BurakErbora Sorry, how do I reference scripts in mvc? Very new to this :( – Aserian Oct 19 '15 at 19:52
  • you can add this to your BundleConfig.cs: `bundles.Add(new ScriptBundle("~/Scripts/unobstrusive-ajax").Include( "~/Scripts/jquery.unobtrusive-ajax.min.js" ));` or use the good old html way: `` – A. Burak Erbora Oct 19 '15 at 19:53
  • Also, take care that you need to define `options` for that ajax helper *before* the form begins, and in razor code @{ ... } – A. Burak Erbora Oct 19 '15 at 19:57
  • @A.BurakErbora It is still redirecting me to the page itself, I will update my question with my current code – Aserian Oct 19 '15 at 20:22