بازخوردهای پروژه‌ها
راهنمایی خروجی HTML
سلام
به نظرتون ایجاد خروجی HTML برای این کتابخانه خیلی دردسر داره یا نه؟
آیا کسی دیگه ای تا حالا سعی کرده بنویسه؟
نظرات مطالب
ASP.NET MVC #12
سلام،
بله، طبق علم بنده، توی هر View فقط با یک مدل میشه کار کرد (یک indstance از یک کلاس رو میشه بهش پاس داد) ؛ بنابراین برای نمایش اطلاعات غیر مرتبطی که نمیشه توی یک کلاس گنجوند باید از چند partial view مجزا استفاده کنیم که هر ویو یک بخش رو نمایش بده و بعد به کمک دستوراتی که خودتون عرض کردین اینا رو توی یک ویوی مجزا نمایش میدیم.
مثلا می‌خوایم موضوعات ، آخرین اخبار و آمار سایت رو توی یک صفحه داشته باشیم؛ حال آنکه اینا هر کدوم یک دیتای مجزان مسلما باید یک partial view برای موضوعات ، یک partial view برای  آخرین اخبار و یک partial view برای آمار سایت ایجاد کنیم و در پایان اینا رو توی مستر پیج (Layout) بوسیله دستورات RenderAction یا Action در کنار هم نمایش میدیم.
توضیح تکمیلی:
توی وب فرم ما یک عدد مستر پیج و چند عدد یوزر کنترل داشتیم و یوزر کنترل هامونو درگ می‌کردیم توی مسترپیج ، توی MVC همین کارو می‌کنیم ، فقط به جای مسترپیج Layout داریم و به جای یوزر کنترل Partial View داریم..... همین!
به جای درگ کردن یوزرکنترل توی مستر پیج هم RenderAction و Action داریم...
{ کلا MVC اومده و همه کارها رو کدی کرده و به جای درگ کردن کنترل توی صفحه و .... باید کد بنویسیم:) }
البته اولش ممکنه برای آدم سخت باشه ولی کم کم که بهش عادت کنی و خروجی html صفحاتتو ببینی که چقدر تمیز و خوشگل ! شده ، ازش خوشت میاد :)))
مطالب
پیاده سازی یک سیستم دسترسی Role Based در Web API و AngularJs - بخش اول
در این مجموعه مقالات قصد دارم یک روش را برای پیاده سازی سیستم Role Based سفارشی شده، به صورت پروژه محور، در اختیار شما دوستان قرار دهم. این مجموعه مقالات در هر دو بخش سرور و کلاینت، در قالب یک پروژه‌ی واقعی ارائه خواهد شد. در سمت سرور از Web API و سیستم Identity 2.1 و در سوی کلاینت از تکنولوژی قدرتمند AngularJs کمک گرفته شده‌است. در این مجموعه قصد دارم تا مراحل کلی و اصلی این سیستم دسترسی را تشریح کنم.

مقدمه ای بر سیستم مبتنی بر نقش کاربران

شاید برای شما هم پیش آمده باشد که برای وب سایت یا وب اپلیکیشن خود، به دنبال یک سیستم دسترسی بوده‌اید که بتوانید در آن نقش‌های گوناگونی را تعریف کنید و هر نقش شامل یک سری دسترسی خاص باشد و همچنین با اضافه شدن هر کاربر به این سیستم، بتوانید آنها را به یک نقش خاص انتساب دهید. برای اجرای این امر، روش‌های گوناگونی وجود دارد. یکی از این روش‌ها در سایت CodeProject توسط آقای Stefan Wloch تشریح شده است. در این مقاله کلیت و هدف یک سیستم Role Based، ساختار نقش‌ها و کاربران و دسترسی‌های آنها به خوبی بیان شده است.
 
و اما کمی درباره روشهای موجود حول توسعه یک سیستم مبتنی بر نقش (Role Based) صحبت کنیم. در ابتدا لازم است یک تعریف کلی از یک سیستم Role Based داشته باشیم:
یک سیستم مدیریت کاربر مبتنی بر نقش، سیستمی است که در آن نقش‌های گوناگونی تعریف می‌گردد. به گونه‌ای که هر نقش شامل یک سری دسترسی هایی است که به کاربر اجازه می‌دهد تا به بخشی از سیستم دسترسی داشته باشد. هر کاربر در این سیستم می‌تواند یک و یا چند نقش متفاوت داشته باشد و بر اساس آن نقش‌ها و دسترسی‌های تعریف شده، درون هر نقش به قسمتی از سیستم دسترسی داشته باشد.
بر اساس این تعریف ما در نهایت سه موجودیت نقش (Role)، دسترسی (Permission) و کاربر (User) را خواهیم داشت. در این سیستم دسترسی را اینگونه تعریف میکنیم:
دسترسی در یک سیستم، مجموعه‌ای از حوزه‌ها و یا ناحیه‌هایی است که در سیستم تعریف می‌شود. این حوزه‌ها می‌توانند شامل یک متد یا مجموعه‌ای از متدهای نوشته شده و یا فراتر از آن، شامل مجموعه‌ای از کنترلرها باشد که کاربر اجازه‌ی فراخوانی آنها را دارد.

بدیهی است کاربر برای ما موجودیتی مشخص است. یک کاربر ویژگی‌هایی مانند نام، نام کاربری، سن، رمز عبور و ... خاص خود را داراست که به واسطه این ویژگی‌ها از کاربری دیگر تمییز داده میشود. کاربر در سیستم طی دو مرحله جداگانه Authentication و Authorization مجاز است تا از بخش‌هایی از سیستم استفاده کند. مرحله Authentication به طور خلاصه شامل مرحله‌ای است که هویت کاربر (به عنوان مثال نام کاربری و رمز عبور) تایید صلاحیت میشود. این مرحله در واقع تایید کننده کاربر است و اما بخش بعدی که ما قصد داریم تا در این مورد راهکاری را ارائه دهیم بخش Authorization است. در این بخش به کاربر بر اساس نقش وی دسترسی‌هایی اعطا می‌گردد و کاربر را به استفاده از بخش‌هایی از سیستم مجاز میدارد.

دیاگرام زیر نمود سه موجودیت کاربر، نقش و دسترسی میباشد.

برای شرح دیاگرام فوق این چنین میتوان گفت که هر کاربر مجاز است چندین نقش داشته باشد و هر نقش نیز میتواند به چندین کاربر انتساب شود. در مورد دسترسی‌ها نیز به همین صورت، هر دسترسی نیز میتواند به چندین نقش انتساب شود.

ارائه یک سیستم مبتنی بر نقش کاربران با استفاده از تکنولوژی Web API و AngularJs

حال که از محتوا و عملکرد یک سیستم مبتنی بر نقش (Role Based) دانش کافی را کسب کردیم، قصد داریم تا به پیاده سازی کامل این سیستم بپردازیم. شاید برای شما سوال باشد که چرا ما قصد داریم تا این معماری را در Web API و AngularJs پیاده سازی کنیم. در جواب به این سوال باید بگوییم که پیاده سازی این روشها در تکنولوژی‌هایی مانند ASP.NET MVC و تکنولوژی‌ةای مشابه آن که صفحه را در سمت سرور می‌سازند، در بسیاری از منابع و مقالات اینترنتی موجود است. ما قصد داریم تا آنچه را که در یک Single Page Application پیاده سازی کرده‌ایم، به اشتراک بگذاریم. بنابراین قصد داریم تا علاوه بر تشریح کامل این سیستم که قسمت اعظم آن در سمت سرور انجام میشود، به تشریح نحوه تبادل اطلاعات و مدیریت دسترسی‌ها در سمت کلاینت نیز بپردازیم.
همانطور که میدانید ما در یک سیستم بر پایه SPA، برای ساخت یک صفحه ممکن است چندین API را فراخوانی کنیم. بنابراین APIهای نوشته شده به تعداد زیاد ولی با عملکرد اتمیک خواهند بود. به عنوان مثال در یک سیستم فروشگاه اینترنتی، برای ویرایش یک محصول، ممکن است دو یا چند متد API (فراخوانی آن محصول برای قرار دادن در فیلدهای ویرایش، ارسال اطلاعات پس از ویرایش به سرور و ...) فراخوانی گردند. با این حساب ما در تعریف و ایجاد دسترسی‌ها دو راه را خواهیم داشت.
  1. تعریف هر یک از متدهای اتمیک به عنوان یک مجوز دسترسی: در این روش نام کنترلر و نام متد به عنوان یک دسترسی تعریف خواهد شد. در این روش به ازای هر متد ما یک آیتم جدید را باید به جدول Permissions، اضافه نماییم و در نهایت برای تعریف یک نقش و انتساب دسترسی‌ها به یک کاربر، بایستی یک مجموعه چک باکس را که در نهایت به یک متد API ختم میشود، فعال یا غیر فعال کنیم.
  2. تعریف ناحیه‌های مختلف و کنترل‌های قابل انجام در آن ناحیه: در این روش ما قصد داریم تا مجموعه‌ای از متد‌ها را که هدف مشترکی را انجام خواهند داد، در یک ناحیه و یک کنترل بگنجانیم و از به وجود آمدن تعداد زیادی مجوز دسترسی جلوگیری نماییم. به عنوان مثال فرض کنید میخواهیم یک سطح دسترسی را با نام ویرایش کاربران تعریف کنیم. همانگونه که گفتیم ممکن است برای یک عملیات، دو و یا چندین متد درون یک کنترلر تعریف شوند. حال ما این متد‌ها را درون یک ناحیه دسترسی قرار خواهیم داد و آن را در یک حوزه و یک کنترل (Area & Control) میگنجانیم.
در بخش بعدی سعی میکنیم تا این مبحث را بازتر کنیم و به صورت عملی مراحل توسعه این مدل را تشریح نماییم.
مطالب
جمع آوری آمار لینک‌های خروجی از سایت توسط Google analytics

چندی قبل مطلب کوتاهی را در مورد Google analytics نوشتم. در حین جستجو درباره‌ی jQuery در وب، به نحوه ردیابی لینک‌های خروجی از سایت توسط Google analytics برخوردم که نحوه پیاده سازی آن به صورت زیر است.
بدیهی است قبل از هر کاری باید اسکریپت مربوط به Google analytics را به انتهای صفحه و جایی که تگ body بسته می‌شود اضافه کنید (قابل دریافت درقسمت Add Website Profile . شماره این اسکریپت برای هر پروفایلی که ایجاد می‌کنید متفاوت است).
سپس:
الف) افزودن ارجاعی از کتابخانه jQuery به هدر صفحه که آن‌را در مطلب شمسی کردن تاریخ بلاگر ملاحظه کردید.
ب) افزودن چند سطر زیر به هدر صفحه
<script type="text/javascript">
$(document).ready(function() {
$("a").click(function() {
var $a = $(this);
var href = $a.attr("href");

// see if the link is external
if ( (href.match(/^http/)) && (! href.match(document.domain)) ) {

// if so, register an event
var category = "outgoing";
var event = "click";
var label = href;

pageTracker._trackPageview('/outgoing/' + href);
pageTracker._trackEvent(category, event, href);
}
});
});
</script>

البته اگر قبلا اسکریپت شمسی کردن تاریخ بلاگر را اضافه کرده بودید فقط محتویات تابع document.ready را باید اضافه کنید (جهت مشاهده نمونه اعمال شده، روی صفحه جاری کلیک راست کنید و سورس صفحه را مشاهده نمائید).

توضیحاتی در مورد کد فوق:
این اسکریپت به روال رخ داد گردان onclick هر لینکی که به خارج از سایت ختم می‌شود (مثلا لینک به یک فایل یا یک سایت خارجی (خارج از سایت))، به صورت خودکار تابع trackPageview مربوط به Google analytics را اضافه می‌کند. این کار تاثیری در عملکرد سایت ندارد و کاربر چیزی را متوجه نخواهد شد، اما به این طریق لینک‌های خروجی در آمار Google analytics ظاهر می‌شوند (مطابق تصاویر زیر).





از این پس آمار تمام لینک‌های خروجی از سایت ، متمایز شده با outgoing ، جمع آوری و نمایش داده خواهند شد.

امکانات بیشتری مانند event tracking نیز قرار است به Google analytics اضافه شود که هنوز در مرحله آزمایشی است و بر روی تمامی اکانت‌ها فعال نشده است.

اشتراک‌ها
تبدیل فایل xls به فایل csv متنی (با جاوا اسکریپت و ActiveXObject)

این نرم افزار با کدهای HTML و با پسوند اجرایی hta تولید شده است که به صورت یک Application می‌تواند اجرا شود.

برای انجام این کار از ActiveXObject‌ها استفاده می‌نماید.

برای تبدیل اطلاعات موجود در sheet‌های مختلف اکسل به فایل csv، مشابه نام هریک از sheet‌های اکسل یک فایل csv متنی ایجاد می‌کند. 

دانلود نرم افزار

کد جاوا اسکریپت

تبدیل فایل xls به فایل csv متنی (با جاوا اسکریپت و ActiveXObject)
مطالب
طراحی گردش کاری با استفاده از State machines - قسمت دوم
معرفی کتابخانه stateless به عنوان جایگزین سبک وزنی برای Windows workflow foundation

کتابخانه سورس باز Stateless، برای طراحی و پیاده سازی «ماشین‌های حالت گردش کاری مانند» تهیه شده و مزایای زیر را نسبت به Windows workflow foundation دارا است:
- جمعا  30 کیلوبایت است!
- تمام اجزای آن سورس باز است.
- دارای API روان و ساده‌ای است.
- امکان تبدیل UML state diagrams، به نمونه معادل Stateless بسیار ساده و سریع است.
- به دلیل code first بودن، کار کردن با آن برای برنامه نویس‌ها ساده‌تر بوده و افزودن یا تغییر اجزای آن با کدنویسی به سادگی میسر است.

دریافت کتابخانه Stateless از Google code و یا از NuGet


پیاده سازی مثال کلید برق با Stateless

در ادامه همان مثال ساده  کلید برق قسمت قبل را با Stateless پیاده سازی خواهیم کرد:
using System;
using Stateless;

namespace StatelessTests
{
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                string on = "On", off = "Off";
                var space = ' ';

                var onOffSwitch = new StateMachine<string, char>(initialState: off);

                onOffSwitch.Configure(state: off).Permit(trigger: space, destinationState: on);
                onOffSwitch.Configure(state: on).Permit(trigger: space, destinationState: off);

                Console.WriteLine("Press <space> to toggle the switch. Any other key will raise an error.");

                while (true)
                {
                    Console.WriteLine("Switch is in state: " + onOffSwitch.State);
                    var pressed = Console.ReadKey(true).KeyChar;
                    onOffSwitch.Fire(trigger: pressed);
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine("Exception: " + ex.Message);
                Console.WriteLine("Press any key to continue...");
                Console.ReadKey(true);
            }
        }
    }
}
کار با ایجاد یک وهله از ماشین حالت (new StateMachine) آغاز می‌شود. حالت آغازین آن (initialState) مطابق مثال قسمت قبل، مساوی off است.
امضای کلاس StateMachine را در ذیل مشاهده می‌کنید؛ جهت توضیح آرگومان‌های جنریک string و char معرفی شده در مثال:
public class StateMachine<TState, TTrigger>
که اولی بیانگر نوع حالات قابل تعریف است و دومی نوع رویداد قابل دریافت را مشخص می‌کند.
برای مثال در اینجا حالات روشن و خاموش، با رشته‌های on و off مشخص شده‌اند و رویداد قابل قبول دریافتی، کاراکتر فاصله است.
سپس نیاز است این ماشین حالت را برای معرفی رویدادهایی (trigger در اینجا) که سبب تغییر حالت آن می‌شوند، تنظیم کنیم. اینکار توسط متدهای Configure و Permit انجام خواهد شد. متد Configure، یکی از حالات از پیش تعیین شده را جهت تنظیم، مشخص می‌کند و سپس در متد Permit تعیین خواهیم کرد که بر اساس رخدادی مشخص (برای مثال در اینجا فشرده شدن کلید space) وضعیت حالت جاری، به وضعیت جدیدی (destinationState) منتقل شود.
نهایتا این ماشین حالت در یک حلقه بی‌نهایت مشغول به کار خواهد شد. برای نمونه یک Thread پس زمینه (BackgroundWorker) نیز می‌تواند همین کار را در برنامه‌های ویندوزی انجام دهد.


یک نکته
علاوه بر روش‌های یاد شده‌ی تشخیص الگوی ماشین حالت که در قسمت قبل بررسی شدند، مورد refactoring انبوهی از if و elseها و یا switchهای بسیار طولانی را نیز می‌توان به این لیست افزود.



استفاده از Stateless Designer برای تولید کدهای ماشین حالت

کتابخانه Stateless دارای یک طراح و Code generator بصری سورس باز است که آن‌را به شکل افزونه‌ای برای VS.NET می‌توانید در سایت Codeplex دریافت کنید. این طراح از کتابخانه GLEE برای رسم گراف استفاده می‌کند.

کار مقدماتی با آن به نحو زیر است:
الف) فایل StatelessDesignerPackage.vsix را از سایت کدپلکس دریافت و نصب کنید. البته نگارش فعلی آن فقط با VS 2012 سازگار است.
ب) ارجاعی را به اسمبلی stateless به پروژه خود اضافه نمائید (به یک پروژه جدید یا از پیش موجود).
ج) از منوی پروژه، گزینه Add new item را انتخاب کرده و سپس در صفحه ظاهر شده، گزینه جدید Stateless state machine را انتخاب و به پروژه اضافه نمائید.
کار با این طراح، با ادیت XML آن شروع می‌شود. برای مثال گردش کاری ارسال و تائید یک مطلب جدید را در بلاگی فرضی، به نحو زیر وارد نمائید:
<statemachine xmlns="http://statelessdesigner.codeplex.com/Schema">
  <settings>
    <itemname>BlogPostStateMachine</itemname>
    <namespace>StatelessTests</namespace>
    <class>public</class>
  </settings>
  <triggers>     
    <trigger>Save</trigger>
    <trigger>RequireEdit</trigger>
    <trigger>Accept</trigger>
    <trigger>Reject</trigger>
  </triggers>
  <states>     
    <state start="yes">Begin</state>
    <state>InProgress</state>     
    <state>Published</state>      
    <state>Rejected</state>      
  </states>
  <transitions>
    <transition trigger="Save" from="Begin" to="InProgress" />

    <transition trigger="Accept" from="InProgress" to="Published" />
    <transition trigger="Reject" from="InProgress" to="Rejected" />

    <transition trigger="Save" from="InProgress" to="InProgress" />

    <transition trigger="RequireEdit" from="Published" to="InProgress" />
    <transition trigger="RequireEdit" from="Rejected" to="InProgress" />
  </transitions>
</statemachine>
حاصل آن گراف زیر خواهد بود:


به علاوه کدهای زیر که به صورت خودکار تولید شده‌اند:
using Stateless;

namespace StatelessTests
{
  public class BlogPostStateMachine
  {
    public delegate void UnhandledTriggerDelegate(State state, Trigger trigger);
    public delegate void EntryExitDelegate();
    public delegate bool GuardClauseDelegate();

    public enum Trigger
    {
      Save,
      RequireEdit,
      Accept,
      Reject,
    }

    public enum State
    {
      Begin,
      InProgress,
      Published,
      Rejected,
    }

    private readonly StateMachine<State, Trigger> stateMachine = null;

    public EntryExitDelegate OnBeginEntry = null;
    public EntryExitDelegate OnBeginExit = null;
    public EntryExitDelegate OnInProgressEntry = null;
    public EntryExitDelegate OnInProgressExit = null;
    public EntryExitDelegate OnPublishedEntry = null;
    public EntryExitDelegate OnPublishedExit = null;
    public EntryExitDelegate OnRejectedEntry = null;
    public EntryExitDelegate OnRejectedExit = null;
    public GuardClauseDelegate GuardClauseFromBeginToInProgressUsingTriggerSave = null;
    public GuardClauseDelegate GuardClauseFromInProgressToPublishedUsingTriggerAccept = null;
    public GuardClauseDelegate GuardClauseFromInProgressToRejectedUsingTriggerReject = null;
    public GuardClauseDelegate GuardClauseFromInProgressToInProgressUsingTriggerSave = null;
    public GuardClauseDelegate GuardClauseFromPublishedToInProgressUsingTriggerRequireEdit = null;
    public GuardClauseDelegate GuardClauseFromRejectedToInProgressUsingTriggerRequireEdit = null;
    public UnhandledTriggerDelegate OnUnhandledTrigger = null;

    public BlogPost()
    {
      stateMachine = new StateMachine<State, Trigger>(State.Begin);
      stateMachine.Configure(State.Begin)
        .OnEntry(() => { if (OnBeginEntry != null) OnBeginEntry(); })
        .OnExit(() => { if (OnBeginExit != null) OnBeginExit(); })
        .PermitIf(Trigger.Save, State.InProgress , () => { if (GuardClauseFromBeginToInProgressUsingTriggerSave != null) return GuardClauseFromBeginToInProgressUsingTriggerSave(); return true; } )
      ;
      stateMachine.Configure(State.InProgress)
        .OnEntry(() => { if (OnInProgressEntry != null) OnInProgressEntry(); })
        .OnExit(() => { if (OnInProgressExit != null) OnInProgressExit(); })
        .PermitIf(Trigger.Accept, State.Published , () => { if (GuardClauseFromInProgressToPublishedUsingTriggerAccept != null) return GuardClauseFromInProgressToPublishedUsingTriggerAccept(); return true; } )
        .PermitIf(Trigger.Reject, State.Rejected , () => { if (GuardClauseFromInProgressToRejectedUsingTriggerReject != null) return GuardClauseFromInProgressToRejectedUsingTriggerReject(); return true; } )
        .PermitReentryIf(Trigger.Save , () => { if (GuardClauseFromInProgressToInProgressUsingTriggerSave != null) return GuardClauseFromInProgressToInProgressUsingTriggerSave(); return true; } )
      ;
      stateMachine.Configure(State.Published)
        .OnEntry(() => { if (OnPublishedEntry != null) OnPublishedEntry(); })
        .OnExit(() => { if (OnPublishedExit != null) OnPublishedExit(); })
        .PermitIf(Trigger.RequireEdit, State.InProgress , () => { if (GuardClauseFromPublishedToInProgressUsingTriggerRequireEdit != null) return GuardClauseFromPublishedToInProgressUsingTriggerRequireEdit(); return true; } )
      ;
      stateMachine.Configure(State.Rejected)
        .OnEntry(() => { if (OnRejectedEntry != null) OnRejectedEntry(); })
        .OnExit(() => { if (OnRejectedExit != null) OnRejectedExit(); })
        .PermitIf(Trigger.RequireEdit, State.InProgress , () => { if (GuardClauseFromRejectedToInProgressUsingTriggerRequireEdit != null) return GuardClauseFromRejectedToInProgressUsingTriggerRequireEdit(); return true; } )
      ;
      stateMachine.OnUnhandledTrigger((state, trigger) => { if (OnUnhandledTrigger != null) OnUnhandledTrigger(state, trigger); });
    }

    public bool TryFireTrigger(Trigger trigger)
    {
      if (!stateMachine.CanFire(trigger))
      {
        return false;
      }
      stateMachine.Fire(trigger);
      return true;
    }

    public State GetState
    {
      get
      {
        return stateMachine.State;
      }
    }
  }
}
توضیحات:

ماشین حالت فوق دارای چهار حالت شروع، در حال بررسی، منتشر شده و رد شده است. معمول است که این چهار حالت را به شکل یک enum معرفی کنند که در کدهای تولیدی فوق نیز به همین نحو عمل گردیده و public enum State معرف چهار حالت ذکر شده است. همچنین رویدادهای ذخیره، نیاز به ویرایش، ویرایش، تائید و رد نیز توسط public enum Trigger معرفی شده‌اند.
در قسمت Transitions، بر اساس یک رویداد (Trigger در اینجا)، انتقال از یک حالت به حالتی دیگر را سبب خواهیم شد.
تعاریف اصلی تنظیمات ماشین حالت، در سازنده کلاس BlogPostStateMachine انجام شده است. این تعاریف نیز بسیار ساده هستند. به ازای هر حالت، یک Configure داریم. در متدهای OnEntry و OnExit هر حالت، یک سری callback function فراخوانی خواهند شد. برای مثال در حالت Rejected یا Approved می‌توان ایمیلی را به ارسال کننده مطلب جهت یادآوری وضعیت رخ داده، ارسال نمود.
متدهای PermitIf سبب انتقال شرطی، به حالتی دیگر خواهند شد. برای مثال رد یا تائید یک مطلب نیاز به دسترسی مدیریتی خواهد داشت. این نوع موارد را توسط delgateهای Guard ایی که برای مدیریت شرط‌ها ایجاد کرده است، می‌توان تنظیم کرد. PermitReentryIf سبب بازگشت مجدد به همان حالت می‌گردد. برای مثال ویرایش و ذخیره یک مطلب در حال انتشار، سبب تائید یا رد آن نخواهد شد؛ صرفا عملیات ذخیره صورت گرفته و ماشین حالت مجددا در همان مرحله باقی خواهد ماند.

نحوه استفاده از ماشین حالت تولیدی:
همانطور که عنوان شد، حداقل استفاده از ماشین‌های حالت، refactoing انبوهی از if و else‌ها است که در حالت مدیریت یک چنین گردش‌های کاری باید تدارک دید.
namespace StatelessTests
{
    public class BlogPostManager
    {
        private BlogPostStateMachine _stateMachine;
        public BlogPostManager()
        {
            configureWorkflow();
        }

        private void configureWorkflow()
        {
            _stateMachine = new BlogPostStateMachine();

            _stateMachine.GuardClauseFromBeginToInProgressUsingTriggerSave = () => { return UserCanPost; };
            _stateMachine.OnBeginExit = () => { /* save data + save state + send an email to admin */ };

            _stateMachine.GuardClauseFromInProgressToPublishedUsingTriggerAccept = () => { return UserIsAdmin; };
            _stateMachine.GuardClauseFromInProgressToRejectedUsingTriggerReject = () => { return UserIsAdmin; };
            _stateMachine.GuardClauseFromInProgressToInProgressUsingTriggerSave = () => { return UserHasEditRights; };
            _stateMachine.OnInProgressExit = () => { /* save data + save state + send an email to user */ };

            _stateMachine.OnPublishedExit = () => { /* save data + save state + send an email to admin */ };
            _stateMachine.GuardClauseFromPublishedToInProgressUsingTriggerRequireEdit = () => { return UserHasEditRights; };

            _stateMachine.OnRejectedExit = () => { /* save data + save state + send an email to admin */ };
            _stateMachine.GuardClauseFromRejectedToInProgressUsingTriggerRequireEdit = () => { return UserHasEditRights; };
        }

        public bool UserIsAdmin
        {
            get
            {
                return true; // TODO: Evaluate if user is an admin.
            }
        }

        public bool UserCanPost
        {
            get
            {
                return true; // TODO: Evaluate if user is authenticated
            }
        }

        public bool UserHasEditRights
        {
            get
            {
                return true; // TODO: Evaluate if user is owner or admin
            }
        }

        // User actions
        public void Save() { _stateMachine.TryFireTrigger(BlogPostStateMachine.Trigger.Save); }
        public void RequireEdit() { _stateMachine.TryFireTrigger(BlogPostStateMachine.Trigger.RequireEdit); }

        // Admin actions        
        public void Accept() { _stateMachine.TryFireTrigger(BlogPostStateMachine.Trigger.Accept); }
        public void Reject() { _stateMachine.TryFireTrigger(BlogPostStateMachine.Trigger.Reject); }
    }
}
در کلاس فوق، نحوه استفاده از ماشین حالت تولیدی را مشاهده می‌کنید. در delegateهای Guard، سطوح دسترسی انجام عملیات بررسی خواهند شد. برای مثال، از بانک اطلاعاتی بر اساس اطلاعات کاربر جاری وارد شده به سیستم اخذ می‌گردند. در متدهای Exit هر مرحله، کارهای ذخیره سازی اطلاعات در بانک اطلاعاتی، ذخیره سازی حالت (مثلا در یک فیلد که بعدا قابل بازیابی باشد) صورت می‌گیرد و در صورت نیاز ایمیلی به اشخاص مختلف ارسال خواهد شد.
برای به حرکت درآوردن این ماشین، نیاز به یک سری اکشن متد نیز می‌باشد. تعدادی از این موارد را در انتهای کلاس فوق ملاحظه می‌کنید. کد نویسی آن‌ها در حد فراخوانی متد TryFireTrigger ماشین حالت است.

یک نکته:
ماشین حالت تولیدی به صورت پیش فرض در حالت State.Begin قرار دارد. می‌توان این مورد را از بانک اطلاعاتی خواند و سپس مقدار دهی نمود تا با هر بار وهله سازی ماشین حالت دقیقا مشخص باشد که در چه مرحله‌ای قرار داریم و TryFireTrigger بتواند بر این اساس تصمیم‌گیری کند که آیا مجاز است عملیاتی را انجام دهد یا خیر.
اشتراک‌ها
پروژه Bootstro.js (طراحی تور مجازی معرفی بخشهای مختلف در صفحه وب)

پلاگینی برای طراحی تور مجازی معرفی یا راهنمایی بخشهای مختلف صفحه وب (guided tour) مخصوصا برای زمانی که کاربران برای اولین بار به صفحه وب شما وارد می‌شوند و بخش‌های جدید اضافه شده به این صفحه وب را می‌خواهید در یک نمای کلی و به صورت اجمالی معرفی کنید.

برای دیدن نسخه نمایشی دمو بر روی دکمه  Click me! I'm Demo  در صفحه ای که باز شد کلیک نمایید و برای ادامه دکمه‌های Next را کلیک کنید.

لینک دانلود پلاگین بر روی Github

پروژه Bootstro.js (طراحی تور مجازی معرفی بخشهای مختلف در صفحه وب)
مطالب
وظیفه app.UseExceptionHandler("/Error") در Blazor Server
علیرغم اینکه در Program.cs یا Startup.cs کد زیر وجود دارد، اما بازهم استثناءها در Blazor Server در قالب یک نوار زرد رنگ، پایین مرورگر نشان داده می‌شوند؛ حال در محیط توسعه باشد و یا در محیط تولید و پابلیش نهایی محصول!
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Error");
            }

برای آزمایش آن، کد فوق را به شکل زیر کامنت می‌کنیم تا در محیط توسعه نیز در صورتیکه استثنایی رخ داد، ما را به صفحه‌ی Error.cshtml پیش فرض هدایت کند:
            //if (env.IsDevelopment())
            //{
                //app.UseDeveloperExceptionPage();
            //}
            //else
            //{
                app.UseExceptionHandler("/Error");
            //}
حال کامپوننت counter را به شکل زیر ویرایش می‌کنیم تا استثنایی به عمد رخ دهد:
@page "/counter"

<PageTitle>Counter</PageTitle>

<h1>Counter</h1>

<p role="status">Current count: @currentCount</p>

<button @onclick="IncrementCount">Click me</button>

@code {
    private int currentCount = 0;

    private void IncrementCount()
    {
        currentCount++;
        throw new Exception("This is my Exception !!");
    }
}
با اجرای برنامه ملاحظه خواهید کرد که به صفحه‌ی Error.cshtml هدایت نخواهید شد! حتی با اینکه استثنایی رخ داد، خط app.UseExceptionHandler("/Error")   اصلا کاری به مدیریت استثناء نداشت. حال سؤالی اینجا پیش می‌آید: پس چرا مایکروسافت در visual studio به تولید کدهای پیش فرض صفحه Error.cshtml و صدا زدن میان افزار فوق در یک اپلیکیشن Blazor Server می‌پردازد؟
در واقع بسیاری از میان افزارهای Asp.Net Core مانند UseExceptionHandler  در تمام فازهای یک اپلیکیشن Blazor Server به درستی کار نمی‌کنند؛ زیرا Blazor Server با SignalR و هابش کار می‌کند. 
هنگام راه‌اندازی یک برنامه‌ی Blazor Server، ابتدا چند درخواست HTTP وجود دارد که از خط لوله‌ی Asp .Net Core عبور می‌کنند؛ در واقع دقیقا قبل از تشکیل هاب و عملیات websocket. در این فاز اگر استثنایی رخ دهد، آنگاه میان افزار  UseExceptionHandler  وارد عمل خواهد شد و صفحه را به Error.cshtml هدایت خواهد نمود و به این دلیل است که این کدها به صورت پیش فرض وجود دارند. بنابراین باید صفحه‌ی Error.cshtml را نیز در اپلیکیشن‌های تولید شده‌ی خود، به زبان‌های موردنظر پروژه‌تان ترجمه کرده و پیام‌های مناسبی را به کاربر نشان دهید.
باید دقت نمود که دیگر پس از این فاز نمی‌توان به این میان افزار متکی بود. برای مدیریت استثناءها در فازهای بعد از این فاز، می‌توان از ErrorBoundary و یا مدیریت دستی استثناءها استفاده نمود.
مطالب
شیوه‌نامه‌های مقدماتی بوت استرپ 4
وقتی صفحه‌ی وبی را باز می‌کنید، تنظیمات بسیاری بر روی ظاهر آن تاثیرگذار هستند. برای مثال خود مرورگر دارای تنظیماتی است که بر روی ظاهر پیش‌فرض عناوین و عناصر مختلف قرار گرفته شده‌ی بر روی صفحه تاثیر گذار است. به این موارد Browser Styles گفته می‌شود که با Custom Styles ما قابلیت بازنویسی را دارند. در این بین، شیوه‌نامه‌های بوت استرپ، بین Browser Styles و شیوه‌نامه‌های سفارشی ما قرار می‌گیرند تا ظاهر بهتری را برای عناصر مختلف صفحه ارائه دهند.


تایپوگرافی مقدماتی بوت استرپ 4

شیوه‌نامه‌های همراه با بوت استرپ، رفتار و تنظیمات پیش‌فرض مرورگر را بازنویسی می‌کنند. این بازنویسی با فایل node_modules\bootstrap\scss\_reboot.scss شروع می‌شود. اگر مجموعه‌ی بوت استرپ را توسط روش معرفی شده‌ی در مطلب «روش‌های مختلف دریافت و نصب بوت استرپ 4» دریافت کرده باشید، کدهای کامل SASS آن، در پوشه‌ی scss این مجموعه، موجود هستند که یکی از آن‌ها فایل reboot است. کار آن نرمال سازی شیوه‌نامه‌ها، به نحوی است که در مرورگرها مختلف و همچنین وسایل نمایشی متفاوت، یکسان به نظر برسند:
 - برای مثال در این فایل از روش اندازه گیری rem استفاده شده‌است تا مدیریت اندازه‌های آن در سکوهای کاری مختلف قابل کنترل شود.
 - در اینجا از margin-top به طور کامل صرفنظر شده‌است، تا بتوان اندازه‌گیری فواصل بین عناصر را بهتر محاسبه کرد. بوت استرپ 4 تنها یک margin را در پایین تمام عناصر صفحه، تنظیم می‌کند. بنابراین آگاهی از وجود این پیش‌فرض، تنظیم فواصل بین عناصر را نیز ساده‌تر می‌کند.
 - در این فایل در همه‌جا از خاصیت inherit استفاده شده‌است تا امکان بازنویسی شیوه‌نامه‌های آن توسط custom styles ما ساده‌تر شود.
 - پیش‌فرض دیگری که در این نگارش از بوت استرپ تنظیم شده‌است، border-box می‌باشد. به این ترتیب اندازه گیری عرض عناصر ساده‌تر می‌شوند. برای مثال اگر عرض یک div را مساوی 200px قرار دهید، یک padding پیش‌فرض نیز برای آن درنظر گرفته شده‌است و padding سفارشی تنظیم شده‌ی برای آن بی‌اثر خواهد بود.
 - در این نگارش، فونت پیش‌فرض صفحه، به فونت پیش‌فرض سیستم تنظیم شده‌است و نه فونت از پیش تعیین شده‌ی خاصی. از این جهت که این قلم‌های سیستمی، دارای ویژگی‌های خاصی هستند که آن‌ها را برای سکوهای کاری مختلف، منحصربفرد می‌کنند.

مثال: نمایش تاثیر بوت استرپ 4 بر روی تایپوگرافی پیش‌فرض مرورگر
<body>
    <div class="container">
        <section class="content" id="mission">
            <h1>Our Commitment <small>to you</small></h1>
            <p>Wisdom Pet Medicine strives to blend the best in traditional and
                <em>alternative medicine</em> in the <strong>diagnosis and
                    treatment</strong> of companion animals including dogs,
                cats, birds, reptiles, rodents, and fish. We apply the wisdom
                garnered in the <mark>centuries old tradition</mark> of
                veterinary medicine, to find the safest treatments
                and&nbsp;cures.</p>
            <p>We strive to be your pet's medical <del>staff</del> experts from
                youth through the senior years. <small>We build preventative
                    health care plans for each and every one of our patients,
                    based on breed, age, and sex, so that your pet receives the
                    most appropriate care at crucial milestones.</small> We
                want to give your pet a long and healthy&nbsp;life.</p>
        </section>

        <section class="content" id="services">
            <h2>Exotic Pets</h2>
            <p>We offer <strong>specialized</strong> care for <em>reptiles,
                    rodents, birds,</em> and other exotic pets.</p>

            <h3>Grooming</h3>
            <p>Our therapeutic <span>grooming</span> treatments help battle
                fleas, allergic dermatitis, and other challenging skin
                conditions.</p>

            <h4>General Health</h4>
            <p>Wellness and senior exams, ultrasound, x-ray, and dental
                cleanings are just a few of our general health services.</p>

            <h5>Nutrition</h5>
            <p>Let our nutrition experts review your pet's diet and prescribe a
                custom nutrition plan for optimum health and disease
                prevention.</p>

            <h6>Pest Control</h6>
            <p>We offer the latest advances in safe and effective prevention
                and treatment of fleas, ticks, worms, heart worm, and other
                parasites.</p>

            <h2>Vaccinations</h2>
            <p>Our veterinarians are experienced in modern vaccination
                protocols that prevent many of the deadliest diseases in pets.</p>
        </section>
    </div>
</body>
با این خروجی:

با اعمال بوت استرپ


بدون اعمال بوت استرپ


در اینجا دو تصویر راملاحظه می‌کنید؛ یکی با اعمال bootstrap.min.css به صفحه‌است و دیگری با حذف آن از صفحه. به این ترتیب مشاهده می‌کنید که صرفا اعمال بوت استرپ به یک صفحه‌ی متداول، کیفیت نمایش آن‌را با بازنویسی شیوه‌نامه‌ی پیش‌فرض مرورگر، به نحو قابل ملاحظه‌ای بهبود بخشیده‌است و آن‌را زیباتر کرده‌است.
در اینجا تنها المان بوت استرپی که به صفحه اضافه شده‌است و جزو استانداردهای HTML نیست، یک div با کلاس container است:
<body>
    <div class="container">
کل محتوای صفحه جهت اعمال شیوه‌نامه‌های بوت استرپ، داخل این div قرار می‌گیرند. اولین تاثیر آن واکنشگرا کردن صفحه‌است و همچنین یک padding را نیز به قسمت‌های چپ و راست صفحه اضافه کرده‌است.
در این مثال تاثیر بوت استرپ را بر روی شیوه‌نامه‌های پیش‌فرض خصوصا  h1 تا h6، مشاهده می‌کنید.
روش دیگر تعریف headings در اینجا، استفاده از کلاس‌هایی با نام‌های مشابه است:
<div class="h1">Test div class H1</div>
علاوه بر آن، کلاس display نیز در اینجا برای تعیین اندازه‌ی headings سفارشی پیش بینی شده‌است که می‌توان از عدد 1 تا 4 را توسط آن تنظیم کرد:
<div class="display-1">Test div class display-1</div>
با این خروجی و اندازه در مقایسه با headings استاندارد که امکان تعریف تیترهایی بزرگ‌تر از اندازه‌های متداول را میسر می‌کنند:


همچنین اگر نیاز به بزرگتر نمایش دادن متن قسمت ابتدایی صفحه وجود داشت، می‌توان از کلاس Lead استفاده کرد:
<p class="lead">Testing a lead class</p>



کلاس‌های کمکی کار با متون در بوت استرپ 4

بوت استرپ 4 به همراه تعدادی کلاس کمکی کار با متون است که نیازهای متداول تایپوگرافی را برآورده می‌کنند:

1) کلاس‌های کمکی محل قرارگیری متون
- کلاس text-justify کار کشیدن و متناسب کردن یک پاراگراف را با گوشه‌های سمت چپ و راست صفحه انجام می‌دهد.
- کلاس text-nowrap از شکسته شدن متن به چندین سطر جلوگیری می‌کند. برای مثال می‌تواند برای نمایش کدها مناسب باشد.
- کلاس متغیر text-xx-pos برای تعیین محل قرارگیری متن کاربرد دارد:
در اینجا ذکر xx اختیاری است و می‌تواند sm، برای اندازه‌های صفحه‌ی بیشتر از 576px و یا md، برای اندازه‌های صفحه‌ی بیشتر از 768px و یا lg، برای اندازه‌های صفحه‌ی بیشتر از 992px و یا xl، برای اندازه‌های صفحه‌ی بیشتر از 1200px باشد. این اندازه‌های یاد شده را در ادامه بیشتر مشاهده خواهید کرد.
همچنین pos می‌تواند left ،center و یا right باشد.
برای مثال کلاس text-sm-center به این معنا است که متن مدنظر در break-point ایی به نام sm، یعنی با اندازه‌ی صفحه‌ی بیشتر از 576px، در وسط صفحه نمایش داده خواهد شد.

2) کلاس‌های نمایش upper-case و lower-case حروف
- کلاس text-lowercase کار نمایش lower-case کل یک پاراگراف اعمالی را انجام می‌دهد.
- کلاس text-uppercase کار نمایش upper-case کل یک پاراگراف اعمالی را انجام می‌دهد.
- کلاس text-capitalize، اولین حرف هر واژه را به صورت بزرگ نمایش می‌دهد.

3) کلاس‌های شیوه‌ی نمایش متون
کلاس font-weight-bold، کلاس font-weight-normal و کلاس font-italic نمایش ضخیم، عادی و یا italic متن را سبب می‌شوند.

مثال: بررسی تاثیر کلاس‌های کمکی کار با متون در بوت استرپ 4
<body>
    <div class="container">
        <section class="content" id="mission">
            <h1 class="text-center text-sm-right text-md-left text-uppercase">Our
                Commitment</h1>
            <p class="lead text-justify">Wisdom Pet Medicine strives to blend
                the best in
                traditional and <em>alternative medicine</em> in the <strong>diagnosis
                    and treatment</strong> of companion animals including dogs,
                cats, birds, reptiles, rodents, and fish. We apply the wisdom
                garnered in the <mark>centuries old tradition</mark> of
                veterinary medicine, to find the safest treatments
                and&nbsp;cures.</p>
            <p class="text-nowrap text-capitalize">We strive to be your pet's
                medical <del>staff</del>
                experts from
                youth through the senior years. <small>We build preventative
                    health care plans for each and every one of our patients,
                    based on breed, age, and sex, so that your pet receives the
                    most appropriate care at crucial milestones.</small> We
                want to give your pet a long and healthy&nbsp;life.</p>
        </section>

        <section class="content" id="services">
            <div class="display-4">Exotic Pets</div>
            <p>We <span class="font-weight-bold">offer</span> <strong class="font-weight-normal">specialized</strong>
                care for <em>reptiles,
                    rodents, birds,</em> and other exotic pets.</p>

            <h3 class="text-left text-md-center text-sm-right">Grooming</h3>
            <p>Our therapeutic <span>grooming</span> treatments help battle
                fleas, allergic dermatitis, and other challenging skin
                conditions.</p>

            <h4>General Health</h4>
            <p>Wellness and senior exams, ultrasound, x-ray, and dental
                cleanings are just a few of our general health services.</p>

            <h5>Nutrition</h5>
            <p>Let our nutrition experts review your pet's diet and prescribe a
                custom nutrition plan for optimum health and disease
                prevention.</p>

            <h6>Pest Control</h6>
            <p>We offer the latest advances in safe and effective prevention
                and treatment of fleas, ticks, worms, heart worm, and other
                parasites.</p>

            <h2>Vaccinations</h2>
            <p>Our veterinarians are experienced in modern vaccination
                protocols that prevent many of the deadliest diseases in pets.</p>
        </section>
    </div>
</body>
با این خروجی


در اینجا اگر کلاس text-right را به heading اضافه کنیم:
<h1 class="text-right">Our Commitment <small>to you</small></h1>
چنین خروجی حاصل می‌شود:


و یا می‌توان این کلاس‌ها را با هم ترکیب کرد:
<h1 class="text-center text-sm-right">Our Commitment <small>to you</small></h1>
این ترکیب به این معنا است:
- متن h1 در حالت عادی در وسط صفحه نمایش داده شود.


- اما متن مدنظر در break-point ایی به نام sm، یعنی با اندازه‌ی صفحه‌ی بیشتر از 576px، در سمت راست صفحه نمایش داده خواهد شد.


این اعداد توسط افزونه‌ی ViewPort نمایش داده شده‌اند.


همچنین تاثیر text-justify را نیز به اولین پاراگراف
<p class="lead text-justify">
به صورت ذیل مشاهده می‌کنید که در مقایسه با تصویر قبلی، سبب کشیده شدن متن و تنظیم آن با سمت راست و چپ صفحه شده‌است:


و یا اگر text-nowrap را به پاراگرافی اعمال کنیم:
<p class="text-nowrap">


سبب نمایش یک سطری آن خواهد شد که در اینجا با پدید آمدن یک اسکرول بار افقی، قابل ملاحظه‌است.


کلاس‌های کمکی کار با لیست‌ها و نقل قول‌ها در بوت استرپ 4

بوت استرپ 4 به همراه کلاس‌هایی کمکی برای کار با لیست‌ها و نقل قول‌ها است؛ مانند:
- کلاس list-unstyled سبب حذف bullets از یک لیست می‌شود.
- برای ایجاد لیست‌های Inline می‌توان از کلاس list-inline بر روی المان UL و سپس list-inline-item بر روی هر LI آن، کمک گرفت.
<body>
    <div class="container">
        <section class="content" id="services">
            <h2>Exotic Pets</h2>
            <p>We offer <strong>specialized</strong> care for <em>reptiles,
                    rodents, birds,</em> and other exotic pets.</p>
            <ul class="list-unstyled">
                <li>Grooming</li>
                <li>General Health</li>
                <li>Nutrition</li>
                <li>Pest Control</li>
                <li>Vaccinations</li>
            </ul>

            <ul class="list-inline">
                <li class="list-inline-item">Grooming</li>
                <li class="list-inline-item">General Health</li>
                <li class="list-inline-item">Nutrition</li>
                <li class="list-inline-item">Pest Control</li>
                <li class="list-inline-item">Vaccinations</li>
            </ul>
        </section>
    </div>
</body>
در این مثال نحوه‌ی حذف bullets و همچنین inline تعریف کردن دو لیست را مشاهده می‌کنید؛ با این خروجی:


در حالت لیست inline، آیتم‌های لیست از چپ به راست در یک سطر نمایش داده می‌شوند. برای مثال می‌تواند برای نمایش breadcrumbs در یک سایت مناسب باشد.
همچنین برای نمایش نقل قول‌ها می‌توان از کلاس blockquote و برای نمایش بهتر امضای آن از کلاس blockquote-footer استفاده کرد:
<body>
    <div class="container">
        <section class="content" id="testimonials">
            <h2>Testimonials</h2>

            <blockquote>
                During the summer, our rabbit, Tonto, began to have severe
                redness and itching on his belly and feet. I'm very thankful to
                the veterinarians and staff at Wisdom for the excellent care
                Tonto received, and for nipping his allergies in the bud, so to
                speak.
                Jane
            </blockquote>

            <blockquote class="blockquote text-right">
                When Samantha, our Siamese cat, began sleeping all the time and
                urinating excessively, we brought her to see the specialists at
                Wisdom. Now, two years later, Samantha is still free from any
                complications of diabetes, and her blood sugar regularly tests
                normal.
                <div class="blockquote-footer">
                    The McPhersons
                </div>
            </blockquote>
        </section>
    </div>
</body>


در اینجا دو blockquote را مشاهده می‌کنید. مورد اول بدون کلاس blockquote است و دومی به همراه این کلاس و یک footer تعریف شده‌است. همچنین می‌توان کلاس‌هایی مانند text-right را نیز به blockquote اضافه کرد.
البته در نگارش 4، حاشیه‌ی خاکستری blockquote که در نگارش سوم آن وجود داشت، حذف شده‌است.


کار با رنگ‌ها در بوت استرپ 4

بوت استرپ، به همراه تعدادی کلاس مخصوص رنگ‌ها است که از آن در همه جا استفاده می‌کند؛ مانند رنگ‌های دکمه‌ها، پس زمینه‌ها و متون.
1) کلاس‌های تعیین رنگ متون:


برای مثال در اینجا بجای Color می‌توان یکی از ثوابت ذیل آن‌را قید کرد؛ مانند text-primary و یا text-danger
این کلاس‌ها برای تعیین رنگ متون و همچنین لینک‌ها کاربرد دارند.


2) کلاس‌های تعیین رنگ پس زمینه:


در اینجا برای نمونه بجای Color می‌توان یکی از ثوابت ذیل آن‌را قید کرد؛ مانند bg-primary و یا bg-danger

مثال: اعمال رنگ‌های زمینه‌ای بوت استرپ
<body>
    <div class="container">
        <section class="content" id="services">
            <h2 class="text-danger">Our Mission</h2>
            <p class="bg-danger text-white">Wisdom Pet Medicine strives to
                blend the best in
                traditional and
                alternative medicine in the diagnosis and treatment of
                companion animals including dogs, cats, birds, reptiles,
                rodents, and fish. We apply the wisdom garnered in the
                centuries old tradition of veterinary medicine, to find the
                safest treatments and cures.</p>

            <ul>
                <li><a class="text-warning" href="#">Grooming</a></li>
                <li><a href="#">General Health</a></li>
                <li><a href="#">Nutrition</a></li>
                <li><a href="#">Pest Control</a></li>
                <li><a href="#">Vaccinations</a></li>
            </ul>

        </section>

        <section class="content" id="testimonials">
            <h2>Testimonials</h2>

            <blockquote class="blockquote bg-faded text-info">
                During the summer, our rabbit, Tonto, began to have severe
                redness and itching on his belly and feet. I'm very thankful to
                the veterinarians and staff at Wisdom for the excellent care
                Tonto received, and for nipping his allergies in the bud, so to
                speak.
                <div class="blockquote-footer">
                    Jane
                </div>
            </blockquote>
        </section>
    </div>
</body>
با این خروجی


در اینجا مثال‌هایی را از اعمال کلاس‌های رنگ‌های بوت استرپ مشاهده می‌کنید. همچنین امکان ترکیب آن‌ها مانند مثال زیر نیز وجود دارد:
<p class="bg-danger text-white">



کدهای کامل این قسمت را از اینجا می‌توانید دریافت کنید: Bootstrap4_02.zip