0

MVC 3と同様- Ajax.BeginForm は完全なポスト バックを行いますが、jquery.unobtrusive-ajax.min.js への参照がページに存在することを確認しました。さらに、IE 9 では問題なく動作しますが、Firefox と Chrome では、div をその部分ビューのコンテンツに置き換えるのではなく、部分ビューだけを完全な Web ページとして取得します。

私はまだMVC3にかなり慣れていないので、簡単なものだと確信しています。

あらすじ:

私のページ内には、ユーザーがリンクをクリックすると表示されるいくつかの異なる .cshtml ファイルがあります。EquipmentData.cshtml ファイル内にはドロップダウンがあり、変更するとコントローラーへの ajax ポストバックを実行して、エディター テンプレートを div に読み込みます。

そのエディター テンプレート (StorageTankData.cshtml) 内には、データのリストがあり、1 つの項目 (StorageTank.cshtml) の編集テンプレートのプレースホルダーがあります。StorageTank.cshtml は、新しいもの (ID がゼロ) を挿入するために初期化されます。ユーザーがデータ リスト内のアイテムの 1 つをクリックすると、コントローラーを再度呼び出して、その特定のアイテムの StorageTank.cshtml 部分ビューを取得し、その html を edit div に置き換えます。

そのすべてがうまく機能します。StorageTank.cshtml テンプレートには、Ajax.Begin フォームがあります。このフォームは、ストレージ タンク データを保存するために別のコントローラー アクションに投稿し、同じコントローラー メソッドを呼び出して StorageTankData.cshtml 部分ビューを取得します。アイデアは、アイテムのリストを更新し、編集フォームを挿入モードにリセットして、それを div に置き換えるというものです。

これは IE では正常に機能しますが、Chrome と Firefox では、ページ全体ではなく、ページの部分的なビューのみが表示され、置換が行われます。保存ポストバックが、ページ全体を生成するものとは異なるコントローラー メソッドに移動するためだと思いますが、それについてどうすればよいかわかりません

IE で見たのは、URL が "/Edit" (ページ全体を制御するアクション) のままで、置換がうまく機能していることです。Chrome と FF では、代わりに URL が「/SaveStorageTank」アクションに変わります。それで、これらのブラウザが完了時に部分的なビューだけを表示する理由を説明していると思います。私が理解できないのは、彼らが先に進んで交換を行わない理由です。

再表示するまで、Firebug または Chrome のいずれにもエラーは表示されません。(もちろん、その部分ビューにはjqueryファイルがロードされていないため、$(document).ready ...のものにつまずきます)。しかし、おそらく、これらのエラーをどこでどのように探すべきかわからないだけです。

さて、話が長くなってすみません。ここにいくつかの詳細があります。

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;" />
}

保存するコントローラーメソッド

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);

StorageTankData の部分ビューを取得するコントローラー メソッド

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

        std.StorageTanks = StorageTankFactory.GetByAccount(accountId);

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

StorageTank の部分ビューを取得するコントローラー メソッド

  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);
    }
4

1 に答える 1

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.

于 2013-01-10T16:45:25.837 に答える