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

تا اینجا به صورت بسیار مختصری با نحوه‌ی مسیریابی برنامه‌های مبتنی بر Blazor توسط دایرکتیو page@ آشنا شدیم. برای مثال با داشتن تعریف زیر در ابتدای یک کامپوننت:
@page "/LearnRouting"

<h3>Learn Routing</h3>
این کامپوننت جدید، صرفنظر از محل قرارگیری فایل آن که برای مثال در پوشه‌ی Pages\LearnBlazor\LearnRouting.razor است، در مسیر https://localhost:5001/LearnRouting قابل دسترسی خواهد شد و برای تعریف مدخل منوی جدید آن، به کامپوننت Shared\NavMenu.razor مراجعه کرده و NavLink جدیدی را برای آن تعریف می‌کنیم:
<li class="nav-item px-3">
    <NavLink class="nav-link" href="LearnRouting">
        <span class="oi oi-list-rich" aria-hidden="true"></span> Learn Routing
    </NavLink>
</li>
در اینجا برچسب مدخل جدید تعریف شده، Learn Routing است و href لینک به آن، دقیقا به مسیریابی تعریف شده اشاره می‌کند.

یک نکته: مسیریابی‌های تعریف شده‌ی در Blazor، حساس به حروف کوچک و بزرگ نیستند.


امکان تعریف بیش از یک مسیریابی برای یک کامپوننت نیز وجود دارد

در کامپوننت‌های Blazor، محدودیتی از لحاظ تعداد بار تعریف دایرکتیو page@ وجود ندارد:
@page "/LearnRouting"
@page "/NewRouting"

<h3>Learn Routing</h3>
در این حالت می‌توان در حین تعریف یک مسیریابی جدید، مسیریابی قبلی را نیز حفظ کرد. در اینجا کامپوننت فوق، از طریق هر دو آدرس https://localhost:5001/LearnRouting و https://localhost:5001/NewRouting تعریف شده، قابل دسترسی‌است.


روش تعریف پارامترهای مسیریابی

تا اینجا اگر مسیر جدید https://localhost:5001/NewRouting/1/2 را درخواست کنیم چه اتفاقی رخ می‌دهد؟


در مورد نحوه‌ی تعریف قالب «یافت نشد» فوق، در قسمت دوم بیشتر بحث شد.
برای تعریف پارامترهای مسیریابی، می‌توان مسیریابی سومی را با پارامترهای مدنظر تعریف کرد که در مثال زیر، ذکر پارامتر دوم اختیاری است؛ چون سومین مسیریابی تعریف شده، امکان پردازش مسیرهایی با یک پارامتر را هم ممکن می‌کند:
@page "/LearnRouting"
@page "/NewRouting"
@page "/LearnRouting/{parameter1}"
@page "/LearnRouting/{parameter1}/{parameter2}"

<h3>Learn Routing</h3>

<p>Parameter1: @Parameter1</p>
<p>Parameter2: @Parameter2</p>

@code
{
    [Parameter]
    public string Parameter1 { set; get; }

    [Parameter]
    public string Parameter2 { set; get; }
}
سپس جهت دست‌یابی به مقادیر این پارامترها می‌توان در قسمت کدهای کامپوننت، از خواص عمومی مزین شده‌ی با ویژگی Parameter استفاده کرد. در اینجا هر خاصیت تعریف شده، باید هم نام پارامتر تعریف شده باشد (و این مورد نیز غیر حساس به حروف بزرگ و کوچک است).
پس از این تعاریف، مسیریابی مانند https://localhost:5001/LearnRouting/1 با یک پارامتر و یا https://localhost:5001/LearnRouting/1/2 که به همراه دو پارامتر است، قابل فراخوانی می‌شود.





روش تعریف لینک به سایر کامپوننت‌های Blazor

در ادامه کامپوننت جدید Pages\LearnBlazor\LearnAdvancedRouting.razor را اضافه می‌کنیم؛ با این محتوای آغازین:
@page "/LearnAdvancedRouting"

<h3>Learn Advanced Routing</h3>
در اینجا قصد نداریم لینک به این کامپوننت را به منوی اصلی برنامه اضافه کنیم؛ بلکه می‌خواهیم از طریق همان کامپوننت LearnRouting.razor ابتدای بحث، این مسیریابی را برقرار کنیم. برای اینکار یا می‌توان از یک anchor tag استاندارد استفاده کرد و یا همانند کامپوننت Shared\NavMenu.razor، از کامپوننت NavLink استفاده نمود. NavLink‌ها نیز همانند anchor tag‌های استاندارد HTML هستند، با این تفاوت که این کامپوننت، افزودن کلاس active مخصوص بوت استرپ را هم بر اساس فعال بودن مسیریابی مرتبط به آن، انجام می‌دهد ("class="nav-link active). به همین علت است که اگر گزینه‌ی منوی خاصی را انتخاب کنیم، این گزینه با رنگ متمایزی نشان داده می‌شود:


بنابراین یک روش تعریف لینک به کامپوننتی دیگر، استفاده از کامپوننت NavLink است که href آن به مسیریابی مقصد اشاره می‌کند:
<NavLink class="btn btn-secondary" href="LearnAdvancedRouting">
    <span class="oi oi-list-rich" aria-hidden="true"></span> Learn Advanced Routing
</NavLink>


ارسال کوئری استرینگ‌ها به کامپوننت‌های مختلف

پس از تعریف لینکی به کامپوننتی دیگر از درون یک کامپوننت، اکنون می‌خواهیم دو کوئری استرینگ param1 و param2 را نیز به آن ارسال کنیم:
<NavLink class="btn btn-secondary" href="LearnAdvancedRouting?param1=value1&param2=value2">
    <span class="oi oi-list-rich" aria-hidden="true"></span> Learn Advanced Routing
</NavLink>
در کامپوننت LearnAdvancedRouting برای دریافت این پارامترها، نیاز است آن‌ها را از URL جاری استخراج کرد:
@page "/LearnAdvancedRouting"
@inject NavigationManager NavigationManager

<h3>Learn Advanced Routing</h3>

<h4>Parameter 1 : @Param1</h4>
<h4>Parameter 2 : @Param2</h4>

@code
{
    string Param1;
    string Param2;

    protected override void OnInitialized()
    {
        base.OnInitialized();

        var absoluteUri = new Uri(NavigationManager.Uri);
        var queryParam = System.Web.HttpUtility.ParseQueryString(absoluteUri.Query);
        Param1 = queryParam["Param1"];
        Param2 = queryParam["Param2"];
    }
}
ابتدا سرویس توکار NavigationManager توسط دایرکتیو inject@ به کامپوننت جاری تزریق شده‌است که برای کار و دسترسی به آن، نیاز به تنظیمات ابتدایی خاصی نیست و پیشتر به مجموعه‌ی سرویس‌های برنامه افزوده شده‌است. برای نمونه توسط آن می‌توان به Uri در حال پردازش، دسترسی یافت. اکنون که این Uri را داریم، با استفاده از متد HttpUtility.ParseQueryString می‌توان به مجموعه‌ی کوئری استرینگ‌های ارسالی، به صورت key/value دسترسی یافت و برای مثال آن‌ها را در روال رویدادگردان OnInitialized، دریافت و با انتساب آن‌ها به دو فیلد تعریف شده، سبب نمایش مقادیر دریافتی شد:



هدایت به یک کامپوننت دیگر با کد نویسی

فرض کنید می‌خواهیم دکمه‌ای را اضافه کنیم که با کلیک بر روی آن، ما را به کامپوننت LearnRouting هدایت می‌کند:
@page "/LearnAdvancedRouting"
@inject NavigationManager NavigationManager

@*<NavLink href="/learnrouting" class="btn btn-secondary">Back to Routing</NavLink>*@
@*<a href="/learnrouting" class="btn btn-secondary">Back to Routing</a>*@
<button class="btn btn-secondary" @onclick="BackToRouting">Back to Routing</button>

@code
{
    private void BackToRouting()
    {
        NavigationManager.NavigateTo("learnrouting");
    }
}
در اینجا روش‌های مختلف تعریف لینک به کامپوننتی دیگر را مشاهده می‌کنید. یا می‌توان از کامپوننت NavLink استفاده کرد و یا از یک anchor tag استاندارد، که href هر دوی آن‌ها به مسیریابی مقصد اشاره می‌کنند و یا می‌توان با استفاده از سرویس NavigationManager و متد NavigateTo آن مانند کدهای فوق، سبب هدایت کاربر به صفحه‌ای دیگر شد.


کدهای کامل این مطلب را از اینجا می‌توانید دریافت کنید: Blazor-5x-Part-10.zip
  • #
    ‫۳ سال و ۴ ماه قبل، شنبه ۴ اردیبهشت ۱۴۰۰، ساعت ۱۵:۰۷
    یک نکته‌ی تکمیلی: امکان قرار دادن کامپوننت‌های Blazor در یک اسمبلی دیگر و مشکل مسیریابی
    می‌توان کامپوننت‌های مورد نظر را در یک اسمبلی دیگر (بجز برنامه‌ی اصلی) قرار دارد؛ اما در این حالت و پس از افزودن ارجاعی به آن‌ها در فایل csproj برنامه‌ی اصلی، اگر در آن‌ها مسیریابی را تعریف کرده باشید، کار نمی‌کنند. برای رفع این مشکل باید به فایل app.razor مراجعه کرد و سپس این اسمبلی‌های ثالث را به سیستم مسیریابی معرفی نمود:
    <Router
        AppAssembly="@typeof(Program).Assembly"
        AdditionalAssemblies="new[] { typeof(Component1).Assembly }">
        @* ... Router component elements ... *@
    </Router>
    در اینجا ویژگی AdditionalAssemblies، آرایه‌ای از اسمبلی‌های ثالث به همراه کامپوننت‌های Blazor را می‌پذیرد.
  • #
    ‫۳ سال و ۴ ماه قبل، دوشنبه ۶ اردیبهشت ۱۴۰۰، ساعت ۱۴:۲۹
    تعدادی پرسش و پاسخ تکمیلی مباحث مسیریابی

    چگونه می‌توان DateTime را به عنوان پارامتر مسیریابی ارسال کرد؟
    Blazor از پارامترهایی از نوع DateTime، در حین مسیریابی پشتیبانی می‌کند؛ با این شرط که قید datetime در حین مسیریابی صریحا ذکر شود:
    @page "/route/{parameter:datetime}"
    یک مثال: کامپوننت index.razor که سبب هدایت به سمت کامپوننت دیگری به همراه ارسال پارامتری از نوع DateTime می‌شود:
    @page "/"
    @inject NavigationManager NavManager
    <button @onclick="CurrentTime">Current Time</button>
    @code {
        public void CurrentTime()
        {
            NavManager.NavigateTo("/time/" + DateTime.Now);
        }
    }
    و کامپوننت Time.razor که قادر است این DateTime را دریافت کرده و به یک پارامتر، Bind کند:
    @page "/time/{param:datetime}"
    <h3>Time</h3>
    <p>@Param</p>
    @code {
        [Parameter]
        public DateTime Param { get; set; }
    }


    چگونه می‌توان به عنوان صفحه‌ی جاری دسترسی یافت؟
    برای اینکار نیاز است از JavaScript interop استفاده کرد. ابتدا برای مثال تابع عمومی getTitle را که بر اساس DOM API مرورگر کار می‌کند، تهیه می‌کنیم:
    window.getTitle = () => {
       return document.title;
    };
    سپس می‌توان از آن در یک کامپوننت Blazor به صورت زیر استفاده کرد:
    @page "/"
    @inject IJSRuntime jsRuntime
    <h2>Page Title: @Title</h2>
    <button class="btn btn-primary" @onclick="@GetTitle">Get Title</button>
    @code {
    public string Title = "";
        
        public async void GetTitle()
        {
                Title = await jsRuntime.InvokeAsync<string>("getTitle");
         }
    }

    چگونه می‌توان مسیری را از طریق کدهای برنامه در یک برگه‌ی مجزای مرورگر باز کرد؟
    در اینجا نیز می‌توان با استفاده از JavaScript interop، متد استاندارد open مرورگر را فراخوانی کرد و پارامتر اول آن‌را به url مدنظر و پارامتر بعدی آن‌را به blank_ تنظیم کرد تا مرورگر آدرس درخواستی را در یک برگه‌ی جدید باز کند:
    @inject IJSRuntime jsRuntime
    
    <button @onclick="NavigateToNewTab">New Tab Navigation</button>
    @code {
    public async Task NavigateToNewTab()
        {
            string url = "/counter";
            await jsRuntime.InvokeAsync<object>("open", url, "_blank");
        }
    }
  • #
    ‫۳ سال و ۳ ماه قبل، چهارشنبه ۲۲ اردیبهشت ۱۴۰۰، ساعت ۱۸:۰۳
    خلاصه‌ی سریع خواص و متدهای NavigationManager 
    NavigationManager.Uri
    #> https://localhost:5001/counter/3?q=hi
    
    NavigationManager.BaseUri
    #> https://localhost:5001/
    
    NavigationManager.NavigateTo("http://new location")
    #> Navigates to new location
    
    NavigationManager.LocationChanged
    #> An event that fires when the navigation location has changed.
    
    NavigationManager.ToAbsoluteUri("test")
    #> https://localhost:5001/test
    
    NavigationManager.ToBaseRelativePath(MyNavigationManager.Uri)
    #> counter/3?q=hi
  • #
    ‫۲ سال و ۹ ماه قبل، شنبه ۱۵ آبان ۱۴۰۰، ساعت ۱۳:۵۰
    بهبود قسمت «ارسال کوئری استرینگ‌ها به کامپوننت‌های مختلف » در Blazor 6x

    در Blazor 6x دیگر نیازی نیست تا با استفاده از «HttpUtility.ParseQueryString»، کار پردازش دستی کوئری استرینگ‌های رسیده، صورت گیرد. برای نمونه فرض کنید Url رسیده، چنین شکلی را دارد:
    /search?filter=some+stuff&page=3&assignee=User1&assignee=User2
    برای دسترسی به مقادیر کوئری استرینگ‌های آدرس فوق، اینبار کافی است به صورت زیر عمل کنیم:
    @code {
        [Parameter]
        [SupplyParameterFromQuery]
        public string Filter { get; set; }
    
        [Parameter]
        [SupplyParameterFromQuery]
        public int? Page { get; set; }
    
        [Parameter]
        [SupplyParameterFromQuery(Name = "assignee")]
        public string[] Assignees { get; set; }
    }
    یعنی ذکر دو ویژگی [Parameter, SupplyParameterFromQuery] بر روی یک خاصیت عمومی کامپوننت، آن‌را آماده‌ی دریافت مقادیر کوئری استرینگ‌های متناظری می‌کند. اگر می‌خواهید نام پارامتر، با نام کوئری استرینگ یکی نباشد (حالت پیش‌فرض)، از خاصیت Name این ویژگی می‌توان استفاده کرد.

    نکته: در اینجا مقادیری مانند ذیل قابل قبول هستند:
    String, bool, DateTime, decimal, double, float, Guid, int, long
    و یا حالت Nullable آن‌ها و یا آرایه‌ای آن‌ها.

    امکان ساخت Urlهایی به همراه کوئری استرینگ‌ها جهت هدایت ساده‌تر به آن‌ها

    به Blazor 6x، متدهای الحاقی UriWithQueryParameter نیز اضافه شده‌اند و کار آن‌ها، افزودن ساده‌تر کوئری‌استرینگ‌ها به Urlها است که نمونه‌ای از آن به صورت زیر است:
    var actualUri = NavigationManager.GetUriWithQueryParameters(new Dictionary<string, object>
            {
                ["full name"] = "John Doe", // Single value
                ["ping"] = new int?[] { 35, 16, null, 87, 240 }
            });
    که چنین Url ای را تولید می‌کند (نمونه‌ای از روش تعریف کوئری استرینگ‌های آرایه‌ای):
    host/?full%20name=John%20Doe&ping=35&ping=16&ping=87&ping=240
    مثال‌های بیشتر آن‌را در اینجا می‌توانید مشاهده کنید.
  • #
    ‫۲ سال و ۶ ماه قبل، شنبه ۷ اسفند ۱۴۰۰، ساعت ۱۲:۵۶
    یک نکته‌ی تکمیلی: امکان تزریق وابستگی‌ها در سازنده‌ی کلاس‌های کامپوننت‌ها در Blazor 7x

    اگر از روش code-behind جهت توسعه‌ی کامپوننت‌های Blazor استفاده می‌کنید، در دات نت 7 و Blazor 7x می‌توانید علاوه بر بکارگیری ویژگی [Inject]، از تزریق مستقیم در سازنده‌ی کلاس‌ها نیز استفاده کنید:
    public class MyComponent : ComponentBase
    {
       public MyComponent(IMyService myService) { ... }
    }
    اطلاعات بیشتر
  • #
    ‫۱ سال و ۷ ماه قبل، دوشنبه ۳ بهمن ۱۴۰۱، ساعت ۱۴:۳۵
    امکان دریافت تائید از کاربر، در حین ترک صفحه‌ی یک فرم ذخیره نشده در دات نت 7

    Blazor در دات نت 7 به همراه امکانات مدیریت بهتر تغییرات آدرس صفحات است. برای مثال توسط آن می‌توان به کاربران در مورد کارهای ذخیره نشده، در صورت شروع به هدایت به صفحه‌ای دیگر، هشدار داد.
    برای مدیریت تغییرات آدرس صفحات، می‌توان از سرویس NavigationManager و متد RegisterLocationChangingHandler آن به صورت زیر استفاده کرد:
    var registration = NavigationManager.RegisterLocationChangingHandler(async cxt =>
    {
        if (cxt.TargetLocation.EndsWith("counter"))
        {
            cxt.PreventNavigation();
        }
    });
    - در این مثال اگر آدرس درخواستی به counter ختم شود، با فراخوانی متد PreventNavigation، مانع آن خواهیم شد.
    - خروجی این متد برای مثال registration در اینجا، از نوع IDisposable است و dispose آن سبب حذف Handler ثبت شده می‌شود.
    - باید بخاطر داشت که امکانات فوق تنها آدرس‌های درون برنامه‌ای را مدیریت می‌کند. برای مدیریت هدایت به آدرس‌های خارجی باید از رخ‌داد beforeunload جاوا اسکریپت استفاده کرد.


    ساده سازی کار با سرویس مدیریت تغییرات آدرس با کامپوننت جدید NavigationLock

    نکات عنوان شده را توسط کامپوننت جدید NavigationLock نیز می‌توان به نحو ساده‌تری مدیریت کرد:
    <NavigationLock OnBeforeInternalNavigation="ConfirmNavigation" ConfirmExternalNavigation />
    در این کامپوننت، OnBeforeInternalNavigation یک callback است که کار ردیابی و مدیریت تغییرات آدرس‌های درون برنامه‌ای را انجام می‌دهد. اگر می‌خواهید هدایت به آدرس‌های خارجی را نیز کنترل کرده و همچنین علاقمند به پیاده سازی دستی رخ‌داد beforeunload جاوا اسکریپت نیستید، می‌توانید خاصیت ConfirmExternalNavigation را نیز قید کنید که با یک browser-specific prompt مدیریت می‌شود.
    از این کامپوننت می‌توان جهت مدیریت یک فرم ذخیره نشده درصورت شروع به هدایت کاربر به آدرسی دیگر، به صورت زیر استفاده کرد:
    <EditForm EditContext="editContext" OnValidSubmit="Submit">
        ...
    </EditForm>
    <NavigationLock OnBeforeInternalNavigation="ConfirmNavigation" ConfirmExternalNavigation />
    
        @code {
            private readonly EditContext editContext;
            ...
    
            // Called only for internal navigations.
            // External navigations will trigger a browser specific prompt.
            async Task ConfirmNavigation(LocationChangingContext context)
            {
                if (editContext.IsModified())
                {
                    var isConfirmed = await JS.InvokeAsync<bool>("window.confirm", 
                       "Are you sure you want to leave this page?");
                    
                    if (!isConfirmed)
                    {
                        context.PreventNavigation();
                    }
                }
            }
        }
    توضیحات:
    - با استفاده از EditContext یک EditForm و متد IsModified آن می‌توان تشخیص داد که اطلاعات فرم جاری توسط کاربر تغییر کرده‌است و هنوز ذخیره شده یا نشده.
    - در اینجا پیاده سازی OnBeforeInternalNavigation را توسط متد ConfirmNavigation مشاهده می‌کنید که در آن بررسی می‌شود آیا کاربر فرم را تغییر داده‌است یا خیر؟ اگر بله، متد confirm جاوا اسکریپت را جهت تائید ترک صفحه نمایش می‌دهد. اگر کاربر آن‌را تائید نکند، توسط متد PreventNavigation، مانع تغییر آدرس صفحه و از دست رفتن اطلاعات خواهد شد.
  • #
    ‫۱ ماه قبل، یکشنبه ۲۱ مرداد ۱۴۰۳، ساعت ۱۹:۰۰

    یک نکته‌ی تکمیلی: امکان تعریف مسیریابی صفحات، با استفاده از ویژگی Route

    عموما مسیریابی‌های صفحات Blazor، به صورت زیر تعریف می‌شوند:

    @page "/counter"

    و اگر نیاز باشد تا این مسیر را در قسمت‌های دیگری هم ذکر کنیم (برای مثال در لینک‌ها و یا متد NavigateTo)، باید دقیقا همین مسیر و عبارت را در چندین قسمت برنامه تکرار کنیم. برای رفع این مشکل، با استفاده از ویژگی Route می‌توان مسیریابی فوق را به صورت زیر بازنویسی کرد:

    @attribute [Route(Constants.CounterRoute)]

    در این حالت می‌توان از مزیت تعریف مسیر مدنظر به صورت یک ثابت، به صورت زیر استفاده کرد:

    public static class Constants
    {
        public const string CounterRoute = "/counter";
    }

    و اگر در قسمت دیگری از برنامه نیاز به ارجاعی به آن بود، می‌توان همین رشته‌ی ثابت را مجددا مورد استفاده قرار داد:

    NavigationManager.NavigateTo(Constants.CounterRoute);