2017-01-04 31 views
1

場景: 我有一個從ViewModel填充行的表。每行都有複選框,允許用戶檢查一個或多個行,然後從下拉菜單中選擇操作來編輯所選行的屬性。模型在郵政上沒有正確提交/綁定

一切工作正常到這一點,我可以讓ViewModel正確傳遞,然後使用它和它的所有屬性在一個POST操作方法。我可以根據用戶選擇的選項進行更改。

但是,由於下拉菜單中的某些選項會導致相當大的且不可逆轉的更改,因此我正在使用GET調用新視圖並僅填充所選行填充新表,並要求用戶確認他們想要做出改變。到目前爲止,一切都還好。新視圖按照預期方式填充,只有在前一個視圖中選擇的行。

問題: 在用戶確認他們的意圖後,使用POST調用Action方法。正確填充當前視圖的ViewModel正確地進入控制器。我得到ViewModel,但不具有與填充視圖相同的屬性。

視圖模型

public class ProjectIndexViewModel 
{   
    public List<ProjectDetailsViewModel> Projects { get; set; } 
    public string FlagFormEditProjects { get; set; } 
    public string FlagFormNewProjectStatus { get; set; } 
} 

List<ProjectDetailsViewModel> Projects是什麼是用於填充表格的行和項目什麼都沒有正確地在控制器中的POST操作方法結合。

初始視圖選中複選框的位置。請注意當選擇其中一個下拉選項時調用的JavaScript函數之一的示例,這是提交表單的原因。

@using (Html.BeginForm("EditProjectsTable", "Project", FormMethod.Get, new { name = "formEditProjects", id = "formEditProjects" })) 
{   
    @Html.HiddenFor(item => item.FlagFormEditProjects) 
    @Html.HiddenFor(item => item.FlagFormNewProjectStatus) 
    .... 
    <table> 
     <thead> 
      .... 
     </thead> 
     <tbody> 
      @for (int i = 0; i < Model.Projects.Count; i++) 
      { 
       <tr> 
        <td>@Html.DisplayFor(x => x.Projects[i].ProjectNumber)</td> 
        <td>@Html.DisplayFor(x => x.Projects[i].ProjectWorkType)</td> 
        .... // more display properties 
        <td> 
         @Html.CheckBoxFor(x => x.Projects[i].Selected, new { @class = "big-checkbox" }) 
         @Html.HiddenFor(x => x.Projects[i].ProjectModelId) 
        </td> 
       </tr> 
      }       
     </tbody> 
    </table> 
} 

function submitFormRemoveProjects() { 
    $("#FlagFormEditProjects").attr({ 
     "value": "RemoveProjects" 
    }); 
    $('#formEditProjects').submit(); 
} 

返回「確認」查看操作方法(正常工作)

[HttpGet] 
[Authorize(Roles = "Sys Admin, Account Admin, User")] 
public async Task<ActionResult> EditProjectsTable([Bind(Include = "Projects,FlagFormEditProjects,FlagformNewProjectStatus")]ProjectIndexViewModel projectIndexViewModel) 
{ 
    // Repopulate the Projects collection of ProjectIndexViewModel to 
    // include only those that have been selected 
    return View(projectIndexViewModel); 
} 

是從操作方法返回上面的視圖(正常工作)注意,操作方法得到所調用的是在Html.BeginForm調用中使用actionName變量動態設置的。

@using (Html.BeginForm(actionName, "Project", FormMethod.Post)) 
{ 
    @Html.AntiForgeryToken() 
    @Html.HiddenFor(model => model.FlagFormNewProjectStatus) 
    .... 
    <table> 
     <thead> 
      .... 
     </thead> 
     <tbody> 
      @for (int i = 0; i < Model.Projects.Count; i++) 
      { 
       <tr> 
        <td>@Html.HiddenFor(x => x.Projects[i].ProjectModelId)</td> 
        <td>@Html.DisplayFor(x => x.Projects[i].ProjectNumber)</td> 
        <td>@Html.DisplayFor(x => x.Projects[i].ProjectWorkType)</td> 
        .... // more display properties 
       </tr> 
      } 
     </tbody> 
    </table> 
    <input type="submit" value="Delete Permanently" /> 
} 

的是從該視圖稱爲控制器操作方法之一的一個例子,並且不具有相同的項目,這是在視圖中。不知何故,它有最初選擇的項目數量相同,但如果只選擇了一個,則它具有最低模型ID的項目。我不知道如何描述發生了什麼。但是總之,正確的ViewModel並沒有進入下面所示的POST方法示例。

[HttpPost] 
[ValidateAntiForgeryToken] 
[Authorize(Roles = "Sys Admin, Account Admin")] 
public async Task<ActionResult> DeleteConfirmedMultipleProjects([Bind(Include = "Projects")] ProjectIndexViewModel projectIndexViewModel) 
{ 
    if (ModelState.IsValid) 
    { 
     // Remove Projects from db and save changes 
     return RedirectToAction("../Project/Index"); 
    } 
    return new HttpStatusCodeResult(HttpStatusCode.BadRequest); 
} 

請幫忙!

+0

牆上的代碼!你只能發佈***最低可行性代碼***? – Win

+0

對不起,我把它清理了一下。 – amartin

+0

你是什麼意思,「我得到的ViewModel,但不是與填充視圖相同的屬性。」以及「DeleteConfirmedMultipleProjects」方法中的預期數據是什麼? –

回答

1

的問題是,當你從第一個視圖提交EditProjectsTable()方法,所有的表單控件的值添加到ModelState

重新填充您的ProjectDetailsViewModel集合不更新ModelState,當您返回認爲,DisplayFor()方法將顯示正確的值,因爲DisplayFor()使用模型的值,但是你

@Html.HiddenFor(x => x.Projects[i].ProjectModelId) 

將使用值爲ModelState,與所有生成表單控件的HtmlHelper方法(PasswordFor()除外)一樣。

解決此問題的一種方法是在返回EditProjectsTable()方法中的視圖之前調用ModelState.Clear()HiddenFor()方法現在將使用模型的值,因爲沒有ModelState值。

[HttpGet] 
[Authorize(Roles = "Sys Admin, Account Admin, User")] 
public async Task<ActionResult> EditProjectsTable(ProjectIndexViewModel projectIndexViewModel) 
{ 
    // Repopulate the Projects collection of ProjectIndexViewModel to 
    // include only those that have been selected 
    ModelState.Clear(); // add this 
    return View(projectIndexViewModel); 
} 

對於爲什麼這是默認的行爲說明,請參閱的this answer第二部分。

備註:您使用的是視圖模型,因此在您的方法中沒有包含[Bind]屬性的點。

+0

@stephenmueke中完成了這一點,感謝您的高度迴應。這解釋了我遇到的問題。你對TempData使用這種場景的優缺點有什麼看法? – amartin

+1

TempData可以使用,但它只支持一個請求,所以如果你使用了這個解決方案,那麼如果用戶點擊F5,所有東西都會丟失(並且用戶不知道發生了什麼)。通常,只用於非關鍵數據(例如,在重定向時從POST方法傳遞友好的成功消息) –

+1

正如附註所述,您的初始POST方法正在進行大量的數據庫調用以重新填充數據(I刪除了大部分代碼,因爲它與問題無關),並且它可能會影響性能,所以我會考慮緩存該數據(比如將其添加到'Session'中),然後再次在「EditProjectsTable」中然後在返回視圖之前將它從'Session'中移除 –

0

我覺得從這個部分COMED您的問題:

@Html.CheckBoxFor(x => x.Projects[i].Selected, new { @class = "big-checkbox" }) 
@Html.HiddenFor(x => x.Projects[i].ProjectModelId) 

我收到了這個錯誤,我所做的是增加一個布爾屬性ProjectDetailsViewModelIsSelected。 那麼你應該有:

@Html.CheckBoxFor(x => x.Projects[i].IsSelected, new { @class = "big-checkbox" }) 

然後在方法你應該增加:

foreach (var project in ProjectIndexViewModel.Projects ) 
     { 
      if (project.IsSelected==true) 
       "put your logic here" 
     } 
+0

我相信'Selected'屬性已經在初始視圖 – amartin