Việc thêm các công việc đã hoàn thành. Công việc tiếp theo của chúng ta là đánh dấu các công việc đã được hoàn thành và ẩn chúng đi.
Hoàn thành các mục với checkbox:
Thêm các mục công việc là rất tuyệt vời nhưng cuối cùng bạn cần hoàn thành chúng. Tìm tới view Views/Todo/Index.cshtml, thêm checkbox cho mỗi mục công việc:
<input type=”checkbox” class=”done-checkbox”>
Lúc này không có gì xảy ra khi bạn kích chuột vào ô checkbox. Giống như phần trước, bạn sẽ thêm hành vi này bằng các form và hành động. Trong trường hợp này bạn sẽ cần một vài mã Javascript.
Thêm các thành phần Form tới view:
Đầu tiên, hãy cập nhật view và bao mỗi checkbox với một thẻ <form>. Sau đó, thêm thành phần hidden tới mỗi mục ID:
Views/Todo/Index.cshtml
<td>
<form asp-action=”MarkDone” method=”POST”>
<input type=”checkbox” class=”done-checkbox”>
<input type=”hidden” name=”id” value=”@item.Id”>
</form>
</td>
Khi vòng lặp foreach chạy sẽ in ra một hàng cho mỗi mục công việc. Input ID cho controller biết được ô checkbox nào đã được chọn. Nếu bạn chạy ứng dụng ngay bây giờ, check box vẫn chưa làm việc bởi vì chưa có nút gửi yêu cầu với dữ liệu của form. Bạn có thể thêm nút submit dưới mỗi checkbox nhưng điều đó thật khó chịu, form nên được submit mỗi khi ta click vào checkbox. Bạn có thể làm điều đó bằng một đoạn mã javascript.
Thêm mã Javascript:
Tìm tệp Site.js trong thư mục wwwroot/js và thêm đoạn mã sau:
wwwroot/js/site.js
$(document).ready(function() {
// Wire up all of the checkboxes to run markCompleted()
$(‘.done-checkbox’).on(‘click’, function(e) {
markCompleted(e.target);
});
});
function markCompleted(checkbox) {
checkbox.disabled = true;
var row = checkbox.closest(‘tr’);
$(row).addClass(‘done’);
var form = checkbox.closest(‘form’);
form.submit();
}
Đoạn mã này sử dụng Jquery để đính kèm một số mã vào checkbox, khi một checkbox được click, hàm markCompleted() sẽ được gọi. Hàm markCompleted() làm một vài thứ:
Thêm thành phần disabled vào checkbox để nó không thể được click lại.
Thêm thành phần done của Css vào thẻ cha của Checkbox để thay đổi giao diện của hàng dựa trên các quy tắc css trong style.css.
Gửi form:
Đó là công đoạn xử lý trên frontend, bây giờ hãy đi xây dựng một hành động mới.
Thêm một hành động tới Controller:
Như bạn có thể đoán, bạn cần thêm một hành động được gọi là MarkDone trong TodoController:
[ValidateAntiForgeryToken]
public async Task<IActionResult> MarkDone(Guid id)
{
if (id == Guid.Empty)
{
return RedirectToAction(“Index”);
}
var successful = await _todoItemService.MarkDoneAsync(id);
if (!successful)
{
return BadRequest(“Could not mark item as done.”);
}
return RedirectToAction(“Index”);
}
Hãy cùng nhau đi tì hiểu chi tiết phương thức này nhé. Đầu tiên phương thức chấp nhận một tham số đầu vào Guid id. Nếu dữ liệu đến bao gồm một trường có tên Id, asp.Net core sẽ phân tích nó thành một Guid. Điều này hoạt động vì thẻ ẩn mà bạn đã thêm vào form có tên là ID. Vì bạn không sử dụng model binding, không có modelstate để kiểm tra tính hợp lệ. Thay vào đó bạn có thể kiểm tra trực tiếp giá trị Guid để đảm bảo nó hợp lệ. Nếu vì một lý do nào đó, tham số Id trong request bị thiếu hoặc không thể phân tích cú pháp như một Guid, id sẽ có giá trị Guid.Empty và hành động sẽ yêu cầu trình duyệt chuyển tới /Todo/Index và làm mới trang.
Tiếp theo controller cần gọi lớp dịch vụ để cập nhật cơ sở dữ liệu. Điều này sẽ được xử lý bằng một phương thức có tên MarkDoneAsync trên interface ItodoItemService, sẽ trả lại đúng hoặc sai phụ thuộc vào việc cập nhật thành công:
var successful = await _todoItemService.MarkDoneAsync(id);
if (!successful)
{
return BadRequest(“Could not mark item as done.”);
}
Cuối cùng nếu mọi thứ ổn, trình duyệt được chuyển hướng tới /Todo/Index và làm mới lại trang. Với view và controller đã được cập nhật, tất cả những gì còn lại là thêm phương thức dịch vụ còn thiếu.
Thêm phương thức Service:
Đầu tiên thêm MarkDoneAsync tới interface:
Services/ITodoItemService.cs
Task<bool> MarkDoneAsync(Guid id);
Sau đó triển khai cụ thể vào TodoItemService:
Services/TodoItemService.cs
public async Task<bool> MarkDoneAsync(Guid id)
{
var item = await _context.Items
.Where(x => x.Id == id)
.SingleOrDefaultAsync();
if (item == null) return false;
item.IsDone = true;
var saveResult = await _context.SaveChangesAsync();
return saveResult == 1; // One entity should have been updated
}
Phương thức này sử dụng Entity Framework Core và where() để tìm một mục bởi Id trong cơ sở dữ liệu. Phương thức SingleOrDefaultAsync() sẽ trả về một mục hoặc null nếu không tìm thấy. Khi bạn chắc chắn rằng mục đó không có giá trị, việc đặt thuộc tính Isdone là một vấn đề đơn giản:
item.IsDone = true;
Thay đổi thuộc tính chỉ ảnh hưởng tới bản sao cụ bộ cho tới khi savechangeAsync() được gọi để thực sự thay đổi trên cơ sở dữ liệu hàm này trả lại 0 hoặc 1.
Đến lúc này mọi thứ đã có vẻ đúng, hãy thử chạy ứng dụng của mình lên và hoàn thành công việc của mình thôi!
Như vậy chúng ta đã xây dựng được các tính năng của ứng dụng nhưng liệu nó đã an toàn hay chưa hãy cùng nhau tìm hiểu ở bài sau nhé!