0

Similar to MVC 3 - Ajax.BeginForm does a full post back, but I've verified that a reference to jquery.unobtrusive-ajax.min.js does exist in my page. Plus, it works great in IE 9, but in Firefox and Chrome, I get just the partial view as a full web page instead of replacing the div with the contents of that partial view.

I'm still pretty new to MVC3, so I'm sure it's something simple.

Synopsis:

Within my page, there are several different .cshtml files which become visible as users click on links. Within the EquipmentData.cshtml file, I have a dropdown which, on change, does an ajax postback to the controller to load an editor template into a div.

Within that editor template (StorageTankData.cshtml), there is a list of data and has a placeholder for a edit template for a single item (StorageTank.cshtml). StorageTank.cshtml is initialized for inserting a new one (with an id of zero); when the user clicks one of the items in the data list, we call the controller again to get the StorageTank.cshtml partial view for that specific item and replace that html into the edit div.

All of that works great. In the StorageTank.cshtml template, I have an Ajax.Begin form which posts to a different controller action to save the storage tank data, and then calls the same controller method to get the StorageTankData.cshtml partial view. The idea is that we'll refresh the list of items and reset the edit form to insert mode and replace that into the div.

That works fine in IE, but in Chrome and Firefox I get just the partial view on the page instead of the whole page with the replacement happening. I think it's because my save postback goes to a different controller method than the one which generates the entire page, but I'm not sure what to do about that.

What I see in IE is that the URL stays on "/Edit" (the action which controls the whole page) and the substitution works great. In Chrome and FF, the url changes to my "/SaveStorageTank" action instead. So, I think that explains why those browser display just the partial view when done. What I don't understand is why they don't go ahead and do the replace.

I don't see errors in either Firebug or Chrome until after the re-display. (Of course, they get tripped up on the $(document).ready... stuff, because that partial view does not have the jquery files loaded). But, maybe I just don't know where/how to look for those errors.

OK, sorry for the long story. Here's some details.

EquipmentEdit.cshtml

@model kpa.mko.bll.viewmodels.clientData.ClientDataFormViewModel

    <table id="equipment">
        <tr class="form-row">
            <td class="form-left">Forklift On Site?</td>
            <td class="form-right">
                @Html.EditorFor(x => x.ForkliftOnSite)
                @Html.ValidationMessageFor(x => x.ForkliftOnSite)
            </td>
        </tr>
        <tr class="form-row">
            <td class="form-left">Equipment</td>
            <td class="form-right">
                @Html.HiddenFor(m => m.AccountId)
                Equipment Type: <select id="ddlEquipmentType">
                    <option value="">--select one--</option>
                    @foreach (var lookupValue in Model.EquipmentTypes.OrderBy(lv => lv.ShortName))
                    {
                        <option value="@lookupValue.LookupValueId">@lookupValue.ShortName</option>
                    }
                </select>
                <div id="divEquipmentEdit" style="display: none;">
                </div>
            </td>
        </tr>
    </table>
    <script type="text/javascript">
        $(document).ready(function () {
            $("#ddlEquipmentType").change(
                function () {
                    var selection = $("#ddlEquipmentType option:selected").text();

                    if (selection == "Storage Tanks") {
                        //Try to get the partial view of the editor template                }
                        $.ajax({
                            url: '@Url.Action("LoadStorageTankDataEditor")',
                            type: "POST",
                            contentType: "application/json; charset=utf-8",
                            data: JSON.stringify({ accountId: $('#AccountId').val() }),
                            dataType: "html",
                            success: function (view) {
                                $('#divEquipmentEdit').show();
                                $('#divEquipmentEdit').html(view);
                            },
                            error: function (xhr, ajaxOptions, thrownError) {
                                alert('error = ' + thrownError);
                                alert('xhr resp text = ' + xhr.responseText);
                            }
                        });
                    } else {
                        $('#divEquipmentEdit').hide();
                    }
                }
            );
        });
    </script>

StorageTankData.cshtml

@model kpa.mko.bll.viewmodels.clientData.StorageTankData

@using kpa.mko.dal.Entities;

Existing Tanks:
<br /><br />
@Html.HiddenFor(m => m.AccountId)
@if (Model.StorageTanks.Count == 0)
{
    <div>No tanks for this account.</div>
}
else
{
    foreach (StorageTank st in Model.StorageTanks)
    {
        @st.ListDescription <a href="javascript:getEditor(@st.StorageTankID);">Edit</a><br />
    }
}

<br /><br />
<div id="divTankEditor">
</div>

<script type="text/javascript">
    $(document).ready(function () {
        getEditor(0);
    });

    function getEditor(storageTankId) {

        //Substitute in the StorageTank editor to the div above.
        $.ajax({
            url: '@Url.Action("LoadStorageTankEditor")',
            type: "POST",
            contentType: "application/json; charset=utf-8",
            data: JSON.stringify({ storageTankId: storageTankId, accountId: $('#AccountId').val() }),
            dataType: "html",
            success: function (view) {
                $('#divTankEditor').html(view);
                if (storageTankId == 0) {
                    $('#btnTankDelete').hide();
                } else {
                    $('#btnTankDelete').show();
                }
            },
            error: function (xhr, ajaxOptions, thrownError) {
                alert('error = ' + thrownError);
                alert('xhr resp text = ' + xhr.responseText);
            }
        });
    }
</script>

StorageTank.cshtml

@model kpa.mko.bll.viewmodels.clientData.StorageTankForEdit

@using kpa.mko.dal.Entities;
@using kpa.mko.bll.factories;

@using (Ajax.BeginForm("SaveStorageTank", "ClientData", new AjaxOptions { InsertionMode = InsertionMode.Replace, UpdateTargetId = "divEquipmentEdit" }))
{
    @Html.HiddenFor(st => st.TankForEdit.StorageTankID)
    <div>Tank Placement:@Html.DropDownListFor(m => m.PlacementSelected, Model.Placements)</div>
    <div>Department: @Html.DropDownListFor(m => m.DepartmentSelected, Model.Departments)</div>
    <div>Volume (gal): @Html.EditorFor(m => m.TankForEdit.VolumeInGallons)</div>
    <div>Content of Tank: @Html.DropDownListFor(m => m.StorageTankContentsSelected, Model.StorageTankContents)</div>
    <div>Secondary Containment? @Html.EditorFor(m => m.TankForEdit.HasSecondaryContainment)</div>
    <div>If so, what type: @Html.EditorFor(m => m.TankForEdit.SecondaryContainmentType)</div>
    <div>Tank Has Overfill Alarm? @Html.EditorFor(m => m.TankForEdit.HasOverfillAlarm)</div>
    <div>If so, what type: @Html.EditorFor(m => m.TankForEdit.OverfillAlarmType)</div>
    <input type="submit" value="Save Tank Data" name="submitButton" /> <input type="submit" value="Delete Tank" id="btnTankDelete" name="submitButton" /> <input type="button" value="Reset" onclick="getEditor(0); return false;" />
}

Controller method to save

public PartialViewResult SaveStorageTank(string submitButton, string accountId)
{
int acctId = int.Parse(accountId);
StorageTankForEdit stfe = new StorageTankForEdit(acctId);
if (TryUpdateModel(stfe))
{
    if (!string.IsNullOrEmpty(submitButton) && submitButton.Equals("Delete Tank"))
    {
        //Delete the tank already.
        StorageTankFactory.Delete(stfe.TankForEdit);
    }
    else
    {
        stfe.TankForEdit.DepartmentFk = int.Parse(stfe.DepartmentSelected);
        stfe.TankForEdit.Placement = (StorageTankPlacement)Enum.Parse(typeof(StorageTankPlacement), stfe.PlacementSelected);
        stfe.TankForEdit.StorageTankContentFk = int.Parse(stfe.StorageTankContentsSelected);
        //@^*#$^ NHibernate doesn't bother to get the content class upon re-display, so we don't see the content type.
        //I think it's caching this tank, so let's try populating the content here and see if it shows up.
        stfe.TankForEdit.StorageTankContent = StorageTankContentFactory.GetById(stfe.TankForEdit.StorageTankContentFk);
        StorageTankFactory.Save(stfe.TankForEdit);
    }
}

//We decided to reset to the insert state.
return LoadStorageTankDataEditor(acctId);

Controller method to get StorageTankData partial view

   public PartialViewResult LoadStorageTankDataEditor(int accountId)
    {
        StorageTankData std = new StorageTankData(accountId);

        std.StorageTanks = StorageTankFactory.GetByAccount(accountId);

        return PartialView("EditorTemplates/StorageTankData", std);
    }

Controller method to get StorageTank partial view

  public PartialViewResult LoadStorageTankEditor(int storageTankId, int accountId)
    {
        StorageTankForEdit st = new StorageTankForEdit(accountId);
        if (storageTankId > 0)
        {
            st.TankForEdit = StorageTankFactory.GetById(storageTankId);
        }
        return PartialView("EditorTemplates/StorageTank", st);
    }
Community
  • 1
  • 1
  • We only _kind of_ figured it out. We could not figure out why the form post done by the Ajax.BeginForm in StorageTank.cshtml failed to replace the partial view. – Scott Peterson Jan 10 '13 at 16:42

1 Answers1

0

We could not figure out why the form post done by the Ajax.BeginForm in StorageTank.cshtml failed to replace the partial view.

What we did was switch that to an Html.BeginForm instead, and use the old jquery ajax call to post the form to our controller. Within the success of that call, we did the substitution manually.

function save() {

//Substitute in the StorageTank editor to the div above.
$.ajax({
    url: '@Url.Action("SaveStorageTank")',
    type: "POST",
    data: $('#stform').serialize(),
    success: function (view) {
        $('#divEquipmentEdit').html(view);
    },
    error: function (xhr, ajaxOptions, thrownError) {
        alert('error = ' + thrownError);
        alert('xhr resp text = ' + xhr.responseText);
    }
});

}

So, we didn't really figure out the root problem or why the Ajax.BeginForm post wouldn't replace the partial view in Chrome and Firefox, but got it working with this approach.