مطالب
React 16x - قسمت 11 - طراحی یک گرید - بخش 1 - کامپوننت صفحه بندی
در طی چند قسمت قصد داریم مثال قسمت قبل را که کار نمایش لیستی از فیلم‌ها را انجام می‌دهد، تبدیل به یک کامپوننت گرید کنیم که دارای امکانات صفحه بندی، فیلتر کردن و مرتب سازی اطلاعات است.


بررسی ساختار کامپوننت Pagination

شبیه به کامپوننت Like که در قسمت قبل ایجاد کردیم، می‌خواهیم کامپوننت جدید Pagination نیز به طور کامل از اشیاء movie مستقل باشد؛ تا در آینده بتوان از آن در جاهای دیگری نیز استفاده کرد. به همین جهت فایل جدید src\components\common\pagination.jsx را ایجاد کرده و سپس با استفاده از میانبرهای imrc و cc در VSCode، ساختار ابتدایی این کامپوننت را ایجاد می‌کنیم. هرچند می‌توان این کامپوننت را به صورت «Stateless Functional Component» نیز طراحی کرد؛ چون state و متد دیگری بجز render نخواهد داشت و تمام اطلاعات خودش را از والد خود دریافت می‌کند.
سپس به کامپوننت movies مراجعه کرده و این کامپوننت خالی را import می‌کنیم:
import Pagination from "./common/pagination";
پس از آن به متد رندر کامپوننت movies مراجعه کرده و پس از بسته شدن tag المان جدول، قصد داریم این کامپوننت صفحه بندی را نمایش دهیم. به همین جهت المان آن‌را در این محل قرار می‌دهیم تا بتوانیم اینترفیس ابتدایی آن‌را پیش از پیاده سازی آن، طراحی کنیم:
<Pagination
  itemsCount={this.state.movies.length}
  pageSize={this.state.pageSize}
  onPageChange={this.handlePageChange}
/>
این کامپوننت نیاز به تعداد کل آیتم‌های فیلم‌ها را دارد؛ به علاوه‌ی اندازه‌ی صفحه، یا همان تعداد ردیفی که قرار است در هر صفحه نمایش داده شود. به این ترتیب کامپوننت صفحه بندی می‌تواند تعداد صفحات مورد نیاز را محاسبه کرده و سپس آن‌ها را رندر کند.
  state = {
    movies: getMovies(),
    pageSize: 4
  };

به همین جهت دو ویژگی itemsCount و pageSize را پیش از هرکاری به تعریف المان صفحه بندی اضافه کرده‌ایم تا داده‌های ورودی آن مشخص شوند. ویژگی itemsCount از تعداد اعضای آرایه‌ی movies حاصل می‌شود و pageSize را به عنوان یک خاصیت جدید شیء منتسب به state تعریف و با عدد 4 مقدار دهی کرده‌ایم.
همچنین در لیست صفحاتی که توسط این کامپوننت رندر می‌شود، باید با کلیک بر روی هر کدام، اطلاعات آن صفحه رندر شود. به همین جهت در اینجا ویژگی onPageChange تعریف شده و به متد جدید handlePageChange در کامپوننت movies، متصل گردیده تا عدد page را دریافت کرده و به آن واکنش نشان دهد:
  handlePageChange = page => {
    console.log("handlePageChange", page);
  };
تا اینجا اینترفیس کامپوننت صفحه بندی را پیش از پیاده سازی آن تعریف کردیم (تعیین ورودی‌ها و خروجی آن). در مرحله‌ی بعد، این کامپوننت را تکمیل می‌کنیم.


نمایش شماره صفحات گرید، در کامپوننت صفحه بندی

برای رندر کامپوننت صفحه بندی، از کلاس‌های مخصوص اینکار که در بوت استرپ تعریف شده‌اند، استفاده می‌کنیم که ساختار کلی آن به صورت زیر است و از یک المان nav که داخل آن ul ای با کلاس pagination و liهایی با کلاس page-item هستند، تشکیل می‌شود. هر li، به همراه یک anchor است؛ با کلاس page-link تا لینک به صفحه‌ای خاص را ارائه دهد که در اینجا بجای لینک، از کلیک بر روی آن‌ها استفاده خواهیم کرد:
import React, { Component } from "react";

class Pagination extends Component {
  render() {
    return (
      <nav>
        <ul className="pagination">
          <li className="page-item">
            <a className="page-link">1</a>
          </li>
        </ul>
      </nav>
    );
  }
}

export default Pagination;

تا اینجا اگر برنامه را ذخیره کرده و اجرا کنید، عدد 1 را در پایین جدول فیلم‌ها مشاهده خواهید کرد:


اکنون باید رندر این liها را بر اساس ورودی‌های این کامپوننت که پیشتر معرفی کردیم، یعنی pageSize و itemsCount، پویا کنیم. به همین جهت نیاز به آرایه‌ای داریم که بر اساس این ورودی‌ها، شماره‌ی صفحات مانند [1,2,3] را ارائه دهد تا بر روی آن متد Array.map را فراخوانی کرده و liهای مورد نیاز را به صورت پویا رندر کنیم:
class Pagination extends Component {
  // ...
  getPageNumbersArray() {
    const { itemsCount, pageSize } = this.props;
    const pagesCount = Math.ceil(itemsCount / pageSize);
    if (pagesCount === 1) {
      return null;
    }

    const pages = new Array();
    for (let i = 1; i <= pagesCount; i++) {
      pages.push(i);
    }
    return pages;
  }
}
در اینجا بر اساس ورودی‌ها، تعداد صفحات محاسبه شده و سپس بر اساس آن‌ها آرایه‌ای از این شماره صفحه‌ها تشکیل و بازگشت داده می‌شود. همچنین اگر تعداد صفحات 1 بود، می‌خواهیم این کامپوننت چیزی را رندر نکند. به همین جهت در اینجا null بازگشت داده شده‌است. دلیل استفاده‌ی از Math.ceil که کوچکترین عدد صحیح بزرگتر یا مساوی خروجی را بازگشت می‌دهد، نیز همین مورد است. توسط این متد، خروجی float دریافتی به integer تبدیل شده و سپس قابلیت مقایسه‌ی با 1 را پیدا می‌کند. برای مثال اگر تعداد ردیف‌های صفحه را به 10 تنظیم کنیم، خروجی این تقسیم در این مثال، 0.9 خواهد بود که شرط بررسی pagesCount === 1 را برآورده نمی‌کند. به همین جهت توسط متد Math.ceil، این خروجی به عدد 1 تقریب زده شده و سبب بازگشت نال از این متد خواهد شد.
سپس به کمک متد map، اعضای این آرایه را تبدیل به لیست liهای نمایش شماره صفحات می‌کنیم. در اینجا key هر li را نیز به شماره صفحه که منحصربفرد است، تنظیم کرده‌ایم:
class Pagination extends Component {
  render() {
    const pages = this.getPageNumbersArray();
    if (!pages) {
      return null;
    }

    return (
      <nav>
        <ul className="pagination">
          {pages.map(page => (
            <li key={page} className="page-item">
              <a className="page-link">{page}</a>
            </li>
          ))}
        </ul>
      </nav>
    );
  }

پس از ذخیره‌ی این کامپوننت و بارگذاری مجدد برنامه در مرورگر، شماره‌ی صفحات رندر شده، در پایین جدول مشخص هستند:


با داشتن 9 فیلم در آرایه‌ی movies و نمایش 4 فیلم به ازای هر صفحه، به 3 صفحه خواهیم رسید که به درستی در اینجا رندر شده‌است. یکبار هم برای آزمایش بیشتر، مقدار pageSize را در کامپوننت movies به 10 تنظیم کنید. در این حالت کامپوننت صفحه بندی نباید رندر شود.


مدیریت انتخاب شماره‌های صفحات

در این قسمت می‌خواهیم مدیریت onPageChange={this.handlePageChange} را که به تعریف المان صفحه بندی در کامپوننت movies اضافه کردیم، تکمیل کنیم. برای این منظور در کامپوننت صفحه بندی، قسمت anchor را به صورت زیر تغییر می‌دهیم تا با کلیک بر روی آن، رخداد onPageChange صادر شود:
<a
   onClick={() => this.props.onPageChange(page)}
   className="page-link"
   style={{ cursor: "pointer" }}
>
   {page}
</a>

تا اینجا اگر برنامه را آزمایش کنیم، با کلیک بر روی لینک‌های شماره صفحات، شماره صفحه‌ی انتخابی، در کنسول توسعه دهندگان مرورگر لاگ می‌شود.

اکنون می‌خواهیم اگر صفحه‌ای انتخاب شد، شماره‌ی آن صفحه با رنگی دیگر نمایش داده شود. در بوت استرپ برای اینکار تنها کافی است کلاس active را به className هر li اضافه کنیم و برعکس. یعنی اگر page ای مساوی صفحه‌ی جاری انتخاب شده بود (currentPage در اینجا)، آنگاه کلاس page-item active، به المان li اضافه شود. بنابراین در این کامپوننت نیاز است عدد currentPage را نیز دریافت کنیم. به همین جهت ویژگی currentPage را به تعریف المان Pagination در کامپوننت movies اضافه می‌کنیم:
<Pagination
  itemsCount={this.state.movies.length}
  pageSize={this.state.pageSize}
  onPageChange={this.handlePageChange}
  currentPage={this.state.currentPage}
/>
این ویژگی نیز مقدار خودش را از state به روز شده دریافت می‌کند:
class Movies extends Component {
  state = {
    movies: getMovies(),
    pageSize: 4,
    currentPage: 1
  };
به روز رسانی state نیز در متد handlePageChange که به ویژگی onPageChange متصل است، بر اساس page دریافتی از کامپوننت صفحه بندی، رخ می‌دهد:
  handlePageChange = page => {
    console.log("handlePageChange", page);
    this.setState({currentPage: page});
  };
بنابراین هرگاه که بر روی یک شماره صفحه در کامپوننت صفحه بندی کلیک می‌شود، رخ‌داد onPageChange متصل به تعریف المان Pagination درج شده‌ی در کامپوننت movies، روی داده و به همراه آن شماره صفحه‌ای، به متد رخ‌دادگران متصل به آن در کامپوننت movies که در اینجا handlePageChange نام دارد، ارسال می‌شود. در این متد state کامپوننت به روز شده و این امر سبب فراخوانی مجدد متد رندر می‌شود که در انتهای آن کامپوننت Pagination درج شده‌است. بنابراین به روز رسانی state، سبب رندر مجدد کامپوننت صفحه بندی با currentPage جدیدی که به آن رسیده‌است، خواهد شد.

پس از این تغییرات، به کامپوننت صفحه بندی مراجعه کرده و بر اساس currentPage دریافتی، کلاس active را به المان li اعمال می‌کنیم:
<li
  key={page}
  className={
    page === this.props.currentPage
    ? "page-item active"
    : "page-item"
  }
>
پس از اعمال این تغییرات، اکنون برنامه در مرورگر به صورت زیر به نظر می‌رسد:


در اولین بار نمایش برنامه، عدد 1 در حالت انتخاب شده قرار دارد؛ چون مقدار currentPage موجود در state، همان عدد 1 است. پس از آن با کلیک بر روی اعداد دیگر، با به روز رسانی state، مقدار currentPage تغییر کرده و کامپوننت صفحه بندی نسبت به آن واکنش نشان می‌دهد.


نمایش صفحه بندی شده‌ی اطلاعات

تا اینجا لیستی که نمایش داده می‌شود، حاوی تمام اطلاعات آرایه‌ی this.state.movies است و بر اساس شماره‌ی صفحه‌ی انتخابی، تغییر نمی‌کند. به همین جهت با استفاده از متد slice، تکه‌ای از آرایه‌ی movies را که بر اساس شماره صفحه‌ی انتخابی و تعداد ردیف‌ها در هر صفحه نیاز است نمایش داده شود، انتخاب کرده و بازگشت می‌دهیم:
  paginate() {
    const first = (this.state.currentPage - 1) * this.state.pageSize;
    const last = first + this.state.pageSize;
    return this.state.movies.slice(first, last);
  }
اکنون در متد رندر کامپوننت movies، بجای کار با کل آرایه‌ی this.state.movies، آرایه‌ی جدید slice شده را توسط متد paginate دریافت کرده:
  render() {
    const { length: count } = this.state.movies;

    if (count === 0) return <p>There are no movies in the database.</p>;

    const movies = this.paginate();
و سپس در همین متد رندر، فراخوانی this.state.movies.map را به movies.map تغییر می‌دهیم، تا تنها لیست مرتبط با هر صفحه‌ی انتخابی نمایش داده شود.

پس از ذخیره‌ی تغییرات و بارگذاری مجدد برنامه، اکنون می‌توان نمایش صفحه بندی شده‌ی اطلاعات را شاهد بود:



بررسی صحت نوع پارامترهای ارسالی به کامپوننت‌ها

تا اینجا فرض بر این است که مصرف کننده‌ی کامپوننت صفحه بندی، دقیقا همان ویژگی‌هایی را که ما طراحی کرده‌ایم، با همان نام‌ها و همان نوع‌ها را حتما به آن ارسال می‌کند. همچنین اگر افزونه‌ی eslint را هم در VSCode نصب کرده باشید، به همراه نصب وابستگی‌های زیر در خط فرمان:
> npm i -g typescript eslint tslint eslint-plugin-react-hooks jshint babel-eslint eslint-plugin-react eslint-plugin-mocha

 به ازای هر خاصیت props استفاده شده‌ی در کامپوننت صفحه بندی، اخطاری را مانند «'currentPage' is missing in props validation eslint(react/prop-types)» مشاهده خواهید کرد:



که عنوان می‌کند props validation این خاصیت استفاده شده، فراموش شده‌است.
در نگارش‌های قبلی React، امکانات بررسی نوع‌های ارسالی به کامپوننت‌ها، جزئی از بسته‌ی اصلی آن بود؛ اما از نگارش 15 به بعد، به بسته‌ی مستقلی منتقل شده‌است که باید به صورت جداگانه‌ای در ریشه‌ی پروژه نصب شود:
> npm i prop-types --save

البته اگر TypeScript را بر روی سیستم خود نصب کرده باشید، دیگر نیازی به نصب بسته‌ی npm فوق را ندارید و prop-types، جزئی از آن است که عموما در یک چنین مسیری قرار دارد و برای کار کردن با آن، تنها ذکر import مرتبط با PropType در ماژول‌های برنامه کافی بوده و برنامه در این حالت بدون مشکل کامپایل می‌شود:
 C:/Users/{username}/AppData/Local/Microsoft/TypeScript/3.6/node_modules/@types/prop-types/index

اکنون در ابتدای فایل کامپوننت صفحه بندی، تعریف زیر را اضافه می‌کنیم:
 import PropTypes from "prop-types";
سپس در انتهای این فایل، اعتبارسنجی props آن‌را تعریف خواهیم کرد:
Pagination.propTypes = {
  itemsCount: PropTypes.number.isRequired,
  pageSize: PropTypes.number.isRequired,
  currentPage: PropTypes.number.isRequired,
  onPageChange: PropTypes.func.isRequired
};

export default Pagination;
همانطور که مشاهده می‌کنید، در اینجا خاصیت جدید propTypes (دقیقا با همین نگارش؛ در غیراینصورت بررسی نوع‌ها کار نخواهد کرد)، به تعریف کلاس Pagination اضافه شده‌است (پس از تعریف کلاس کامپوننت به صورت مستقل اضافه می‌شود).
سپس مقدار این خاصیت جدید را به شیءای تنظیم می‌کنیم که نام خواص آن، دقیقا همان نام خواص و رویدادهای props استفاده شده‌ی در این کامپوننت است. در ادامه توسط PropTypes ای که در ابتدای ماژول import می‌شود، کار تعریف نوع این خواص و اجباری بودن آن‌ها را می‌توان مشخص کرد که برای مثال در اینجا سه خاصیت تعریف شده از نوع عددی و اجباری بوده و onPageChange، از نوع func است.
پس از اضافه کردن Pagination.propTypes و مقدار دهی آن، خطاهای eslint ای که در تصویر فوق مشاهده کردید، برطرف می‌شوند. همچنین اگر فراخوان کامپوننت Pagination مثلا بجای itemsCount یک رشته‌ی فرضی را وارد کند (برای آزمایش آن در کامپوننت movies، در تعریف المان Pagination، مقدار itemsCount را یک رشته وارد کنید)، چنین خطایی در مرورگر ظاهر خواهد شد که عنوان می‌کند itemsCount یک عدد را می‌پذیرد و نوع ورودی آن اشتباه است:

البته این خطا فقط در حالت development مشاهده می‌شود و در حالت توزیع برنامه، خیر.

بنابراین تعریف propTypes یک best practice ایجاد کامپوننت‌های React است که نه فقط بررسی نوع‌ها را فعال می‌کند، بلکه می‌تواند به عنوان مستندات آن نیز در جهت تعیین props مورد نیاز، همچنین نوع و اجباری بودن آن‌ها، اطلاعات کاملی را ارائه کند.


کدهای کامل این قسمت را از اینجا می‌توانید دریافت کنید: sample-11.zip
نظرات مطالب
بازسازی کد: ارتباط یک طرفه و دو طرفه بین کلاس ها
بنده به هیچ کدام از این روش‌ها انتقادی نداشتم. تصمیم طراحی باید بر اساس شرایط پروژه اتخاد بشه. 
در مورد استفاده از navigation property 
در entity framework استفاده از Navigation property برعکس (در مثال شما کالکشن Orders در کلاس Customer) برای ایجاد یک رابطه یک به چند الزامی نیست. (برای ایجاد relation نیازی به حضوری این collection نیست و حضور خصوصیت Customer در کلاس Order کافی است) و در صورتی که شما از نظر کسب و کاری نیازی به چنین خصوصیتی نداشته باشید می‌توانید از ایجاد آن صرف نظر کنید. 
public class Order
{
    public int Id { get; set; }
    public Customer Customer { get; set; }
}
public class Customer
{
    public int Id { get; set; }
}
......

modelBuilder.Entity<Order>().HasRequired(ff => ff.Customer);


حضور خصوصیت Orders در کلاس Customer در مثال شما از نظر کسب و کاری به این معنی است که شما برای رسیدن به لیست سفارشات نیاز دارید که به شی مشتری دسترسی پیدا کنید. 
نکته اصلی این بازسازی کد اجتناب از استفاده بی مورد ارتباط دوطرفه است.
اشتراک‌ها
DocumentDB پایگاه داده NoSQL مایکروسافت
 Azure DocumentDB is a NoSQL document database service designed from the ground up to natively support JSON and JavaScript directly inside the database engine. It’s the right solution for applications that run in the cloud when predictable throughput, low latency, and flexible query are key. Microsoft consumer applications like MSN use DocumentDB in production to support millions of users.
DocumentDB پایگاه داده NoSQL مایکروسافت
مطالب
Blazor 5x - قسمت 28 - برنامه‌ی Blazor WASM - نمایش لیست اطلاعات دریافتی از Web API
در قسمت قبل، سرویس و کامپوننت دریافت اطلاعات اتاق‌ها را از Web API برنامه، تکمیل کردیم. در این قسمت با استفاده از اطلاعات مهیا شده، UI آن‌را نیز تکمیل خواهیم کرد.


نمایش منتظر بمانید در حین بارگذاری اولیه‌ی کامپوننت

کامپوننت‌هایی که قرار است اطلاعات را از یک Web API دریافت کنند، مدتی باید منتظر بمانند تا عملیات رفت و برگشت به سرور، تکمیل شود. در این بین می‌توان یک loading را به کاربر نمایش داد:
@page "/hotel/rooms"

@if (Rooms is not null && Rooms.Any())
{

}
else
{
    <div style="position:fixed;top:50%;left:50%;margin-top:-50px;margin-left:-100px;">
        <img src="images/loader.gif" />
    </div>
}

@code {
    IEnumerable<HotelRoomDTO> Rooms = new List<HotelRoomDTO>();
    // ... 
}
- فیلد Rooms را در قسمت قبل، در متد LoadRooms، از Web API دریافت و مقدار دهی کردیم. تا زمان تکمیل عملیات این متد، فیلد Rooms، فاقد عضوی است؛ بنابراین قسمت else شرط فوق اجرا می‌شود که یک loading را نمایش خواهد داد. مابقی UI برنامه در قسمت if آن قرار می‌گیرد.
- هر زمانیکه کار روال رویدادگردان OnInitializedAsync به پایان برسد (که شامل اجرای متد LoadRooms نیز هست)، سبب فراخوانی خودکار StateHasChanged می‌شود. این فراخوانی، UI را مجددا رندر می‌کند. به همین جهت است که پس از پایان کار، محتوای if، رندر خواهد شد.
- از این loading سفارشی که در میانه‌ی صفحه نمایش داده می‌شود، می‌توان در فایل wwwroot\index.html نیز بجای loading پیش‌فرض آن استفاده کرد:
  <body>
    <div id="app">
      <div
        style="
          position: fixed;
          top: 50%;
          left: 50%;
          margin-top: -50px;
          margin-left: -100px;
        "
      >
        <img src="images/ajax-loader.gif" />
      </div>
    </div>

افزودن خواصی جدید به HotelRoomDTO

می‌خواهیم به کاربر امکان تغییر تعداد روزهای اقامت را بدهیم. این انتخاب باید در لیست اتاق‌های نمایش داده شده، با تغییر تعداد روزهای اقامت (TotalDays) و هزینه‌ی جدید متناظر با آن (TotalAmount)، منعکس شود. به همین جهت این خواص را به HotelRoomDTO، اضافه می‌کنیم:
namespace BlazorServer.Models
{
    public class HotelRoomDTO
    {
        // ...

        public int TotalDays { get; set; }
        public decimal TotalAmount { get; set; }
    }
}
محاسبات مربوط به این خواص را هم می‌توان در همان کامپوننت HotelRooms.razor، پس از بارگذاری لیست اتاق‌ها از Web API، انجام داد:
@code
{
     HomeVM HomeModel = new HomeVM();
    // ...

    private async Task LoadRoomsAsync()
    {
        Rooms = await HotelRoomService.GetHotelRoomsAsync(HomeModel.StartDate, HomeModel.EndDate);
        foreach (var room in Rooms)
        {
            room.TotalAmount = room.RegularRate * HomeModel.NoOfNights;
            room.TotalDays = HomeModel.NoOfNights;
        }
    }
}


افزودن امکان تغییر تعداد روزهای اقامت در همان صفحه‌ی نمایش لیست اتاق‌ها


همانطور که در تصویر فوق هم مشاهده می‌کنید، می‌خواهیم در این صفحه نیز کاربر بتواند زمان شروع اقامت و مدت مدنظر را تغییر دهد. به همین جهت، HomeModel ای را که در قسمت قبل از Local Storage دریافت کردیم، به فرم زیر متصل می‌کنیم تا اجزای آن در این فرم، نمایش داده شده و قابل تغییر شوند:
@if (Rooms is not null && Rooms.Any())
{
    <EditForm Model="HomeModel" OnValidSubmit="SaveBookingInfo" class="bg-light">
        <div class="pt-3 pb-2 px-5 mx-1 mx-md-0 bg-secondary">
            <DataAnnotationsValidator />
            <div class="row px-3 mx-3">
                <div class="col-6 col-md-4">
                    <div class="form-group">
                        <label class="text-warning">Check in Date</label>
                        <InputDate @bind-Value="HomeModel.StartDate" class="form-control" />
                    </div>
                </div>
                <div class="col-6 col-md-4">
                    <div class="form-group">
                        <label class="text-warning">Check Out Date</label>
                        <input @bind="HomeModel.EndDate" disabled="disabled"
                            readonly="readonly" type="date" class="form-control" />
                    </div>
                </div>
                <div class=" col-4 col-md-2">
                    <div class="form-group">
                        <label class="text-warning">No. of nights</label>
                        <select class="form-control" @bind="HomeModel.NoOfNights">
                            <option value="Select" selected disabled="disabled">(Select No. Of Nights)</option>
                            @for (var i = 1; i <= 10; i++)
                            {
                                <option value="@i">@i</option>
                            }
                        </select>
                    </div>
                </div>

                <div class="col-8 col-md-2">
                    <div class="form-group" style="margin-top: 1.9rem !important;">
                        @if (IsProcessing)
                        {
                            <button class="btn btn-success btn-block form-control">
                                <i class="fa fa-spin fa-spinner"></i>Processing...
                            </button>
                        }
                        else
                        {
                            <input type="submit" value="Update" class="btn btn-success btn-block form-control" />
                        }
                    </div>
                </div>
            </div>
        </div>
    </EditForm>
نکته‌ی مهم این فرم، مدیریت قسمت کلیک بر روی دکمه‌ی Update است که سبب فراخوانی روال رویدادگران OnValidSubmit می‌شود:
@code {
    bool IsProcessing;

    // ...

    private async Task SaveBookingInfo()
    {
        IsProcessing = true;
        HomeModel.EndDate = HomeModel.StartDate.AddDays(HomeModel.NoOfNights);
        await LocalStorage.SetItemAsync(ConstantKeys.LocalInitialBooking, HomeModel);
        await LoadRoomsAsync();
        IsProcessing = false;
    }
}
در ابتدای عملیات، فیلد جدید IsProcessing را به true تنظیم می‌کنیم. این مورد سبب می‌شود تا برچسب دکمه‌ی Update به Processing... تغییر کند. سپس فیلد محاسباتی EndDate را بر اساس اطلاعات جدید فرم، به روز رسانی می‌کنیم. در ادامه، مجددا این اطلاعات را در Local Storage ذخیره سازی کرده و کار LoadRoomsAsync را انجام می‌دهیم که به همراه آن، خواص جدید تعداد روزها و هزینه‌ی اقامت نیز مجددا محاسبه می‌شوند. در آخر برچسب دکمه‌ی Update را به حالت اول باز می‌گردانیم.

سؤال: زمانیکه IsProcessing به true تنظیم می‌شود که هنوز کار متد رویدادگردان SaveBookingInfo به پایان نرسیده‌است و فراخوانی خودکار StateHasChanged در پایان متدهای رویدادگردان صورت می‌گیرد. پس چطور است که سبب رندر مجدد UI و تغییر برچسب دکمه‌ی Update می‌شود؟
پاسخ به این سؤال را در قسمت 6 این سری با بررسی چرخه‌ی حیات کامپوننت‌ها، مشاهده کردیم:
«البته متدهای رویدادگردان async، دوبار سبب فراخوانی ضمنی StateHasChanged می‌شوند؛ یکبار زمانیکه قسمت sync متد به پایان می‌رسد (در این مثال یعنی تا قبل از اولین await نوشته شده) و یکبار هم زمانیکه کار فراخوانی کلی متد به پایان خواهد رسید»


نمایش لیست اتاق‌ها


نمایش لیست اتاق‌ها مطابق تصویر فوق، دو قسمت اصلی را دارد:
الف) نمایش لیست تصاویر منتسب به یک اتاق، توسط کامپوننت carousel بوت استرپ
@foreach (var room in Rooms)
{
            <div class="row p-2 my-3 " style="border-radius:20px; border: 1px solid gray">
                <div class="col-12 col-lg-3 col-md-4">
                    <div id="carouselExampleIndicators_@room.Id"
                        class="carousel slide mb-4 m-md-3 m-0 pt-3 pt-md-0"
                        data-ride="carousel">
                        <ol class="carousel-indicators">
                            @{
                                int imageIndex = 0;
                                int innerImageIndex = 0;
                            }
                            @foreach (var image in room.HotelRoomImages)
                            {
                                if (imageIndex == 0)
                                {
                                    <li data-target="#carouselExampleIndicators_@room.Id"
                                        data-slide-to="@imageIndex" class="active"></li>

                                }
                                else
                                {
                                    <li data-target="#carouselExampleIndicators_@room.Id"
                                        data-slide-to="@imageIndex"></li>
                                }
                                imageIndex++;
                            }
                        </ol>
                        <div class="carousel-inner">
                            @foreach (var image in room.HotelRoomImages)
                            {
                                var imageUrl = $"{ImagesBaseAddress}/{image.RoomImageUrl}";
                                if (innerImageIndex == 0)
                                {
                                    <div class="carousel-item active">
                                        <img class="d-block w-100" style="border-radius:20px;"
                                            src="@imageUrl" alt="First slide">
                                    </div>
                                }
                                else
                                {
                                    <div class="carousel-item">
                                        <img class="d-block w-100" style="border-radius:20px;"
                                            src="@imageUrl" alt="First slide">
                                    </div>
                                }

                                innerImageIndex++;
                            }
                        </div>
                        <a class="carousel-control-prev" href="#carouselExampleIndicators_@room.Id"
                            role="button" data-slide="prev">
                            <span class="carousel-control-prev-icon" aria-hidden="true"></span>
                            <span class="sr-only">Previous</span>
                        </a>
                        <a class="carousel-control-next" href="#carouselExampleIndicators_@room.Id"
                            role="button" data-slide="next">
                            <span class="carousel-control-next-icon" aria-hidden="true"></span>
                            <span class="sr-only">Next</span>
                        </a>
                    </div>
                </div>
}
- هرچند این قطعه کد، طولانی به نظر می‌رسد اما قسمت‌های مختلف آن صرفا بر اساس مستندات سایت بوت استرپ، جهت تشکیل ساختار ابتدایی و استاندارد کامپوننت carousel، تهیه شده‌اند.
- سپس در حلقه‌ای که برای نمایش لیست اتاق‌ها تهیه کرده‌ایم، قسمت‌های مختلف carousel را تکمیل می‌کنیم که در اینجا نیاز به ایندکس تصاویر، لیست تصاویر و یک Id منحصربفرد برای این carousel خاص را دارد تا بتوان چندین وهله از آن‌را در صفحه قرار داد که این id را بر اساس Id اتاق مشخص کرد‌ه‌ایم.

دو نکته:
- در این مثال برای تعریف لینک به تصاویر، کد زیر را مشاهده می‌کنید:
var imageUrl = $"{ImagesBaseAddress}/{image.RoomImageUrl}";
و این ImagesBaseAddress، به صورت زیر تعریف شده که همان آدرس برنامه‌ی blazor server ای است که مشخصات اتاق‌ها و تصاویر را ثبت می‌کند:
@code {
   string ImagesBaseAddress = "https://localhost:5006";
بنابراین اگر می‌خواهید تصاویر را هم مشاهده کنید، باید برنامه‌ی مجزای blazor server این سری نیز در حال اجرا باشد.
- کامپوننت carousel برای اجرا، نیاز به فایل lib/bootstrap/dist/js/bootstrap.bundle.min.js را نیز دارد. به همین جهت مدخل اسکریپت آن‌را باید به فایل wwwroot\index.html اضافه کرد.

ب) نمایش جزئیات نام و هزینه‌ی اتاق
قسمت دوم حلقه‌ی foreach نمایش لیست اتاق‌ها، جهت نمایش جزئیات هر اتاق تعریف شده‌است:
@foreach (var room in Rooms)
{
                <div class="col-12 col-lg-9 col-md-8">
                    <div class="row pt-3">
                        <div class="col-12 col-lg-8">
                            <p class="card-title text-warning" style="font-size:xx-large">@room.Name</p>
                            <p class="card-text">
                                @((MarkupString)room.Details)
                            </p>
                        </div>
                        <div class="col-12 col-lg-4">
                            <div class="row pb-3 pt-2">
                                <div class="col-12 col-lg-11 offset-lg-1">
                                    <a href="@($"hotel/room-details/{room.Id}")" class="btn btn-success btn-block">Book</a>
                                </div>
                            </div>
                            <div class="row ">
                                <div class="col-12 pb-5">
                                    <span class="float-right">
                                        <span class="float-right">Occupancy : @room.Occupancy adults </span><br />
                                        <span class="float-right pt-1">Room Size : @room.SqFt sqft</span><br />
                                        <h4 class="text-warning font-weight-bold pt-4">
                                            <span style="border-bottom:1px solid #ff6a00">
                                                @room.TotalAmount.ToString("#,#.00;(#,#.00#)")
                                            </span>
                                        </h4>
                                        <span class="float-right">Cost for  @room.TotalDays nights</span>
                                    </span>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
}
- در این مثال از MarkupString استفاده شده تا بتوان یک محتوای HTML ای را در صفحه نمایش داد.
- هر اتاق نمایش داده شده، لینکی را به صفحه‌ی خاص خودش نیز دارد که آن‌را در قسمت بعدی تکمیل می‌کنیم.
- در اینجا TotalAmount و TotalDays محاسباتی و قابل تغییر بر اساس انتخاب کاربر نیز درج شده‌اند.


یک تمرین: در برنامه‌ی Blazor Server، سرویسی را جهت درج مشخصات امکانات رفاهی هتل تهیه کردیم. این امکانات رفاهی را از طریق Web API برنامه دریافت و سپس در برنامه‌ی سمت کلاینت نمایش دهید.
بنابراین تکمیل این تمرین شامل تهیه‌ی موارد زیر است که کدنویسی آن، با دو قسمت اخیر این سری دقیقا یکی است و نکته‌ی جدیدی را به همراه ندارد (و کدهای کامل آن را از انتهای بحث می‌توانید دریافت کنید):
- تهیه‌ی HotelAmenityController در پروژه‌ی Web API که به کمک IAmenityService، لیست امکانات رفاهی را بازگشت می‌دهد.
- تهیه‌ی ‍ClientHotelAmenityService در پروژه‌ی WASM که همانند ClientHotelRoomService قسمت قبل ، از Web API، لیست HotelAmenityDTO‌ها را دریافت می‌کند.
- ثبت سرویس جدید ‍ClientHotelAmenityService در Program.cs.
- در آخر حلقه‌ای را بر روی لیست HotelAmenityDTO دریافتی از ClientHotelRoomService در کامپوننت Index.razor تشکیل داده و آن‌ها را نمایش می‌دهیم.


کدهای کامل این مطلب را از اینجا می‌توانید دریافت کنید: Blazor-5x-Part-28.zip
اشتراک‌ها
آخرین بتای Bootstrap 5 منتشر شد

Our final beta for Bootstrap 5 has come with some amazing new changes, including a new component!), documentation updates, and more. We’ve also fixed some important bugs since our last release, in particular with our dependencies. Next up is our stable release! 

آخرین بتای Bootstrap 5 منتشر شد
اشتراک‌ها
Bootstrap 4.4.0 منتشر شد
  • New responsive containers! Over a year in the making, fluid up to a particular breakpoint, available for all responsive tiers.
  • New responsive .row-cols classes for quickly specifying the number of columns across breakpoints. This one is huge for those of you who have asked for responsive card decks. 
Bootstrap 4.4.0 منتشر شد
اشتراک‌ها
انتخاب ساختار داده مناسب در حل مشکلات

Data structure is a particular way of organizing data in a computer. The developer must choose the appropriate data structure for better performance. If the developer chooses bad data structure, the system does not perform well. This article explains the each data structure advantages and usage. 

انتخاب ساختار داده مناسب در حل مشکلات
اشتراک‌ها
پشتیبانی SQL Server 2017 از بانک های گراف محور

Graph extensions in SQL Server 2017 will facilitate users in linking different pieces of connected data to help gather powerful insights and increase operational agility. Graphs are well suited for applications where relationships are important, such as fraud detection, risk management, social networks, recommendation engines, predictive analysis, dependence analysis, and IoT applications. In this session we will demonstrate how you can use SQL Graph extensions to build your application using graph data. 

پشتیبانی SQL Server 2017 از بانک های گراف محور
نظرات اشتراک‌ها
اندازه گیری دما، مختصات جغرافیایی، لرزه یا تکانه و تنظیم نمودن هشدار دهنده توسط NET Micro Framework
سلام.
مشکل اصلی این که Netduino تو ایران یا نیست یا خیلی گرونه.یعنی من که پیدا نکردم و مجبور شدم پروژه خانه هوشمندی که با تیم روش کار می‌کردیم به سمت Arduino و Rassbery-pi ببریم و با Python و C برنامه‌ها رو بنویسیم.
که البته خیلی هم خوب جواب داد
مطالب
بخش سوم - بررسی امکانات و کدنویسی در کامپایلر Svelte
در بخش‌های قبل تا حدودی با کامپایلر Svelte و ساختار فایل‌های آن آشنا شدیم. در این بخش با چند مثال قصد دارم امکاناتی را که Svelte در اختیارمان قرار میدهد، شرح دهم. 

Dynamic attributes : 
در بخش قبل دیدیم که با استفاده از علامت ( آکولاد {} ) میتوانیم مقادیر موجود در تگ اسکریپت را در html خود رندر کنیم. ولی از این علامت میتوان برای مقدار دهی attribute‌ها هم استفاده کرد. 
<script>
let src = 'https://svelte.dev/tutorial/image.gif';
let name = 'Rick Astley';
</script>

<img src={src} alt="{name} dancing">
اگر این کد را در بدنه کامپوننت خود یعنی همان فایل App.svelte قرار دهیم، به درستی کار خواهد کرد و مقدار‌های متغیر‌های src و name، در تگ img ما قرار خواهند گرفت. چند نکته در اینجا وجود دارد که بهتر است به آنها اشاره کنم.

  • نکته اول : اگر در تگ img مقدار alt را وارد نکنیم و یا alt در این تگ وجود نداشته باشد، یک هشدار توسط کامپایلر svelte برای ما با عنوان <img> element should have an alt attribute> ایجاد میشود. زمان ساخت یک برنامه بسیار مهم است تا قوانین نوشتن یک کد html خوب را رعایت کنیم تا برای تمامی کاربران احتمالی برنامه قابل استفاده باشد. در همین مثال با ایجاد یک هشدار Svelte تلاش میکند که ما را از اشتباه در نوشتن کد html مطلع سازد.

  • نکته دوم : اگر نام یک آبجکت تعریف شده و یک attribute، برابر باشد میتوانیم از نسخه کوتاه شده یا Shorthand attributes در svelte استفاده کنیم. به طور مثال در مثال بالا میتوانیم از کد زیر در خط 6 استفاده کنیم.
<img {src} alt="{name} dancing">


Nested components :
همانطور که قبلا اشاره کرده بودم، همه‌چیز در svelte از کامپوننت‌ها تشکیل میشود. در یک برنامه واقعی درست نیست که تمامی کد برنامه را مستقیما در کامپوننت اولیه برنامه بنویسیم. به جای آن بهتر است هر بخش از وبسایت، کامپوننت مجزایی داشته باشد تا کدها و استایل‌ها و html ما نظم و خوانایی بیشتری پیدا کنند.
یک فایل دیگر را در کنار کامپوننت App.svelte که در بخش قبلی ایجاد کرده بودیم، به نام Nested.svelte ایجاد کنید و در آن کد زیر را قرار دهید.
<script>
  export let siteName = "dotnettips";
</script>

<p>this is a nested component for third tutorial on {siteName}</p>
در این مرحله ما یک کامپوننت جدید را ایجاد کردیم که برای نمایش آن باید به App.svelte اضافه شود. برای انجام اینکار محتوای App.svelte را به کد زیر تغییر دهید.
<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.svelte حدودا تعداد زیادی متغیر export شده داریم که دقیقا همنام با آنها در App.svelte یک آبجکت به نام pkg مقدار دهی شده‌است (خط 4-9). در این شرایط نیازی به نوشتن تک تک خصیصه‌ها مانند کاری که در خط 12 کردیم نیست. خیلی ساده میتوانیم از امکانات ES6 برای destruct کردن این آبجکت و ست کردن این خصیصه‌ها استفاده کنیم؛ به این صورت : 
<Info {...pkg}/>



مروری کوتاه بر مدیریت Event ها  در svelte:
قدرت اصلی svelte، واکنش پذیر بودن آن است. به این معنی که همیشه DOM را با وضعیت یا state برنامه هماهنگ نگه میدارد. یکی از مواردی که بطور مثال این امکان را به خوبی نمایش میدهد، event‌ها هستند. 
به مثال زیر توجه کنید:
<script>
  let count = 0;

  function handleClick() {
    count += 1;
  }
</script>

<p>Count : {count}</p>
<button on:click={handleClick}>
  Clicked {count} {count === 1 ? 'time' : 'times'}
</button>
در مثال بالا در خط 10، یک button را ایجاد کردیم که به متد click آن یک function اختصاص داده شده که در خط 4 تعریف کرده بودیم. برای bind کردن یک event در svelte از on: استفاده میکنیم. البته دقت کنید که در خط 10، نام function بدون پرانتز نوشته شده‌است؛ چرا که ما قصد اجرای آن را نداریم و صرفا میخواهیم این فانکشن پس از کلیک شدن بر روی button اجرا شود. در خط 5 هم در بدنه فانکشن خود که پس از هر کلیک اجرا خواهد شد، یک واحد به متغیر count اضافه میکنیم. در خطوط 9 و 11 هم مقدار count را نمایش داده‌ایم. 
در بخش‌های بعد به جزئیات کار با event‌ها بیشتر میپردازیم.

واکنش پذیری یا Reactivity در svelte :
svelte به صورت خودکار DOM را پس از اینکه وضعیت کامپوننت یا آبجکت‌های شما تغییر کند، آپدیت میکند؛ ولی در این بین چندین حالت وجود دارند که به صورت خودکار svelte از تغییر وضعیت کامپوننت شما آگاه نمیشود. 
عملگر :$
به مثال زیر توجه کنید. 
<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 را در خود نگه دارد. اگر این کد را اجرا نمایید، متوجه خواهید شد که مقدار double پس از تغییر count، تغییری نخواهد کرد. چرا که بر خلاف سایر فریم ورک‌ها، مانند react برای بهبود performance برنامه، svelte از Virtual-DOM استفاده نمیکند و این امر سبب میشود که کل کدهای کامپوننت ما در هر بار تغییر، در وضعیت بخشی از آن، مجددا اجرا نشود و مستقیما بخش مورد نظر در DOM تغییر کند. این امر سبب شده از نظر performance در کامپوننت‌های پیچیده که به طور مثال از انیمیشن یا visualisation‌ها استفاده میکنند، کامپوننت‌های svelte چندین برابر پرسرعت‌تر از سایر فریم ورک‌های مشابه عمل کند. ولی اگر بخواهیم که مقدار doubled آپدیت شود چه کاری باید کرد؟ 
راهکار اول مقدار دهی doubled در function مرتبط است، چرا که svelte همیشه به عملگر مساوی = در کد جاوا اسکریپت نگاه میکند. ولی این راهکار در مثال بالا منطقی به نظر نمی‌آید چرا که وظیفه متد ما صرفا دو برابر کردن مقدار count است.
راهکار دوم استفاده از عملگر :$  قبل از تعریف متغیر میباشد که به نظر راهکار مناسب‌تری است. پس در کد بالا فقط لازم است خط سوم را به کد زیر تغییر دهیم تا برنامه بدرستی مقدار doubled را پس از تغییر count محاسبه کند. 
$: doubled = count * 2;
به این طریق میتوانیم متغیر خود را وادار کنیم درصورت تغییر مقادیر، در سمت راست مساوی، مقدار doubled را مجددا محاسبه نماید.

استفاده از این عملگر در svelte به تعریف متغیر‌ها محدود نمیشود. ما هر جائیکه نیاز به واکنش پذیری نسبت به کامپوننت‌ها یا متغیر‌های خود داشتیم، میتوانیم از این عملگر استفاده کنیم. به طور مثال برای لاگ کردن اطلاعات، هر بار که مقدار count تغییر کرد میتوانیم از کد زیر استفاده کنیم. 
$: console.log(`the count is ${count}`);
همچنین میتوانیم به آسانی گروهی از فعالیت‌ها را با هم واکنش پذیر کنیم. مثال :
$: {
  console.log(`the count is ${count}`);
  alert(`I SAID THE COUNT IS ${count}`);
}
حتی میتوان از این عملگر قبل از بلاک‌های کد، مانند if یا for استفاده کرد. مثال:
$: 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>

در مثال بالا پس از هر کلیک، یک عدد به آرایه numbers اضافه میشود؛ ولی در خط 11 که این آرایه را نمایش میدهیم، مقدار آن تغییر نخواهد کرد! همانطور که قبلا اشاره کرده بودم svelte برای اینکه متوجه شود تغییری در آبجکت‌های ما صورت گرفته است، به مشاهده عملگر مساوی در کدهای ما میپردازد و چون اینجا در خط 6، ما از متد push آرایه برای افزودن مقداری جدید به آن درحال استفاده هستیم svelte واکنشی به این تغییر ندارد. برای رفع این مشکل دو راهکار وجود دارد. 
راهکار اول استفاده از عملگر مساوی و reassign کردن مقدار آبجکت یا متغیر مورد نظر است به این صورت : 
  function addNumber() {
    let newNumber = numbers.length + 1;
    numbers.push(newNumber);
    numbers = numbers;
  }
راهکار دوم استفاده از امکانات ES6 جاوا اسکریپت است برای افزودن مقدار جدید به آرایه که چون در این روش از مساوی استفاده میکنیم، تغییرات در numbers واکنش پذیر خواهد بود. به این صورت : 
  function addNumber() {
    let newNumber = numbers.length + 1;
    numbers = [...numbers, newNumber];
  }

مروری بر Two way bindings :
فرض کنید قصد داریم که یک input برای دریافت نام در صفحه قرار داده و در متغیری به نام name ذخیره کنیم. سپس این متغیر را در صفحه نشان دهیم. برای انجام اینکار حدودا با توجه به مطالبی که تا الان آموخته‌ایم میتوان کد زیر را تولید کرد : 
<script>
  let name = "";
  function updateName(event) {
    name = event.target.value;
  }
</script>

<h4>My Name Is {name}</h4>
<input value={name} on:input={updateName} />
در خط 9 یک input به کامپوننت خود اضافه کردیم و مقدار value آن را به متغیر name نسبت دادیم. تا اینجا فقط اگر name تغییری کند آن را در input میتوانیم نمایش دهیم ولی پس از تایپ در این input به صورت خودکار مقدار name آپدیت نمیشود چون ما در این حالت از One way binding درحال استفاده هستیم. به همین جهت یک متد برای آپدیت مقدار name توسط input هم باید مینوشتیم که اگر دقت کنید در کد فوق از ایونت input تگ input در خط 11 به همین منظور استفاده شده‌است. در خط سوم اینبار متد ما یک پارامتر هم به نام event دارد. به طور پیش فرض میتوانیم همیشه از طریق این پارامتر به ایونت آبجکت مبداء که فانکشن را صدا زده است، دسترسی داشته باشیم که خیلی ساده از طریق این ایونت در خط 4 مقدار name را پس تغییر در input آپدیت میکنیم. 
شاید کد بالا کاری که خواستیم را انجام داده باشد. ولی پیاده سازی Two way binding به شکلی که نوشتیم ساده نیست و مشکلات خاص خودش را دارد. خبر خوب این است که svelte این امکان را به صورت توکار محیا کرده است. 
برای اتصال یک attribute به یک متغیر یا آبجکت به صورت دو طرفه از کلمه :bind قبل از نام attribute در svelte استفاده میشود. مثال بالا را اگر با استفاده از این توضیح بازنویسی کنیم، به این کد خواهیم رسید:
<script>
  let name = "";
</script>

<h4>My Name Is {name}</h4>
<input bind:value={name} />
همانطور که مشاهده میکنید دیگر نیازی به نوشتن یک فانکشن مجزا و گرفتن اطلاعات از طریق پارامتر ایونت نیست و به سادگی مقدار value به متغیر name به صورت دو طرفه متصل شده است.


expressing logic :
html امکانی برای پیاده سازی منطق برنامه، برخلاف svelte ندارد. در ادامه با syntax پیاده سازی منطق برنامه در کنار کدهای html در svelte آشنا میشویم. 

If blocks
برای نوشتن If در svelte از if# در میان دو آکولاد {} استفاده میشود و برای پایان دادن به آن از if/ در میاد دو آکولاد. به این صورت : 
{#if condition}
    <!-- you html codes ...  -->
{/if}
به مثال زیر توجه کنید : 
در این مثال قصد داریم پس از کلیک بر روی کلید لاگین، وضعیت کاربر را لاگین شده قرار دهیم و کلید لاگین را مخفی کنیم. سپس کلید دیگری برای خروج از برنامه به نام logout را نمایش دهیم که با کلیک بر روی آن کاربر logout شود و مجددا کلید لاگین نمایش داده شود. برای شبیه سازی این سناریو از کد زیر میتوانیم استفاده کنیم :
<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}
کدهای داخل اسکریپت که چندان نیازی به توضیح ندارند و صرفا برای toggle کردن وضعیت کاربر هستند. ولی در خطوط 9 - 13 و 15 - 19 طریقه نوشتن if در svelte را با یک مثال واقعی مشاهده میکنید.
در مثال بالا دو شرط با یک مقدار مشابه true/false نوشته شده است که بهتر بود مانند سایر زبان‌های برنامه نویسی از else در اینجا استفاده میشد. در ادامه با نحوه نوشتن else در svelte آشنا خواهیم شد.
Else blocks
برای نوشتن else در svelte از else: در میاد دو آکولاد {} بین یک block مانند if میتوانیم استفاده کنیم. به این صورت :
{#if condition}
    <!-- you html code when condition is true -->
{:else}
    <!-- you html code when condition is false -->
{/if}
باز نویسی دو if block در مثال بالا با استفاده از else : 
{#if user.loggedIn}
<button on:click={toggle}>
Log out
</button>
{:else}
<button on:click={toggle}>
Log in
</button>
{/if}

همینطور شما میتوانید از else 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}

نکته تکمیلی : در svelte علامت # همیشه به معنای شروع یک بلاک کد است و علامت / به معنای پایان بلاک. همینطور علامت : نشان دهنده ادامه بلاک کد است. شاید در ابتدا کمی گیج کننده به نظر بیاید ولی در مثال‌های بعدی بیشتر با این علائم آشنا میشویم.

each blocks : 
برای حرکت در میان لیستی از اطلاعات در svelte میتوانیم از each# استفاده نماییم و مانند بلاک if برای خاتمه دادن به آن از each/ استفاده میشود. همینطور از کلمه کلیدی as هم برای دسترسی به هر آیتم در لیست استفاده میشود. به این صورت : 
{#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>
در خط 2 ما یک آرایه از گربه‌ها را تعریف کرده ایم و در خط 11 با استفاده از each به ازای هر کدام از آیتم‌های داخل این آرایه یک تگ li ایجاد میکنیم که لینک مرتبط با آن آیتم را نمایش میدهد. البته کد فوق را به شکل دیگری با استفاده از امکانات destructing جاوا اسکریپت هم میتوانستیم بنویسیم. به این صورت : 
<ul>
{#each cats as {id,name}}
<li><a target="_blank" href="https://www.youtube.com/watch?v={id}">
{name}
</a></li>
{/each}
</ul>
در این حالت ابتدا id و name به صورت مجزا destruct شده اند و میتوانیم مانند مثال بالا از آنها بدون نوشتن نام آیتم استفاده کنیم . مانند خط 4 کد بالا.
همینطور در each بلاک‌ها امکان دریافت ایندکس جاری آیتم از طریق آرگومان دوم هم وجود دارد. به این صورت : 
<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>
اگر به کد فوق دقت کنید درخط 2 مقدار i ایندکس آیتم جاری آرایه ما یعنی همان cats است.


نکته : در این بخش من سعی کردم تا حدودی به ترتیب بخش آموزشی خود وبسایت Svelte، موارد را بیان کنم؛ ولی با توجه به اینکه شاید دوستان ترجیح بدهند روش آموزشی خود  آن وبسایت که امکان تغییر و نوشتن کد را هم محیا کرده است، امتحان کنند  لینک آن  را به اشتراک میگذارم.