اشتراک‌ها
یادگیری AngularJs با codecademy

Learn to build web apps using AngularJS 1.x. By the end of the course, you'll be able to use AngularJS to create your own apps. 

یادگیری AngularJs با codecademy
اشتراک‌ها
استایل دهی به برنامه های Xamarin.Forms با CSS

Some months ago a feature landed in Xamarin.Forms that seemed to truly polarize the Xamarin.Forms community: support for styling applications using CSS. Some argued that it was an unnecessary introduction to "Web" technology to the native development experience, and others that it simply isn't the right solution to the problem.  While I sympathize with the latter opinion and think there's plenty of room for some good debate on the right path forward, I count myself as part of a third camp: I think that CSS is a powerful (and frequently maligned) solution to the problem of styling native mobile applications.

استایل دهی به برنامه های Xamarin.Forms با CSS
اشتراک‌ها
اضافه کردن HTTPS به وب سایت

For a good and secure web – and also for faster performance, new APIs on the web such as Service Workers, better search ranking and more – using HTTPS for your web site is key. I’ll walk you through how to easily get started with that here. 

اضافه کردن HTTPS  به وب سایت
مطالب
مدیریت پیشرفته‌ی حالت در React با Redux و Mobx - قسمت نهم - مثالی از کتابخانه‌ی mobx-react
در ادامه‌ی سری کار با MobX، می‌خواهیم نکاتی را که در سه قسمت قبل مرور کردیم، در قالب یک برنامه پیاده سازی کنیم:


این برنامه از چهار کامپوننت تشکیل شده‌است:
- کامپوننت App که در برگیرنده‌ی سه کامپوننت زیر است:
- کامپوننت BasketItemsCounter: جمع تعداد آیتم‌های انتخابی توسط کاربر را نمایش می‌دهد؛ به همراه دکمه‌ای برای خالی کردن لیست انتخابی.
- کامپوننت ShopItemsList: لیست محصولات موجود در فروشگاه را نمایش می‌دهد. با کلیک بر روی هر آیتم آن، آیتم انتخابی به لیست انتخاب‌های او اضافه خواهد شد.
- کامپوننت BasketItemsList: لیستی را نمایش می‌دهد که حاصل انتخاب‌های کاربر در کامپوننت ShopItemsList است (یا همان سبد خرید). در ذیل این لیست، جمع نهایی قیمت قابل پرداخت نیز درج می‌شود. همچنین اگر کاربر بر روی دکمه‌ی remove هر ردیف کلیک کند، یک واحد از چند واحد انتخابی، حذف خواهد شد.

بنابراین در اینجا سه کامپوننت مجزا را داریم که با هم تبادل اطلاعات می‌کنند. یکی جمع تعداد محصولات خریداری شده را، دیگری لیست محصولات موجود را و آخری لیست خرید نهایی را نمایش می‌دهد. همچنین این سه کامپوننت، فرزند یک دیگر هم محسوب نمی‌شوند و انتقال اطلاعات بین این‌ها نیاز به بالا بردن state هر کدام و قرار دادن آن‌ها در کامپوننت App را دارد تا بتوان پس از آن از طریق props آن‌ها را بین سه کامپوننت فوق که اکنون فرزند کامپوننت App محسوب می‌شوند، به اشتراک گذاشت. روش بهتر اینکار، استفاده از یک مخزن حالت سراسری است تا حالت‌های این کامپوننت‌ها را نگهداری کرده و داده‌‌ها را بین آن‌ها به اشتراک بگذارد که در اینجا برای حل این مساله از کتابخانه‌های mobx و mobx-react استفاده خواهیم کرد.


برپایی پیش‌نیازها

برای پیاده سازی برنامه‌ی فوق، یک پروژه‌ی جدید React را ایجاد می‌کنیم:
> create-react-app state-management-with-mobx-part4
> cd state-management-with-mobx-part4
در ادامه کتابخانه‌ها‌ی زیر را نیز در آن نصب می‌کنیم. برای این منظور پس از باز کردن پوشه‌ی اصلی برنامه توسط VSCode، دکمه‌های ctrl+` را فشرده (ctrl+back-tick) و دستور زیر را در ترمینال ظاهر شده وارد کنید:
> npm install --save bootstrap mobx mobx-react mobx-react-devtools mobx-state-tree
توضیحات:
- برای استفاده از شیوه‌نامه‌های بوت استرپ، بسته‌ی bootstrap نیز در اینجا نصب می‌شود.
- اصل کار برنامه توسط دو کتابخانه‌ی mobx و کتابخانه‌ی متصل کننده‌ی آن به برنامه‌های react که mobx-react نام دارد، انجام خواهد شد.
- چون می‌خواهیم از افزونه‌ی  mobx-devtools نیز استفاده کنیم، نیاز است دو بسته‌ی mobx-react-devtools و همچنین mobx-state-tree را که جزو وابستگی‌های آن است، نصب کنیم.

سپس بسته‌های زیر را که در قسمت devDependencies فایل package.json درج خواهند شد، باید نصب شوند:
> npm install --save-dev babel-eslint customize-cra eslint eslint-config-react-app eslint-loader eslint-plugin-babel eslint-plugin-css-modules eslint-plugin-filenames eslint-plugin-flowtype eslint-plugin-import eslint-plugin-no-async-without-await eslint-plugin-react eslint-plugin-react-hooks eslint-plugin-react-redux eslint-plugin-redux-saga eslint-plugin-simple-import-sort react-app-rewired typescript
علت آن‌را در قسمت قبل بررسی کردیم. این وابستگی‌ها برای فعالسازی react-app-rewired و همچنین eslint غنی سازی شده‌ی آن مورد استفاده قرار می‌گیرند. به علاوه سه قسمت زیر را نیز از قسمت قبل، به پروژه اضافه می‌کنیم:
- افزودن فایل جدید config-overrides.js به ریشه‌ی پروژه، تا پشتیبانی ازlegacy" decorators spec" فعال شود.
- اصلاح فایل package.json و ویرایش قسمت scripts آن برای استفاده‌ی از react-app-rewired، تا امکان تغییر تنظیمات webpack به صورت پویا در زمان اجرای برنامه، میسر شود.
- همچنین فایل غنی شده‌ی eslintrc.json. را نیز به ریشه‌ی پروژه اضافه می‌کنیم.


تهیه سرویس لیست محصولات موجود در فروشگاه

این برنامه از یک لیست درون حافظه‌ای، برای تهیه‌ی لیست محصولات موجود در فروشگاه استفاده می‌کند. به همین جهت پوشه‌ی service را افزوده و سپس فایل جدید src\services\productsService.js را با محتوای زیر، ایجاد می‌کنیم:
const products = [
  {
    id: 1,
    name: "Item 1",
    price: 850
  },
  {
    id: 2,
    name: "Item 2",
    price: 900
  },
  {
    id: 3,
    name: "Item 3",
    price: 1500
  },
  {
    id: 4,
    name: "Item 4",
    price: 1000
  }
];

export default products;


ایجاد کامپوننت نمایش لیست محصولات


پس از مشخص شدن لیست محصولات قابل فروش، کامپوننت جدید src\components\ShopItemsList.jsx را به صورت زیر ایجاد می‌کنیم:
import React from "react";

import products from "../services/productsService";

const ShopItemsList = ({ onAdd }) => {
  return (
    <table className="table table-hover">
      <thead className="thead-light">
        <tr>
          <th>Name</th>
          <th>Price</th>
          <th>Action</th>
        </tr>
      </thead>
      <tbody>
        {products.map(product => (
          <tr key={product.id}>
            <td>{product.name}</td>
            <td>{product.price}</td>
            <td>
              <button
                className="btn btn-sm btn-info"
                onClick={() => onAdd(product)}
              >
                Add
              </button>
            </td>
          </tr>
        ))}
      </tbody>
    </table>
  );
};

export default ShopItemsList;
- این کامپوننت آرایه‌ی products را از طریق سرویس services/productsService دریافت کرده و سپس با استفاده از متد Array.map، حلقه‌ای را بر روی عناصر آن تشکیل داده که در نتیجه، سبب درج trهای متناظر با آن می‌شود؛ تا هر ردیف این جدول، یک آیتم از محصولات موجود را نیز نمایش دهد.
- در اینجا همچنین هر ردیف، به همراه یک دکمه‌ی Add نیز هست که قرار است با کلیک بر روی آن، متد رویدادگردان onAdd فراخوانی شود. این متد نیز از طریق props این کامپوننت دریافت می‌شود. کتابخانه‌های مدیریت حالت، تمام خواص و رویدادگردان‌های مورد نیاز یک کامپوننت را از طریق props، تامین می‌کنند.
- فعلا این کامپوننت به هیچ مخزن داده‌ای متصل نیست و فقط طراحی ابتدایی آن آماده شده‌است.


ایجاد کامپوننت نمایش لیست خرید کاربر (سبد خرید)


اکنون که می‌توان توسط کامپوننت لیست محصولات، تعدادی از آن‌ها را خریداری کرد، کامپوننت جدید src\components\BasketItemsList.jsx را برای نمایش لیست نهایی خرید کاربر، به صورت زیر پیاده سازی می‌کنیم:
import React from "react";

const BasketItemsList = ({ items, totalPrice, onRemove }) => {
  return (
    <>
      <table className="table table-hover">
        <thead className="thead-light">
          <tr>
            <th>Name</th>
            <th>Price</th>
            <th>Count</th>
            <th>Action</th>
          </tr>
        </thead>
        <tbody>
          {items.map(item => (
            <tr key={item.id}>
              <td>{item.name}</td>
              <td>{item.price}</td>
              <td>{item.count}</td>
              <td>
                <button
                  className="btn btn-sm btn-danger"
                  onClick={() => onRemove(item.id)}
                >
                  Remove
                </button>
              </td>
            </tr>
          ))}

          <tr>
            <td align="right">
              <strong>Total: </strong>
            </td>
            <td>
              <strong>{totalPrice}</strong>
            </td>
            <td></td>
            <td></td>
          </tr>
        </tbody>
      </table>
    </>
  );
};

export default BasketItemsList;
- عملکرد این کامپوننت نیز شبیه به کامپوننت نمایش لیست محصولات است؛ با این تفاوت که لیستی که به آن از طریق props ارسال می‌شود:
const BasketItemsList = ({ items, totalPrice, onRemove }) => {
لیست محصولات انتخابی کاربر است.
- همچنین هر ردیف نمایش داده شده، به همراه یک دکمه‌ی Remove آیتم انتخابی نیز هست که به متد رویدادگردان onRemove متصل شده‌است.
- در ردیف انتهایی این لیست، مقدار totalPrice که یک خاصیت محاسباتی است، درج می‌شود.
- فعلا این کامپوننت نیز به هیچ مخزن داده‌ای متصل نیست و فقط طراحی ابتدایی آن آماده شده‌است.


ایجاد کامپوننت نمایش تعداد آیتم‌های خریداری شده


کاربر اگر آیتمی را از لیست محصولات انتخاب کند و یا محصول انتخاب شده را از لیست خرید حذف کند، تعداد نهایی باقی مانده را می‌توان در کامپوننت src\components\BasketItemsCounter.jsx مشاهده کرد:
import React, { Component } from "react";

class BasketItemsCounter extends Component {
  render() {
    const { count, onRemoveAll } = this.props;
    return (
      <div>
        <h1>Total items: {count}</h1>
        <button
          type="button"
          className="btn btn-sm btn-danger"
          onClick={() => onRemoveAll()}
        >
          Empty Basket
        </button>
      </div>
    );
  }
}

export default BasketItemsCounter;
- این کامپوننت یک خاصیت و یک رویدادگردان را از طریق props خود دریافت می‌کند. خاصیت count، جمع نهایی موجود در سبد خرید را نمایش می‌دهد و فراخوانی onRemoveAll، سبب پاک شدن تمام آیتم‌های موجود در سبد خرید خواهد شد.
- فعلا این کامپوننت نیز به هیچ مخزن داده‌ای متصل نیست و فقط طراحی ابتدایی آن آماده شده‌است.


نمایش ابتدایی سه کامپوننت توسط کامپوننت App

اکنون که این سه کامپوننت تکمیل شده‌اند، می‌توان المان‌های آن‌ها را در فایل src\App.js درج کرد تا در صفحه نمایش داده شوند:
import React, { Component } from "react";

import BasketItemsCounter from "./components/BasketItemsCounter";
import BasketItemsList from "./components/BasketItemsList";
import ShopItemsList from "./components/ShopItemsList";

class App extends Component {
  render() {
    return (
      <main className="container">
        <div className="row">
          <BasketItemsCounter />
        </div>

        <hr />

        <div className="row">
          <h2>Products</h2>
          <ShopItemsList />
        </div>

        <div className="row">
          <h2>Basket</h2>
          <BasketItemsList />
        </div>
      </main>
    );
  }
}

export default App;


طراحی مخزن‌های حالت MobX مخصوص برنامه


می‌توان همانند Redux کل state برنامه را داخل یک شیء store ذخیره کرد و یا چون در اینجا می‌توان طراحی مخزن حالت MobX را به دلخواه انجام داد، می‌توان چندین مخزن حالت را تهیه و به هم متصل کرد؛ مانند تصویری که مشاهده می‌کنید. در اینجا:
- src\stores\counter.js: مخزن داده‌ی حالت کامپوننت شمارشگر است.
- src\stores\market.js: مخزن داده‌ی کامپوننت‌های لیست محصولات و سبد خرید است.
- src\stores\index.js: کار ترکیب دو مخزن قبل را انجام می‌دهد.

در ادامه کدهای کامل این مخازن را مشاهده می‌کنید:

مخزن حالت src\stores\counter.js
import { action, observable } from "mobx";

export default class CounterStore {
  @observable totalNumbersInBasket = 0;

  constructor(rootStore) {
    this.rootStore = rootStore;
  }

  @action
  increase = () => {
    this.totalNumbersInBasket++;
  };

  @action
  decrease = () => {
    this.totalNumbersInBasket--;
  };
}
- کار این مخزن، تامین عدد جمع آیتم‌های انتخابی توسط کاربر است که در کامپوننت شمارشگر نمایش داده می‌شود.
- در اینجا خاصیت totalNumbersInBasket به صورت observable تعریف شده‌است و با تغییر آن چه به صورت مستقیم، با مقدار دهی آن و یا توسط دو action تعریف شده، سبب به روز رسانی UI خواهد شد.
- می‌شد این مخزن را با مخزن src\stores\market.js یکی کرد؛ اما جهت ارائه‌ی مثالی در مورد نحوه‌ی تعریف چند مخزن و روش برقراری ارتباط بین آن‌ها، به صورت مجزایی تعریف شد.

مخزن حالت src\stores\market.js
import { action, computed, observable } from "mobx";

export default class MarketStore {
  @observable basketItems = [];

  constructor(rootStore) {
    this.rootStore = rootStore;
  }

  @action
  add = product => {
    const selectedItem = this.basketItems.find(item => item.id === product.id);
    if (selectedItem) {
      selectedItem.count++;
    } else {
      this.basketItems.push({
        ...product,
        count: 1
      });
    }

    this.rootStore.counterStore.increase();
  };

  @action
  remove = id => {
    const selectedItem = this.basketItems.find(item => item.id === id);
    selectedItem.count--;

    if (selectedItem.count === 0) {
      this.basketItems.remove(selectedItem);
    }

    this.rootStore.counterStore.decrease();
  };

  @action
  removeAll = () => {
    this.basketItems = [];
    this.rootStore.counterStore.totalNumbersInBasket = 0;
  };

  @computed
  get totalPrice() {
    return this.basketItems.reduce((previous, current) => {
      return previous + current.price * current.count;
    }, 0);
  }
}
- کار این مخزن تامین مدیریت آرایه‌ی basketItems است که بیانگر اشیاء انتخابی توسط کاربر می‌باشد.
- توسط متد add آن در کامپوننت نمایش لیست محصولات، می‌توان آیتمی را به این آرایه اضافه کرد. در اینجا چون شیء product مورد استفاده دارای خاصیت count نیست، روش افزودن آن‌را توسط spread operator برای درج خواص شیء product اصلی و سپس تعریف آن‌را مشاهده می‌کنید. این فراخوانی، سبب افزایش یک واحد به عدد شمارشگر نیز می‌شود.
- متد remove آن در کامپوننت سبد خرید، مورد استفاده قرار می‌گیرد تا کاربر بتواند اطلاعاتی را از این لیست حذف کند. این فراخوانی، سبب کاهش یک واحد از عدد شمارشگر نیز می‌شود.
- متد removeAll آن در کامپوننت شمارشگر بالای صفحه استفاده می‌شود تا سبب خالی شدن آرایه‌ی آیتم‌های انتخابی گردد و همچنین عدد آن‌را نیز صفر کند.
- خاصیت محاسباتی totalPrice آن در پایین جدول سبد خرید، جمع کل هزینه‌ی قابل پرداخت را مشخص می‌کند.

مخزن حالت src\stores\index.js

در اینجا روش یکی کردن دو مخزن حالت یاد شده را به صورت خاصیت‌های عمومی یک مخزن کد ریشه، مشاهده می‌کنید:
import CounterStore from "./counter";
import MarketStore from "./market";

class RootStore {
  counterStore = new CounterStore(this);
  marketStore = new MarketStore(this);
}

export default RootStore;
هر مخزن مجزایی که تعریف شده، دارای یک پارامتر سازنده‌است که با مقدار شیء this کلاس RootStore مقدار دهی می‌شود. با این روش می‌توان بین مخازن کد مختلف ارتباط برقرار کرد. برای نمونه درمخزن حالت MarketStore، این پارامتر سازنده، امکان دسترسی به خاصیت counterStore و سپس تمام خاصیت‌ها و متدهای عمومی آن‌را فراهم می‌کند:
export default class MarketStore {
  @observable basketItems = [];

  constructor(rootStore) {
    this.rootStore = rootStore;
  }

  @action
  removeAll = () => {
    this.basketItems = [];
    this.rootStore.counterStore.totalNumbersInBasket = 0;
  };
}


تامین مخازن حالت تمام کامپوننت‌های برنامه

پس از ایجاد مخازن حالت، اکنون نیاز است آن‌ها را در اختیار سلسه مراتب کامپوننت‌های برنامه قرار دهیم. به همین جهت به فایل src\index.js مراجعه کرده و آن‌را به صورت زیر تغییر می‌دهیم:
import "./index.css";
import "bootstrap/dist/css/bootstrap.css";

import makeInspectable from "mobx-devtools-mst";
import { Provider } from "mobx-react";
import React from "react";
import ReactDOM from "react-dom";

import App from "./App";
import * as serviceWorker from "./serviceWorker";
import RootStore from "./stores";

const rootStore = new RootStore();

if (process.env.NODE_ENV === "development") {
  makeInspectable(rootStore); // https://github.com/mobxjs/mobx-devtools
}

ReactDOM.render(
  <Provider {...rootStore}>
    <App />
  </Provider>,
  document.getElementById("root")
);

serviceWorker.unregister();
- در اینجا ابتدا import فایل css بوت استرپ را مشاهده می‌کنید که در برنامه استفاده شده‌است.
- سپس یک وهله‌ی جدید از RootStore را که حاوی خاصیت‌های عمومی counterStore و marketStore است، ایجاد می‌کنیم.
- اگر علاقمند باشید تا حین کار با MobX، جزئیات پشت صحنه‌ی آن‌را توسط افزونه‌ی mobx-devtools ردیابی کنید، روش آن‌را در اینجا با فراخوانی متد makeInspectable مشاهده می‌کنید. مقدار process.env.NODE_ENV نیز بر اساس پروسه‌ی جاری node.js اجرا کننده‌ی برنامه‌ی React تامین می‌شود. اطلاعات بیشتر
- قسمت آخر این تنظیمات، محصور کردن کامپوننت App که بالاترین کامپوننت در سلسله مراتب کامپوننت‌های برنامه است، با شیء Provider می‌باشد. در این شیء توسط spread operator، سبب درج خواص عمومی rootStore، به عنوان مخازن قابل استفاده شده‌ایم. تنظیم {rootStore...} معادل عبارت زیر است:
<Provider counterStore={rootStore.counterStore} marketStore={rootStore.marketStore}>
به این ترتیب تمام کامپوننت‌های برنامه می‌توانند با دو مخزن کد ارسالی به آن‌ها کار کنند. در ادامه مشاهده می‌کنیم که چگونه این ویژگی‌ها، سبب تامین props کامپوننت‌ها خواهند شد.


اتصال کامپوننت ShopItemsList به مخزن حالت marketStore

پس از ایجاد rootStore و محصور کردن کامپوننت App توسط شیء Provider در فایل src\index.js، اکنون باید قسمت export default کامپوننت‌های برنامه را جهت استفاده‌ی از مخازن حالت، یکی یکی ویرایش کرد:
import { inject, observer } from "mobx-react";
import React from "react";

import products from "../services/productsService";

const ShopItemsList = ({ onAdd }) => {
  return (
  // ...
  );
};

export default inject(({ marketStore }) => ({
  onAdd: marketStore.add
}))(observer(ShopItemsList));
در اینجا فراخوانی متد inject، سبب دسترسی به ویژگی marketStore تامین شده‌ی توسط شیء Provider می‌شود. تمام ویژگی‌هایی که به شیء Provider ارائه می‌شوند، در اینجا به صورت خواصی که توسط Object Destructuring قابل استخراج هستند، قابل دسترسی می‌شوند. سپس props این کامپوننت را که متد onAdd را می‌پذیرد، از طریق marketStore.add تامین می‌کنیم. در آخر کامپوننت ShopItemsList باید به صورت یک observer بازگشت داده شود تا تغییرات store را تحت نظر قرار داده و به این صورت امکان به روز رسانی UI را پیدا کند.


اتصال کامپوننت BasketItemsList به مخزن حالت marketStore

در اینجا نیز سطر export default را جهت دریافت خاصیت marketStore، از شیء Provider تامین شده‌ی در فایل src\index.js، ویرایش می‌کنیم. به این ترتیب سه props مورد انتظار این کامپوننت، توسط خاصیت‌های basketItems (آرایه‌ی اشیاء انتخابی توسط کاربر)، totalPrice (خاصیت محاسباتی جمع کل هزینه) و  متد رویدادگردان onRemove (برای حذف یک آیتم) تامین می‌شوند. در آخر کامپوننت را به صورت observer محصور کرده و بازگشت می‌دهیم تا تغییرات در مخزن حالت آن، سبب به روز رسانی UI آن شوند:
import { inject, observer } from "mobx-react";
import React from "react";

const BasketItemsList = ({ items, totalPrice, onRemove }) => {
  return (
  // ...
  );
};

export default inject(({ marketStore }) => ({
  items: marketStore.basketItems,
  totalPrice: marketStore.totalPrice,
  onRemove: marketStore.remove
}))(observer(BasketItemsList));


اتصال کامپوننت BasketItemsCounter به دو مخزن حالت counterStore و marketStore

در اینجا روش استفاده‌ی از decorator syntax کتابخانه‌ی mobx-react را بر روی یک کامپوننت کلاسی مشاهده می‌کنید. تزئین کننده‌ی inject، امکان دسترسی به مخازن حالت تزریقی به شیء Provider را میسر کرده و سپس توسط آن می‌توان props مورد انتظار کامپوننت را از مخازن متناظر استخراج کرده و در اختیار کامپوننت قرار داد. همچنین این کامپوننت توسط تزئین کننده‌ی observer نیز علامت گذاری شده‌است. در این حالت نیازی به تغییر سطر export default نیست.
import { inject, observer } from "mobx-react";
import React, { Component } from "react";

@inject(rootStore => ({
  count: rootStore.counterStore.totalNumbersInBasket,
  onRemoveAll: rootStore.marketStore.removeAll
}))
@observer
class BasketItemsCounter extends Component {
  render() {
    const { count, onRemoveAll } = this.props;
    return (
      // ...
    );
  }
}

export default BasketItemsCounter;

کدهای کامل این قسمت را می‌توانید از اینجا دریافت کنید: state-management-with-mobx-part4.zip
مطالب
تقویم شمسی کاملا Native برای Blazor
یکی از مزایای Blazor، استفاده از دانش C# / HTML / CSS (که خیلی از ما اینها را هم اکنون بلد هستیم) برای نوشتن برنامه‌های وب (SPA / PWA)، برنامه‌های Android / iOS / Windows و وب‌سایت‌هایی با قابلیت Pre Rendering و SEO Friendly است. با یک بار کدنویسی به کمک Blazor، ولی با Configuration‌های متفاوت می‌توان خروجی‌های مختلفی را برای پلتفرم‌های مختلف گرفت؛ برای مثال Blazor Hybrid خروجی Android / iOS / Windows و Blazor Web Assembly خروجی PWA / SPA و در نهایت Blazor Static خروجی وب سایت می‌دهد. به علاوه حالت Blazor Server نیز وجود دارد که امروزه بزرگ‌ترین مزیت آن، Development experience فوق‌العاده‌اش هست که در آن با استفاده از Hot Reload، می‌توان تغییرات در فایل‌های SCSS / C# / Razor را به صورت آنی، بدون نیاز به Build مجدد، رفرش کردن و از دست دادن State مشاهده نمود. امکان استفاده از Nuget Packageهای DotNet ای در Android / iOS / Windows / Web در کنار امکان استفاده از امکانات Native هر پلتفرم نیز از دیگر مزایای این روش است.

اما یکی از موانع استفاده‌ی جدی از Blazor در پروژه‌های داخلی، نبود تقویم شمسی است که سبک بوده و پیش نیاز خاصی جز خود Blazor نداشته باشد. یک راه حل جدید برای حل این مشکل، استفاده از Bit Components است که اخیرا به صورت Open Source ارائه شده است. شما می‌توانید Repository مربوطه را Fork نموده، Clone نمایید، به فولدر src بروید و با ویژال استودیو، Bit.Client.Web.BlazorUI.sln را باز کنید و سورس کامپوننت‌ها را به همراه تست‌های خودکار آن ببینید.
در سایت مربوطه نیز می‌توانید دمویی از بیش از ۲۷ کامپوننت را شامل File uploader، Drop Down، Date Picker، Color Picker، Tree list و... مشاهده کنید که هر کدام دارای Documentation کامل بوده و آماده به استفاده در پروژه‌های شما هستند.
برای استفاده از Bit Components در پروژه خود، ابتدا Package مربوطه را نصب نمایید و سپس فایل js و css مربوطه را نیز به index.html یا Host.cshtml یا Layout.cshtml اضافه کنید (بسته به تنظیمات پروژه‌تان).
در Bit Components جز معدود مواردی که چند خطی با JavaScript توسعه داده شده‌است، کمپوننت‌ها با C# / Razor / CSS توسعه داده شده‌اند. این روش نسبت به روش‌هایی که بر روی کمپوننت‌های کاملا JavaScript ای، اصطلاحا Wrapper ایجاد می‌کنند، دارای دو مزیت سرعت بالاتر و تضمین کار کردن آن در حالت‌های مختلف مانند Pre Rendering است.
<link href="_content/Bit.Client.Web.BlazorUI/styles/bit.blazorui.min.css" rel="stylesheet" />
<script src="_content/Bit.Client.Web.BlazorUI/scripts/bit.blazorui.min.js"></script>  
همچنین در فایل Imports.razor نیز using زیر را اضافه کنید
@using Bit.Client.Web.BlazorUI
به همین سادگی! حال برای تست، از Bit Button به صورت زیر استفاده کنید و اگر درست بود، می‌توانید سراغ کامپوننت‌های پیچیده‌تر همچون Date Picker بروید.
<BitButton>Hello!</BitButton>
برای Bit Date Picker نیز در razor خود یک Property یا Field برای نگه‌داری Date انتخاب شده داشته باشید (برای مثال به اسم BirthDate) که لازم است از جنس DateTimeOffset باشد (دقت کنید، نمایش و گرفتن تاریخ به شمسی یا میلادی می‌تواند باشد که این بر اساس Culture جاری سیستم است (توضیحات اضافه‌تر در قسمت پایانی مقاله)، ولی در نهایت شما DateTimeOffset میلادی انتخاب شده را خواهید داشت)
<BitDatePicker SelectedDate="@BirthDate"></BitDatePicker>
این کامپوننت دارای تنظیمات بسیاری است که می‌توانید در این صفحه آنها را مطالعه و در پروژه خود تست نمایید. اما بد نیست در مورد قسمت Culture Info که کمی پیچیده‌تر است، توضیحاتی داشته باشیم.
در C# .NET، کلاس CultureInfo، وظیفه نگهداری مواردی چون چند زبانگی، تقویم‌های مختلف (اعم از شمسی و...)، موارد مربوط به ارز (برای مثال علامت $ یا ریال و...) را به عهده دارد. از جمله مزایای BitDatePicker، سازگاری با CultureInfo است، به نحوی که CultureInfo.CurrentUICulture هر چه که باشد، بر اساس آن عمل می‌کند. بنابراین می‌توانید در Program.cs پروژه Blazor خود بنویسید:
CultureInfo.CurrentUICulture = new CultureInfo("fa-IR");
و وقتی BitDatePicker در یکی از صفحات باشد، چون fa-IR از Persian Calendar استفاده می‌کند، پس تقویم به صورت شمسی نمایش داده می‌شود.

سوال اول: اگر بخواهیم در کل سیستم، تقویم شمسی باشد، ولی در یکی از صفحات میلادی چه؟ خب می‌توانیم در آن صفحه، به شکل زیر از BitDatePicker استفاده کنیم:
<BitDatePicker Culture="@(new System.Globalization.CultureInfo("en-US"))" />

سوال دوم: تقویم شمسی نمایش داده شده، اسامی ماه‌ها را به صورت فینگلیش نمایش می‌دهد و یا اسامی خلاصه شده روزها صحیح نیست!
این به خود BitDatePicker ربطی ندارد، بلکه به CultureInfo فارسی خود dotnet مرتبط است، اما شما چگونه می‌توانید این مورد را بهبود بدید؟
شما می‌توانید ابتدا با
var cultureInfo = CultureInfo.CreateSpecificCulture("fa-IR")
یک CultureInfo فارسی قابل ویرایش بسازید، برای مثال بنویسید
cultureInfo.DateTimeFormat.MonthNames = new[] { "فروردین", "اردیبهشت", "خرداد", "تیر", "مرداد", "شهریور", "مهر", "آبان", "آذر", "دی", "بهمن", "اسفند", "" };  
یک نمونه پیاده‌سازی کامل در اینجا
در ادامه لازم هست چه Culture Info ای را که خودتان سفارشی سازی کرده‌اید، یا Culture Info‌های سیستمی را در CultureInfo.CurrentUICulture قرار بدهید تا BitDatePicker از آن پیروی کند.
در صورت بروز هر گونه مشکلی یا درخواست اضافه شدن امکانی، در repo مربوطه روی GitHub می‌توانید یک issue را ثبت کنید.
اشتراک‌ها
دوره بررسی تازه‌های ASP.NET 5 از Tuts plus
ASP.NET is almost 15 years old and it has evolved to become a flexible and extremely powerful platform. But 15 years is a long time, especially in the world of web technology, and the folks at Microsoft knew it was time to change ASP.NET. In this course, Envato Tuts+ instructor Jeremy McPeak will walk you through the major changes to ASP.NET in version 5. Along the way, you'll see how these changes will affect your project and how the new features can make your life as a developer easier. You'll also get a first look at the new Tag Helpers, inversion of control and dependency injection features, with practical examples.
دوره بررسی تازه‌های ASP.NET 5 از Tuts plus
مطالب
پیاده سازی پروژه‌های React با TypeScript - قسمت دوم - تعیین نوع‌های پیشرفته‌تر props
در قسمت قبل با معرفی نوع props توسط TypeScript، مجبور به تکمیل اجباری تک تک آن‌ها شدیم؛ اما در React می‌توان props را به صورت اختیاری و یا با مقادیری پیش‌فرض نیز تعریف کرد.


روش تعیین props پیش‌فرض توسط TypeScript

اگر بخواهیم توسط روش‌های خود React، مقادیر پیش‌فرض props را تعیین کنیم، می‌توان از defaultProps به صورت زیر با تعریف یک شیء جاوا اسکریپتی از پیش مقدار دهی شده، استفاده کرد:
Head.defaultProps = {
   title: "Hello",
   isActive: true
};
اما در حالت استفاده‌ی از TypeScript و یا حتی نگارش ES6 آن (React در حالت پیش‌فرض آن)، می‌توان مقادیر پیش‌فرض props را با مقدار دهی مستقیم متغیرهای حاصل از Object Destructuring آن، تعیین کرد:
type Props = {
  title: string;
  isActive: boolean;
};

export const Head = ({ title = "Hello", isActive = true }: Props) => {
در اینجا هر متغیری که با مقداری پیش‌فرض، مقدار دهی شده باشد، اختیاری در نظر گرفته شده و اگر دارای مقدار پیش‌فرضی نباشد، باید به صورت اجباری در حین تعریف المان این کامپوننت، ذکر شود.
در این حالت انتظار داریم که در حین استفاده و تعریف المان کامپوننت Head، اگر برای مثال ویژگی isActive را ذکر نکردیم، کامپایلر TypeScript خطایی را گزارش نکند؛ که اینطور نیست:


هنوز هم در اینجا می‌توان خطای عدم تعریف خاصیت isActive را مشاهده کرد. برای رفع این مشکل، به صورت زیر عمل می‌کنیم:
type Props = {
  title: string;
  isActive?: boolean;
};

export const Head = ({ title, isActive = true }: Props) => {
در حین تعریف یک type، اگر خاصیتی با علامت ? ذکر شود، به معنای اختیاری بودن آن است. همچنین در اینجا مقدار پیش‌فرض title را هم حذف کرده‌ایم تا تعریف آن اجباری شود. بنابراین در typeها، تمام خواص اجباری هستند؛ مگر اینکه توسط ? به صورت اختیاری تعریف شوند. این مورد هم مزیتی است که در ابتدای طراحی props یک کامپوننت، باید در مورد اختیاری و یا اجباری بودن آن‌ها بیشتر فکر کرد. همچنین نیازی به استفاده از روش‌های غیراستانداردی مانند Head.defaultProps خود React نیست. ذکر مقدار پیش‌فرض متغیرهای حاصل از Object Destructuring، جزئی از جاوااسکریپت استاندارد است و یا مشخص سازی خواص اختیاری در TypeScript، فقط مختص به پروژه‌های React نیست و در همه جا به همین شکل کاربرد دارد.
اکنون با تعریف isActive?: boolean، دیگر شاهد نمایش خطایی در حین تعریف المان Head، بدون ذکر خاصیت isActive، نخواهیم بود.


تعریف انواع و اقسام نوع‌های props

تا اینجا نوع‌های ساده‌ای مانند string و boolean و همچنین نحوه‌ی تعریف اجباری و اختیاری آن‌ها را بررسی کردیم. در ادامه یک نمونه‌ی کامل‌تر را مشاهده می‌کنید:
type User = {
  name: string;
};

type Props = {
  title: string;
  isActive?: boolean;
  count: number;
  options: string[];
  status: "loading" | "loaded";
  thing: {};
  thing2: {
    name: string;
  };
  user: User;
  func: () => void;
};
- در ابتدا نوع‌های متداولی مانند number و string ذکر شده‌اند.
- سپس نحوه‌ی تعریف آرایه‌ای از رشته‌ها را مشاهده می‌کنید.
- یا می‌توان مقدار یک خاصیت را تنها به مقادیری خاص محدود کرد؛ مانند خاصیت status در اینجا و اگر در حین مقدار دهی این خاصیت، از مقدار دیگری استفاده شود، تایپ‌اسکریپت، خطایی را صادر می‌کند.
- در ادامه سه روش تعریف اشیاء تو در تو را مشاهده می‌کنید؛ خاصیت thing از نوع یک شیء خالی تعریف شده‌است (بجای آن می‌توان از نوع object هم استفاده کرد). خاصیت thing2 از نوع یک شیء که دارای خاصیت رشته‌ای name است، تعریف شده و یا بهتر است این نوع تعاریف را به یک type مستقل دیگر مانند User منتقل کرد و سپس از آن جهت تعیین نوع خاصیتی مانند user استفاده نمود.
- در اینجا حتی می‌توان یک خاصیت را که از نوع یک تابع است، تعریف کرد. در این تعریف، void نوع خروجی آن است.


روش تعریف props تابعی در TypeScript

برای بررسی روش تعریف نوع توابع ارسالی از طریق props، ابتدا کامپوننت جدید src\components\Button.tsx را ایجاد می‌کنیم. سپس آن‌را به صورت زیر تکمیل خواهیم کرد:
import React from "react";

type Props = {
  // onClick(): string;  method returns string
  // onClick(): void  method returns nothing;
  // onClick(text: string): void; method with params
  // onClick: () => void; function returns nothing
  onClick: (text: string) => void; // function with params
};

export const Button = ({ onClick }: Props) => {
  return <button onClick={() => onClick("hi")}>Click Me</button>;
};
در این کامپوننت، متغیر onClick حاصل از Object Destructuring شیء props دریافتی، یک تابع است که قرار است با کلیک بر روی دکمه‌ای که در این کامپوننت قرار دارد، پیامی را به کامپوننت والد ارسال کند.
با توجه به تعریف { onClick }، در همان لحظه، خطای any بودن نوع آن از طرف TypeScript گزارش داده می‌شود. بنابراین نوع جدید Props را ایجاد کرده و برای onClick، نوع متناسبی را تعریف می‌کنیم. در اینجا 4 روش مختلف تعریف نوع function را در TypeScript مشاهده می‌کنید؛ دو حالت آن با ذکر پرانتزها و درج امضای متد انجام شده و دو حالت دیگر به کمک arrow functions پیاده سازی شده‌اند.
برای نمونه آخرین حالت تعریف شده از روش arrow functions استفاده می‌کند که متداول‌ترین روش تعریف نوع توابع است (چون عنوان می‌کند که نوع onClick، یک تابع است و آن‌را شبیه به یک متد معمولی نمایش نمی‌دهد) که در آن در ابتدا امضای پارامترهای این تابع مشخص شده‌اند و در ادامه پس از <=، نوع خروجی این تابع تعریف شده‌است که void می‌باشد (این تابع چیزی را بر نمی‌گرداند).

در آخر، تعریف المان آن‌را به صورت زیر به فایل src\App.tsx اضافه می‌کنیم که onClick آن یک مقدار را دریافت کرده و سپس آن‌را در کنسول نمایش می‌دهد.
البته خروجی از نوع void، در اینجا بسیار معمول است؛ چون هدف از این نوع توابع بیشتر ارسال مقادیری به کامپوننت در برگیرنده‌ی آن‌ها است (مانند value در اینجا) و اگر برای مثال خروجی رشته‌ای را داشته باشند، باید در حین درج و تعریف المان آن‌ها، برای نمونه یک "return "value1 را هم در انتهای کار قرار داد که عملا استفاده‌ای ندارد و بی‌معنا است:
import { Button } from "./components/Button";
import { Head } from "./components/Head";
// ...

function App() {
  return (
    <div className="App">
      <Head title="Hello" />
      <Button
        onClick={(value) => {
          console.log(value);
        }}
      />
  // ...
اشتراک‌ها
فریمورک تهیه شده با Asp.net Mvc و Asp.net Web Api به صورت NTier با پشتیبانی از چند زبانگی

These are some key features of the framework itself:

Modular and NLayered architecture
Multi-tenancy
Dependency Injection
Domain Driven Design
Unit of work implementation
Flexible Localization system
Automatic data filters
Audit logging
Setting management
Menu management
Authorization
Exception handling
Validation
Logging
Event bus for domain events
Auto-creating Web API layer for Application Services
Auto-creating Javascript proxy layer to use Web API layer
Javascript helper methods for ajax, notifications, message boxes, making UI busy...
Easily working with embedded resources
Useful extension and helper methods   

فریمورک تهیه شده با Asp.net Mvc و Asp.net Web Api به صورت NTier با پشتیبانی از چند زبانگی
اشتراک‌ها
کتابخانه JSIL
JSIL is a compiler that transforms .NET applications and libraries from their native executable format - CIL bytecode - into standards-compliant, cross-browser JavaScript. You can take this JavaScript and run it in a web browser or any other modern JavaScript runtime. Unlike other cross-compiler tools targeting JavaScript, JSIL produces readable, easy-to-debug JavaScript that resembles the code a developer might write by hand, while still maintaining the behavior and structure of the original .NET code.  Demo
کتابخانه JSIL