這節我們來修改更新資料的頁面
順便講到一個重要的寫法,就是ViewModel
一開始我們可以看到最右邊,按下Edit進入編輯頁面
而編輯頁面也跟新增頁面在Controller有兩個方法,至於要走哪個,跟新增頁面的觀念是一樣的
那我們就來看一下,一進入頁面前的Controller裡的程式在做什麼
public async Task<IActionResult> Edit(Guid? id)
{
if (id == null)
{
return NotFound();
}
var news = await _context.News.FindAsync(id);
if (news == null)
{
return NotFound();
}
return View(news);
}
一開始會有(Guid? id)
傳進來,那這個id的值哪來?就是從網址取得的
接著我們再繼續往下看
public async Task<IActionResult> Edit(Guid? id)
{
if (id == null)
{
return NotFound(); //如果id取不到就返回404找不到的頁面
}
var news = await _context.News.FindAsync(id); //從資料庫找符合這個id的一筆資料
if (news == null)
{
return NotFound(); //如果找不到一樣返回404找不到的頁面
}
return View(news); //找到了就把資料丟去View顯示出來
}
接著我們再來看下面的編輯頁面有些什麼問題?
簡單來說就是很多欄位不該給使用者去輸入,所以這時候Dto又派上用場了
namespace Kcg.Dtos
{
public class NewsEditDto
{
public Guid NewsId { get; set; }
public string Title { get; set; }
public string Contents { get; set; }
public int DepartmentId { get; set; }
public DateTime StartDateTime { get; set; }
public DateTime EndDateTime { get; set; }
public Boolean Enable { get; set; }
}
}
我們只撈出我們要輸入的欄位就好
public async Task<IActionResult> Edit(Guid? id)
{
if (id == null)
{
return NotFound();
}
var news = await (from a in _context.News
where a.NewsId == id
select new NewsEditDto
{
EndDateTime = a.EndDateTime,
NewsId = a.NewsId,
StartDateTime = a.StartDateTime,
Title = a.Title,
Contents = a.Contents,
DepartmentId = a.DepartmentId
}).SingleOrDefaultAsync();
if (news == null)
{
return NotFound();
}
return View(news);
}
接著再修改View的Edit的Model跟刪掉不要的欄位
@model NewsEditDto
@{
ViewData["Title"] = "Edit";
}
<h1>Edit</h1>
<h4>News</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form asp-action="Edit">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<input type="hidden" asp-for="NewsId" />
<div class="form-group">
<label asp-for="Title" class="control-label"></label>
<input asp-for="Title" class="form-control" />
<span asp-validation-for="Title" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Contents" class="control-label"></label>
<input asp-for="Contents" class="form-control" />
<span asp-validation-for="Contents" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="DepartmentId" class="control-label"></label>
<input asp-for="DepartmentId" class="form-control" />
<span asp-validation-for="DepartmentId" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="StartDateTime" class="control-label"></label>
<input asp-for="StartDateTime" class="form-control" />
<span asp-validation-for="StartDateTime" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="EndDateTime" class="control-label"></label>
<input asp-for="EndDateTime" class="form-control" />
<span asp-validation-for="EndDateTime" class="text-danger"></span>
</div>
<div class="form-group form-check">
<label class="form-check-label">
<input class="form-check-input" asp-for="Enable" /> @Html.DisplayNameFor(model => model.Enable)
</label>
</div>
<div class="form-group">
<input type="submit" value="Save" class="btn btn-primary" />
</div>
</form>
</div>
</div>
<div>
<a asp-action="Index">Back to List</a>
</div>
這樣雖然程式可以執行了,但還有個小問題
我部門的地方想要做成下拉選單,該怎麼做呢?
既然想要讓使用者可以選擇部門,那我們勢必要再撈出部門的資料往View送
但我們已經有送NewsEditDto了,要怎麼再送另外一包東西呢?用ViewData?這不會是一個好選擇,畢竟這算是頁面的重要資料之一
所以這個時候我們可以創一個ViewModel,那ViewModel是什麼呢?
其實ViewModel跟Dto一樣,沒有什麼額外的功能,只是一個帶有目的的類別
而ViewModel的目的就是,要傳到View裡的重要資料,都往這裡放的一個類別就會把它叫做ViewModel
所以第一節課用Dto傳到View是當時暫時的做法,真正的做法應該是要定義一個ViewModel然後傳值去View
那這邊我們的ViewModel要定義什麼內容呢?就是一包Dto一包部門的資料
namespace Kcg.ViewModels
{
public class NewsEditViewModel
{
public NewsEditDto News { get; set; }
public List<Department> Departments { get; set; }
}
}
再修改一下Controller的程式
public async Task<IActionResult> Edit(Guid? id)
{
if (id == null)
{
return NotFound();
}
var NewsEditViewModel = new NewsEditViewModel();
NewsEditViewModel.News = await (from a in _context.News
where a.NewsId == id
select new NewsEditDto
{
EndDateTime = a.EndDateTime,
NewsId = a.NewsId,
StartDateTime = a.StartDateTime,
Title = a.Title,
Contents = a.Contents,
DepartmentId = a.DepartmentId,
Enable = a.Enable
}).SingleOrDefaultAsync();
NewsEditViewModel.Departments = await _context.Department.ToListAsync();
if (NewsEditViewModel.News == null)
{
return NotFound();
}
return View(NewsEditViewModel);
}
html的model改成
@model NewsEditViewModel
接著我們開始做下拉選單,我這邊先不用.net core MVC的Tag helper的寫法,這以後會再教的時候再示範
先單純用HTML原生的寫法
<div class="form-group">
<label asp-for="News.DepartmentId" class="control-label"></label>
<select name="News.DepartmentId" class="form-select">
@foreach (var temp in Model.Departments)
{
<option value="@temp.DepartmentId">@temp.Name</option>
}
</select>
<span asp-validation-for="News.DepartmentId" class="text-danger"></span>
</div>
修改完後畫面如,下拉選單就出現了
那可以來修改看看了嗎?當然還不行,我們更新的程式還沒調整完
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(Guid id, NewsEditDto news)
{
if (id != news.NewsId)
{
return NotFound();
}
if (ModelState.IsValid)
{
var update = _context.News.Find(news.NewsId);
if (update != null)
{
update.Title = news.Title;
update.Contents = news.Contents;
update.DepartmentId = news.DepartmentId;
update.StartDateTime = news.StartDateTime;
update.EndDateTime = news.EndDateTime;
update.Enable = news.Enable;
update.UpdateEmployeeId = 1;
update.UpdateDateTime = DateTime.Now;
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
}
return View(news);
}
本來是無條件使用者傳什麼來就全部更新,改為特定更新部分欄位,影片會有更詳細的解說
接著我們就來試試看功能能不能正常運作,做一些小修改後按save
查看明細就說發現資料都正確的被更新了
代表我們這次的調整是沒有問題的
這邊難度開始有點提升了,越來越多觀念的綜合應用了,不懂得可能要再多看幾遍,多自己試幾遍了
這節總結
傳到View的資料往往可能不只有資料表的資料,可能還有頁面其他的資料
所以這個時候我們通常會用ViewModel把這些資料都裝進去再傳去View
而ViewModel的定義就是傳去View的Model資料我們會把它定義為ViewModel
這樣以後或者是別人在看,看到 ViewModel的命名檔案,就知道它的作用是這個
另外Bind的想法就跟新增是一樣的,將原有字串的Bind方式改為用Dto的方式進行
最後就是局部特定欄位更新的方法
範例檔:下載