Blazor 5x - قسمت یازدهم - مبانی Blazor - بخش 8 - کار با جاوا اسکریپت
اندازه‌ی قلم متن
تخمین مدت زمان مطالعه‌ی مطلب: ده دقیقه

در حین کار با برنامه‌های وب، چشم‌پوشی از جاوا اسکریپت عملا ممکن نیست؛ هرچند با Blazor، امکان انجام کارهایی را یافته‌ایم که پیشتر با MVC و یا Razor pages میسر نبودند، اما هیچگاه به تنهایی نمی‌تواند جایگزین کامل جاوا اسکریپت، در تولید برنامه‌های وب باشد. بنابراین ضروری است که نحوه‌ی یکپارچگی جاوا اسکریپت را با برنامه‌های مبتنی بر Blazor، بررسی کنیم.


ایجاد کامپوننت جدید BlazorJS

برای بررسی نحوه‌ی تعامل جاوا اسکریپت و Blazor، در ابتدا کامپوننت جدید Pages\LearnBlazor\BlazorJS.razor را ایجاد کرده:
@page "/BlazorJS"

<h3>BlazorJS</h3>

@code
{
}
و همچنین مدخل منوی آن‌را نیز بر اساس مسیریابی ابتدای فایل این کامپوننت، به فایل Shared\NavMenu.razor اضافه می‌کنیم:
<li class="nav-item px-3">
    <NavLink class="nav-link" href="BlazorJS">
        <span class="oi oi-list-rich" aria-hidden="true"></span> BlazorJS
    </NavLink>
</li>


روش فراخوانی کدهای جاوا اسکریپتی از طریق کدهای سی‌شارپ Blazor

فرض کنید می‌خواهیم در حین کلیک بر روی دکمه‌ای مانند دکمه‌ی حذف، ابتدا تائیدیه‌ای را توسط تابع confirm جاوا اسکریپتی، از کاربر اخذ کنیم. روش انجام چنین کاری در برنامه‌های مبتنی بر Blazor به صورت زیر است:
@page "/BlazorJS"

@inject IJSRuntime JsRuntime

<h3>BlazorJS</h3>

<div class="row">
    <button class="btn btn-secondary" @onclick="TestConfirmBox">Test Confirm Button</button>
</div>
<div class="row">
    @if (ConfirmResult)
    {
        <p>Confirmation has been made!</p>
    }
    else
    {
        <p>Confirmation Pending!</p>
    }
</div>

@code {
    string ConfirmMessage = "Are you sure you want to click?";
    bool ConfirmResult;

    async Task TestConfirmBox()
    {
        ConfirmResult = await JsRuntime.InvokeAsync<bool>("confirm", ConfirmMessage);
    }
}
توضیحات:
- در اینجا می‌خواهیم تابع استاندارد confirm جاوا اسکریپتی را از طریق کدهای سی‌شارپ، با کلیک بر روی دکمه‌ی Test Confirm Button، فراخوانی کنیم. به همین جهت onclick@ این دکمه، به متد TestConfirmBox کدهای UI سی‌شارپ این کامپوننت، متصل شده‌است.
- برای دسترسی به توابع جاوا اسکریپتی، نیاز است سرویس توکار IJSRuntime را به کدهای کامپوننت تزریق کنیم که روش انجام آن‌را توسط دایرکتیو inject@ مشاهده می‌کنید. برای دسترسی به این سرویس توکار، نیاز به تنظیمات ابتدایی خاصی نیست و اینکار پیشتر انجام شده‌است.
- سرویس JsRuntime تزریق شده، دو متد مهم InvokeVoidAsync و InvokeAsync را جهت فراخوانی توابع جاوا اسکریپتی به همراه دارد. اگر تابعی، خروجی غیر void داشته باشد، باید از متد InvokeAsync استفاده کرد. برای مثال خروجی تابع استاندارد confirm، از نوع boolean است. بنابراین نوع این خروجی را به صورت یک آرگومان جنریک متد InvokeAsync مشخص کرده‌ایم.
- اولین پارامتر متد InvokeAsync، نام رشته‌ای تابع جاوا اسکریپتی است که قرار است صدا زده شود. پارامترهای اختیاری بعدی که به صورت params object?[]? args تعریف شده‌اند، لیست نامحدود آرگومان‌های ورودی این متد هستند.
- فیلد ConfirmMessage، پیامی را جهت اخذ تائید، تعریف می‌کند که به عنوان پارامتر متد confirm، توسط JsRuntime.InvokeAsync فراخوانی خواهد شد.
- فیلد ConfirmResult، نتیجه‌ی فراخوانی متد confirm جاوا اسکریپتی را به همراه دارد.
- در اینجا روش عکس العمل نشان دادن به خروجی دریافتی از متد جاوااسکریپتی را نیز مشاهده می‌کنید. پس از پایان متد TestConfirmBox که یک متد رویدادگران است، همانطور که در مطلب بررسی «چرخه‌ی حیات کامپوننت‌ها» نیز بررسی کردیم، متد StateHasChanged، در پشت صحنه فراخوانی می‌شود که سبب رندر مجدد UI خواهد شد. بنابراین در حین رندر مجدد UI، بر اساس مقدار جدید ConfirmResult دریافت شده‌ی از کاربر، با تشکیل یک if/else@، می‌توان به نتیجه‌ی تائید یا عدم تائید کاربر، واکنش نشان داد. با این توضیحات در اولین بار نمایش کامپوننت جاری چون مقدار ConfirmResult مساوی false است، پیام زیر را مشاهده می‌کنیم:


اما در ادامه با کلیک بر روی دکمه و تائید پیام ظاهر شده، عبارت زیر ظاهر می‌شود:



روش افزودن یک کتابخانه‌ی خارجی جاوا اسکریپتی به پروژه‌های Blazor

فرض کنید می‌خواهیم پیام‌های برنامه را توسط کتابخانه‌ی معروف جاوا اسکریپتی Toastr نمایش دهیم؛ با این دمو.
مرحله‌ی اول کار با این کتابخانه، دریافت فایل‌های CSS و JS آن است. برای این منظور قصد داریم از برنامه‌ی مدیریت بسته‌های LibMan استفاده کنیم:
dotnet tool install -g Microsoft.Web.LibraryManager.Cli
libman init
libman install bootstrap --provider unpkg --destination wwwroot/lib/bootstrap
libman install jquery --provider unpkg --destination wwwroot/lib/jquery
libman install toastr --provider unpkg --destination wwwroot/lib/toastr
بنابراین خط فرمان را در ریشه‌ی پروژه گشوده و پنج دستور فوق را اجرا می‌کنیم. دستور اول، ابزار خط فرمان LibMan را نصب می‌کند. دستور دوم، یک فایل libman.json خالی را در این پوشه ایجاد می‌کند و سه دستور بعدی، جی‌کوئری، بوت استرپ و toastr را دریافت و در پوشه‌ی wwwroot/lib قرار می‌دهند. Toastr برای اجرا، نیاز به jQuery نیز دارد.
البته تعاریف مداخل آن‌ها به فایل libman.json نیز اضافه می‌شوند. مزیت آن، اجرای دستور libman restore برای بازیابی و نصب مجدد تمام بسته‌های ذکر شده‌ی در فایل libman.json است.

پس از دریافت بسته‌های سمت کلاینت آن، مداخل مرتبط را به فایل Pages\_Host.cshtml برنامه‌ی Blazor Server اضافه خواهیم کرد (و یا در فایل wwwroot/index.html برنامه‌های Blazor WASM).
<head>
    <base href="~/" />
    <link rel="stylesheet" href="lib/toastr/build/toastr.min.css" />

</head>
<body>
 
    <script src="lib/jquery/dist/jquery.min.js"></script>
    <script src="lib/toastr/build/toastr.min.js"></script>
    <script src="_framework/blazor.server.js"></script>
</body>
مدخل فایل css آن‌را در قسمت head و فایل js آن‌را پیش از بسته شدن تگ body تعریف می‌کنیم. در اینجا نیازی به ذکر پوشه‌ی آغازین wwwroot نیست؛ چون base href تعریف شده، به این پوشه اشاره می‌کند.

یک نکته: می‌توان فایل csproj برنامه را به صورت زیر تغییر داد تا کار اجرای دستور libman restore را قبل از build، به صورت خودکار انجام دهد:
<Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup>
    <TargetFramework>net5.0</TargetFramework>
  </PropertyGroup>

  <Target Name="DebugEnsureLibManEnv" BeforeTargets="BeforeBuild" Condition=" '$(Configuration)' == 'Debug' ">
    <!-- Ensure libman is installed -->
    <Exec Command="libman --version" ContinueOnError="true">
      <Output TaskParameter="ExitCode" PropertyName="ErrorCode" />
    </Exec>
    <Error Condition="'$(ErrorCode)' != '0'" Text="libman is required to build and run this project. To continue, please run `dotnet tool install -g Microsoft.Web.LibraryManager.Cli`, and then restart your command prompt or IDE." />
    <Message Importance="high" Text="Restoring dependencies using 'libman'. This may take several minutes..." />
    <Exec WorkingDirectory="$(MSBuildProjectDirectory)" Command="libman restore" />
  </Target>
</Project>


روش فراخوانی یک کتابخانه‌ی خارجی جاوا اسکریپتی در پروژه‌های Blazor

پس از افزودن فایل‌های سمت کلاینت toastr و تعریف مداخل آن در فایل Pages\_Host.cshtml برنامه‌ی Blazor Server جاری، اکنون می‌خواهیم از این کتابخانه استفاده کنیم. یک روش کار با این نوع کتابخانه‌های عمومی و سراسری به صورت زیر است:
- ابتدا فایل خالی جدید wwwroot\js\common.js را ایجاد می‌کنیم.
- سپس تابع عمومی و سراسری ShowToastr را بر اساس امکانات کتابخانه‌ی toastr و مستندات آن، به صورت زیر ایجاد می‌کنیم:
window.ShowToastr = (type, message) => {
  // Toastr don't work with Bootstrap 4.2
  toastr.options.toastClass = "toastr"; // https://github.com/CodeSeven/toastr/issues/599

  if (type === "success") {
    toastr.success(message, "Operation Successful", { timeOut: 20000 });
  }
  if (type === "error") {
    toastr.error(message, "Operation Failed", { timeOut: 20000 });
  }
};
چون تابع ShowToastr به شیء window انتساب داده شده‌است، در سراسر برنامه‌ی جاری قابل دسترسی است.
سطر اول آن هم برای رفع عدم تداخل با بوت استرپ 4x اضافه شده‌است. بوت استرپ 4x به همراه کلاس‌های CSS مشابهی است که نیاز است با تنظیم toastClass به مقداری دیگر، این تداخل را برطرف کرد.

- در ادامه مدخل تعریف فایل wwwroot\js\common.js را به انتهای تگ body فایل Pages\_Host.cshtml اضافه می‌کنیم:
    <script src="lib/jquery/dist/jquery.min.js"></script>
    <script src="lib/toastr/build/toastr.min.js"></script>
    <script src="js/common.js"></script>
    <script src="_framework/blazor.server.js"></script>
</body>

در آخر برای آزمایش آن به کامپوننت Pages\LearnBlazor\BlazorJS.razor مراجعه کرده و تابع سراسری ShowToastr را دقیقا مانند روشی که در مورد تابع confirm بکار بردیم، توسط سرویس JsRuntime، فراخوانی می‌کنیم:
@page "/BlazorJS"

@inject IJSRuntime JsRuntime


<div class="row">
    <button class="btn btn-success" @onclick="@(()=>TestSuccess("Success Message"))">Test Toastr Success</button>
    <button class="btn btn-danger" @onclick="@(()=>TestFailure("Error Message"))">Test Toastr Failure</button>
</div>

@code {
    async Task TestSuccess(string message)
    {
        await JsRuntime.InvokeVoidAsync("ShowToastr", "success", message);
    }

    async Task TestFailure(string message)
    {
        await JsRuntime.InvokeVoidAsync("ShowToastr", "error", message);
    }
}
در اینجا دو دکمه، جهت فراخوانی متد ShowToastr با پارامترهای مختلفی تعریف شده‌اند. چون تابع ShowToastr خروجی ندارد، به همین جهت اینبار از متد InvokeVoidAsync استفاده کرده‌ایم. پارامتر اول آن، نام متد ShowToastr است. پارامتر‌های دوم و سوم آن با آرگومان‌های (type, message) تعریف شده‌ی تابع ShowToastr تطابق دارند. به علاوه در این مثال، روش ارسال پارامترها را نیز در onlick@ توسط arrow functions مشاهده می‌کنید.



کاهش کدهای تکراری فراخوانی متدهای جاوا اسکریپتی با تعریف متدهای الحاقی

می‌توان جهت کاهش تکرار کدهای استفاده از تابع ShowToastr، متدهای الحاقی زیر را برای سرویس IJSRuntime تهیه کرد:
using System.Threading.Tasks;
using Microsoft.JSInterop;

namespace BlazorServerSample.Utils
{
    public static class JSRuntimeExtensions
    {
        public static ValueTask ToastrSuccess(this IJSRuntime JSRuntime, string message)
        {
            return JSRuntime.InvokeVoidAsync("ShowToastr", "success", message);
        }

        public static ValueTask ToastrError(this IJSRuntime JSRuntime, string message)
        {
            return JSRuntime.InvokeVoidAsync("ShowToastr", "error", message);
        }
    }
}
و سپس فضای نام آن‌را به فایل Imports.razor_ معرفی نمود تا در تمام کامپوننت‌های برنامه قابل استفاده شوند.
@using BlazorServerSample.Utils
به این ترتیب به فراخوانی‌های ساده شده‌ی زیر خواهیم رسید:
async Task TestSuccess(string message)
{
   //await JsRuntime.InvokeVoidAsync("ShowToastr", "success", message);
   await JsRuntime.ToastrSuccess(message);
}


فراخوانی یک متد عمومی واقع در کامپوننت فرزند از طریق کامپوننت والد

فرض کنید در کامپوننت فرزند Pages\LearnBlazor\LearnBlazor‍Components\ChildComponent.razor که در قسمت‌های قبل آن‌را تکمیل کردیم، متد عمومی زیر تعریف شده‌است:
@inject IJSRuntime JsRuntime


@code {
    // ...

    public async Task TestSuccess(string message)
    {
        await JsRuntime.ToastrSuccess(message);
    }
}
اکنون اگر بخواهیم این متد عمومی را از طریق کامپوننت والد یا دربرگیرنده‌ی آن فراخوانی کنیم، نیاز است از مفهوم جدیدی به نام ref استفاده کرد. برای این منظور به کامپوننت Pages\LearnBlazor\ParentComponent.razor مراجعه کرده و تغییرات زیر را اعمال می‌کنیم:
@page "/ParentComponent"

<ChildComponent
    OnClickBtnMethod="ShowMessage"
    @ref="ChildComp"
    Title="This title is passed as a parameter from the Parent Component">
    <ChildContent>
        A `Render Fragment` from the parent!
    </ChildContent>
    <DangerChildContent>
        A danger content from the parent!
    </DangerChildContent>
</ChildComponent>

<div class="row">
    <button class="btn btn-success" @onclick="@(()=>ChildComp.TestSuccess("Done!"))">Show Alert</button>
</div>


@code {
    ChildComponent ChildComp;
    // ...
}
با استفاده از ref@ که به فیلد ChildComp انتساب داده شده‌است، می‌توان ارجاعی از کامپوننت فرزند را (وهله‌ای از کلاس مرتبط با آن‌را) در کامپوننت جاری بدست آورد و سپس از آن جهت فراخوانی متدهای عمومی کامپوننت فرزند استفاده کرد.


کدهای کامل این مطلب را از اینجا می‌توانید دریافت کنید: Blazor-5x-Part-11.zip
  • #
    ‫۳ سال و ۴ ماه قبل، پنجشنبه ۲ اردیبهشت ۱۴۰۰، ساعت ۱۶:۵۲
    یک نکته‌ی تکمیلی: امکان فراخوانی کدهای #C از طریق کدهای جاوااسکریپت، در برنامه‌های Blazor

    در مطلب جاری، روش فراخوانی توابع جاوااسکریپتی را از طریق کدهای #C برنامه‌های Blazor بررسی کردیم؛ عکس آن نیز میسر است و یکی از کاربردهای آن، ارسال نتایج کتابخانه‌های جاوااسکریپتی، به کدهای یک کامپوننت است. برای مثال کاربری در یک کامپوننت تقویم باز شده، روزی را انتخاب می‌کند. می‌خواهیم نتیجه‌ی این انتخاب او را که در سمت کدهای جاوااسکریپتی رخ‌داده، به نحوی به کدهای #C یک کامپوننت منتقل کنیم و یا حتی محاسباتی را در سمت کدهای #C انجام دهیم و به کدهای جاوااسکریپتی منتقل کنیم.

    الف) فراخوانی متدهای استاتیک #C از طریق کدهای جاوااسکریپتی
    فرض کنید متد استاتیک HelpMessage را می‌خواهیم از طریق کدهای جاوااسکریپتی فراخوانی کنیم. برای این منظور، یک چنین تابعی باید به ویژگی JSInvokable مزین شود:
    @page "/js-sample"
    
    
    <button class="btn btn-primary" onclick="JsFunctionHelper.invokeDotnetStaticFunction()">Invoke Static Method</button>
    
    @code
    {
        [JSInvokable]
        public static Task<string> HelpMessage()
        {
            return Task.FromResult("Help text from C# static function");
        }
    }
    در اینجا یک دکمه را هم مشاهده می‌کنید که از ویژگی onclick استاندارد HTML استفاده کرده‌است. یعنی متدی را که فراخوانی می‌کند، در حقیقت یک کد جاوا اسکریپتی است و نه یک متد #C واقع در کامپوننت جاری.
    سپس در سمت در فایل Client\wwwroot\main.js برای فراخوانی متد HelpMessage خواهیم داشت:
    window.JsFunctionHelper = {
      invokeDotnetStaticFunction: function () {
        DotNet.invokeMethodAsync("BlazorRazorSample.Client", "HelpMessage").then(
          (data) => {
            console.log(data);
          }
        );
      }
    };
    در اینجا تابع سراسری جدیدی به نام invokeDotnetStaticFunction تعریف شده‌است (همان تابعی که توسط دکمه‌ی قرار گرفته در کامپوننت فراخوانی می‌شود). این تابع با استفاده از متد DotNet.invokeMethodAsync استاندارد Blazor، کار فراخوانی متد استاتیک HelpMessage واقع در فضای نام BlazorRazorSample.Client را انجام می‌دهد. چون این فراخوانی async است، نتیجه‌ی نهایی را از طریق یک callback دریافت کرده و لاگ می‌کند.

    ب) فراخوانی متدهای غیر استاتیک #C از طریق کدهای جاوااسکریپتی
    فراخوانی instance methodهای کامپوننت‌ها از طریق کدهای #C، کمی پیچیده‌تر است:
    @page "/js-sample"
    @implements IDisposable
    
    @inject IJSRuntime jSRuntime
    
    <button class="btn btn-primary" @onclick="CallInstanceMethod">Invoke Instance Method</button>
    
    @code
    {
        private DotNetObjectReference<JsSample> objectReference;
    
        [JSInvokable]
        public string GetAddress()
        {
            return "123 Main Street";
        }
    
        protected override async Task OnAfterRenderAsync(bool firstRender)
        {
            if(firstRender)
            {
                objectReference = DotNetObjectReference.Create(this);
            }
        }
    
        private async Task CallInstanceMethod()
        {
            await jSRuntime.InvokeVoidAsync("JsFunctionHelper.invokeDotnetInstanceFunction", objectReference);
        }
    
        public void Dispose()
        {
            objectReference?.Dispose();
        }
    }
    - در این حالت نیاز است ارجاعی از وهله‌ی کامپوننت جاری را به متد جاوااسکریپتی ارسال کرد. به همین جهت در ابتدا توسط متد DotNetObjectReference.Create، این ارجاع را ایجاد کرده و سپس توسط متد jSRuntime.InvokeVoidAsync آن‌را به سمت کدهای جاوا اسکریپتی ارسال می‌کنیم. در مثال فوق، JsSample همان نام کامپوننت جاری است.
    - همچنین در اینجا onclick تعریف شده، به متدی داخل همین کامپوننت اشاره می‌کند.
    - این ارجاع نیز باید در پایان کار کامپوننت، Dispose شود. به همین جهت implements IDisposable@ را مشاهده می‌کنید.

    اکنون کدهای جاوا اسکریپتی که از این وهله‌ی دریافتی استفاده می‌کند، به صورت زیر خواهد بود. در این کدها addressProvider همان objectReference دریافتی است که توسط آن می‌توان متد غیراستاتیک GetAddress کامپوننت را فراخوانی کرد:
    window.JsFunctionHelper = {
      invokeDotnetInstanceFunction: function (addressProvider) {
        addressProvider.invokeMethodAsync("GetAddress").then((data) => {
          console.log(data);
        });
      }
    };
    • #
      ‫۳ سال و ۲ ماه قبل، شنبه ۲۹ خرداد ۱۴۰۰، ساعت ۰۴:۱۶
      آیا راه حلی برای فراخوانی متدهای غیر استاتیک #C از طریق کدهای جاوااسکریپتی در یک فایل js وجود دارد؟ برای مثال API گوگل مپ جزییات یک آدرس درخواست شده را به یک فایل js برمیگرداند، از چه روشی میتوان این مقدار را به کامپوننت برای پردازش و ذخیره ارسال کرد؟ 

      کامپوننتی که کار پردازش نتیجه API را انجام میدهد باید خروجی خود را بصورت یک EventCallBack به والد خود برای ذخیره ارسال کند، از این جهت متدها استاتیک نیستند. آیا از روش غیر استاتیک که معرفی کردید برای این سناریو میشود استفاده کرد یا روش دیگری وجود دارد؟ 
      ممنون.
      • #
        ‫۳ سال و ۲ ماه قبل، شنبه ۲۹ خرداد ۱۴۰۰، ساعت ۰۴:۲۱
        آیا از روش غیر استاتیک که معرفی کردید برای این سناریو میشود استفاده کرد 
        بله.
        • #
          ‫۱ سال و ۹ ماه قبل، شنبه ۷ آبان ۱۴۰۱، ساعت ۰۰:۴۲
          باسلام؛ من توابع کتابخانه‌های جاوا اسکریپتی که سراسری هستند و در تمام کامپوننت‌های برنامه قابل دسترسی خواهند بود به صفحه index.html اضافه کردم .مشکل اینجاست که یکی از  کتابخانه‌های جاوا اسکرپیتی، یکی از متدهاش نیاز به یک تگ html داخل MainLayout.razor  دارد که احیانا چون این کتابخانه قبل ساخت کامل  کد‌های اhtm صدا زده میشود که تگ   html  رو پیدا نمیکند و به خطا میخورد.  چه روشی برای اینکه کتابخانه با تاخیر اجرا شود یا بعد از لود کامل  کدهای html؟
  • #
    ‫۳ سال و ۴ ماه قبل، پنجشنبه ۲ اردیبهشت ۱۴۰۰، ساعت ۱۸:۴۱
    یک نکته‌ی تکمیلی: امکان بارگذاری با تاخیر فایل‌های جاوااسکریپتی در برنامه‌های Blazor

    در مطلب جاری، فرض بر این است که توابع جاوا اسکریپتی، سراسری هستند و قرار است در تمام کامپوننت‌های برنامه قابل دسترسی باشند. به همین جهت ارجاع مستقیمی از فایل js. آن‌ها را در فایل index.html و یا Host_، قرار می‌دهیم. اما اگر تنها یک کامپوننت، نیاز به اسکریپت خاصی را داشته باشد و نه تمام کامپوننت‌های دیگر، چطور؟
    در این حالت Blazor از مفهومی به نام JavaScript Isolation پشتیبانی می‌کند. برای توضیح آن، فایل جدید Client\wwwroot\MyMdl.Js را به پروژه اضافه کرده و سپس به صورت زیر تکمیل می‌کنیم:
    export function showPrompt(message) {
      return prompt(message, "Type name");
    }
    
    export function showAlert(message) {
      return prompt(message, "Hello");
    }
    - همانطور که مشاهده می‌کنید، در اینجا توابع export شده‌اند (جزو پیشنیازهای JavaScript Isolation است) و در حقیقت یک ES-6 module تشکیل شده‌است.
    - برخلاف قبل، مدخل جدیدی را از این فایل، به فایل‌های index.html و یا Host_  اضافه نمی‌کنیم. چون می‌خواهیم فقط کامپوننتی که به آن نیاز دارد، آن‌را بارگذاری کند.

    سپس کامپوننت جدید Client\Pages\JsIsolation.razor را به صورت زیر تکمیل خواهیم کرد:
    @page "/js-isolation"
    
    @inject IJSRuntime jSRuntime
    
    <button class="btn btn-primary" @onclick="Prompt">Prompt</button>
    <button class="btn btn-primary" @onclick="ShowAlert">Alert</button>
    
    
    @code
    {
        private IJSObjectReference module;
    
        protected override async Task OnAfterRenderAsync(bool firstRender)
        {
            if(firstRender)
            {
                module = await jSRuntime.InvokeAsync<IJSObjectReference>("import", "./MyMdl.Js");
            }
        }
    
        private async Task Prompt()
        {
            var result = await module.InvokeAsync<string>("showPrompt", "What's your name?");
        }
    
        private async Task ShowAlert()
        {
            await module.InvokeVoidAsync("showAlert", "Hello!");
        }
    }
    - کار در قسمت OnAfterRenderAsync و در اولین بار رندر کامپوننت شروع می‌شود. در اینجا روش بارگذاری و import یک ماژول جاوااسکریپتی را مشاهده می‌کنید. در این حالت، این فایل js. پس از فراخوانی متد InvokeAsync بارگذاری شده و اطلاعات آن تنها در همین کامپوننت قابل دسترسی خواهد بود.
    - اکنون که module یا IJSObjectReference را در اختیار داریم، می‌توان با استفاده از متدهای InvokeAsync و یا InvokeVoidAsync، با متدهای موجود در آن کار کرد.
  • #
    ‫۳ سال و ۴ ماه قبل، پنجشنبه ۲ اردیبهشت ۱۴۰۰، ساعت ۱۹:۴۶
    یک نکته‌ی تکمیلی: امکان نوشتن فایل‌های CSS اختصاصی کامپوننت‌ها در برنامه‌های Blazor

    در نکته‌ی قبلی، در مورد JavaScript Isolation بحث شد؛ یک چنین قابلیتی در مورد فایل‌های css. هم وجود دارد و جزو تازه‌های Blazor 5x است. در این حالت (CSS Isolation) می‌توان شیوه‌نامه‌ای را تهیه کرد که تنها مختص به یک کامپوننت است و بر روی سایر کامپوننت‌ها تاثیری ندارد. این قابلیت نیاز به کدنویسی خاصی نداشته و به سادگی قابل استفاده‌است: در این حالت اگر کامپوننتی برای مثال Counter.razor نام دارد، برای ایجاد فایل css مختص به آن، تنها کافی است در کنار فایل آن، فایل Counter.razor.css را ایجاد و تکمیل کرد. به این ترتیب هر شیوه‌نامه‌ای که در این فایل تنظیم شود، فقط بر روی کامپوننت Counter تاثیرگذار خواهد بود؛ حتی اگر از این کامپوننت در کامپوننت‌های دیگر نیز استفاده شود.
    h1 {
        color:red;
    }
    برای نمونه شیوه‌نامه‌ی فوق تنها به المان h1 کامپوننت Counter اعمال می‌شود و هیچ تاثیری را بر روی سایر کامپوننت‌های برنامه نخواهد داشت:


    همانطور که مشاهده می‌کنید، در زمان build برنامه، تمام این شیوه‌نامه‌های اختصاصی کامپوننت‌های مختلف برنامه، تبدیل به یک فایل خواهند شد (با نام project_name.styles.css) که توسط ویژگی‌های HTML ای که به صورت خودکار تولید می‌شوند، از یکدیگر متمایز خواهند شد.

     اگر علاقمند باشیم تا این شیوه‌نامه به فرزندان کامپوننت Counter نیز به ارث برسد، باید از روش زیر استفاده کرد (استفاده از deep combinator):
    ::deep h1 {
       color:red;
    }
  • #
    ‫۳ سال و ۴ ماه قبل، شنبه ۴ اردیبهشت ۱۴۰۰، ساعت ۱۵:۲۸
    یک نکته‌ی تکمیلی: دریافت خروجی مستقیم از کدهای جاوااسکریپتی در برنامه‌های Blazor، بدون تهیه‌ی فایل‌های js. خارجی
    با استفاده از تابع eval جاوااسکریپت می‌توان کدهای جاوااسکریپتی را به صورت مستقیم هم اجرا کرد؛ چند مثال:
    - اجرای یک سطر کد جاوااسکریپتی به صورت مستقیم و دریافت خروجی آن:
    var timeZoneOffSet = await jsRuntime.InvokeAsync<int>("eval", "new Date().getTimezoneOffset()");
    - فراخوانی یک متد خود اجرا شونده و دریافت خروجی آن:
    var ianaTimeZoneName = jsRuntime.Invoke<string>("eval",
        "(function(){try { return ''+ Intl.DateTimeFormat().resolvedOptions().timeZone; } catch(e) {} return 'UTC';}())");
  • #
    ‫۳ سال و ۲ ماه قبل، شنبه ۲۹ خرداد ۱۴۰۰، ساعت ۲۳:۴۷
    برای دستیابی به متدهای غیر استاتیکی سی شارپ در کامپوننت‌ها از طریق کدهای جاوااسکریپتی و بدون فراخوانی آنها توسط IJSRuntime میتوان از روش زیر استفاده کرد.
    var object;
    
    window.JsFunctionHelper = {MapSuccessResponse: function (instance, address) {
           object = instance;
           return instance.invokeMethodAsync("GetAddress", valuesFromApi);
           }
     };
    
     function getValuesFromApi() {
          .
          .
          .
    
          object.invokeMethodAsync("GetAddress", valuesFromApi);
     }
    در این مثال API گوگل مپ جزییات یک آدرس درخواست شده را به یک فایل js برمیگرداند، و میخواهم این مقادیر را به کامپوننت برای پرازش ارسال کنم. ابتدا یک متغیر با دسترسی global تعریف میکنیم و آن را برابر با  ارجاعی که از وهله‌ی کامپوننت جاری ساخته شده قرار میدهیم. در نهایت شی  invokeMethodAsync از این روش برای فراخوانی متد به همراه پارامتر در کامپوننت در دسترس است.
    نحوه امکان فراخوانی کدهای #C از طریق کدهای جاوااسکریپت در برنامه‌های Blazor به عنوان یک نکته تکمیلی در بخش نظرات همین پست آمده.

  • #
    ‫۳ سال قبل، یکشنبه ۱۷ مرداد ۱۴۰۰، ساعت ۱۳:۲۳
    یک نکته‌ی تکمیلی: روش ارسال یک المان razor به کدهای جاوا اسکریپتی
    فرض کنید قصد داریم قطعه کد جاوا اسکریپتی زیر را که یک المان را جهت هدایت فوکوس به آن، نیاز دارد، در کدهای #C فراخوانی کنیم:
    window.setFocus = function (element) {
      element.focus();
    };
    برای مقدار دهی این element در کدهای #C باید به صورت زیر عمل کرد:
    الف) در اینجا نیز از ref@ برای دسترسی به المان استفاده خواهیم کرد:
    <input @ref="@ReferenceToInputControl" />
    در این حالت input ای که توسط Blazor رندر می‌شود، چنین شکلی را پیدا می‌کند:
    <input _bl_bc0f34fa-16bd-4687-a8eb-9e3838b5170d="">
    هدف این است که بدون دستکاری Id المان، بتواند آن‌را به صورت منحصربفردی مشخص کند.

    ب) سپس شیء ReferenceToInputControl را در کدهای کامپوننت به صورت زیر تعریف می‌کنیم:
    private ElementReference ReferenceToInputControl;
    که قابلیت ارسال به کدهای جاوا اسکریپتی را دارد:
    await JSRuntime.InvokeVoidAsync("setFocus", ReferenceToInputControl);
    در این قطعه کد، ReferenceToInputControl همان element ای است که تابع setFocus تعریف شده نیاز دارد.

    توجه! اگر قصد دارید قطعه کد #C فوق را در روال‌های رویدادگردان چرخه‌ی حیات کامپوننت فراخوانی کنید، اینکار باید در OnAfterRender صورت گیرد؛ چون پیش از آن ref@ هنوز تشکیل نشده‌است.
  • #
    ‫۳ سال قبل، یکشنبه ۲۴ مرداد ۱۴۰۰، ساعت ۱۳:۳۸
    روش استفاده از TypeScript در پروژه‌های Blazor
    شاید علاقمند باشید تا اسکریپت‌های مورد نیاز یک پروژه‌ی Blazor را با TypeScript تهیه کنید؛ تا از مزایای بررسی نوع‌ها، intellisense قوی، null checking و غیره بهره‌مند شوید و سپس توسط کامپایلر آن، حاصل را به کدهای نهایی js تبدیل کنید. برای اینکار می‌توان مراحل زیر را طی کرد:

    الف) تهیه فایل تنظیمات کامپایلر TypeScript
    نیاز است فایل tsconfig.json را در ریشه‌ی پروژه، جائیکه فایل csproj قرار دارد، با محتوای زیر ایجاد کرد:
    {
      "compilerOptions": {
        "strict": true,
        "removeComments": false,
        "sourceMap": false,
        "noEmitOnError": true,
        "target": "ES2020",
        "module": "ES2020",
        "outDir": "wwwroot/scripts"
      },
      "include": [
        "Scripts/**/*.ts"
      ],
      "exclude": [
        "node_modules"
      ]
    }
    در این حالت فرض بر این است که فایل‌های ts. در پوشه‌ی scripts قرار گرفته‌اند و فایل‌های نهایی کامپایل شده در پوشه‌ی wwwroot/scripts تولید خواهند شد.

    ب) فعالسازی کامپایلر TypeScript به ازای هر بار build برنامه
    برای اینکار نیاز است فایل csproj را به صورت زیر تکمیل کرد:
    <Project Sdk="Microsoft.NET.Sdk.Razor">
      <ItemGroup>
        <PackageReference Include="Microsoft.TypeScript.MSBuild" Version="4.3.5">
          <PrivateAssets>all</PrivateAssets>
          <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
        </PackageReference>
      </ItemGroup>
      <ItemGroup>
        <Content Remove="tsconfig.json" />
      </ItemGroup>
      <ItemGroup>
        <TypeScriptCompile Include="tsconfig.json">
          <CopyToOutputDirectory>Never</CopyToOutputDirectory>
        </TypeScriptCompile>
      </ItemGroup>
    </Project>
    با اینکار ابزار TypeScript.MSBuild اضافه شده، بر اساس tsconfig.json قسمت الف، کار کامپایل فایل‌های ts را به صورت خودکار انجام می‌دهد.

    ج) یک مثال از تبدیل کدهای js به ts
    فرض کنید کدهای سراسری زیر را داریم که به شیء window اضافه شده‌اند:
    window.exampleJsFunctions = {
      showPrompt: function (message) {
        return prompt(message, 'Type anything here');
      }
    };
    اکنون برای تبدیل آن به ts.، می‌توان به صورت زیر، فضای نام و کلاسی را ایجاد کرد:
    namespace JSInteropWithTypeScript {
       export class ExampleJsFunctions {
            public showPrompt(message: string): string {
                return prompt(message, 'Type anything here');
            }
        }
    }
    
    export function showPrompt(message: string): string {
       var fns = new JSInteropWithTypeScript. ExampleJsFunctions();
       return fns.showPrompt(message);
    }
    قسمت مهم آن، export function انتهایی است. این موردی است که توسط Blazor قابل شناسایی و استفاده است.

    د) روش استفاده از خروجی کامپایل شده‌ی TypeScript در کامپوننت‌های Blazor
    پس از کامپایل قطعه کد فوق، ابتدا مسیر قابل دسترسی به فایل js قرار گرفته شده در پوشه‌ی wwwroot را مشخص می‌کنیم که همواره با الگوی زیر است. همچنین اینبار IJSObjectReference است که امکان دسترسی به export function یاد شده را میسر می‌کند:
    private const string ScriptPath = "./_content/----namespace-here---/scripts/file.js";
    private IJSObjectReference scriptModule;
    دو تعریف فوق، فیلدهایی هستند که در سطح کامپوننت تعریف می‌شوند. سپس مقدار دهی آن‌ها در OnAfterRenderAsync صورت می‌گیرد تا کار import ماژول را انجام دهد:
    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
      if (scriptModule == null)
        scriptModule = await JSRuntime.InvokeAsync<IJSObjectReference>("import", ScriptPath);
    پس از این مرحله، امکان کار با ماژول بارگذاری شده، به صورت متداولی میسر می‌شود و می‌توان export function‌ها را در اینجا فراخوانی کرد:
    await scriptModule.InvokeVoidAsync("exported fn name", params);
    در آخر کار هم باید آن‌را dispose کرد؛ که روش آن به صورت زیر است:
    - ابتدا باید این کامپوننت، IAsyncDisposable را پیاده سازی کند:
    public partial class MyComponent : IAsyncDisposable
    سپس پیاده سازی آن به صورت زیر انجام می‌شود:
    public async ValueTask DisposeAsync()
    {
      if (scriptModule != null)
      {
        await scriptModule.DisposeAsync();
      }
    }
  • #
    ‫۲ سال و ۹ ماه قبل، شنبه ۲۲ آبان ۱۴۰۰، ساعت ۱۹:۴۶
    تکمیل JavaScript Isolation در Blazor 6x

    همانطور که کمی بالاتر نیز عنوان شد، CSS Isolation جزئی از تازه‌های Blazor 5x بود؛ اکنون مشابه این قابلیت جهت فایل‌های js. به Blazor 6x هم اضافه شده‌است و روش کار با آن نیز همانند CSS Isolation است (البته این قابلیت از نگارش 5 هم وجود داشت؛ اما اینبار دیگر نیازی به تعریف فایل ماژول آن در wwwroot نیست). یعنی اگر کامپوننت ما در مسیر Pages/Panel.razor قرار داشته باشد، می‌توان برای این تک فایل، فایل js. متناظری را به نام Pages/Panel.razor.js تعریف کرد؛ با الگوی Component>.razor.js>. سپس اگر محتوی این فایل js. به صورت زیر باشد:
    export function error(){
        alert('oops, an error');
    }
    روش فراخوانی آن در همان کامپوننت به صورت زیر خواهد بود (یعنی در اصل دیگر مهم نیست که این فایل js. کجا قرار می‌گیرد، هنگام publish به خروجی کپی خواهد شد):
    var module = await JS.InvokeAsync<IJSObjectReference>("import", "./Panel.razor.js");
    await module.InvokeVoidAsync("error");
    مزیت اینکار، نزدیک نگه داشتن static assets یک کامپوننت، در کنار آن است و به این ترتیب قابل درک‌تر کردن برنامه:
    Pages/Panel.razor
    Pages/Panel.razor.js
    Pages/Panel.razor.css
    به علاوه JS Isolation دو مزیت دیگر را هم به همراه دارد:
    - دیگر نیازی به تعریف توابع و متدهای جاوا اسکریپتی در global namespace نیست (این متدها و اشیاء، به شیء سراسری window اضافه نمی‌شوند). Isolation در اینجا در اصل به معنای امکان استفاده‌ی از JavaScript modules است.
    - استفاده کنندگان از پروژه‌های کتابخانه‌ای دیگر نیازی به الحاق دستی این فایل‌ها ندارند. منظور از الحاق دستی یا ذکر src تگ script اضافه شده به index.html، در مطلب تولید کتابخانه‌های Razor توضیح داده شده‌است و از الگوی content/{PACKAGE ID}/{SCRIPT PATH AND FILENAME (.js)}_/. جهت مشخص سازی نام نهایی فایل js. به همراه کتابخانه، پیروی می‌کند. البته اگر نیاز است این نوع فایل‌ها در کتابخانه‌ها مستقیما استفاده شوند، باید مسیر فوق در کدها حتما ذکر شود:
    _module = await JS.InvokeAsync<IJSObjectReference>("import", "./_content/RazorClassLibrary/componentName.razor.js");

    یک نکته: روش صحیح کار با ماژول‌ها همانطور که در نکات فوق نیز بررسی شد، به صورت زیر است و باید در OnAfterRenderAsync شروع شده و سپس در آخر کار Dispose شوند:
    export function showPrompt(message) {
      return prompt(message, 'Type anything here');
    }

    @page "/call-js-example-6"
    @implements IAsyncDisposable
    @inject IJSRuntime JS
    
    <h1>Call JS Example 6</h1>
    
    <p>
        <button @onclick="TriggerPrompt">Trigger browser window prompt</button>
    </p>
    
    <p>
        @result
    </p>
    
    @code {
        private IJSObjectReference? module;
        private string? result;
    
        protected override async Task OnAfterRenderAsync(bool firstRender)
        {
            if (firstRender)
            {
                module = await JS.InvokeAsync<IJSObjectReference>("import", 
                    "./scripts.js");
            }
        }
    
        private async Task TriggerPrompt()
        {
            result = await Prompt("Provide some text");
        }
    
        public async ValueTask<string?> Prompt(string message) =>
            module is not null ? 
                await module.InvokeAsync<string>("showPrompt", message) : null;
    
        async ValueTask IAsyncDisposable.DisposeAsync()
        {
            if (module is not null)
            {
                await module.DisposeAsync();
            }
        }
    }
    • #
      ‫۲ سال و ۹ ماه قبل، شنبه ۲۲ آبان ۱۴۰۰، ساعت ۲۰:۵۵
      اضافه شدن JavaScript initializers   به Blazor 6x

      در اینجا می‌توان فایل ویژه‌ای به نام NAME.lib.module.js را به پوشه‌ی wwwroot پروژه اضافه کرد که name آن، همان نام اسمبلی، کتابخانه و در اصل package identifier پروژه‌است؛ با این محتوا:
      export function beforeStart(options, extensions) {
          console.log("beforeStart");
      }
      
      export function afterStarted(blazor) {
          console.log("afterStarted");
      }
      قالب این محتوا باید به همین نحو باشد و معرف اجرای کدهایی پیش از و پس از load برنامه است. به این ترتیب می‌توان به این مزایا دست یافت:
      - سفارشی سازی نحوه‌ی بارگذاری یک برنامه‌ی Blazor
      - اجرای کدهای سفارشی، پیش و پس از بارگذاری برنامه
      - امکان تنظیم ویژگی‌های Blazor

      یک مثال: بارگذاری یک اسکریپت پس از کامل شدن بارگذاری Blazor
      <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>
  • #
    ‫۲ سال و ۹ ماه قبل، شنبه ۲۲ آبان ۱۴۰۰، ساعت ۲۱:۰۹
    امکان رندر کامل یک کامپوننت Blazor توسط کدهای جاوااسکریپتی در Blazor 6x

    مرحله‌ی اول آماده سازی یک کامپوننت، جهت دسترسی به آن توسط کدهای جاوااسکریپتی، ثبت آن به نحو زیر در فایل Program.cs است:
    builder.RootComponents.RegisterForJavaScript<Counter>(identifier: "counter");
    البته نمونه‌ی blazor server آن به صورت زیر است:
    builder.Services.AddServerSideBlazor(options =>
    {
        options.RootComponents.RegisterForJavaScript<Counter>(identifier: "counter");
    });
    پس از آن، کدهای جاوااسکریپتی فراخوان کامپوننت Counter، به صورت زیر خواهند بود:
    <button onclick="callCounter()">Call Counter</button>
    
    <script>
      async function callCounter() {
         let containerElement = document.getElementById('my-counter');
         await Blazor.rootComponents.add(containerElement, 'counter', { incrementAmount: 10 });      
      }
    </script>
     
    <div id="my-counter">
    </div>
    که نتیجه‌ی نهایی این فراخوانی، در div ای با id مساوی my-counter، رندر خواهد شد. در اینجا نحوه‌ی ارسال پارامتری را نیز به این کامپوننت، مشاهده می‌کنید.
  • #
    ‫۲ سال و ۹ ماه قبل، شنبه ۲۲ آبان ۱۴۰۰، ساعت ۲۳:۱۳
    امکان تبدیل رخدادهای توکار مرورگرها به دایرکتیوهای Blazor در Blazor6x

    یکسری دایرکتیو مانند onclick@ و امثال آن، از پیش در Blazor تعریف شده‌اند که امکان مدیریت رویدادهای جاوااسکریپتی را در کدهای سی‌شارپ میسر می‌کنند. اما تعداد این‌ها زیاد نیست. برای مثال تعداد رویدادهای قابل تعریف و پشتیبانی شده‌ی توسط مرورگرها قابل ملاحظه‌است. در Blazor 6x روشی جهت دسترسی ساده‌تر به این رویدادها ارائه شده‌است که شامل این مراحل است. برای نمونه فرض کنید می‌خواهیم به رویداد paste مرورگر دسترسی پیدا کنیم و یک دایرکتیو سفارشی oncustompaste@ را برای آن تهیه کنیم:
    <input @oncustompaste="HandleCustomPaste" />
    برای اینکار در ابتدا قطعه کد زیر را پس از blazor.webassembly.js در فایل index.html ثبت می‌کنیم (یا می‌توان از روش export function afterStarted که در بالا عنوان شد هم استفاده کرد):
    <script> 
        Blazor.registerCustomEventType('custompaste', { 
            browserEventName: 'paste', 
            createEventArgs: event => { 
                // This example only deals with pasting text, but you could use arbitrary JavaScript APIs 
                // to deal with users pasting other types of data, such as images 
                return { 
                    eventTimestamp: new Date(), 
                    pastedData: event.clipboardData.getData('text') 
                }; 
            } 
        }); 
    </script>
    در اینجا برای رویداد paste مرورگر، تعدادی آرگومان تهیه شده و بازگشت داده می‌شود. آرگومان اول در اینجا یک مقدار اختیاری و نمایشی‌است و آرگومان دوم به شیء رویداد paste، دسترسی یافته و متن آن‌را بازگشت می‌دهد.
    پس از اینکار، معادل دو پارامتر بازگشت داده شده را به صورت زیر در کدهای سی‌شارپ تهیه می‌کنیم:
    namespace BlazorCustomEventArgs.CustomEvents 
    { 
        [EventHandler("oncustompaste", typeof(CustomPasteEventArgs), enableStopPropagation: true, enablePreventDefault: true)] 
        public static class EventHandlers 
        { 
            // This static class doesn't need to contain any members. It's just a place where we can put 
            // [EventHandler] attributes to configure event types on the Razor compiler. This affects the 
            // compiler output as well as code completions in the editor. 
        } 
     
        public class CustomPasteEventArgs : EventArgs 
        { 
            // Data for these properties will be supplied by custom JavaScript logic 
            public DateTime EventTimestamp { get; set; } 
            public string PastedData { get; set; } 
        } 
    }
    ابتدا از EventArgs ارث‌بری شده و معادل تاریخ و متن بازگشت داده شده، تبدیل به یک EventArgs سفارشی می‌شود. سپس نوع آن، به ویژگی EventHandler ای که بالای یک کلاس استاتیک خالی قرار گرفته شده، ارسال می‌شود. اینکار صرفا جهت اطلاع کامپایلر صورت می‌گیرد.
    یک نکته: در اینجا نام oncustompaste به همان نام custompaste کدهای جاوااسکریپتی اشاره می‌کند. نام تعریف شده‌ی در قسمت سی‌شارپ، یک on در ابتدا اضافه‌تر دارد. اینکار سبب می‌شود که اکنون بتوان یک رویدادگردان oncustompaste@ سفارشی را که قابل مدیریت در کدهای سی‌شارپ است، داشت:
    @page "/"
    
    <p>Try pasting into the following text box:</p>
    <input @oncustompaste="HandleCustomPaste" />
    <p>@message</p>
    
    @code {
        string message;
        void HandleCustomPaste(CustomPasteEventArgs eventArgs)
        {
            message = $"At {eventArgs.EventTimestamp.ToShortTimeString()}, you pasted: {eventArgs.PastedData}";
        }
    }
  • #
    ‫۲ سال و ۸ ماه قبل، سه‌شنبه ۷ دی ۱۴۰۰، ساعت ۱۶:۱۰
    یک نکته: نمی‌توان تگ‌های اسکریپت را داخل فایل‌های کامپوننت‌های Blazor قرار داد
    اگر سعی کنیم به هر دلیلی یک تگ script را داخل یک فایل کامپوننت Blazor قرار دهیم، با خطای کامپایلر زیر متوقف خواهیم شد:
    error RZ9992: Script tags should not be placed inside components because they cannot be updated dynamically.
    To fix this, move the script tag to the 'index.html' file or another static location. For more information see https://go.microsoft.com/fwlink/?linkid=872131
    علت اینجا است که رندر مجدد کامپوننت‌ها در Blazor، می‌توانند سبب اجرای چندین باره‌ی یک چنین اسکریپت‌هایی شوند. به همین جهت تصمیم گرفته‌اند که inline-scripts را ممنوع کنند. برای رفع این خطا یا می‌توان به روشی که عنوان کرده عمل کرد و محتوای اسکریپت را به یک فایل مجزا منتقل کرد (همانند نکات این مطلب) و یا اگر به هر دلیلی نمی‌خواهیم این‌کار را انجام دهیم، می‌توان به صورت زیر عمل کرد تا خطای کامپایلر فوق ندید گرفته شود ( یعنی می‌دانیم که قرار است چه اتفاقی رخ دهد):
    <script suppress-error="BL9992" />
  • #
    ‫۲ سال و ۳ ماه قبل، پنجشنبه ۱۵ اردیبهشت ۱۴۰۱، ساعت ۰۸:۲۴
    امکان تهیه Custom Elements در NET 6 Blazor.

    در آخرین نسخه Blazor این امکان فراهم شده است که بتوانیم از کامپوننت‌های Blazor درون پروژه‌های React/Vue, Angular, ... استفاده کنیم (+). البته این فیچر هنوز به صورت آزمایشی می‌باشد و ممکن است API آن تغییر کند. 
    در ادامه یک مثال از این قابلیت را مشاهده خواهید کرد. 
    ایجاد پروژه Blazor
    یک دایرکتوری ایجاد کرده و درون آن یک پروژه blazorwasm با نام blazor_wasm ایجاد کنید:
    dotnet new blazorwasm blazor_wasm
    
    برای استفاده از این فیچر میبایست پکیج  Microsoft.AspNetCore.Components.CustomElements را نصب کنیم:
    dotnet add package Microsoft.AspNetCore.Components.CustomElements --version 0.1.0-alpha.21466.1
    در ادامه یک کامپوننت Todo ایجاد خواهیم کرد:
    @page "/todo"
    
    <PageTitle>Todo</PageTitle>
    
    <h1>Todo (@todos.Count(todo => !todo.IsDone))</h1>
    
    <ul>
        @foreach (var todo in todos)
        {
            <li>
                <input type="checkbox" @bind="todo.IsDone" />
                <input @bind="todo.Title" />
            </li>
        }
    </ul>
    
    <input placeholder="Something todo" @bind="newTodo" />
    <button @onclick="AddTodo">Add todo</button>
    
    @code {
    
        public class TodoItem
        {
            public string? Title { get; set; }
            public bool IsDone { get; set; }
        }
    
        private List<TodoItem> todos = new();
        private string? newTodo;
    
        private async void AddTodo(MouseEventArgs e)
        {
            if (!string.IsNullOrWhiteSpace(newTodo))
            {
                todos.Add(new TodoItem { Title = newTodo });
                newTodo = string.Empty;
            }
        }
    }
    برای تبدیل کامپوننت فوق به یک Custom Element درون فایل Program.cs خط زیر را اضافه میکنیم:
    builder.RootComponents.RegisterAsCustomElement<Todo>("todo-element");

    استفاده از کامپوننت فوق درون یک پروژه React
    npx create-react-app blazor_react && cd blazor_react
    برای استفاده از Custom Element موردنظر دو خط زیر را به فایل public/index.html اضافه میکنیم:
    <script src="_content/Microsoft.AspNetCore.Components.CustomElements/BlazorCustomElements.js"></script>
    <script src="_framework/blazor.webassembly.js"></script>
    همچنین لازم است یک پراکسی نیز درون پروژه ایجاد کنیم (درون فایل package.json)؛ با اینکار اسکریپت‌های موردنیاز فوق از سمت سرور دریافت خواهند شد:
    "proxy": "BLAZOR_APP_ADDRESS", // for example: http://localhost:5269 
    در نهایت درون فایل App.js می‌توانیم از کامپوننت Todo استفاده کنیم:
    function App() {
      return (
        <div className="App">
          <todo-element />
        </div>
      );
    }
    
    export default App;
     

  • #
    ‫۱ سال و ۵ ماه قبل، جمعه ۱۲ اسفند ۱۴۰۱، ساعت ۲۲:۳۹
    سلام
    پروژه هتل رو از گیت هاب گرفتم، تقریبا بدون مشکل اجرا شد، بعد از ارتقا به net 7. با خطاهای زیادی روبرو شدم، این دوتا رو نمیدونم چکارش باید کرد.

      • #
        ‫۱ سال و ۵ ماه قبل، شنبه ۱۳ اسفند ۱۴۰۱، ساعت ۰۴:۴۳
        پروژه WebAPI این خطا رو میده
        System.InvalidOperationException: The request reached the end of the pipeline without executing the endpoint: 'BlazorWasm.WebApi.Controllers.HotelAmenityController.GetHotelAmenities (BlazorWasm.WebApi)'. Please register the EndpointMiddleware using 'IApplicationBuilder.UseEndpoints(...)' if using routing.
           at Microsoft.AspNetCore.Builder.ApplicationBuilder.<>c.<Build>b__18_0(HttpContext context)
           at Microsoft.AspNetCore.Builder.Extensions.MapMiddleware.Invoke(HttpContext context)
           at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
           at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
           at Swashbuckle.AspNetCore.SwaggerUI.SwaggerUIMiddleware.Invoke(HttpContext httpContext)
           at Swashbuckle.AspNetCore.Swagger.SwaggerMiddleware.Invoke(HttpContext httpContext, ISwaggerProvider swaggerProvider)
           at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddlewareImpl.Invoke(HttpContext context)

  • #
    ‫۱ سال و ۵ ماه قبل، دوشنبه ۱۵ اسفند ۱۴۰۱، ساعت ۱۷:۴۹
    یک نکته‌ی تکمیلی: ساده سازی بازیابی بسته‌های libman
    می‌شود عملیات libman restore را که توسط وظیفه‌ی DebugEnsureLibManEnv در این مطلب انجام شده، صرفا با افزودن بسته‌ی زیر نیز پیاده سازی و جایگزین کرد:
    <PackageReference Include="Microsoft.Web.LibraryManager.Build" Version="2.1.175" />
    • #
      ‫۱ سال و ۵ ماه قبل، جمعه ۱۹ اسفند ۱۴۰۱، ساعت ۱۲:۵۶
      چگونه از طریق خط فرمان یک بسته‌ی نصب شده‌ی توسط libman را به روز کنیم؟
      فقط کافی است در پوشه‌ای که libman.json وجود دارد، دستور libman update packageName را صادر کنیم.
  • #
    ‫۱ سال و ۵ ماه قبل، شنبه ۲۷ اسفند ۱۴۰۱، ساعت ۰۰:۰۴
    آیا با استفاده از یکی از این روشها امکان استفاده از کنترل‌های جاوا اسکریپتی دوو اکستریم شرکت دوو اکسپرس نیز در بلیزر وجود دارد؟ به عنوان مثال استفاده از کامپوننت گرید آن فریموورک به شکلی منطقی در بلیزر قابلیت پیاده سازی و بهره برداری دارد یا خیر؟ 
    • #
      ‫۱ سال و ۵ ماه قبل، شنبه ۲۷ اسفند ۱۴۰۱، ساعت ۰۰:۱۱
      بله. ولی نیازی به اینکار نیست. نگارش بعدی Blazor به همراه یک Grid توکار خواهد بود که هم اکنون هم قابل استفاده‌است. به علاوه کامپوننت‌های اختصاصی Blazor هم به همراه Grid نوشته شده‌ی با خود Blazor هستند.