[Blazor] ToDoList 만들기
- 웹 프로그래밍/Blazor
- 2022. 12. 15. 19:55
개요
- Blazor 학습 목적으로 컴포넌트 단위로 나눠서 ToDoList 만들기 진행하였습니다.
참고
- 저 같은 경우에는 ToDoList 를 어떻게 만들까 고민하다가, 지인을 통해 https://codesandbox.io/s/mashup-todolist-fwv17?fontsize=14 해당 사이트에서 보여주는 ToDoList 를 똑같이 만들어 보는게 좋다고 하여, Blazor 를 이용하여 최대한 동일하게 만들어 보려고 노력 하였습니다.
Blazor 코드
- 저는 우선 개발을 컴포넌트 단위로 나눠서 개발 진행하였습니다.
- 하나의 페이지에, C# 코드를 한번에 넣을 수도 있지만 그렇게 되면 코드의 복잡성이 매우 올라가고, 가독성에도 좋지 않아서 최대한 Component 로 만들 수 있는 것들은 Component 로 빼서 만들었습니다.
- ToDoList 에서 만든 컴포넌트 목록은 다음과 같습니다.
- CircleButtonComponent.razor
- DateComponent.razor
- DoingWorkCount.razor
- InputComponent.razor
- TodoListComponent.razor
- 위와 같이 총 5가지의 컴포넌트들을 만들었습니다.
- 위 컴포넌트들의 코드는 다음과 같습니다.
CircleButtonComponent.razor
<div class="btn-container">
<button class="@(IsClicked == true ? "circle-add-button" : "circle-not-add-button")"
type="button"
@onclick="(() => CircleChange.InvokeAsync())">
@ButtonContent
</button>
</div>
@code {
[Parameter]
public bool IsClicked { get; set; }
[Parameter]
public string? ButtonContent { get; set; }
[Parameter]
public EventCallback CircleChange { get; set; }
}
.btn-container {
display: flex;
justify-content: center;
}
.circle-add-button {
display: block;
height: 80px;
width: 80px;
border-radius: 50%;
border: none;
background: #38d9a9;
font-size: 100px;
line-height: 80px;
color: white;
text-align: center;
transition: all ease 0.5s;
-webkit-transform: rotate(0deg);
}
.circle-add-button:hover {
background: #63e6be;
}
.circle-not-add-button {
height: 80px;
width: 80px;
border-radius: 50%;
border: none;
background: #ff8787;
font-size: 100px;
line-height: 80px;
color: white;
transition: all ease 0.5s;
-webkit-transform: rotate(45deg);
}
DateComponent.razor
<h1>@CurrentDate</h1>
<h5>@CurrentDayOfWeek</h5>
@code {
[Parameter]
public string? CurrentDate { get; set; }
[Parameter]
public string? CurrentDayOfWeek { get; set; }
}
DoingWorkCount.razor
@using ToDoList.Data
<div class="doing-work">
할 일
@todos!.Where(x => x.IsDone == false).Count()
개 남음
</div>
@code {
[Parameter]
public List<TodoItem>? todos { get; set; }
}
.doing-work {
margin-top: 40px;
color: #20c997;
font-size: 20px;
font-weight: bold;
}
InputComponent.razor
<form class="container" @onsubmit=Enter hidden="@IsClicked">
<div>
<input class="item1" placeholder="할 일을 입력 후, Enter 를 누르세요"
@bind-value="newTodo" />
<button type="submit" hidden class="item2" @onclick="(() => OnClick.InvokeAsync(newTodo))">추가</button>
</div>
</form>
@code {
[Parameter]
public string? newTodo { get; set; }
[Parameter]
public bool IsClicked { get; set; }
[Parameter]
public EventCallback<string> OnClick { get; set; }
[Parameter]
public EventCallback Enter{ get; set; }
}
.container {
display: flex;
justify-content: center;
align-content: center;
align-items: center;
text-align: center;
padding: 10px;
width: 100%;
}
.item1 {
flex-direction: row;
width: 850px;
height: 50px;
border-radius: 10px;
}
.item2 {
flex-direction: row;
width: 150px;
height: 50px;
border-radius: 10px;
}
TodoListComponent.razor
@using ToDoList.Data
<div>
<ul>
@foreach (var todo in todos!)
{
<li>
<div class="continer" @onmouseover="(() => MouseOver.InvokeAsync(todo.Id))"
@onmouseout="(() => MouseOut.InvokeAsync(todo.Id))">
<input class="check-button rounded-checkbox"
type="checkbox"
checked="@todo.IsDone"
onchange="@(()=>{ CheckedClick.InvokeAsync((todo.Id, true));})" />
<div class="@(todo.IsDone == true ? "content-check-item" : "content-not-check-item")">@todo.Title</div>
<input
type="button"
hidden="@todo.IsDisabled"
class="img-button"
@onclick="(() => DeleteOnClick.InvokeAsync(todo.Id))"></input>
</div>
</li>
}
</ul>
</div>
@code {
[Parameter]
public List<TodoItem>? todos { get; set; }
[Parameter]
public EventCallback<int> DeleteOnClick { get; set; }
[Parameter]
public EventCallback<(int, bool)> CheckedClick { set;get; }
[Parameter]
public EventCallback<int> MouseOver { get; set; }
[Parameter]
public EventCallback<int> MouseOut { get; set; }
}
li {
list-style: none;
}
.continer {
display: flex;
}
.rounded-checkbox {
width: 35px;
height: 35px;
font-size: 30px;
margin-top: 13px;
margin-right: 25px;
border-radius: 50%;
vertical-align: middle;
border: 1px solid gray;
appearance: none;
-webkit-appearance: none;
outline: none;
cursor: pointer;
}
.rounded-checkbox:checked {
appearance: auto;
clip-path: circle(50% at 50% 50%);
accent-color: #9b59b6;
}
.content-check-item {
flex-direction: column;
font-size: 30px;
margin-top: 6px;
color: #ced4da;
}
.content-not-check-item {
flex-direction: column;
font-size: 30px;
margin-top: 6px;
color: #495057;
}
.img-button {
background: url("../images/trash.png") no-repeat center center;
border: none;
width: 15px;
height: 20px;
cursor: pointer;
background-size: cover;
flex-direction: column;
font-size: 20px;
margin-top: 8px;
margin-left: auto;
justify-content: center;
text-align: center;
}
- 위와 같이 각각의 컴포넌트 코드들을 보았습니다.
- 이제는 해당 컴포넌트들을 종합적으로 사용하여 사용자에게 보여주는 TodoList Page 를 생성하여 코드를 작성해 보도록 하겠습니다.
Todo.razor
@page "/todo"
@using ToDoList.Component
@using ToDoList.Data
<PageTitle>Todo</PageTitle>
<DateComponent CurrentDate="@CurrentDate" CurrentDayOfWeek="@CurrentDayOfWeek"></DateComponent>
<DoingWorkCount todos="@todos"></DoingWorkCount>
<hr />
<CircleButtonComponent
IsClicked="@IsClicked"
ButtonContent="@ButtonContent"
CircleChange="CircleChange">
</CircleButtonComponent>
<InputComponent newTodo="@newTodo"
Enter="Enter"
OnClick="AddTodo"
IsClicked="IsClicked">
</InputComponent>
<TodoListComponent todos="@todos"
CheckedClick="((int id, bool state) args) => StateChanged(args.id, args.state)"
MouseOver="MouseOver"
MouseOut="MouseOut"
DeleteOnClick="DeleteData">
</TodoListComponent>
@code {
private List<TodoItem> todos = new();
private string? newTodo { get; set; }
private string? CurrentDate { get; set; }
private string? CurrentDayOfWeek { get; set; }
private bool IsClicked { get; set; } = true;
private int ClickCount { get; set; } = 1;
private string? ButtonContent { get; set; } = "+";
protected override void OnInitialized()
{
base.OnInitialized();
CurrentDate = GetDate();
CurrentDayOfWeek = GetCurrentDayOfWeek();
}
private void AddTodo(string keyword)
{
if (!string.IsNullOrWhiteSpace(keyword))
{
var newId = todos.Count > 0 ? todos.Max(t => t.Id) + 1 : 1;
todos.Add(new TodoItem { Id = newId, Title = keyword });
newTodo = string.Empty;
}
}
protected void DeleteData(int id)
{
todos.Remove(todos.Find(t => t.Id == id)!);
}
protected void StateChanged(int id, bool state)
{
var todo = todos.First(t => t.Id == id);
todo.IsDone = !todo.IsDone;
}
protected void MouseOver(int id)
{
var todo = todos.Find(t => t.Id == id);
todo.IsDisabled = false;
}
protected void MouseOut(int id)
{
var todo = todos.Find(t => t.Id == id);
todo.IsDisabled = true;
}
private void Enter()
{
AddTodo(newTodo!);
}
private void CircleChange()
{
ClickCount++;
IsClicked = ClickCount % 2 == 0 ? false : true;
ButtonContent = IsClicked == true ? "+" : "+";
}
private string GetDate()
{
DateTime today = DateTime.Today;
string title = $"{today.Year.ToString()}년 {today.Month.ToString()}월 {today.Day.ToString()}일";
return title;
}
private string GetCurrentDayOfWeek()
{
DateTime nowDt = DateTime.Now;
string[] days = { "일요일", "월요일", "화요일", "수요일", "목요일", "금요일", "토요일" };
string day = string.Empty;
for (int index = 0; index < days.Length; index++)
{
if (Convert.ToInt32(nowDt.DayOfWeek) == index)
{
day = days[index];
break;
}
}
return day;
}
}
body {
background: #e9ecef;
}
- 위와 같이 앞서 만든 5개의 컴포넌트들을
Todo.razor
페이지에서 사용하고 있는 것을 확인할 수 있습니다.
실행 결과
- 실행 결과, 원래 만들려고 했던 TodoList 와 완전히 동일하지는 않지만, 비슷하게 개발은 되었습니다...ㅎㅎ
첨부파일
- 전체 코드는 아래 올려 놓도록 하겠습니다~!
728x90
'웹 프로그래밍 > Blazor' 카테고리의 다른 글
[Blazor] Grafana DashBoard iframe URL http 로 가져오는 방법 (0) | 2023.01.06 |
---|---|
[Blazor] iframe 태그 이용하여 grafana DashBaord HTTPS URL 불러오기 (0) | 2023.01.06 |
[Blazor] LocalStorage 사용법 (1) | 2022.12.10 |
[Blazor] JWT 예제 (0) | 2022.12.10 |
[Blazor] JWT (0) | 2022.12.09 |
이 글을 공유하기