3

現在、変更されたインデックスビューを使用して設定ページを作成しようとしています。目標は、ユーザーがすべての設定を表示し、1つのビュー内ですべての設定を変更し、1つのボタンを使用してすべての設定を保存できるようにすることです。設定はAjaxを使用して更新する必要があります。

私の現在のアプローチ:

意見:

<script language="javascript">
    $(function() {
        $('#editSettings').submit(function () {
            if ($(this).valid()) {
                $.ajax({
                    url: this.action,
                    type: this.method,
                    data: $(this).serialize(),
                    success: function (result)
                    {
                        alert(result);                       
                    }
                });
            }
            return false;
        });
    });
</script>

[ ... ]

@using (Ajax.BeginForm("Edit", "Settings", new AjaxOptions {UpdateTargetId = "result"}, new { @class = "form-horizontal", @id = "editSettings" } ))
{
    foreach (Setting item in ViewBag.Settings) 
    {
        @Html.Partial("_SingleSetting", item)
    }
    <input type="submit" value="modify" />
}

設定をロードするための部分ビュー:

        <div class="control-group">
            <label class="control-label">@settingName</label>
            <div class="controls">
                @Html.EditorFor(model => model.Value)
                <span class="help-inline">@settingDescription</span>
            </div>
        </div>

モデル:

[Table("Settings")]
public class Setting
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int SettingId { get; set; }

    public string Name { get; set; }

    [Required(AllowEmptyStrings = true)]
    [DisplayFormat(ConvertEmptyStringToNull = false)]
    public string Value { get; set; }
}

を使用してViewBagを設定していますViewBag.Settings = _db.Settings.ToList();

jQueryはデータを次のメソッドに解析します。

    [HttpPost]
    public ActionResult Edit(IList<Setting> setting)
    {
        Console.WriteLine(setting.Count);
        return Content(""); // Currently for testing purposes only. Breakpoint is set to setting.Count
    }

Count設定が。であるため、エラーをスローしますnull。この問題を解決する方法がよくわかりません。

誰かが私にヒントを与えることができますか?

SOに関するこのトピックでは、Ajaxを使用せずにコレクションを更新する方法についてすでに説明しています。しかし、私は要点を理解できません。

ご協力ありがとうございました。

4

1 に答える 1

13

を使用して、Ajax.BeginFormjQueryを使用してフォームをもう一度ajax化しています。それは必要ありません。しかし、コードの本当の問題は、パーシャルの入力フィールドの名前です。naming conventionリストにバインドするためにデフォルトのモデルバインダーで使用されているものを尊重していません。

完全な例を見てみましょう(簡単にするためにEntity Frameworkなどのすべてのノイズを削除します):

モデル:

public class Setting
{
    public int SettingId { get; set; }
    public string Name { get; set; }
    public string Value { get; set; }
}

コントローラ:

public class SettingsController : Controller
{
    public ActionResult Index()
    {
        // No idea why you are using ViewBag instead of view model
        // but I am really sick of repeating this so will leave it just that way
        ViewBag.Settings = Enumerable.Range(1, 5).Select(x => new Setting
        {
            SettingId = x,
            Name = "setting " + x,
            Value = "value " + x
        }).ToList();
        return View();
    }

    [HttpPost]
    public ActionResult Edit(IList<Setting> setting)
    {
        // Currently for testing purposes only. Breakpoint is set to setting.Count
        return Content(setting.Count.ToString()); 
    }
}

ビュー(~/Views/Settings/Index.cshtml):

@using (Html.BeginForm("Edit", "Settings", FormMethod.Post, new { @class = "form-horizontal", id = "editSettings" }))
{
    foreach (Setting item in ViewBag.Settings) 
    {
        @Html.Partial("_SingleSetting", item)
    }
    <input type="submit" value="modify" />
}

@section scripts {
    <script type="text/javascript">
        $('#editSettings').submit(function () {
            if ($(this).valid()) {
                $.ajax({
                    url: this.action,
                    type: this.method,
                    data: $(this).serialize(),
                    success: function (result) {
                        alert(result);
                    }
                });
            }
            return false;
        });
    </script>
}

設定部分(~/Views/Settings/_SingleSetting.cshtml):

@model Setting
@{
    var index = Guid.NewGuid().ToString();
    ViewData.TemplateInfo.HtmlFieldPrefix = "[" + index + "]";
}

<input type="hidden" name="index" value="@index" />

<div class="control-group">
    <label class="control-label">@Html.LabelFor(x => x.Name)</label>
    <div class="controls">
        @Html.EditorFor(model => model.Value)
    </div>
</div>

HTMLヘルパーが入力フィールドに適切な名前を生成し、命名規則を尊重するために、パーシャル内でHtmlFieldPrefixを変更する必要があることに注意してください。


では、 ViewCrapをカットして、適切に実行しましょう(つまり、もちろんビューモデルを使用します)。

いつものように、ビューモデルを書くことから始めます。

public class MyViewModel
{
    public IList<Setting> Settings { get; set; }
}

次に、コントローラーを適合させます。

public class SettingsController : Controller
{
    public ActionResult Index()
    {
        var model = new MyViewModel();

        // you will probably wanna call your database here to 
        // retrieve those values, but for the purpose of my example that
        // should be fine
        model.Settings = Enumerable.Range(1, 5).Select(x => new Setting
        {
            SettingId = x,
            Name = "setting " + x,
            Value = "value " + x
        }).ToList();
        return View(model);
    }

    [HttpPost]
    public ActionResult Edit(IList<Setting> setting)
    {
        // Currently for testing purposes only. Breakpoint is set to setting.Count
        return Content(setting.Count.ToString()); 
    }
}

ビュー(~/Views/Settings/Index.cshtml):

@model MyViewModel

@using (Html.BeginForm("Edit", "Settings", FormMethod.Post, new { @class = "form-horizontal", id = "editSettings" }))
{
    @Html.EditorFor(x => x.Settings)
    <input type="submit" value="modify" />
}

@section scripts {
    <script type="text/javascript">
        $('#editSettings').submit(function () {
            if ($(this).valid()) {
                $.ajax({
                    url: this.action,
                    type: this.method,
                    data: $(this).serialize(),
                    success: function (result) {
                        alert(result);
                    }
                });
            }
            return false;
        });
    </script>
}

設定モデルのエディターテンプレート(~/Views/Settings/EditorTemplates/Settings.cshtml):

@model Setting
<div class="control-group">
    <label class="control-label">@Html.LabelFor(x => x.Name)</label>
    <div class="controls">
        @Html.EditorFor(model => model.Value)
    </div>
</div>

現在、すべてが慣例により機能しています。foreachループを作成する必要はありません。インデックスビューの@Html.EditorFor(x => x.Settings)呼び出しは、ビューモデルのSettingsプロパティを分析し、それが他のモデル(Settingこの場合)のコレクションであることを検出します。そのため、このコレクションのループを開始し、このコレクション~/Views/Settings/EditorTemplates/Setting.cshtmlの各要素に対して自動的にレンダリングされる対応するエディターテンプレート()を検索します。したがって、ビューにループを記述する必要はありません。また、コードを単純化することに加えてHtml.EditorFor(x => x.Value)、エディターテンプレートのが入力フィールドの適切な名前を生成するようになりました。

于 2013-03-04T07:51:23.163 に答える