반응형

Blazor 앱은 .NET 메서드에서 JavaScript함수를 호출하고, JavaScript함수에서 .NET메서드를 호출할 수 있다.

이것을 JavaScript interop이라고 한다. 

 

Blazor의 .NET 메서드에서 JavaScript 함수 호출

1. index.html body 태그안에 script를 추가한다. 

    <div id="app">Loading...</div>

    <div id="blazor-error-ui">
        An unhandled error has occurred.
        <a href="" class="reload">Reload</a>
        <a class="dismiss">🗙</a>
    </div>
    <script src="_framework/blazor.webassembly.js"></script>
    <script>
        window.showAlert = (comment) => {
            alert(comment);
        };
    </script>

2. index.razor에서 버튼을 클릭하면 showAlert 함수를 호출하도록 한다.

  • .NET에서 JS를 호출하려면 IJSRuntime 추상화를 삽입하고 InvokeVoidAsync 함수를 호출한다.
@page "/"
@inject IJSRuntime JS

<h1>Hello, world!</h1>

Welcome to your new app.


<p>
		<button @onclick="ShowAlert">알림창 띄우기</button>
</p>

<SurveyPrompt Title="How is Blazor working for you?" />

@code {
		private async Task ShowAlert()
		{
				await JS.InvokeVoidAsync("showAlert", "Hello, world!");
		}
}

3. 알림창 띄우기 버튼을 클릭하면 alert 을 확인할 수 있다. 

showAlert 자바스크립트 함수를 index.html에서 별도의 파일로 옮겨보자. => JS isolation

1. wwwroot/js/script.js 에 아래 함수 저장

export function showAlert(comment) {
    alert(comment);
}

2. index.razor 수정

IJSInProcessObjectReference는 함수를 동기적으로 호출할 수 있는 JS 개체에 대한 참조를 나타냅니다.

@page "/"
@inject IJSRuntime JS

<h1>Hello, world!</h1>

Welcome to your new app.


<p>
		<button @onclick="ShowAlert">알림창 띄우기</button>
</p>

<SurveyPrompt Title="How is Blazor working for you?" />

@code {

		private IJSObjectReference module;

		protected override async Task OnAfterRenderAsync(bool firstRender)
		{
				if (firstRender)
				{
						module = await JS.InvokeAsync<IJSObjectReference>("import",
								"./js/scripts.js");
				}
		}

		private async Task ShowAlert()
		{
				await module.InvokeVoidAsync("showAlert", "Hello, world!");
		}
}

3. 알림창 띄우기 버튼을 클릭하면 alert 을 확인할 수 있다. 

 

 

ref : https://bigexecution.tistory.com/49

반응형
반응형

Blazor 서버 기본 구조

Dependency Injection, Router, _imports, Layout 등을 알아본다

 

 

 

Dependency Injection

이를 간단하게 알아보기 위해서 이미 존재하고있던 Data폴더에 FoodService.cs를 생성한다.

이후 이 안에 Food class를 생성해주고 프로퍼티값으로 Name, Price를 선언해준다.

그리고 cs파일을 생성하면서 생성된 FoodService class안에

public List<Food> GetFoods()
{
List<Food> foods = new List<Food>()
{
new Food() {Name = "bap", Price = 7000},
new Food() {Name = "KimBap", Price = 3000},
new Food() {Name = "ChoBap", Price = 9000}
};
return foods;
}

위와 같이 List<Food>를 반환해주는 함수를 생성해준다.

이후에 Blazor App을 생성하면서 존재하는 Index.razor파일안에 아래와 같은 코드를 추가해주면

웹 페이지에 하드코딩해서 넣어준 서비스의 목록들이 나오긴 한다.

 

@using BlazorApp.Data;

<div>
@foreach(var food in _foodService.GetFood())
{
<div>@food.Name</div>
<div>@food.Price</div>
}

</div>
@code{
FoodSerive _foodService= new FoodService();
}

다만, 위와 같이 코드를 작성하게 된다면 코드간의 의존성이 너무 심해진다는 문제가 발생한다.

따라서 Interface을 활용하여 이를 해결해주자.

다시 FoodService.cs로 돌아와서 Interface를 선언해주자 이렇게 해주면 이 FoodService를 사용하고싶은

사람들은 이 interface를 구현해야한다는 규칙이 생기게 된다.

public interface IFoodService
{
IEnumeralbe<Food> GetFoods();
}

public class FoodService : IFoodService
{
	public IEnumeralbe<Food> GetFoods()
	{
		List<Food> foods = new List<Food>()
		{
		new Food() {Name = "bap", Price = 7000},
		new Food() {Name = "KimBap", Price = 3000},
		new Food() {Name = "ChoBap", Price = 9000}
		};
		return foods;
	}
}

하지만 아직까지 razor의 컴포넌트들마다 일일이 new FoodService()를 호출해야하는 문제가 존재한다.

이를 해결하기 위해서 Dependency Injection이다.

이는 Startup.cs안에 ConfigureServices 함수안에 service.AddSingleton<IFoodService, FoodService>();로 선언하게 되면

이제 new를 통해서 선언하는 것이아닌

@inject IFoodService foodSerivce를 통해서 FoodService를 new를 통해서 생성하지 않고 사용이 가능하다.

[예시]

@using BlazorApp.Data;

@inject IFoodService foodSerivce

<div>
@foreach(var food in foodSerivce.GetFood())
{
<div>@food.Name</div>
<div>@food.Price</div>
}

</div>

하지만 이러한 방식이 전역적으로 사용되는것은 아니다.

이를 살펴보자.

public class SingletonService : IDisposable
{
public Guid ID {get; set;} //간단하게 ID를 생성하는 인터페이스이다.

public SingletonService() //cotr + tab,tab으로 해당 class의 생성자를 간단하게 생성할 수 있다.
{
ID = Guid.NewGuid();
}

public Dispose()
{
Console.WriteLine("SingletonService Disposed!");
}
}

public class TransientService : IDisposable
{

public Guid ID {get; set;} //간단하게 ID를 생성하는 인터페이스이다.

public TransientService() //cotr + tab,tab으로 해당 class의 생성자를 간단하게 생성할 수 있다.
{
ID = Guid.NewGuid();
}

public Dispose()
{
Console.WriteLine("TransientService Disposed!");
}
}

public class ScopeService : IDisposable
{
public Guid ID {get; set;} //간단하게 ID를 생성하는 인터페이스이다.

public ScopeService() //cotr + tab,tab으로 해당 class의 생성자를 간단하게 생성할 수 있다.
{
ID = Guid.NewGuid();
}

public Dispose()
{
Console.WriteLine("ScopeService Disposed!");
}

}

[생명주기]

3가지 모드를 파악해보자.

 

다음처럼 생성

service.AddSingleton<SingletonService>();

service.AddTransient<TransientService>();

service.AddScoped<ScopedService>();

 

​다음 처럼 사용 (주로 상단에 선언)

@inject SingletonService singleton;

@inject TransientService transient;

@inject ScopedService scoped;

@inject 가 있는 동일페이지에서 다음 코드가 있을때 guid를 통해  id 가 페이지개 갱신 될때마다 재생성 되는 것을 보면 생명 주기를 파악 할 수 있다

<div>

<h1>Singleton</h1>

Guid: @singleton.ID

<h1>Singleton</h1>

Guid: @transient.ID

<h1>Singleton</h1>

Guid: @scoped.ID

<div>

이렇게 하고 생명주기를 살펴보면

singleton와scoped의 id는 변화하지 않고

transient의 id는 변화한다.

하지만 새로고침을 실행하면 singleton의 id는 바뀌지 않지만 나머지 두개는 변화한다.

(Balzor Server 프로젝트의 경우에는 변화가 없지만 클라이언트(Blazor WebAssembly) 사이드같은 경우에는 singleton 방식도 guid 도 변경 된다)

즉, 변동하지 않고 모두에게 똑같이 보여야 한다면 singleton을

유저마다 갱신되어 보여야 한다면 나머지 2개를 선택하여 사용해야 하는데

Transient는 말그래도 웹페이지의 요청이 일어날때마다 바뀌고

Scoped는 웹에 접속할때 마다 변경된다.

 

 

SPA(single Page Application)

SPA는 기존에 존재하던 정적 페이지가 아닌 동적 페이지라고 할 수 있다

기존에는 웹서버에서 전체를 실시간으로 만들어 보내주는 방식이였지만

일부 변경되는 데이터에 따라(Dom) 모습이 달라지는 것을 동적페이지 방식이라 한다=> SPA

 

 

레이아웃

다음 과 같은 레이웃의 범위를 알아보면 아래 코드로 글자가 어디에 들어 가있는지 보면 된다 a, d , c

 

MainLayout.razor

@inherits LayoutComponentBase

<div class="sidebar">
    <p style="color : #be0000;">
        a
    </p>
    
    <NavMenu />
</div>

<div class="main">
    <div class="top-row px-4">
        c
        <a href="https://docs.microsoft.com/aspnet/" target="_blank">About</a>
    </div>

    <div class="content px-4">
        d
        @Body
    </div>
</div>

_imports.razor 알아보기

이 컴포넌트 안에 사용되는 @키워드들은 같은 폴더안에 있는 모든 컴포넌트들에게 적용되게 된다.

imports.razor 의 내용을 ABC 라는 폴더에 넣어놓고
내용을 새로만든 레이아웃 @layout MainLayout2.razor

해놓으면 해당 폴더에 있는 razor 페이지들의 기본 레이아웃은 MainLayout 이 아닌  MainLayout2 으로 변경된다

 

 

기본 레이아웃은 자체는 다음 코드를 상속받아 레이아웃 자체를 구성한다

@inherits LayoutComponentBase

 반드시 하나는 이 것을 상속받는 레잉아웃이 있어야 한다 이것이 적용된 페이지가

MainLayout.razor 이다

 

@inherits를 사용하게되면 class를 상속해서 사용할 수 있게 된다.

@layout키워드를 사용하면 레이아웃을 바꿔서 적용할 수 있다.

키워드를 사용하지 않고 모든 레이아웃을 변경하기 위해서는 App.razor에 DefaultLayout을 변경해주면 된다.

 

 

 

Router : 주소/sports/55  과 같이 주소 뒤에 어떤 페이지를 보여줄지에 대한 것

<Router AppAssembly="@typeof(Program).Assembly">
    <Found Context="routeData">
        <RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
    </Found>
    <NotFound>
        <LayoutView Layout="@typeof(MainLayout)">
            <p>Sorry, there's nothing at this address.</p>
        </LayoutView>
    </NotFound>
</Router>

 

<Router 는 다음 처럼 되어 있다 Found 가 RenderFragment 로 내용을 보여주기 위한 형태인것을 알 수있다(템플릿)

그리고 Router는 상대적 기준으로 생각하고 작성해야 한다 즉 현재 경로 위쪽이 무엇인지 기본적으로 절대경로로 생각하면 안된다는것

 

 

 

 

 아래코드에서

<div class="top-row pl-4 navbar navbar-dark">
    <a class="navbar-brand" href="">BlazorApp</a>
    <button class="navbar-toggler" @onclick="ToggleNavMenu">
        <span class="navbar-toggler-icon"></span>
    </button>
</div>

<div class="@NavMenuCssClass" @onclick="ToggleNavMenu">
    <ul class="nav flex-column">
        <li class="nav-item px-3">
            <NavLink class="nav-link" href="" Match="NavLinkMatch.All">
                <span class="oi oi-home" aria-hidden="true"></span> Home
            </NavLink>
        </li>
        <li class="nav-item px-3">
            <NavLink class="nav-link" href="counter">
                <span class="oi oi-plus" aria-hidden="true"></span> Counter
            </NavLink>
        </li>
        <li class="nav-item px-3">
            <NavLink class="nav-link" href="fetchdata">
                <span class="oi oi-list-rich" aria-hidden="true"></span> Fetch data
            </NavLink>
        </li>
        <li class="nav-item px-3">
            <NavLink class="nav-link" href="user">
                <span class="oi oi-list-rich" aria-hidden="true"></span> User
            </NavLink>
        </li>
    </ul>
</div>

@code {
    private bool collapseNavMenu = true;

    private string NavMenuCssClass => collapseNavMenu ? "collapse" : null;

    private void ToggleNavMenu()
    {
        collapseNavMenu = !collapseNavMenu;
    }
}

 

 

 이 부분은 

<div class="top-row pl-4 navbar navbar-dark">
    <a class="navbar-brand" href="">BlazorApp</a>
    <button class="navbar-toggler" @onclick="ToggleNavMenu">
        <span class="navbar-toggler-icon"></span>
    </button>
</div>

이 처럼 화면이 작아지는 모바일 환경에서 메뉴표현등으로 나타내기 위한 버튼으로 화면이 큰 윈도우 환경에선 기본적으로는 화면을 줄이지 않는 이상 안보일 수 있다

 

 

NavLink : 이동 시키는 <a href > 와 유사한데 비주얼 적으로 차이가 있는데 버튼을 누를때 스타일 적인 것만 다르다

상단에 Home, FetchData 등의 버튼을 참고하면 된다

 NavLink 는

<a href  를 써서 동일하게 구현할 수도 있고

NavigationManager 를 인젝션 하여

 이렇게 구현하여 구현 할 수도 있다

 

counter 페이지가 뜰때 다음처럼 값을 받을 수도 있다 값은 CurrentCount 로 들어가게 된다

 

 

https://localhost:44337/counter/30  30을 입력하면 CurrentCount 값이 30 이 된다

 

 

 

 

ref : https://blog.naver.com/whro152/223041082396

반응형
반응형

[색상 변경하기 예제]

 

 

Cascading Parameter 는 변수를 전달하는데 다른 페이지에

<Cascading Parameter 로 동일하게 선언되어 있는 변수가 있다면 그곳 까지 값이 전달된다

 

TableTemplate 은 C# 의 tempalte 같은 것인데 당연히 동일하지 않고 엇비슷하다


Cascading Parameter알아보기

만약 우리가 동일한 Parameter변수들을 넘겨주는 경우가 발생한다면 한번에 흘러내리는 것처럼

변수를 넘겨주게 하는 문법이다.

사용 방법은 매우 간단한데

<CascadingValue Name ="ThemeColor" Value="_selectedColor">

<ShowUser ...>

<ShowUser2 ...>

</CascadingValue>

태그를 사용하여 사용할 범위를 지정해 준다.

Name은 구분하기 위한 Key이며 Value는 범위내에 뿌릴 값이다.

이렇게 사용하게 되면 범위내에 있는 ShowUser, ShowUser2의 컴포넌트에 자동적으로 Value = "_selectedColor"

Parameter가 흘러내리듯이 정해지게 된다.

이를 ShowUser,Showuser2의 컴포넌트에서 사용하기 위해서는

[CascadingParameter(Name = "ThemeColor")]

string _color {get; set;}

위와 같이 사용하여 _color의 값에 Value="_selectedColor"가 Binding된것처럼 사용할 수 있게 된다.

[추가]

컴포넌트의 깊이에 상관없이 Parameter가 전달된다는 특징이 있다.

[Templated Component]알아보기

C#의 제너릭형식과 유사한 문법이다.

RenderFragment로 이루어진 Razor Component이다.

[넘어가는 이야기]

[c#에는 c++에 없는 partial class가 존재한다.

이는 a라는 파일에서 partial class a를 작성하고

추후에 b라는 파일에서 aprtial class a를 이어서 작성하게 되면 두 class를 하나로 합친 것처럼

작성하게 해주는 키워드이다. 현재 Blazor와는 상관없는 c#문법이므로 넘어가도 상관없다.] 딱히 이 부분은 크게 상관없는 부분이다.

 

정리하자면 TableTemplate.razor를 생성한다.

1) 코드 부분에 [Parameter] public RenderFragment Header {get; set;}를 작성한다.

2) HTML 부분에 사용하기 위한 장소에 @Header를 선언해준다.

3) 다른 razor 컴포넌트에서 <TableTemplate></TableTemplate>를 선언해준다.

4) <TableTemplate></TableTemplate>의 범위안에 <Header></Header>를 서언하여

RenderFragment Header를 사용한다는 것을 알려준다.

5) <Hedaer></Header>의 범위안에 사용하고 싶은 HTML의 태그들을 지정해준다.

6) 이렇게 사용하게 되면 TableTemplate.razor의 @Header부분에 지정한 태그들이 들어가게 된다.

 

 

TableTemplate 예시

노란색 화살표가 해당 부분이 .razor 페이지로 들어가는 부분이 된다

이때 해당 부분을 대체하는데 쓰이는 역할로 RenderFragment 가 사용된다 : 태그를 그리는 느낌

TableTemplate 을 통해 보여지는 부분의 개수를 변경하거나 스타일을 다르게 하는 등이 가능해질 수 있다

 

 

ref : https://blog.naver.com/whro152/223039845752

반응형
반응형

User.razor 전체 코드

 

컴포넌트 분리

저번 포스팅에 @code부분인 AddUser()와 KickUser()를 컴포넌트화 시키기 위해 새로운 Blazor파일을 생성해 분리하도록 해보자

ShowUser 블레이저 파일 생성

ShowUser.razor

ShowUser.razor파일을 생성해 User.razor에있던 리스트 생성과 유저 추가, 삭제하는 함수들을 옮겼다.

 

설명

User.razor

-<ShowUser></ShowUser> : 새로 생성한 ShowUser.razor를 사용하기 위한 처리

-<ShowUser User ="_users"> : ShowUser.razor의 User변수와 User.razor의 _user의 변수를 공유한다

-@ref ="_showUser" : 부모쪽에서 자식에 접근하는 방식(이 처리가 없으면 @code에 ShowUser는 null임)

CallbackTest="CallbackTestFunc" : 콜백을 사용할 때

 

ShowUser.razor

[Parameter] : 를 통해  다른 블레이저 파일에서 접근 가능하도록 명시한다.

(블레이저에서는 Action보다는 EventCallback을 추천한다. StateHasChanged()가 없어도 리프래시 처리 )

 

유저리스트에 요소 추가

유저리스트에 요소 삭제

"CallbackTest"문자가 보임 : 콜백 호출이 잘되고 있다

 

 

ref : https://funfunhanblog.tistory.com/419

반응형
반응형
@page "/bind"

<p>
    <input @bind="inputValue" />
</p>

<p>
    <input @bind="InputValue" />
</p>

<ul>
    <li><code>inputValue</code>: @inputValue</li>
    <li><code>InputValue</code>: @InputValue</li>
</ul>

@code {
    private string? inputValue;

    private string? InputValue { get; set; }
}

위 처럼 string 변수에 있는 문자 내용을 input 쪽 변수에  바인딩 시킬수도 있고

 

<button class="@inputValue"   type="button"

처럼 버튼의 스타일 또한 변경이 가능하다

<button class="@inputValue"   type="button" disabled="@(_users.Count() >= 5)"

이렇게 유저의 카운트가 0 보다 크면 버튼이 안보이게 처리 또한 가능하다

_users 이 글을 참고해보면 된다

https://3dmpengines.tistory.com/2375

 

만약 데이터가 html 즉 UI 상에 변경된 것이 보이지 않는 상황이 다음 time 관련 코드에서 적용이 안될 수 있는데

 

 

이때는 StateHasChanged(); 함수를 호출해주면 변경된 값이 HTML/UI 상에 보이게 된다

 

 

반응형
반응형

유저의 목록과 추가및 제거에 대한 변수와 함수를 바인딩하는 것을 알아본다

 

기본적으로 html 부분에 code 쪽의 변수가 바인딩 되어 있는 경우 변수가 바뀔때 자동으로 데이터가 html 상에서 갱신된다

 

[유저 목록과 추가하기]

유저의 목록 기본적으로 3개를 만들어 for 문으로 3개가 보여지도록 처리해놓는다

@page "/user"
@using BlazorApp.Data;

<h3>Online Users</h3>

<p>
    Users : <b>@_users.Count</b>
</p>
<br/>

<ul class="list-group">
    @foreach(var user in _users)
    {
        <li @key="user" class="list-group-item">
            <label>@user.Name</label>
        </li>
    }
</ul>


<br/>

<div class="contrainer">
    <div class="row">
        <div class="col-md-6">
            <input type="text" class="form-control" placeholder="Add User" @bind-value="_inputName"/>
        </div>
        <div class="col-md-6">
            <button class="btn btn-primary" type="button" @onclick="AddUser" > Add a user </button>
        </div>
    </div>

</div>


@code {
    List<UserData> _users = new List<UserData>();

    string _inputName;

    protected override void OnInitialized()
    {
        _users.Add(new UserData { Name = "John" });
        _users.Add(new UserData { Name = "Jane" });
        _users.Add(new UserData { Name = "Jack" });
    }

    void AddUser()
    {
        _users.Add(new UserData { Name = _inputName });
        _inputName = string.Empty;
    }

}

 

user 명을 추가 하면 li 목록에 추가 되는 코드이다

 

함수 AddUser 또한 @onclick= 에 연결 할 수 있다

 

 

 

 

[제거]

@page "/user"
@using BlazorApp.Data;

<h3>Online Users</h3>

<p>
    Users : <b>@_users.Count</b>
</p>
<br/>

<ul class="list-group">
    @foreach(var user in _users)
    {
        <li @key="user" class="list-group-item">
            <button type="button" class=" btn btn-link" @onclick="(()=>KickUser(user))" > X </button>
            <label>@user.Name</label>
        </li>
    }
</ul>


<br/>

<div class="contrainer">
    <div class="row">
        <div class="col-md-6">
            <input type="text" class="form-control" placeholder="Add User" @bind-value="_inputName"/>
        </div>
        <div class="col-md-6">
            <button class="btn btn-primary" type="button" @onclick="AddUser" > Add a user </button>
        </div>
    </div>

</div>


@code {
    List<UserData> _users = new List<UserData>();

    string _inputName;

    protected override void OnInitialized()
    {
        _users.Add(new UserData { Name = "John" });
        _users.Add(new UserData { Name = "Jane" });
        _users.Add(new UserData { Name = "Jack" });
    }

    void AddUser()
    {
        _users.Add(new UserData { Name = _inputName });
        _inputName = string.Empty;
    }

    void KickUser(UserData user)
    {
        _users.Remove(user);
    }

}

 

 

이름 앞에 X 표시 버튼을 추가하였고 x 를 누르면 유저목록에서 유저가 제거된다

기본 3개에서 하나를 제거한 상태이다

 

@onclick="(()=>KickUser(user))"  이 코드는 람다형태로 onclick="KickUser(user)" 의 형태로는 지원이 되지 않기 때문에 람다로 연결해주면 가능하다

 

반응형
반응형

 

 

Binding.razor 페이지를 만들고 다음 처럼 작성한다

@code 부분의 변수가 html 코드 중 @_value 로 연결 되게 처리 할 수 있고

html 에서는 @bind-value=  들로 @code 쪽 갑과 연결 되어 양방향 연결이 되게 할 수 있다

@page "/binding"

<h3>Binding</h3>

<p>Value : @_value</p>

<input type="range" step="1" @bind-value="_value" @bind-value:event="oninput" />

@code {
    int _value = 10;

}

 

 

 

메뉴 레이아웃중 하단에 binding 텝을 만들어주면

<div class="top-row pl-4 navbar navbar-dark">
    <a class="navbar-brand" href="">BlazorApp</a>
    <button class="navbar-toggler" @onclick="ToggleNavMenu">
        <span class="navbar-toggler-icon"></span>
    </button>
</div>

<div class="@NavMenuCssClass" @onclick="ToggleNavMenu">
    <ul class="nav flex-column">
        <li class="nav-item px-3">
            <NavLink class="nav-link" href="" Match="NavLinkMatch.All">
                <span class="oi oi-home" aria-hidden="true"></span> Home
            </NavLink>
        </li>
        <li class="nav-item px-3">
            <NavLink class="nav-link" href="counter">
                <span class="oi oi-plus" aria-hidden="true"></span> Counter
            </NavLink>
        </li>
        <li class="nav-item px-3">
            <NavLink class="nav-link" href="fetchdata">
                <span class="oi oi-list-rich" aria-hidden="true"></span> Fetch data
            </NavLink>
        </li>
        <li class="nav-item px-3">
            <NavLink class="nav-link" href="binding">
                <span class="oi oi-list-rich" aria-hidden="true"></span> Binding
            </NavLink>
        </li>
    </ul>
</div>

@code {
    private bool collapseNavMenu = true;

    private string NavMenuCssClass => collapseNavMenu ? "collapse" : null;

    private void ToggleNavMenu()
    {
        collapseNavMenu = !collapseNavMenu;
    }
}

 

 

환경은 Blazor Server 이다

반응형

+ Recent posts