表單 Taghelper大概有這幾項
- Form Tag Helper
- Input Tag Helper
- Label Tag Helper
- Select Tag Helper
- Textarea Tag Helper
- Validation Tag Helper
表單
前置作業
_layout.cshtml 加入 bootstrap 選單
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
|
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>@ViewBag.Title</title>
<environment include="Development">
<link href="~/lib/bootstrap/dist/css/bootstrap.css" rel="stylesheet"/>
</environment>
<environment exclude="Development">
<link rel="stylesheet"
integrity="ha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T"
crossorigin="anonymous"
href="https://sstackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"
asp-fallback-href="~/lib/bootstrap/dist/css/bootstrap.css"
asp-fallback-test-class="sr-only" asp-fallback-test-property="position"
asp-fallback-test-value="absolute"
asp-suppress-fallback-integrity="true"/>
</environment>
</head>
<body>
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<div class="container">
<a class="navbar-brand" href="#">Navbar</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav mr-auto">
<li class="nav-item active">
<a class="nav-link" asp-controller="home" asp-action="index">狗清單 </a>
</li>
<li class="nav-item">
<a class="nav-link" asp-controller="home" asp-action="create">新增狗</a>
</li>
</ul>
</div>
</div>
</nav>
<div class="container">
@RenderBody()
</div>
@RenderSection("Scripts",required: false)
</body>
</html>
|
重點說明
@RenderBody()
會載入當下 View 的內容回傳到這個位置。
@RenderSection("Scripts",required: false)
會載入當下 View 自定的@Section Scripts
內容,很適合載入當下頁面需要載入一些js/css,第二個參數假如當前 View 沒定義 @Section Scripts
就會報錯
上面敘述看不懂可以看一下這張圖,來源:c# - Determine what View is going to be rendered in @RenderBody() - Stack Overflow。
推薦可以看認識View - View的種類 - iT 邦幫忙::一起幫忙解決難題,拯救 IT 人的一天這篇。
新增表單
在 Controller 建立 Create 方法
1
2
3
4
|
public IActionResult Create()
{
return View();
}
|
建立 Create View
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
@model WebApplication3.Models.Dog
<form asp-controller="home" asp-action="create" method="post">
<div class="form-group row">
<label asp-for="Name" class="col-sm-2 col-form-label"></label>
<div class="col-sm-10">
<input asp-for="Name" />
</div>
</div>
<div class="form-group row">
<label asp-for="Address" class="col-sm-2 col-form-label"></label>
<div class="col-sm-10">
<input asp-for="Address" />
</div>
</div>
<div class="form-group row">
<button class="btn btn-primary">送出</button>
</div>
</form>
|
select 選單
建立 enum
WebApplication3\Models\AddressEnum.cs
1
2
3
4
5
6
7
8
9
10
|
namespace WebApplication3.Models
{
public enum AddressEnum
{
tw,
jp,
cn,
us,
}
}
|
1
2
3
4
|
<div class="form-group row">
<label asp-for="Address"class="col-sm-2 col-form-label" ></label>
<select asp-for="Address" asp-items="Html.GetEnumSelectList<AddressEnum>()"></select>
</div>
|
Html.GetEnumSelectList
可參考:HtmlHelper.GetEnumSelectList 方法 (Microsoft.AspNetCore.Mvc.ViewFeatures) | Microsoft Docs
原始碼結果
模型綁定
模型綁定是將 HTTP request 的資料映射到 Controller 操作法法上對應的參數。
操作方法中的參數可以是簡單類別,如整數、字串等,也可以是Class。
程式讀值順序:
- Form values(表單值)
- Route values (路由值)
- Query String (url參數值)
IRepository 增加 add
新增public Dog Add(Dog dog);
WebApplication3\Models\IDogRepository.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
|
using System.Collections.Generic;
namespace WebApplication3.Models
{
public interface IDogRepository
{
public Dog getDog(int id);
public IEnumerable<Dog> GetAllDogs();
public Dog Add(Dog dog);
}
}
|
新增這個方法
1
2
3
4
5
6
|
public Dog Add(Dog dog)
{
dog.Id = _dogList.Max(s => s.Id) + 1;
_dogList.Add(dog);
return dog;
}
|
完整程式碼
WebApplication3\Models\MockDogRepository.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
|
using System.Collections.Generic;
using System.Linq;
namespace WebApplication3.Models
{
public class MockDogRepository : IDogRepository
{
private List<Dog> _dogList;
public MockDogRepository()
{
_dogList = new List<Dog>()
{
new Dog(){Id=1,Name ="dog1",Address="tw"},
new Dog(){Id=2,Name ="dog2",Address="tw"},
new Dog(){Id=3,Name ="dog2",Address="tw"},
};
}
public Dog Add(Dog dog)
{
dog.Id = _dogList.Max(s => s.Id) + 1;
_dogList.Add(dog);
return dog;
}
public IEnumerable<Dog> GetAllDogs()
{
return _dogList;
}
public Dog getDog(int id)
{
return _dogList.FirstOrDefault(a => a.Id == id);
}
}
}
|
Controller 建立 Create 增加資料方法(Post)
- 添加
HttpGet
和 HttpPost
Attribute 到 Create 方法
- RedirectToActionResult 傳回放到 Post Create 方法
RedirectToAction("Details", new { id = newDog.Id });
1
2
3
4
5
6
7
8
9
10
11
12
|
[HttpGet]
public IActionResult Create()
{
return View();
}
[HttpPost]
public RedirectToActionResult Create(Dog dog)
{
Dog newDog = _dogRepostory.Add(dog);
return RedirectToAction("Details", new { id = newDog.Id });
}
|
AmbiguousActionException: Multiple actions matched.
遇到這個錯誤不用緊張,是遇到路由重複宣告。這時候標註 HttpGet
和 HttpPost
可以解決這個問題。
模型驗證
表單做簡單基本驗證。
必填(Required)
1
2
|
[Required]
public string Name { get; set; }
|
完整程式如下。
1
2
3
4
5
6
7
8
9
10
11
12
|
using System.ComponentModel.DataAnnotations;
namespace WebApplication3.Models
{
public class Dog
{
public int Id { get; set; }
[Required]
public string Name { get; set; }
public string Address { get; set; }
}
}
|
確認驗證是否正確
1
2
3
4
5
6
7
8
9
10
|
[HttpPost]
public IActionResult Create(Dog dog)
{
if (ModelState.IsValid)
{
Dog newDog = _dogRepostory.Add(dog);
return RedirectToAction("Details", new { id = newDog.Id });
}
return View();
}
|
完整寫法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
|
using Microsoft.AspNetCore.Mvc;
using WebApplication3.Models;
using WebApplication3.ViewModels;
namespace WebApplication3.Controllers
{
public class HomeController : Controller
{
private readonly IDogRepository _dogRepostory;
public HomeController(IDogRepository dogRepository)
{
_dogRepostory = dogRepository;
}
public IActionResult Index()
{
var model = _dogRepostory.GetAllDogs();
return View(model);
}
public IActionResult Details(int? id)
{
HomeDetailsViewModel homeDetailsViewModel = new HomeDetailsViewModel()
{
Dog = _dogRepostory.getDog(id??1),
PageTitle = "狗的詳情"
};
return View(homeDetailsViewModel);
}
[HttpGet]
public IActionResult Create()
{
return View();
}
[HttpPost]
public IActionResult Create(Dog dog)
{
if (ModelState.IsValid)
{
Dog newDog = _dogRepostory.Add(dog);
return RedirectToAction("Details", new { id = newDog.Id });
}
return View();
}
}
}
|
新增錯誤訊息
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
@model WebApplication3.Models.Dog
<form asp-controller="home" asp-action="create" method="post">
<div asp-validation-summary="All"></div>
<div class="form-group row">
<label asp-for="Name" class="col-sm-2 col-form-label"></label>
<div class="col-sm-10">
<input asp-for="Name" class="form-control" />
<span asp-validation-for="Name"></span>
</div>
</div>
<div class="form-group row">
<label asp-for="Address" class="col-sm-2 col-form-label" ></label>
<div class="col-sm-10">
<select asp-for="Address" asp-items="Html.GetEnumSelectList<AddressEnum>()" class="form-control" ></select>
</div>
</div>
<div class="form-group row">
<button class="btn btn-primary">送出</button>
</div>
</form>
|
重點:
<div asp-validation-summary="All"></div>
錯誤訊息全部顯示在這邊。
- 新增
<span asp-validation-for="Name"></span>
顯示單個錯誤訊息在這邊。
定義自訂錯誤訊息
WebApplication3\WebApplication3\Models\Dog.cs
1
2
3
4
5
6
7
8
9
10
11
12
|
using System.ComponentModel.DataAnnotations;
namespace WebApplication3.Models
{
public class Dog
{
public int Id { get; set; }
[Required(ErrorMessage = "請輸入姓名")]
public string Name { get; set; }
public string Address { get; set; }
}
}
|
其他驗證
1
2
3
4
5
6
7
8
9
10
11
12
|
[ValidateNever]: Indicates that a property or parameter should be excluded from validation.
[CreditCard]: Validates that the property has a credit card format. Requires jQuery Validation Additional Methods.
[Compare]: Validates that two properties in a model match.
[EmailAddress]: Validates that the property has an email format.
[Phone]: Validates that the property has a telephone number format.
[Range]: Validates that the property value falls within a specified range.
[RegularExpression]: Validates that the property value matches a specified regular expression.
[Required]: Validates that the field isn't null. See [Required] attribute for details about this attribute's behavior.
[StringLength]: Validates that a string property value doesn't exceed a specified length limit.
[Url]: Validates that the property has a URL format.
[Remote]: Validates input on the client by calling an action method on the server. See [Remote] attribute for details about this attribute's behavior.
|
可參考:Model validation in ASP.NET Core MVC | Microsoft Docs
Email驗證可以用
1
|
[RegularExpression(@"^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$", ErrorMessage = "郵箱格式不正確")]
|
可以參考:Deali-Axy/AspNetCore-Learning-Mvc: ☀AspNetCore學習筆記(Mvc篇),學生管理系統
顯示 label 自訂名稱
WebApplication3\WebApplication3\Models\Dog.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
using System.ComponentModel.DataAnnotations;
namespace WebApplication3.Models
{
public class Dog
{
public int Id { get; set; }
[Display( Name = "姓名")]
[Required(ErrorMessage = "請輸入姓名")]
public string Name { get; set; }
[Display(Name = "居住地")]
public string Address { get; set; }
}
}
|
錯誤訊息累加
可以 Attribute 寫在一起。
1
2
|
[Required(ErrorMessage = "請輸入姓名"),MaxLength(30,ErrorMessage ="姓名不能超過30個字")]
public string Name { get; set; }
|
也可以分開都能
1
2
3
4
|
[Display( Name = "姓名")]
[Required(ErrorMessage = "請輸入姓名")]
[MaxLength(30, ErrorMessage = "姓名不能超過30個字")]
public string Name { get; set; }
|
發現 .Net Core 還會好心幫你改好 html 東西。
不知道是好事還壞事XD
下拉選單文字調整
WebApplication3\WebApplication3\Models\AddressEnum.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
using System.ComponentModel.DataAnnotations;
namespace WebApplication3.Models
{
public enum AddressEnum
{
[Display(Name = "台灣")]
tw,
[Display(Name = "日本")]
jp,
[Display(Name = "中國")]
cn,
[Display(Name = "美國")]
us,
}
}
|
WebApplication3\WebApplication3\Models\Dog.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
using System.ComponentModel.DataAnnotations;
namespace WebApplication3.Models
{
public class Dog
{
public int Id { get; set; }
[Display( Name = "姓名")]
[Required(ErrorMessage = "請輸入姓名")]
[MaxLength(30, ErrorMessage = "姓名不能超過30個字")]
public string Name { get; set; }
[Display(Name = "居住地")]
public AddressEnum Address { get; set; }
}
}
|
WebApplication3\Models\MockDogRepository.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
|
using System.Collections.Generic;
using System.Linq;
namespace WebApplication3.Models
{
public class MockDogRepository : IDogRepository
{
private List<Dog> _dogList;
public MockDogRepository()
{
_dogList = new List<Dog>()
{
new Dog(){Id=1,Name ="dog1",Address=AddressEnum.tw},
new Dog(){Id=2,Name ="dog2",Address=AddressEnum.tw},
new Dog(){Id=3,Name ="dog2",Address=AddressEnum.jp},
};
}
public Dog Add(Dog dog)
{
dog.Id = _dogList.Max(s => s.Id) + 1;
_dogList.Add(dog);
return dog;
}
public IEnumerable<Dog> GetAllDogs()
{
return _dogList;
}
public Dog getDog(int id)
{
return _dogList.FirstOrDefault(a => a.Id == id);
}
}
}
|
我們這邊加個請選擇選項。
1
2
3
4
5
6
7
8
9
|
<div class="form-group row">
<label asp-for="Address" class="col-sm-2 col-form-label" ></label>
<div class="col-sm-10">
<select asp-for="Address" asp-items="Html.GetEnumSelectList<AddressEnum>()" class="form-control" >
<option value="">請選擇</option>
</select>
<span asp-validation-for="Address" class="text-danger"></span>
</div>
</div>
|
送出會遇到
解決方法,在Enum加個問號。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
using System.ComponentModel.DataAnnotations;
namespace WebApplication3.Models
{
public class Dog
{
public int Id { get; set; }
[Display( Name = "姓名")]
[Required(ErrorMessage = "請輸入姓名")]
[MaxLength(30, ErrorMessage = "姓名不能超過30個字")]
public string Name { get; set; }
[Display(Name = "居住地")]
public AddressEnum? Address { get; set; }
}
}
|
相關文章
這邊以前學 Laravel View 時候,我還記得他有方法可以控制 Boostrap 選單是不是當頁 selected 狀態,這邊原生沒有,但可以透過自己擴充來解決這個問題
1
2
3
4
|
$(document).ready(function() {
$('ul.nav.navbar-nav').find('a[href="' + location.pathname + '"]')
.closest('li').addClass('active');
});
|