[Blazor] javascript import 방법

:pushpin: 해결책은 맨 마지막에 나와있습니다.

:spiral_notepad:시행착오를 겪으면서 찾아본 문서

https://learn.microsoft.com/ko-kr/aspnet/core/blazor/javascript-interoperability/?view=aspnetcore-6.0

문서에서 나온대로 따라하면 제대로 되지 않습니다. github에도 버그로 기록되어 Merge된 것으로 보아 MS공식문서이지만 신뢰할 수는 없는듯 하네요..https://github.com/dotnet/AspNetCore.Docs/issues/28044

MS권장방법1 : _Layout.cshtml에서 blazor.server.js 로딩 이후에 import

<body>
    ...
    <script src="_framework/blazor.{webassembly|server}.js"></script>
    <script src="{SCRIPT PATH AND FILE NAME (.js)}"></script>
</body>

적용 결과(실패)

@using Microsoft.AspNetCore.Components.Web
@namespace BlazorApp1.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

<!DOCTYPE html>
<html dir="ltr" lang="en">

<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta name="description" content="">
    <meta name="author" content="">
    <link rel="shortcut icon" type="image/png" href="assets/images/logos/favicon.png" />
    <link rel="stylesheet" href="assets/css/styles.min.css" />
    <component type="typeof(HeadOutlet)" render-mode="ServerPrerendered" />
</head>

<body>
    @RenderBody()

    <div id="blazor-error-ui">
        <environment include="Staging,Production">
            An error has occurred. This application may no longer respond until reloaded.
        </environment>
        <environment include="Development">
            An unhandled exception has occurred. See browser dev tools for details.
        </environment>
        <a href="" class="reload">Reload</a>
        <a class="dismiss">🗙</a>
    </div>

    <!-- Default -->
    <script src="_framework/blazor.server.js"></script>
    <!-- External js -->
    <!-- ============================================================== -->
    <script src="assets/libs/jquery/dist/jquery.min.js"></script>
    <script src="assets/libs/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
    <script src="assets/js/sidebarmenu.js"></script>
    <script src="assets/js/app.min.js"></script>
    <script src="assets/libs/apexcharts/dist/apexcharts.min.js"></script>
    <script src="assets/libs/simplebar/dist/simplebar.js"></script>
    <script src="assets/js/dashboard.js"></script>
</body>
</html>

MS권장방법2 : _Layout.cshtml에서 blazor.server.js 로딩할 때 autostart 속성을 false로 바꾸고, 별도의 JS함수로 Blazor Start 이후 document에 script를 로드한다.

<body>
    ...
    <script src="_framework/blazor.{webassembly|server}.js" 
        autostart="false"></script>
    <script>
      Blazor.start().then(function () {
        var customScript = document.createElement('script');
        customScript.setAttribute('src', 'scripts.js');
        document.head.appendChild(customScript);
      });
    </script>
</body>

적용결과 (아래와 같이 열심히 삽질하여 하드코딩했으나 또 실패)

@using Microsoft.AspNetCore.Components.Web
@namespace BlazorApp1.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

<!DOCTYPE html>
<html dir="ltr" lang="en">

<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta name="description" content="">
    <meta name="author" content="">
    <link rel="shortcut icon" type="image/png" href="assets/images/logos/favicon.png" />
    <link rel="stylesheet" href="assets/css/styles.min.css" />
    <component type="typeof(HeadOutlet)" render-mode="ServerPrerendered" />
</head>

<body>
    @RenderBody()

    <div id="blazor-error-ui">
        <environment include="Staging,Production">
            An error has occurred. This application may no longer respond until reloaded.
        </environment>
        <environment include="Development">
            An unhandled exception has occurred. See browser dev tools for details.
        </environment>
        <a href="" class="reload">Reload</a>
        <a class="dismiss">🗙</a>
    </div>

    <!-- Default -->
    <script src="_framework/blazor.server.js" autostart="false"></script>
    <!-- External js -->
    <script>
        Blazor.start().then(function () {
            var customScript1 = document.createElement('script');
            customScript1.setAttribute('src', 'assets/libs/jquery/dist/jquery.min.js'); document.head.appendChild(customScript1);
            var customScript2 = document.createElement('script');
            customScript2.setAttribute('src', 'assets/libs/bootstrap/dist/js/bootstrap.bundle.min.js'); document.head.appendChild(customScript2);
        
            var customScript3 = document.createElement('script');
            customScript3.setAttribute('src', 'assets/js/sidebarmenu.js'); document.head.appendChild(customScript3);
            var customScript4 = document.createElement('script');
            customScript4.setAttribute('src', 'assets/js/app.min.js'); document.head.appendChild(customScript4);
            var customScript5 = document.createElement('script');
            customScript5.setAttribute('src', 'assets/libs/apexcharts/dist/apexcharts.min.js'); document.head.appendChild(customScript5);
            var customScript6 = document.createElement('script');
            customScript6.setAttribute('src', 'assets/libs/simplebar/dist/simplebar.js'); document.head.appendChild(customScript6);
            var customScript7 = document.createElement('script');
            customScript7.setAttribute('src', 'assets/js/dashboard.js'); document.head.appendChild(customScript7);
        });
    </script>
*@
</body>
</html>

시행착오3) blazor.server.js가 로딩되기 이전에 호출되도록 순서를 바꾼다 ⇒ 실패

공식문서에서는 반드시 blazor.server.js를 호출한 뒤에 외부 Javascript를 불러오라고 되어있으나, 안됩니다.

github에 bug로 기록되어 1월에 고쳐진 것으로 보이며, 그 해결책으로는 호출순서를 바꾸라고 되어있으나 그렇게 해도 안됩니다.


:pushpin:지금부터 해결책을 설명합니다.

가장 먼저, Blazor 페이지의 상단에 IJSRuntime을 DI합니다.

@page "/test"
@inject IJSRuntime JS

그런 다음 페이지의 코드 부분에 OnAfterRenderAsync 메서드를 override하여 JSRuntime을 사용해서 Import하는 것입니다. 필요한 Javascript 파일을 각각 import하는 코드를 작성하면 됩니다.

@code {
    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            await JS.InvokeAsync<IJSObjectReference>("import", "./assets/libs/jquery/dist/jquery.min.js");
            await JS.InvokeAsync<IJSObjectReference>("import", "./assets/libs/bootstrap/dist/js/bootstrap.bundle.min.js");
            await JS.InvokeAsync<IJSObjectReference>("import", "./assets/js/sidebarmenu.js");
            await JS.InvokeAsync<IJSObjectReference>("import", "./assets/js/app.min.js");
            await JS.InvokeAsync<IJSObjectReference>("import", "./assets/libs/apexcharts/dist/apexcharts.min.js");
            await JS.InvokeAsync<IJSObjectReference>("import", "./assets/libs/simplebar/dist/simplebar.js");
            await JS.InvokeAsync<IJSObjectReference>("import", "./assets/js/dashboard.js");
        }
    }
}

이 방법은 Blazor에서 Javascript와 상호작용(Interaction)할 때도 마찬가지로 쓸 수 있습니다.

해결책이 나와있는 링크

https://code-maze.com/how-to-call-javascript-code-from-net-blazor-webassembly/
(해결책만 찾았다하면 마제형님꺼네요..)

적용 결과

6개의 좋아요

정말좋은 팁이네요 고생 삽질하신것이 보이네요

4개의 좋아요

“개발은 8할이 삽질이다”
평균 지능을 가진 제가 마음 속에 새겨놓은 좌우명입니다…

6개의 좋아요

ASP.NET Core Razor 구성 요소 수명 주기 | Microsoft Learn

해당 내용은 공식문서에서도 찾아보실 수 있습니다. 예전에 trumbowyg 넣어보면서 삽질한 경험이 있어 기억나네요 ㅎㅎ

프론트에 영향을 주는 JS를 사용하는 경우에는 수명주기를 신경써야하는 것 같더라구요

4개의 좋아요

@나가코 감사합니다! 한번 읽어볼게요

3개의 좋아요

좋은 경험 공유해주셔서 감사합니다 ㅠㅠ 고생 많으셨겠어요.

2개의 좋아요