TOC

This article is currently in the process of being translated into Vietnamese (~90% done).

Tag Helpers:

The Select Tag Helper

Tag Helper Select cho phép ta tạo ra element SELECT trong HTML dựa vào dữ liệu. Đây là một trong những phần nâng cao của Tag Helper. Ví dụ trong phần này sẽ phức tạp hơn những phần trước.

Đầu tiên, hãy xem element SELECT làm gì. Đây là ví dụ về HTML:

<select>
    <option value="1">The Godfather</option>
    <option value="2">Forrest Gump</option>
    <option value="3">Fight Club</option>
</select>

Trong trình duyệt, nó có dạng như sau:

Bạn có thể thấy, element SELECT sẽ trả về một hay nhiều lựa chọn trong danh sách (mặc định là dropdown list), cho phép bạn chọn một trong số. Các lựa chọn được liệt kê bởi element OPTION và mỗi trong số chúng có một giá trị (ví dụ một ID) cũng như là nhãn hiển thị (trong ví dụ là tên phim).

Tuy nhiên, không cần viết mã cho toàn bộ thẻ OPTION - chúng ta có thể sinh chúng tự động dựa trên dữ liệu (thường là cơ sở dữ liệu) với Tag Helper Select.

The for and items attributes

Như nhiều Tag Helper khác, Tag Helper Select có thuộc tính asp-for, cho phép bạn liên kết với dữ liệu từ Model. Thêm nữa, thuộc tính asp-items cho phép bạn lấy dữ liệu từ nguồn cho element SELECT

Thuộc tính asp-for liên kết với một thuộc tính trong Model và asp-items cần một danh sách các lựa chọn có thể có. Trong khi cả asp-for và asp-items có thể tới từ một Model thì thông dụng ta có thể tạo ra một ViewModel nhất định cho mục đích này, nó sẽ kết hợp Model bạn đang làm việc và danh sách các option có thể. Ví dụ ta có một FORM để cập nhật người dùng với các dữ liệu như tên, email ... và hơn nữa có thể chọn đất nước mà họ đang sinh sống. Danh sách đất nước này có thể tới từ nguồn khác, ví dụ từ cơ sở dữ liệu. Bạn có thể tạo ViewModel như sau:

public class EditUserViewModel
{
	public WebUser User { get; set; }
	
	public List<string> Countries { get; set; }
}

Lớp WebUser như sau:

public class WebUser
{		
	public string FirstName { get; set; }

	public string LastName { get; set; }

	public string Country { get; set; }
}

Bây giờ khi bạn muốn hiển thị View Edit User, bạn có thể tạo ViewModel và truyền vào đối tượng WebUser và danh sách đất nước. Rõ ràng, cả hai được lấy từ một nơi nào đó ví dụ cơ sở dữ liệu nhưng để minh họa, tôi chỉ đơn giản tạo như sau trong Controller:

public IActionResult EditUser()
{
	EditUserViewModel viewModel = new EditUserViewModel();
	viewModel.User = new WebUser()
	{
		FirstName = "John",
		LastName = "Doe",
		Country = "USA"
	};
	viewModel.Countries = new List<string>()
	{
		"USA",
		"Great Britain",
		"Germany"
	};
	return View(viewModel);
}

Chúng ta có View dùng Tag Helper như sau:

@model HelloMVCWorld.ViewModels.EditUserViewModel

<form asp-action="UpdateUser" method="post">
    <input asp-for="User.FirstName" placeholder="First name" />
    <input asp-for="User.LastName" placeholder="Last name" />

    <select asp-items="@(new SelectList(Model.Countries))" asp-for="User.Country"></select>
    
    <input type="submit" value="Save" />
</form>

Cần chú ý thẻ SELECT tại đây, tôi dùng Select Tag Helper để sinh ra dropdown list. Chú ý tới asp-items, tôi tạo một SelectList dựa trên danh sách quốc gia. Lí do là asp-items mong đợt danh sách được cung cấp chứa các thể hiện của lớp SelectItem - phiên bản này của hàm khởi tạo SelectList tự động tạo ra danh sách cho chúng ta dựa trên nguồn đã có. Mã HTML có dạng như sau:

<select id="User_Country" name="User.Country">
	<option selected="selected">USA</option>
	<option>Great Britain</option>
	<option>Germany</option>
</select>

Chú ý USA được chọn trước (với thuộc tính selectef) vì tchúng ta đã gán giá trị cho quốc gia hiện tại của Model trong Controller - toàn bộ điều này được thực hiện chỉ với một dòng HTML/Razor, nhờ có Tag Helper!

Lists with key/values

Một điều bạn có thể thắc mắc ở ví dụ trên là: Không có value nào được cấp cho thẻ OPTION, nghĩa là phần text của item được chọn cũng là value khi gửi FORM. Lí do là chúng ta tạo ra thẻ option từ một danh sách chuỗi đơn giản (trong trường hợp này là tên quốc gia). Tuy nhiên, trong hầu hết trường hợp, bạn có thể tạo ra danh sách dựa trên kết hợp của các key và value. Ví dụ, quốc gia có thể lấy tự cơ sở dư liệu mà chúng ta dùng số là ID và tựa đề là tên của quốc gia. Rất may chúng ta có thể làm một cách dễ dàng.

Có thể nói rằng bạn có một lớp Country, chứa hai thuộc tính: IdName. Khi tạo ViewModel, bạn có thể lấy dữ liệu về các quốc gia từ cơ sở dữ liệu hoặc từ các nguồn khác. Trong View, chỉ đơn giản dùng hàm tạo khác của SelectList, cho phép bạn xác định thuộc tính value và text của đối tượng, như sau:

<select asp-items="@(new SelectList(Model.Countries, "Id", "Name"))" asp-for="User.CountryId"></select><br /><br />

Thật đơn giản và linh hoạt!

Working with enums

Một trong những kịch bản thường dùng cho Tag Helper Select là với enumerations (enums). Một enumeration là một danh sách các lựa chọn có thể, được định nghĩa trong mã chương trình, với mỗi lựa chọn tương ứng với một con số. Enum hữu ích trong nhiều tình huống và bạn có thể tìm thấy rất nhiều enumeration trong framework. Một ví dụ của enumeration là DayOfWeek, dùng cho thuộc tính có cùng tên trong cấu trúc DateTime. Ta định nghĩa như sau:

public enum DayOfWeek
{
	Sunday = 0,
	Monday = 1,
	Tuesday = 2,
	Wednesday = 3,
	Thursday = 4,
	Friday = 5,
	Saturday = 6
}

Bạn có thể dễ dàng định nghĩa enumeration của bạn như sau:

public enum WebUserStatus
{
	Unknown,
	Active,
	Deleted
}

Nếu bạn không gán số thì phần tử đầu tiên là 0, tiếp theo là 1 và cứ như vậy.

Với enumeration WebUserStatus thì hãy thử dùng nó cho element SELECT. Rất may cho chúng ta, có phương thức helper trong lớp HtmlHelper mà ta có thể dùng cho mục đích này:

<select asp-items="@(Html.GetEnumSelectList<HelloMVCWorld.Models.WebUserStatus>())" asp-for="Status"></select>

Kết quả trả về một dropdown list với các lựa chọn trong enumeration WebUserStatus!

Các thành phần của enum nhìn dễ hiểu, nhưng vì không được dùng các ký tự như khoảng trắng và các ký tự đặc biệt nên bạn sẽ tìm vào tạo enum với tên không quen mắt với người dùng như sau:

public enum WebUserValidationStatus
{
	Unknown,		
	NotVerifiedYet,
	VerifiedByMail,
	VerifiedByPhone
}

Chắc chắn là vẫn có thể hiểu nhưng chúng không thuận mắt cho người dùng. May là bạn có để chuyển sang dạng người dùng có thể đọc. Nếu bạn dùng data annotationDisplay, những giá trị này sẽ được dán nhãn cho enum. Ví dụ:

public enum WebUserValidationStatus
{
	Unknown,		
	[Display(Name = "Not verified yet")]
	NotVerifiedYet,
	[Display(Name = "Verified by e-mail")]
	VerifiedByMail,
	[Display(Name = "Verified by phone")]
	VerifiedByPhone
}

Và tại đây nó có dạng như sau:

Working with groups

Element Select của HTML hỗ trợ nhóm dữ liệu, có nghĩa bạn có thể đưa element dạng mào đầu chứ không phải là element thực sự. Tag Helper Select có hỗ trợ phần này, nhưng nó yêu cầu bạn phải tạo ra nhóm các thành phần thay vì riêng lẻ như ví dụ trên. Vậy chúng ta tạo ra ViewModel để chứa các item nhóm lại:

using Microsoft.AspNetCore.Mvc.Rendering;
using System.Collections.Generic;

namespace HelloMVCWorld.ViewModels
{
	public class GroupedMoviesViewModel
	{
		public GroupedMoviesViewModel()
		{
			this.Movies = new List<SelectListItem>();
		}

		public List<SelectListItem> Movies { get; set; }
	}
}

Sau đó, tạo ViewModel, bạn cần tạo ra thể hiện của lớp SelectListItem để chứa các item, và tạo ra thể hiện của lớp SelectListGroup cho mỗi nhóm mà bạn muốn gán SelectListItem vào. Có vẻ phức tạp như bạn thấy dưới đây:

public IActionResult SelectGroups()
{
	GroupedMoviesViewModel viewModel = new GroupedMoviesViewModel();

	SelectListGroup dramaMovies = new SelectListGroup() { Name = "Dramas" };
	viewModel.Movies.Add(new SelectListItem()
	{
		Group = dramaMovies,
		Text = "Forrest Gump"
	});
	viewModel.Movies.Add(new SelectListItem()
	{
		Group = dramaMovies,
		Text = "Fight Club"
	});

	SelectListGroup comedyMovies = new SelectListGroup() { Name = "Comedies" };
	viewModel.Movies.Add(new SelectListItem()
	{
		Group = comedyMovies,
		Text = "Anchorman: The Legend of Ron Burgundy"
	});
	viewModel.Movies.Add(new SelectListItem()
	{
		Group = comedyMovies,
		Text = "Step Brothers"
	});

	return View(viewModel);
}

The result will look like this:

Như bạn thấy trong đoạn mã HTML, group được tạo ra với thẻ OPTGROUP thay vì thẻ OPTION dùng cho element thông thường:

<select>
	<optgroup label="Dramas">
		<option>Forrest Gump</option>
		<option>Fight Club</option>
	</optgroup>
	<optgroup label="Comedies">
		<option>Anchorman: The Legend of Ron Burgundy</option>
		<option>Step Brothers</option>
	</optgroup>
</select>

Working with multiple selection lists

Trong một vài tình huống, bạn muốn người dùng có thể chọn một cùng lúc hơn một lựa chọn. Chúng ta không thể làm được với dropdown list, là mặc định của HTML SELECT, nhưng thực tế có thể làm được thông qua thuộc tính multiple. Khi đó thẻ SELECT sẽ trả về listbox thay cho dropdown list, cho phép bạn chọn nhiều item:

Rất may cho chúng ta, Select Tag Helper cũng hỗ trợ - thực tế, nó dùng danh sách đa lựa chọn nếu thuộc tính được dùng trong asp-for là IEnumerable, ví dụ List. Đây là một ví dụ của ViewModel chứa tên các phim và hiển thị ở View, trong khi chấp nhận các lựa chọn trong thuộc tính FavoriteMovieIds:

public class FavoriteMoviesViewModel
{
	public List<Movie> Movies { get; set; }

	public List<int> FavoriteMovieIds { get; set; }
}

Dùng chúng trong View đơn giản như sau:

<select asp-for="FavoriteMovieIds" asp-items="@(new SelectList(Model.Movies, "Id", "Title"))"></select>

This will result in the list you see in the screenshot above. The HTML looks much as it has before, with the addition of the multiple attribute:

<select id="FavoriteMovieIds" multiple="multiple" name="FavoriteMovieIds">
	<option value="1">The Godfather</option>
	<option value="2">Forrest Gump</option>
	<option value="3">Fight Club</option>
</select>

Summary

The HTML SELECT element is one of the more complex HTML elements, with lots of possibilities, which is why the Select Tag Helper is also a bit more complex to use, as seen in the examples above. However, that also means that it can save you even more time than usual!


This article has been fully translated into the following languages: Is your preferred language not on the list? Click here to help us translate this article into your language!