強く型付けされたビューからDropDownListをポストバックする

Nov 24 2020

これは、データベースからのドロップダウンリストのバインドに関する以前の質問に関連しています。ドロップダウンリストはバインドされて入力されていますが、フォームを送信すると、ブラウザで「オブジェクト参照がオブジェクトのインスタンスに設定されていません」というエラーがスローされます。

デバッグ時に、Html.DropDownListFor(...)ヘルパーメソッドのm => m.SelectedDepartmentパラメーターにクイックウォッチを追加し、「ラムダ式をデリゲート型ではないため、型「オブジェクト」に変換できません」を取得しました。 。

見る:

@model BudgetDemo.Models.BudgetsActualsViewModel
@using (Html.BeginForm("GetBudgetsActuals", "BudgetsActuals", FormMethod.Post))
{
    @Html.DropDownListFor(m => m.SelectedDepartment, Model.Departments, 
        "Select Department", new { @class = "form-control" })
}

ViewModel:

public class BudgetsActualsViewModel
{
    [Display(Name = "Cost Center/Department")]
    [Required(ErrorMessage = "Cost Center/Department is required.")]
    [StringLength(62)]
    public string SelectedDepartment { get; set; }
    public List<SelectListItem> Departments { get; set; }
}

コントローラ:

// GET: Render view with dropdowns
public ActionResult GetBudgetsActuals()
{
    repo = new BudgetDemoRepository();
    ModelState.Clear();

    try
    {                
        return View(repo.GetBudgetsActuals());
    }
    catch
    {
        return View("Error");
    }
}

リポジトリ:

public BudgetsActualsViewModel GetBudgetsActuals()
{
    ...
    BudgetsActualsViewModel budgetsActuals = new BudgetsActualsViewModel() 
        { Departments = new List<SelectListItem>() };
    
    // Query returning correct data from DB here

    for (int i = 0; i < ds.Tables[0].Rows.Count; i++)
    {
        budgetsActuals.Departments.Add(                    
            new SelectListItem
            {
                Text = ds.Tables[0].Rows[i]["Department"].ToString(),
                Value = ds.Tables[0].Rows[i]["ListValue"].ToString()
            }
        );
    }

    return budgetsActuals;
}

更新

現在動作しています。次のコードが更新/追加され、選択した部門の値を含む成功メッセージを含むポストバック後にフォームが再レンダリングされるようになりました。

見る:

@model BudgetDemo.Models.BudgetsActualsViewModel
@Html.AntiForgeryToken()

@using (Html.BeginForm("GetBudgetsActuals", "BudgetsActuals",  
    FormMethod.Post))
{
     @Html.ValidationSummary(true, "", new { @class = "text-danger" })

     @if (TempData["SuccessMessage"] != null)
     {
         <p class="alert alert-success"
             id="successMessage">@TempData["SuccessMessage"]</p>
     }

    @Html.DropDownListFor(m => m.SelectedDepartment, Model.Departments, 
        "Select Department", new { @class = "form-control" })
    @Html.ValidationMessageFor(model => model.SelectedDepartment, "", 
        new { @class = "text-danger" })    
}

コントローラー:

// GET
public ActionResult GetBudgetsActuals()
{
    Populate Department dropdown from DB and Year dropdown (static) here
    repo = new BudgetDemoRepository();
    //ModelState.Clear();

    try
    {
        return View(repo.GetBudgetsActuals());
    }
    catch
    {
        return View("Error");
    }
}

// POST
[HttpPost]
public ActionResult GetBudgetsActuals(BudgetsActualsViewModel model)
{
    try
    {
        repo = new BudgetDemoRepository();
        if (ModelState.IsValid)
        {
            TempData["SuccessMessage"] = "Value of SelectedDepartment is: " 
                + model.SelectedDepartment;
            return View(repo.GetBudgetsActuals(model));
        }
        else
        {
            model.Departments = repo.GetBudgetsActuals().Departments;
            return View(model);
        }
    }
    catch
    {
        return View("Error");
    }
}

リポジトリ-POSTを処理するための新しいオーバーロードされたメソッド

public BudgetsActualsViewModel GetBudgetsActuals(BudgetsActualsViewModel model)
{
    ....
    // Get the data again
    ....
 
    budgetsActuals.SelectedDepartment = model.SelectedDepartment;
    for (int i = 0; i < ds.Tables[0].Rows.Count; i++)
    {
        budgetsActuals.Departments.Add(
            new SelectListItem
            {
                Text = ds.Tables[0].Rows[i]["Department"].ToString(),
                Value = ds.Tables[0].Rows[i]["ListValue"].ToString()
            }
         );
     }   
     return budgetsActuals;
}

回答

1 JamshaidK. Nov 24 2020 at 15:58

を変更controllerして、POST呼び出しをサポートする新しいAPIを作成します。次のようにします。

次のように、ビューがロードされたときに呼び出されるgetメソッドをこのメソッドに追加します。

[HttpGet]
public ActionResult GetBudgetsActuals()
{
    repo = new BudgetDemoRepository();
    ModelState.Clear();

    try
    {                
        return View(repo.GetBudgetsActuals());
    }
    catch
    {
        return View("Error");
    }
}

フォームの送信時に呼び出されるpostメソッドを追加します。

[HttpPost] // Decorate with this attribute
public ActionResult PostBudgetsActuals(BudgetDemo.Models.BudgetsActualsViewModel model)
{
    var selectedDepartment = model.SelectedDepartment;
   // dostuff with the model
   return somethingFromHere;
}

そして、次のようにビューを変更します。

@model BudgetDemo.Models.BudgetsActualsViewModel
@using (Html.BeginForm("PostBudgetsActuals", "BudgetsActuals", FormMethod.Post))
{
    @Html.DropDownListFor(m => m.SelectedDepartment, Model.Departments, 
        "Select Department", new { @class = "form-control" })
}

DropDownListForメソッドのオーバーロードで問題が発生した場合は、次のDropDownListようなメソッドを試すこともできます。

@using (Html.BeginForm("PostBudgetsActuals", "BudgetsActuals", FormMethod.Post))
{
    @Html.DropDownList("SelectedDepartment", Model.Departments,
        "Select Department", new { @class = "form-control" })
}

更新

// POST
[HttpPost]
public ActionResult GetBudgetsActuals(BudgetsActualsViewModel model)
{
    try
    {
        repo = new BudgetDemoRepository();
        if (model.SelectedDepartment != null)
        { 
            TempData["SuccessMessage"] = "Value of SelectedDepartment is:
                " + model.SelectedDepartment;
        }
        model.Departments = repo.GetBudgetsActuals().Departments;
        return View(model);
     }
     catch
     {
         return View("Error");
     }
}