[Blazor] 재사용 컴포넌트 만들기

개요

  • Blazor 에서 재사용 가능한 컴포넌트 만드는 방법에 대해서 정리 진행합니다.

테스트 예제 내용

  • Creater.razor 컴포넌트가 있고, 해당 컴포넌트에서는 현재 EditForm 컴포넌트를 사용한 코드가 있습니다.
  • 예시로, EditForm 컴포넌트를 다른 컴포넌트에서도 재사용 가능하다고 가정을 하였을 때, 해당 부분을 따로 컴포넌트로 만들어서 부모 컴포넌트에서 사용 가능하도록 하는 예제 코드를 작성해 봅니다.

재사용 진행할 컴포넌트 코드

  • 재사용 진행할 컴포넌트 코드 내용은 다음과 같습니다.
@if (Model is not null)
{
    <EditForm Model="@Model" OnValidSubmit="FormSubmit" OnInvalidSubmit="@HandleInvalidSubmit">
        <DataAnnotationsValidator />
        <ValidationSummary />
        <div class="form-group">
            <label class="control-label" for="FirstName">FirstName</label>
            <InputText @bind-Value="Model.FirstName" id="firstname" class="form-control" placeholder="Enter FirstName" />
            <ValidationMessage For="() => Model.FirstName" />
        </div>
        <div class="form-group">
            <label class="control-label" for="LastName">LastName</label>
            <InputText @bind-Value="Model.LastName" id="lastname" class="form-control" placeholder="Enter LastName" />
            <ValidationMessage For="() => Model.LastName" />
        </div>
        <div class="form-group form-check">
            <label class="form-check-label" for="IsEnrollment">
                <InputCheckbox @bind-Value="Model.IsEnrollment" id="IsEnrollment" class="form-check-input" />
                Is Enrollment
            </label>
        </div>
        <div class="form-group">
            <input type="submit" value="Create" class="btn btn-primary"
                   disabled="@Busy" />
        </div>

        <input name="IsEnrollment" type="hidden" value="false" />
    </EditForm>
}
  • 위 코드는 부모 컴포넌트인 Create.razor 에서 사용중입니다.
  • 해당 코드를 다른 컴포넌트에서도 추후에 사용 가능하다고 판단이 되어, EditorForm.razor 새로운 컴포넌트를 생성하고 해당 컴포넌트에 재사용 가능하도록 코드를 분리할 예정입니다.
  • 그럼, EditForm.razor 컴포넌트를 생성후, 다음 코드를 추가합니다.

EditForm.razor

@using BeomBeomJoJoBlazor.Models.Candidates

@if (Model is not null)
{
    <EditForm Model="@Model" OnValidSubmit="FormSubmit" OnInvalidSubmit="@HandleInvalidSubmit">
        <DataAnnotationsValidator />
        <ValidationSummary />
        <div class="form-group">
            <label class="control-label" for="FirstName">FirstName</label>
            <InputText @bind-Value="Model.FirstName" id="firstname" class="form-control" placeholder="Enter FirstName" />
            <ValidationMessage For="() => Model.FirstName" />
        </div>
        <div class="form-group">
            <label class="control-label" for="LastName">LastName</label>
            <InputText @bind-Value="Model.LastName" id="lastname" class="form-control" placeholder="Enter LastName" />
            <ValidationMessage For="() => Model.LastName" />
        </div>
        <div class="form-group form-check">
            <label class="form-check-label" for="IsEnrollment">
                <InputCheckbox @bind-Value="Model.IsEnrollment" id="IsEnrollment" class="form-check-input" />
                Is Enrollment
            </label>
        </div>
        <div class="form-group">
            <input type="submit" value="Create" class="btn btn-primary"
                   disabled="@Busy" />
        </div>

        <input name="IsEnrollment" type="hidden" value="false" />
    </EditForm>
}

@code  {

    [Parameter]
    public Candidate? Model { get; set; }

    [Parameter]
    public bool Busy { get; set; }

    [Parameter]
    public EventCallback<bool> OnSubmitCallback{ get; set; }

    protected async Task FormSubmit()
    {
        if(OnSubmitCallback.HasDelegate)
        {
            await OnSubmitCallback.InvokeAsync(true);
        }
    }

    protected async Task HandleInvalidSubmit()
    {
        if(OnSubmitCallback.HasDelegate)
        {
            await OnSubmitCallback.InvokeAsync(false);
        }
    }
}
  • EditorForm.razor 컴포넌트 코드는 위와 같습니다.
  • 여기서 핵심은 부모 컴포넌트에서 재사용할 코드를 EditorForm.razor 컴포넌트로 분리를 한 것입니다.
  • 또한, FormSubmit, HandleInvalidSubmit 과 같이 부모에서 사용하는 이벤트를 전달하기 위해서 EventCallback 속성인 OnSubmitCallback 속성을 bool 타입으로 추가하였습니다.
  • 이를 통해, 자식 컴포넌트에서 부모 컴포넌트로 Parameter 값을 전달할 수 있습니다.
  • 다음은 부모 컴포넌트인 Create.razor 코드 입니다.

Create.razor

@page "/Candidates/Create"
@using BeomBeomJoJoBlazor.Models.Candidates
@using Microsoft.EntityFrameworkCore
@inject IDbContextFactory<CandidateAppDbContext> _ContextFactory

<PageTitle>Candidate Create</PageTitle>

<h1>Create</h1>

<hr />

@if (!String.IsNullOrEmpty(StatusMessage))
{
    var statusMessageClass = StatusMessage.StartsWith("Error") ? "danger" : "sueccess";
    <div class="alert alert-@statusMessageClass alert-dismissible" role="alert">
        <button type="button" class="close" data-dismiss="alert"
        aria-label="Close"><span aria-hidden="true">&times;</span></button>
        @StatusMessage
    </div>
}

<div class="row">
    <div class="col-md-4">
        <BeomBeomJoJoBlazor.Pages.Candidates.Components.EditorForm
            Model="@Model" Busy="@Busy" 
             OnSubmitCallback="@OnSubmitCallback">
        </BeomBeomJoJoBlazor.Pages.Candidates.Components.EditorForm>
    </div>
</div>

<div>
    <a href="/Candidates">Back to List</a>
</div>

@code {
    public Candidate? Model { get; set; }

    public string? StatusMessage { get; set; }

    public bool Busy { get; set; }

    protected override void OnInitialized()
    {
        Model = new();
    }

    protected async Task FormSubmit()
    {
        // 중복 저장 방지
        if(Busy)
            return;

        Busy = true;

        try
        {
            if (Model is not null)
            {
                Model.Age = 0;

                using var context = _ContextFactory.CreateDbContext();
                context.Candidates.Add(Model);
                await context.SaveChangesAsync();
                Model = new() { IsEnrollment = Model.IsEnrollment };

                StatusMessage = "Success";
            }
        }
        catch
        {
            StatusMessage = "Error";
        }
        finally
        {
            Busy = false;
        }
    }

    protected void HandleInvalidSubmit()
    {
        StatusMessage = string.Empty;
    }

    protected async Task OnSubmitCallback(bool isValid)
    {
        if(isValid)
        {
            await FormSubmit();
        }
        else
        {
            HandleInvalidSubmit();
        }
    }
}
  • 여기서 핵심은 다음 코드 입니다.
<BeomBeomJoJoBlazor.Pages.Candidates.Components.EditorForm
            Model="@Model" Busy="@Busy" 
             OnSubmitCallback="@OnSubmitCallback">
</BeomBeomJoJoBlazor.Pages.Candidates.Components.EditorForm>
  • BeomBeomJoJoBlazor.Pages.Candidates.Components.EditorForm 재사용 가능한 컴포넌트를 선언 후, 속성으로 Model, Busy, OnSubmitCallback 3개의 속성을 추가 하였습니다.
  • 이로써, 부모의 속성 값들이 자식 컴포넌트인 BeomBeomJoJoBlazor.Pages.Candidates.Components.EditorForm 속성 값으로 전달되어 사용할 수 있게 되었습니다.
  • 또한 OnSubmitCallback 메서드를 통해서 OnSubmitCallback 값에 따라 FormSubmit 메서드를 호출할 지, HandleInvalidSubmit 메서드를 호출할 지 여부도 결정할 수 있게 되었습니다.
  • 이렇게 재사용 가능한 컴포넌트를 만들 수 있습니다.
728x90

이 글을 공유하기

댓글

Designed by JB FACTORY