<script> let src = 'https://svelte.dev/tutorial/image.gif'; let name = 'Rick Astley'; </script> <img src={src} alt="{name} dancing">
- نکته اول : اگر در تگ img مقدار alt را وارد نکنیم و یا alt در این تگ وجود نداشته باشد، یک هشدار توسط کامپایلر svelte برای ما با عنوان <img> element should have an alt attribute> ایجاد میشود. زمان ساخت یک برنامه بسیار مهم است تا قوانین نوشتن یک کد html خوب را رعایت کنیم تا برای تمامی کاربران احتمالی برنامه قابل استفاده باشد. در همین مثال با ایجاد یک هشدار Svelte تلاش میکند که ما را از اشتباه در نوشتن کد html مطلع سازد.
- نکته دوم : اگر نام یک آبجکت تعریف شده و یک attribute، برابر باشد میتوانیم از نسخه کوتاه شده یا Shorthand attributes در svelte استفاده کنیم. به طور مثال در مثال بالا میتوانیم از کد زیر در خط 6 استفاده کنیم.
<img {src} alt="{name} dancing">
<script> export let siteName = "dotnettips"; </script> <p>this is a nested component for third tutorial on {siteName}</p>
<script> import Nested from "./Nested.svelte"; export let name; </script> <h1>Hello {name}!</h1> <Nested siteName="dotnettips.info" />
Hello world! this is a nested component for third tutorial on dotnettips.info
در مثال بالا ما یک کامپوننت جدید را ایجاد کرده و از طریق دستور import به App.svelte اضافه کردیم. نکتهای که در اینجا وجود دارد، نحوه مقدار دهی props در کامپوننتها است. اگر به خط 9 دقت کنیم، کامپوننت ما از طریق تگ جدیدی با نام (Nested) به بدنه html برنامه اضافه شده است که یک attribute به نام siteName دارد. siteName متغیر export شده در کامپوننت Nested.svelte است که در کامپوننتها به این صورت مقدار دهی میشود. قبلا نحوه مقدار دهی این خصیصهها را در فایلهای جاوا اسکریپت مشاهده کرده بودیم. نکته دیگری که باید به آن دقت داشت این است که خصیصه siteName مقدار پیش فرض dotnettips را در Nested.svelte به خود اختصاص داده بود. به همین جهت اگر ما siteName را هنگام استفاده از کامپوننت مقدار دهی نکنیم، از مقدار پیش فرض خود استفاده خواهد کرد. ولی اینجا ما با مقدار دهی آن، siteName را به dotnettips.info تغییر دادهایم.
نکته مهم : دقت داشته باشید کامپوننتهای شما همیشه باید با حروف بزرگ شروع شوند؛ به طور مثال در صورت نوشتن <nested/> محتوای کامپوننت نمایش داده نخواهد شد. svelte، از طریق زیر نظر گرفتن حروف کوچک و بزرگ در ابتدای تگها، بین تگهای html و کامپوننتها تمایز قائل میشود.
Spread props :
تا اینجا به صورت خلاصه با props یا خصیصهها آشنا شدهاید و دیدیم که با export کردن یک متغیر در یک کامپوننت، میتوانیم آن را هنگام استفاده مقدار دهی نماییم. برای اینکه تمرینی هم باشد با توجه به مطالبی که تاکنون گفته شده، پروژهی جدیدی را ایجاد کنید و محتوای App.svelte را مانند کد زیر تغییر دهید.
<script> import Info from './Info.svelte'; const pkg = { name: 'svelte', version: 3, speed: 'blazing', website: 'https://svelte.dev' }; </script> <Info name={pkg.name} version={pkg.version} speed={pkg.speed} website={pkg.website}/>
همانطور که در خط دوم کد میبینید، کامپوننتی به نام Info.svelte به این بخش اضافه شدهاست. این کامپوننت را با محتوای زیر ایجاد نمایید:
<script> export let name; export let version; export let speed; export let website; </script> <p> The <code>{name}</code> package is {speed} fast. Download version {version} from <a href="https://www.npmjs.com/package/{name}">npm</a> and <a href={website}>learn more here</a> </p>
اگر برنامه را اجرا کنید یک چنین خروجی را مشاهده خواهید کرد:
The svelte package is blazing fast. Download version 3 from npm and learn more here
<Info {...pkg}/>
<script> let count = 0; function handleClick() { count += 1; } </script> <p>Count : {count}</p> <button on:click={handleClick}> Clicked {count} {count === 1 ? 'time' : 'times'} </button>
<script> let count = 0; let doubled = count * 2; function handleClick() { count += 1; } </script> <button on:click={handleClick}> Clicked {count} {count === 1 ? 'time' : 'times'} </button> <p>{count} doubled is {doubled}</p>
$: doubled = count * 2;
$: console.log(`the count is ${count}`);
$: { console.log(`the count is ${count}`); alert(`I SAID THE COUNT IS ${count}`); }
$: if (count >= 10) { alert(`count is dangerously high!`); count = 9; }
<script> let numbers = [1, 2, 3, 4]; function addNumber() { let newNumber = numbers.length + 1; numbers.push(newNumber); } $: sum = numbers.reduce((t, n) => t + n, 0); </script> <p>{numbers.join(' + ')} = {sum}</p> <button on:click={addNumber}>Add a number</button>
function addNumber() { let newNumber = numbers.length + 1; numbers.push(newNumber); numbers = numbers; }
function addNumber() { let newNumber = numbers.length + 1; numbers = [...numbers, newNumber]; }
مروری بر Two way bindings :
<script> let name = ""; function updateName(event) { name = event.target.value; } </script> <h4>My Name Is {name}</h4> <input value={name} on:input={updateName} />
<script> let name = ""; </script> <h4>My Name Is {name}</h4> <input bind:value={name} />
{#if condition} <!-- you html codes ... --> {/if}
<script> let user = { loggedIn: false }; function toggle() { user.loggedIn = !user.loggedIn; } </script> {#if user.loggedIn} <button on:click={toggle}> Log out </button> {/if} {#if !user.loggedIn} <button on:click={toggle}> Log in </button> {/if}
{#if condition} <!-- you html code when condition is true --> {:else} <!-- you html code when condition is false --> {/if}
{#if user.loggedIn} <button on:click={toggle}> Log out </button> {:else} <button on:click={toggle}> Log in </button> {/if}
{#if condition} <!-- you html code when condition is true --> {:else if condition2} <!-- you html code when condition2 is true --> {:else} <!-- you html code when condition and condition2 are false --> {/if}
{#each list as item} <!-- you html code per each item in list --> {/each}
<script> let cats = [ { id: 'J---aiyznGQ', name: 'Keyboard Cat' }, { id: 'z_AbfPXTKms', name: 'Maru' }, { id: 'OUtn3pvWmpg', name: 'Henri The Existential Cat' } ]; </script> <h1>The Famous Cats of YouTube</h1> <ul> {#each cats as cat} <li><a target="_blank" href="https://www.youtube.com/watch?v={cat.id}"> {cat.name} </a></li> {/each} </ul>
<ul> {#each cats as {id,name}} <li><a target="_blank" href="https://www.youtube.com/watch?v={id}"> {name} </a></li> {/each} </ul>
<ul> {#each cats as { id, name }, i} <li><a target="_blank" href="https://www.youtube.com/watch?v={id}"> {i + 1}: {name} </a></li> {/each} </ul>
نکته : در این بخش من سعی کردم تا حدودی به ترتیب بخش آموزشی خود وبسایت Svelte، موارد را بیان کنم؛ ولی با توجه به اینکه شاید دوستان ترجیح بدهند روش آموزشی خود آن وبسایت که امکان تغییر و نوشتن کد را هم محیا کرده است، امتحان کنند لینک آن را به اشتراک میگذارم.
معرفی Reactive extensions
علت اینجا است که نوع آرگومان جنریک به صورت خودکار توسط نوع پارامتر ارسالی به متد قابل تشخیص است (در اینجا چون ToObservable یک متد الحاقی است، اولین پارامتر آن، عناصر توالی query هستند که از نوع IEnumerable of int تعریف شدند).
برای مطالعه بیشتر مراجعه کنید به C# specs (ECMA-334) part 25.6.4 Inference of type arguments
نمایش گزارش در پنجره جدید
ایجاد هوک سفارشی useClickOutside
برای این منظور فایل جدید src\components\useClickOutside.tsx را ایجاد کرده و به صورت زیر تکمیل میکنیم:
import { useEffect } from "react"; const useClickOutside = (ref, handler) => { useEffect(() => { const listener = (event) => { if (!ref.current || ref.current.contains(event.target)) { return; } handler(event); }; document.addEventListener("mousedown", listener); document.addEventListener("touchstart", listener); return () => { document.removeEventListener("mousedown", listener); document.removeEventListener("touchstart", listener); }; }, [handler, ref]); }; export { useClickOutside };
- متد هوک سفارشی ما، دو پارامتر ref و handler را دریافت میکند. ref به DOM Element جاری اشاره میکند و handler تابعی است که هنگام کلیک در خارج از ناحیهی یک DOM Element خاص، اجرا میشود.
- سپس یک listener را تعریف کردهایم که این تابع handler را اجرا میکند؛ البته به شرطیکه DOM Element ارسالی وجود داشته باشد و خود target هم نباشد.
- در ادامه این listener را به رخدادهای mousedown و touchstart متصل کرده و پاکسازی آنها را هم در قسمت return متد useEffect انجام دادهایم.
- همچنین چون میخواهیم تنها در صورت تغییر پارامترهای ارسالی به هوک سفارشی جاری، این useEffect به روز رسانی شود، این پارامترها را در قسمت Dependency List مربوط به متد useEffect نیز ذکر کردهایم.
تا اینجا اگر کدهای فوق را دنبال کنید، چون پسوند این فایل tsx است، خطاهای تایپاسکریپتی زیر را مشاهده خواهید کرد که به دلیل انتساب ضمنی نوع any، به این پارامترهای بدون نوع است:
استفاده از هوک سفارشی useClickOutside
بنابراین قدم بعدی کار، تکمیل نوعهای مرتبط با این پارامترها است. برای این منظور، ابتدا سعی میکنیم تا این هوک را در کامپوننت src\components\ReducerButtons.tsx قسمت قبلی استفاده کنیم تا نسبت به نوع پارامترهای ارسالی به این هوک، درک بهتری را پیدا کنیم:
import { useClickOutside } from "./useClickOutside"; // ... export const ReducerButtons = () => { const [state, dispatch] = useReducer(reducer, initialState); const ref = useRef<HTMLDivElement>(null); useClickOutside(ref, () => { console.log("clicked outside"); }); return ( <div ref={ref}> // ... </div> ); };
- ابتدا importهای لازم را به ابتدای ماژول افزودهایم.
- سپس با استفاده از هوک useRef که در قسمت چهارم آنرا بررسی کردیم، ارجاعی را به المان div رندر شده، بدست آوردهایم.
- در آخر هوک سفارشی جدید useClickOutside را فراخوانی کردهایم که آرگومان اول آن به DOM Element مربوط به div اشاره میکند و پارامتر دوم آن، تابعی است که پس از کلیک در خارج از ناحیهی آن، اجرا خواهد شد.
تعیین نوعهای پارامترهای هوک سفارشی
تا اینجا متوجه شدیم که handler، چیزی بجز یک تابع که void را بازگشت میدهد (void <= ())، نیست. همچنین نوع شیء ref را هم میتوان با نزدیک کردن اشارهگر ماوس، به متغیر ref در کامپوننت ReducerButtons، مشاهده کرد:
بر این اساس، تعاریف نوعهای پارامترهای هوک سفارشی useClickOutside به صورت زیر مشخص میشوند:
const useClickOutside = ( ref: React.RefObject<HTMLDivElement>, handler: () => void ) => {
const listener = (event: React.MouseEvent<HTMLElement>) => {
عنوان میکند که نوع event.target، از نوع Node، که مورد نظر متد contains است، نیست. برای رفع آن فقط کافی است تبدیل نوع زیر را انجام داد:
ref.current.contains(event.target as Node)
برای رفع این خطا، نوع پارامتر تابع handler را نیز بر اساس رویداد ارسالی به آن، مشخص میکنیم:
const useClickOutside = ( ref: React.RefObject<HTMLDivElement>, handler: (event: React.MouseEvent<HTMLElement>) => void ) => {
برای درک بهتر این خطا، اشارهگر ماوس را به محل تعریف این متد نزدیک میکنیم، تا بتوان امضای آنرا مشاهده کرد. در حالت mousedown، پارامتر دوم این متد، از نوع MouseEvent است:
(method) Document.addEventListener<"mousedown">(type: "mousedown", listener: (this: Document, ev: MouseEvent) => any, options?: boolean | AddEventListenerOptions | undefined): void (+1 overload)
(method) Document.addEventListener<"touchstart">(type: "touchstart", listener: (this: Document, ev: TouchEvent) => any, options?: boolean | AddEventListenerOptions | undefined): void (+1 overload)
const useClickOutside = ( ref: React.RefObject<HTMLDivElement>, handler: (event: MouseEvent | TouchEvent) => void ) => { useEffect(() => { const listener = (event: MouseEvent | TouchEvent) => {
یک نکتهی تکمیلی: در اینجا با تعریف <ref: React.RefObject<HTMLDivElement، دیگر ref ارسالی، هیچ المان دیگری را بجز div نمیتواند بپذیرد. برای عمومیتر کردن آن، میتوان بر روی آن کلیک راست کرد و گزینهی Go to definition را انتخاب نمود:
بنابراین حالت عمومیتر آن، استفاده از HTMLElement ای است که HTMLDivElement از آن ارث بری کردهاست:
const useClickOutside = ( ref: React.RefObject<HTMLElement>, handler: (event: MouseEvent | TouchEvent) => void ) => {
با این تغییرات، کدهای نهایی این قسمت، به صورت زیر در خواهند آمد:
import { useEffect } from "react"; const useClickOutside = ( ref: React.RefObject<HTMLElement>, handler: (event: MouseEvent | TouchEvent) => void ) => { useEffect(() => { const listener = (event: MouseEvent | TouchEvent) => { if (!ref.current || ref.current.contains(event.target as Node)) { return; } handler(event); }; document.addEventListener("mousedown", listener); document.addEventListener("touchstart", listener); return () => { document.removeEventListener("mousedown", listener); document.removeEventListener("touchstart", listener); }; }, [handler, ref]); }; export { useClickOutside };
نگاهی به اجزای سیستم راهبری بوت استرپ 3
تعیین موقعیت کاربر در صفحه به کمک breadcrumbs در Bootstrap
در مورد قسمت breadcrumb، مطالب مانند قبل و پیشنیاز ذکر شده است. با این تفاوت که بهتر است بجای ul از ol استفاده شود؛ چون ترتیب این عناصر مهم است. ol به معنای ordered list میباشد:
<ol class="breadcrumb"> <li><a href="#">خانه</a></li> <li><a href="#">خدمات</a></li> <li class="active">محصولات</li> </ol>
یک سؤال: اگر نخواهیم این خطوط مورب ظاهر شوند و برای مثال علاقمند باشیم تا از گلیف آیکنهای معرفی شده در قسمت قبل استفاده کنیم، چه باید کرد؟
برای این منظور نیاز است با نحوه رندر خطوط مورب در بوت استرپ آشنا شویم. بنابراین فایل bootstrap-rtl.css را گشوده و چند سطر ذیل را جستجو کنید:
.breadcrumb > li + li:before { content: "/\00a0"; padding: 0 5px; color: #cccccc; }
.breadcrumb > li + li:before { content: none; }
<ol class="breadcrumb"> <li><a href="#">خانه</a> <span class="glyphicon glyphicon-circle-arrow-left"></span></li> <li><a href="#">خدمات</a> <span class="glyphicon glyphicon-circle-arrow-left"></span></li> <li class="active">محصولات</li> </ol>
اگر از رنگ گلیف آیکنهای نمایش داده شده راضی نیستید، آنها را نیز میتوانید در فایل CSS سفارشی خود تغییر دهید. برای مثال:
.glyphicon { color: #cdae51; }
تعریف برگهها در Twitter Bootstrap
در مورد تعریف برگهها، بوت استرپ 3 با نگارش 2 آن، تفاوتی ندارد و تمام نکات مطلب «نگاهی به اجزای تعاملی Twitter Bootstrap» در اینجا نیز صادق هستند. یک ul باید تعریف شود و سپس برای نمونه کلاسهای nav nav-tabs را به آنها اضافه خواهیم کرد تا به شکل tab به نظر برسند. برگه فعال نیز با کلاس active مشخص میشود.
یک نکته جدید: در بوت استرپ 3 میتوان یک برگه را کاملا در عرض صفحه کشید و امتداد داد:
<ul class="nav nav-pills nav-justified"> <li><a href="#">Home</a></li> <li><a href="#">About</a></li> <li class="active"><a href="#">Services</a></li> <li><a href="#">Photo Gallery</a></li> <li><a href="#">Contact</a></li> </ul>
به تصویر ذیل خواهیم رسید که در آن tab، در امتداد صفحه کشیده شده است:
تعریف navbar در بوت استرپ 3
اصول کلی navbar بوت استرپ 3 همانند بوت استرپ 2 است؛ با چند تفاوت کوچک:
- کلاس btn-navbar بوت استرپ 2 به کلاس navbar-btn در بوت استرپ 3 تغییر نام یافته است.
- کلاس navbar-inner بوت استرپ 2 کلا حذف شده است.
- کلاسهای nav-list به کلاسهای list-group تغییر نام یافتهاند.
- کلاس brand با navbar-brand جایگزین شده است.
- کلاسهای navbar-brand و navbar-toggle باید داخل المانی با کلاس navbar-header محصور شوند.
- کلاس nav باید به همراه navbar-nav باشد.
- کلاسهای جدید navbar-default navbar-text navbar-btn navbar-header اضافه شدهاند.
یک مثال:
<div class="container"> <h4 class="alert alert-info"> nav</h4> <div class="row"> <nav class="navbar navbar-default navbar-inverse navbar-fixed-top" role="navigation"> <div class="navbar-header"> <button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#collapse"> <span class="sr-only">Toggle navigation</span> <span class="icon-bar"></span><span class="icon-bar"></span><span class="icon-bar"></span> </button> </div> <div class="collapse navbar-collapse" id="collapse"> <ul class="nav navbar-nav"> <li><a href="#">Home</a></li> <li><a href="#">About</a></li> <li class="active"><a href="#">Services</a></li> <li><a href="#">Photo Gallery</a></li> <li><a href="#">Contact</a></li> </ul> </div> </nav> </div> <!-- end row --> </div> <!-- /container -->
body { padding-top: 70px; }
توضیحات:
- CSS سفارشی، سبب خواهد شد تا تگ h4 بالای صفحه، زیر navbar مخفی نشده و قابل مشاهده باشد.
- کار با المان nav دارای کلاس navbar navbar-default شروع میشود. همچنین role=navigation نیز به این المان اضافه شدهاست. این مورد کمکی خواهد بود به افرادی با قدرت بینایی کم که از screen readers استفاده میکنند.
- داخل navbar-default یک div دیگر با کلاس navbar-header اضافه شده است. این مورد قابلیتهای واکنشگرای navbar را فراهم میسازد. به این ترتیب زمانیکه عرض صفحه کمتر میشود، مانند تصویر دوم، یک دکمه ویژه نمایش داده خواهد شد.
- هرجایی در بوت استرپ ویژگیهای data- را ملاحظه کردید، یعنی قرار است اطلاعاتی در اختیار اجزای جاوا اسکریپتی آن قرار گیرند. برای نمونه data-target به یک div با آی دی مساوی collapse اشاره میکند. به این معنا که اگر در زمان کم بودن عرض صفحه، دکمه ویژه واکنشگرای navbar ظاهر شد، با کلیک بر روی آن دکمه، div یاد شده را نمایش بده.
- span تعریف شده با کلاس sr-only به معنای اطلاعاتی است که صرفا جهت screen readers تدارک دیده شدهاند.
فایلهای نهایی این قسمت را از اینجا نیز میتوانید دریافت کنید:
bs3-sample03.zip
این مرورگرها در صورتیکه پیاده سازی پروتکل Open Search را در سایت شما پیدا کنند، به صورت خودکار امکان افزودن آنرا به عنوان منبع جستجوی جدیدی جهت جعبه متنی جستجوی خود ارائه میدهند. در ادامه قصد داریم با جزئیات پیاده سازی آن آشنا شویم.
تهیه OpenSearchResult سفارشی
برنامه باید بتواند محتوای XML ایی ذیل را مطابق پروتکل Open Search به صورت پویا تهیه و در اختیار مرورگر قرار دهد:
<?xml version="1.0" encoding="UTF-8" ? /> <OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/"> <ShortName>My Site's Asset Finder</ShortName> <Description>Find all your assets</Description> <Url type="text/html" method="get" template="http://MySite.com/Home/Search/?q=searchTerms"/> <InputEncoding>UTF-8</InputEncoding> <SearchForm>http://MySite.com/</SearchForm> </OpenSearchDescription>
using System; using System.Text; using System.Web; using System.Web.Mvc; using System.Xml; namespace WebToolkit { public class OpenSearchResult : ActionResult { public string ShortName { set; get; } public string Description { set; get; } public string SearchForm { set; get; } public string FavIconUrl { set; get; } public string SearchUrlTemplate { set; get; } public override void ExecuteResult(ControllerContext context) { if (context == null) throw new ArgumentNullException("context"); var response = context.HttpContext.Response; writeToResponse(response); } private void writeToResponse(HttpResponseBase response) { response.ContentEncoding = Encoding.UTF8; response.ContentType = "application/opensearchdescription+xml"; using (var xmlWriter = XmlWriter.Create(response.Output, new XmlWriterSettings { Indent = true })) { xmlWriter.WriteStartElement("OpenSearchDescription", "http://a9.com/-/spec/opensearch/1.1/"); xmlWriter.WriteElementString("ShortName", ShortName); xmlWriter.WriteElementString("Description", Description); xmlWriter.WriteElementString("InputEncoding", "UTF-8"); xmlWriter.WriteElementString("SearchForm", SearchForm); xmlWriter.WriteStartElement("Url"); xmlWriter.WriteAttributeString("type", "text/html"); xmlWriter.WriteAttributeString("template", SearchUrlTemplate); xmlWriter.WriteEndElement(); xmlWriter.WriteStartElement("Image"); xmlWriter.WriteAttributeString("width", "16"); xmlWriter.WriteAttributeString("height", "16"); xmlWriter.WriteString(FavIconUrl); xmlWriter.WriteEndElement(); xmlWriter.WriteEndElement(); xmlWriter.Close(); } } } }
تهیه OpenSearchController
در ادامه برای استفاده از Action Result سفارشی تهیه شده، نیاز است یک کنترلر را نیز به برنامه اضافه کنیم:
using System.Web.Mvc; namespace Readers { public partial class OpenSearchController : Controller { public virtual ActionResult Index() { var fullBaseUrl = Url.Action(result: MVC.Home.Index(), protocol: "http"); return new OpenSearchResult { ShortName = ".NET Tips", Description = ".NET Tips Contents Search", SearchForm = fullBaseUrl, FavIconUrl = fullBaseUrl + "favicon.ico", SearchUrlTemplate = Url.Action(result: MVC.Search.Index(), protocol: "http") + "?term={searchTerms}" }; } } }
الف) آدرسهای مطرح شده در آن باید مطلق باشند و نه نسبی. به همین جهت پارامتر protocol در اینجا ذکر شده است تا سبب تولید یک چنین آدرسهایی گردد.
ب) Url.Action ایی که در اینجا استفاده شده است مطابق تعاریف T4MVC است؛ ولی کلیات آن با نمونه پیش فرض ASP.NET MVC تفاوتی نمیکند. توسط T4MVC بجای ذکر نام اکشن متد و کنترلر مد نظر به صورت رشتهای، میتوان به صورت Strongly typed به این موارد ارجاع داد.
ج) تنها نکته مهم این کلاس، خاصیت SearchUrlTemplate است. قسمت انتهایی آن یعنی ={searchTerms} همیشه ثابت است. اما ابتدای این آدرس باید به کنترلر جستجوی شما که قادر است پارامتری را به شکل کوئری استرینگ دریافت کند، اشاره نماید.
د) FavIconUrl به آدرس یک آیکن در سایت شما اشاره میکند. برای نمونه ذکر favicon.ico پیش فرض سایت میتواند مفید باشد.
معرفی OpenSearchController به Header سایت
<link href="@Url.Action(result: MVC.OpenSearch.Index(), protocol: "http")" rel="search" title=".NET Tips Search" type="application/opensearchdescription+xml" />
@page "/LearnRouting" <h3>Learn Routing</h3>
<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>
یک نکته: مسیریابیهای تعریف شدهی در Blazor، حساس به حروف کوچک و بزرگ نیستند.
امکان تعریف بیش از یک مسیریابی برای یک کامپوننت نیز وجود دارد
در کامپوننتهای Blazor، محدودیتی از لحاظ تعداد بار تعریف دایرکتیو page@ وجود ندارد:
@page "/LearnRouting" @page "/NewRouting" <h3>Learn Routing</h3>
روش تعریف پارامترهای مسیریابی
تا اینجا اگر مسیر جدید 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; } }
پس از این تعاریف، مسیریابی مانند https://localhost:5001/LearnRouting/1 با یک پارامتر و یا https://localhost:5001/LearnRouting/1/2 که به همراه دو پارامتر است، قابل فراخوانی میشود.
روش تعریف لینک به سایر کامپوننتهای Blazor
در ادامه کامپوننت جدید Pages\LearnBlazor\LearnAdvancedRouting.razor را اضافه میکنیم؛ با این محتوای آغازین:
@page "/LearnAdvancedRouting" <h3>Learn Advanced Routing</h3>
بنابراین یک روش تعریف لینک به کامپوننتی دیگر، استفاده از کامپوننت 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¶m2=value2"> <span class="oi oi-list-rich" aria-hidden="true"></span> Learn Advanced Routing </NavLink>
@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"]; } }
هدایت به یک کامپوننت دیگر با کد نویسی
فرض کنید میخواهیم دکمهای را اضافه کنیم که با کلیک بر روی آن، ما را به کامپوننت 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"); } }
کدهای کامل این مطلب را از اینجا میتوانید دریافت کنید: Blazor-5x-Part-10.zip