کتاب Razor Components Succinctly
OVERVIEW
Razor components are specific building blocks within the Blazor framework. They can perform many roles: representing a specific piece of the user interface, a view component, or a tag helper; or representing a layout or an entire page. In Razor Components Succinctly, you will explore how to create and work with both simple and advanced Razor components. Longtime Succinctly author Ed Freitas will show you how to write a basic component using one-way data binding and events, and then two-way data binding, event callbacks, life cycle methods, and component references. Finally, you'll see how to enable component reuse by creating a component template.
TABLE OF CONTENTS
Razor Components
Setup and Fundamentals
Component Fundamentals
Component Features
Using Components
Templating
Author
Ed Freitas
ISBN
978-1-64200-211-9
Published on
April 16, 2021
Pages
102
FluentValidation #2
NotNull | اطمینان از اینکه خاصیت مورد نظر Null نباشد |
NotEmpty | اطمینان از اینکه خاصیت مورد نظر Null یا رشته خالی نباشد (یا مقدار پیش فرض نباشد، مثلا 0 برای int) |
NotEqual | اطمینان از اینکه خاصیت مورد نظر برابر مقدار تعیین شده نباشد (یا برابر مقدار خاصیت دیگری نباشد) |
Equal | اطمینان از اینکه خاصیت مور نظر برابر مقدار تعیین شده باشد (یا برابر مقدار خاصیت دیگری باشد) |
Length | اطمینان از اینکه طول رشتهی خاصیت مورد نظر در محدوده خاصی باشد |
LessThan | اطمینان از اینکه مقدار خاصیت مورد نظر کوچکتر از مقدار تعیین شده باشد (یا کوچکتر از خاصیت دیگری) |
LessThanOrEqual | اطمینان از اینکه مقدار خاصیت مورد نظر کوچکتر یا مساوی مقدار تعیین شده باشد (یا کوچکتر مساوی مقدار خاصیت دیگری) |
GreaterThan | اطمینان از اینکه مقدار خاصیت مورد نظر بزرگتر از مقدار تعیین شده باشد (یا بزرگتر از مقدار خاصیت دیگری) |
GreaterThanOrEqual | اطمینان از اینکه مقدار خاصیت مورد نظر بزرگتر مساوی مقدار تعیین شده باشد (یا بزرگتر مساوی مقدار خاصیت دیگری) |
Matches | اطمینان از اینکه مقدار خاصیت مورد نظر با عبارت باقائده (Regular Expression) تنظیم شده مطابقت داشته باشد |
Must | اعتبارسنجی یک predicate با استفاده از Lambada Expressions. اگر عبارت Lambada مقدار true برگرداند اعتبارسنجی با موفقیت انجام شده و اگر false برگرداند، اعتبارسنجی با شکست مواجه شده است. |
اطمینان از اینکه مقدار خاصیت مورد نظر یک آدرس ایمیل معتبر باشد | |
CreditCard | اطمینان از اینکه مقدار خاصیت مورد نظر یک Credit Card باشد |
RuleFor(customer => customer.Surname).NotEqual(customer => customer.Forename);
RuleFor(customer => customer.Surname).NotNull().WithMessage("Please ensure that you have entered your Surname");
اعتبارسنجی تنها در مواقع خاص
RuleFor(customer => customer.CustomerDiscount).GreaterThan(0).When(customer => customer.IsPreferredCustomer);
When(customer => customer.IsPreferred, () => { RuleFor(customer => customer.CustomerDiscount).GreaterThan(0); RuleFor(customer => customer.CreditCardNumber).NotNull(); });
تعیین نحوه برخورد با اعتبارسنجیهای زنجیره ای
RuleFor(x => x.Surname).NotNull().NotEqual("foo");
RuleFor(x => x.Surname).Cascade(CascadeMode.StopOnFirstFailure).NotNull().NotEqual("foo");
public class PersonValidator : AbstractValidator<Person> { public PersonValidator() { // First set the cascade mode CascadeMode = CascadeMode.StopOnFirstFailure; // Rule definitions follow RuleFor(...) RuleFor(...) } }
راه اول ایجاد یک کلاس که از PropertyValidator مشتق میشود. برای توضیح نحوه استفاده از این راه، تصور کنید که میخواهیم یک اعتبارسنج سفارشی درست کنیم تا چک کند که یک لیست حتماً کمتر از 10 آیتم داخل خود داشته باشد. در این صورت کدی که بایستی نوشته شود به صورت زیر خواهد بود:
using System.Collections.Generic; using FluentValidation.Validators; public class ListMustContainFewerThanTenItemsValidator<T> : PropertyValidator { public ListMustContainFewerThanTenItemsValidator() : base("Property {PropertyName} contains more than 10 items!") { } protected override bool IsValid(PropertyValidatorContext context) { var list = context.PropertyValue as IList<T>; if(list != null && list.Count >= 10) { return false; } return true; } }
برای استفاده از این Validator سفارشی نیز میتوان از متد SetValidator به صورت زیر استفاده نمود:
public class PersonValidator : AbstractValidator<Person> { public PersonValidator() { RuleFor(person => person.Pets).SetValidator(new ListMustContainFewerThanTenItemsValidator<Pet>()); } }
راه دیگر استفاده از آن تعریف یک Extension Method میباشد که در این صورت میتوان از آن به صورت زنجیره ای مانند دیگر Validatorها استفاده نمود:
public static class MyValidatorExtensions { public static IRuleBuilderOptions<T, IList<TElement>> MustContainFewerThanTenItems<T, TElement>(this IRuleBuilder<T, IList<TElement>> ruleBuilder) { return ruleBuilder.SetValidator(new ListMustContainFewerThanTenItemsValidator<TElement>()); } }
public class PersonValidator : AbstractValidator<Person> { public PersonValidator() { RuleFor(person => person.Pets).MustContainFewerThanTenItems(); } }
راه دوم استفاده از متد Custom میباشد. برای توضیح نحوه استفاه از این متد مثال قبل (چک کردن تعداد آیتمهای لیست) را به صورت زیر بازنویسی میکنیم:
public class PersonValidator : AbstractValidator<Person> { public PersonValidator() { Custom(person => { return person.Pets.Count >= 10 ? new ValidationFailure("More than 10 pets is not allowed.") : null; }); } }
public class PersonValidator : AbstractValidator<Person> { public PersonValidator() { RuleFor(person => person.Pets).Must(HaveFewerThanTenPets).WithMessage("More than 9 pets is not allowed"); } private bool HaveFewerThanTenPets(IList<Pet> pets) { return pets.Count < 10; } }
پ.ن.
در این دو مقاله سعی شد تا ویژگیهای FluentValidation به صورت انتزاعی توضیح داده شود. در قسمت بعد نحوه استفاده از این کتابخانه در یک برنامه ASP.NET MVC نشان داده خواهد شد.
Contents
- Get to know our basic Angular TabsComponent (in master branch here)
- Declare a template within a template using
ng-template
in Angular - Pass a reference of an ng-template to a component and render it in Angular
- Pass data to be rendered in a dynamic ng-template using ngTemplateOutletContext in Angular
- Define an anchor point where to render dynamic components in Angular
- Dynamically instantiate an Angular component
- Destroy a dynamically instantiated Angular component
چرا برنامه نویسی موبایل؟
با افزایش روزافزون SmartPhone ها و تبلتها، بازار تکنولوژی به این سمت سوق پیدا کردهاست. از این رو شرکتهای ارائه دهنده نرم افزاری، از این موقعیت استفاده کرده و هر کدام پلتفرم متفاوتی را برای برنامه نویسی بر روی این اسمارت فونها ارائه دادهاند. یکی از بزرگترین دغدغههای امروزه شرکتهای برنامه نویسی و توسعه نرم افزار موبایل، انتخاب درست پلتفرم برای توسعه نرم افزار میباشد. در این مقاله قصد دارم یکی از این پلت فرمها را بررسی کرده و معرفی کنم.
شرکت xamarin کار خود را در سال 2011 با ارایه نسخه Cross Platform پلتفرم .Net به نام Mono آغاز کرد. بعد از ارایه این نسخه از .Net، زامارین به کمک Mono توانست پیاده سازی بر روی Android و IOS را به نامهای MonoForAndroid و MonoTouch ارایه دهد. بعد از این نسخهها برنامه نویسان توانستند بر روی سیستم عاملهای اندروید و آی او اس به صورت Native کد خود را به زبان C# نوشته و آنها را اجرا کنند.
چرا باید از Xamarin استفاده کنم؟
در ادامه مقاله قصد دارم شما را با برخی از ویژگیهای زامارین آشنا کرده و مزایای استفاده از آن را بیان کنم.
Xamarin امکانی را فراهم کردهاست که برنامه نویسان به دو روش متفاوت قادر خواهند بود برنامههای خود را بنویسند:
Xamarin Native :1
زامارین به شما این امکان را میدهد که بتوانید به صورت مستقیم برای هر پلتفرم به صورت جداگانه برنامه نویسی کنید. در اندروید اینکار با Xamarin.Droid و در IOS اینکار با Xamarin.Touch امکانپذیر است. مزیتهای استفاده از این روش عبارتند از:
· بهره گیری از یک زبان برنامه نویسی
همانطور که میدانید یادگیری یک زبان برنامه نویسی هزینهی زیادی را برای یک سازمان و یا یک شخص به همراه دارد. در زامارین این امکان فراهم شدهاست که با استفاده از تنها یک زبان برنامه نویسی مانند C# ، برنامه نویسان بتوانند برای پلتفرمهای مختلف برنامه بنویسند. در نظر داشته باشید که UI در هر پلتفرم به صورت جداگانه پیاده سازی میشود. به طور مثال در اندروید به وسیلهی Android Xml میتوانید ظاهر برنامه خود را پیاده سازی کنید و منطقهای خود را با زبان C# برای تمامی پلت فرمها به صورت یکسان بنویسید.
· تجربه کاربری Native
زامارین به شما این امکان را خواهد داد که با استفاده از کنترلهای Native هر پلتفرم به تجربه کاربری همان پلت فرم دسترسی پیدا کنید و اپلیکیشنی با ظاهر و UX همان پلتفرم بسازید.
· استفاده 100% از امکانات هر پلتفرم
زامارین به دلیل Native بودن این امکان را به برنامه نویسان ارائه میدهد که با استفاده از یک زبان و با بکارگیری Cycle Life مخصوص هر پلتفرم، به 100% امکانات و API های هر پلتفرم دسترسی پیدا کنند.
· Performance
به دلیل اینکه برنامههای زامارین به صورت Native اجرا میشوند Performance بالایی دارند.
· دسترسی به API های موجود در .Net
شما قادر خواهید بود با دانش موجود مانند Entity Framework Code و.. به فریم ورک .Net دسترسی پیدا کرده و از API های درون آن استفاده کنید. زامارین از یکی از پیاده سازیهای .Net Standard استفاده میکند.
· استفاده مجدد از کد
در زامارین قادر خواهید بود که از کدهای خود، استفاده مجدد کرده و این امر سبب مدیریت بهتر بر روی کد، کد نویسی کمتر، هزینه نگهداری کد کمتر، توسعه راحتتر اپلیکیشن و ... میشود.
· تست خودکار
در زامارین شما میتوانید برای کدهای خود تست خودکار نوشته و آنها را به صورت خودکار تست کنید.
· Bind کردن Library های Objective-C و Java
زامارین طوری طراحی شدهاست که دست شما را در هیچگونه شرایطی نخواهد بست. شما میتوانید به صورت مستقیم کدهایی را که به زبان های Java و Objective-C نوشته شدهاند، به پروژه اضافه کرده و هیچگونه نگرانی از بابت کدهای از قبل نوشته شده که به زبانهای Objective-C و Java هستند، نداشته باشید.
· Designer
در زمارین این امکان وجود دارد که در هر پلتفرم از طریق Designer مخصوص به آن پلتفرم، UI خود را طراحی و پیاده سازی کنید.
· Async
در برنامه نویسی غیر همزمان ( Asynchronous Programming ) این امکان وجود دارد که برنامه شما بدون توقف، یک قسمت از کد را اجرا کرده و منتظر اجرای قسمتهای دیگر کد نشود؛ یا به اصطلاح برنامه از حالت Response خارج نشود. در زبانهای Java ، Objective-C و Swift اینکار باید با CallBack و به صورت Manual مدیریت شود؛ اما #C این امکان را فراهم آورده است که به راحتی اینکار را انجام داده و برنامه خود را همیشه در حالت پاسخ دهی نگه دارید.
public async Task<List<FeedItem>> GetFeedItems(DateTime date) { var feed = "http://planet.xamarin.com/feed/"; var response = await httpClient.GetStringAsync(feed); var items = await ParseFeedAsync(response); return items.Where(item => item.Published.Date == date).ToList(); }
· Parallel Programming
در برنامه نویسی موازی( Parallel Programming ) برخلاف برنامه نویسی MultiThread که بر روی یک هسته CPU اجرا میشود، بر روی چندین هسته CPU به صورت موازی اجرا میشود. زامارین از این نوع برنامه نویسی پشتیبانی میکند.
Xamarin.Forms: 2
پس از معرفی Xamarin Forms API شما میتوانید علاوه بر مزیتهایی که در بالا اشاره شد، کدهای Logic خود را با زبان C# و کدهای UI خود را با زبان XAML پیاده سازی کرده و با یک بار نوشتن کد، در پلتفرمهای مختلف خروجی خود را مشاهده کنید. مزیت استفاده از Xamarin Forms عبارتند از:
· استفاده از کد واحد برای پیاده سازی UI و Logic
یکی از بهترین مزیتهایی را که میتوان به آن اشاره نمود این است که شما کافیست یک بار کد خود را بنویسید و Xamarin Forms کد شما را در پلت فرمهای متفاوت پیاده سازی خواهد کرد.
<?xml version="1.0" encoding="UTF-8"?> <TabbedPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="MyApp.MainPage"> <TabbedPage.Children> <ContentPage Title="Profile" Icon="Profile.png"> <StackLayout Spacing="20" Padding="20" VerticalOptions="Center"> <Entry Placeholder="Username" Text="{Binding Username}"/> <Entry Placeholder="Password" Text="{Binding Password}" IsPassword="true"/> <Button Text="Login" TextColor="White" BackgroundColor="#77D065" Command="{Binding LoginCommand}"/> </StackLayout> </ContentPage> <ContentPage Title="Settings" Icon="Settings.png"> <!-- Settings --> </ContentPage> </TabbedPage.Children> </TabbedPage>
برای پیاده سازی UI در Xamarin Forms باید از XAML استفاده کنید. همچنین مانند روش قبلی میتوانید از زبان C# برای پیاده سازی منطق، استفاده نمایید.
با استفاده از Xamarin Forms شما تجربه نوشتن کد Cross Platform را در کنار Native بودن آن خواهید داشت.
· استفاده از یک کنترل مخصوص یک پلتفرم در بین کد ( Embedding )
در Xamarin Forms این امکان وجود دارد که اگر شما خواستید یک کنترل مخصوص IOS را در بین کدهای خود استفاده کنید، بتوانید به راحتی اینکار را انجام دهید.( (Embedding
اگر به تصویر بالا دقت کنید متوجه خواهید شد که در یکسری از کنترلها، تصاویر متفاوت هستند. در نسخه اندروید یک Action Button در قسمت پایین صفحه مشاهده میکنید که در نسخهی IOS آن موجود نیست. یعنی به صورت مستقیم کنترل Action Button که مختص به پلت فرم اندروید میباشد، درون Xamarin Forms استفاده شده است.
· دسترسی به هر پلتفرم به طور مستقیم
شما قادر خواهید بود به طور مستقیم به هر پلت فرم دسترسی پیدا کرده و به طور مثال در هر پلتفرم، UI مخصوص به خود را با Property های مخصوص به خود طراحی کنید.
· UITest و Test Cloud
در Xamarin Forms میتوانید برای UI خود تست نوشته و آنها را به وسیله Xamarin Test Cloud بر روی صدها Device متفاوت تست کنید. (این امکان فقط برای Android و IOS وجود دارد.)
· Life Cycle مشابه در تمامی پلتفرم ها
همانطور که میدانید پلتفرمهای مختلف، Life Cycle های متفاوتی برای مدیریت اپلیکیشن دارند. یکی از مزیتهای استفاده از Xamarin Forms این است که شما میتوانید برای تمامی پلتفرمها بهوسیلهی یک Life Cycle یکسان کد بنویسید.
· Previewer
یکی از بهترین قابلیتهایی که در Xamarin Forms اضافه شدهاست این است که شما قادر خواهید بود به صورت Real Time خروجی فایل XAML خود را به وسیله Previewer مشاهده نمایید.
· Performance Profiler
به وسیله Xamarin Profiler شما میتوانید میزان مصرف حافظه، Performance و ... را در اندروید و IOS اندازه گیری نمایید.
نکات قابل توجه:
Ø استفاده همزمان از Xamarin Forms و Xamarin Native
شما میتوانید کدهای خود را با حداکثر Reusability نوشته و در صورت لزوم با کدهای Xamarin Native ترکیب کنید.
Ø Documentation خیلی خوب
زمارین مستندات جامع و کاملی را در سایت خود گردآوری کرده که میتوانید به راحتی از آن برای فهم تمامی قسمتهای Xamarin استفاده کنید.
import { decorate } from "mobx"; class Count { value = 0; } decorate(Count, { value: observable }); const count = new Count();
بازنویسی مثال ورود متن و نمایش آن با Mobx decorators
در اینجا یک text-box، به همراه دو div در صفحه رندر خواهند شد که قرار است با ورود اطلاعاتی در text-box، یکی از آنها (text-display) این اطلاعات را به صورت معمولی و دیگری (text-display-uppercase) آنرا به صورت uppercase نمایش دهد. روش کار انجام شده هم مستقل از React است و به صورت مستقیم با استفاده از DOM API عمل شدهاست. این مثال را پیشتر در اولین قسمت بررسی MobX، ملاحظه کردید. اکنون اگر بخواهیم بجای شیءای که توسط متد observable کتابخانهی MobX محصور شدهاست:
const text = observable({ value: "Hello world!", get uppercase() { return this.value.toUpperCase(); } });
ابتدا یک پروژهی جدید React را ایجاد میکنیم:
> create-react-app state-management-with-mobx-part3 > cd state-management-with-mobx-part3 > npm start
> npm install --save mobx
<!DOCTYPE html> <html lang="en"> <head> <title>MobX Basics, part 3</title> <meta charset="UTF-8" /> <link href="src/styles.css" /> </head> <body> <main> <input id="text-input" /> <p id="text-display"></p> <p id="text-display-uppercase"></p> </main> <script src="src/index.js"></script> </body> </html>
import { autorun, computed, observable } from "mobx"; const input = document.getElementById("text-input"); const textDisplay = document.getElementById("text-display"); const loudDisplay = document.getElementById("text-display-uppercase"); class Text { @observable value = "Hello World"; @computed get uppercase() { return this.value.toUpperCase(); } } const text = new Text(); input.addEventListener("keyup", event => { text.value = event.target.value; }); autorun(() => { input.value = text.value; textDisplay.textContent = text.value; loudDisplay.textContent = text.uppercase; });
اکنون اگر در همین حال، برنامه را با دستور npm start اجرا کنیم، با خطای زیر متوقف خواهیم شد:
./src/index.js SyntaxError: \src\index.js: Support for the experimental syntax 'decorators-legacy' isn't currently enabled (8:3): 6 | 7 | class Text { > 8 | @observable value = "Hello World"; | ^ 9 | @computed get uppercase() { 10 | return this.value.toUpperCase(); 11 | }
راه حل اول: از Decorators استفاده نکنیم!
یک راه حل مشکل فوق این است که بدون هیچ تغییری در ساختار پروژهی React خود، اصلا از decorator syntax استفاده نکنیم. برای مثال اگر یک کلاس متداول MobX ای چنین شکلی را دارد:
import { observable, computed, action } from "mobx"; class Timer { @observable start = Date.now(); @observable current = Date.now(); @computed get elapsedTime() { return this.current - this.start + "milliseconds"; } @action tick() { this.current = Date.now(); } }
import { observable, computed, action, decorate } from "mobx"; class Timer { start = Date.now(); current = Date.now(); get elapsedTime() { return this.current - this.start + "milliseconds"; } tick() { this.current = Date.now(); } } decorate(Timer, { start: observable, current: observable, elapsedTime: computed, tick: action });
همچنین در این حالت بجای استفاده از کامپوننتهای کلاسی، باید از روش بکارگیری متد observer برای محصور کردن کامپوننت تابعی تعریف شده استفاده کرد (تا دیگر نیازی به ذکر observer class@ نباشد):
const Counter = observer(({ count }) => { return ( // ... ); });
راه حل دوم: از تایپاسکریپت استفاده کنید!
create-react-app امکان ایجاد پروژههای React تایپاسکریپتی را با ذکر سوئیچ typescript نیز دارد:
> create-react-app my-proj1 --typescript
{ "compilerOptions": { // ... "experimentalDecorators": true } }
فعالسازی MobX Decorators در پروژههای استاندارد React مبتنی بر ES6
MobX از legacy" decorators spec" پشتیبانی میکند. یعنی اگر پروژهای از spec جدید استفاده کند، دیگر نخواهد توانست با MobX فعلی کار کند. این هم مشکل MobX نیست. مشکل اینجا است که باید دانست کلا decorators در زبان جاوااسکریپت هنوز در مرحلهی آزمایشی قرار دارند و تکلیف spec نهایی و تائید شدهی آن مشخص نیست.
برای فعالسازی decorators در یک پروژهی React استاندارد مبتنی بر ES6، شاید کمی جستجو کنید و به نتایجی مانند افزودن فایل babelrc. به ریشهی پروژه و نصب افزونههایی مانند babel/plugin-proposal-decorators @babel/plugin-proposal-class-properties@ برسید. اما ... اینها بدون اجرای دستور npm run eject کار نمیکنند و اگر این دستور را اجرا کنیم، در نهایت به یک فایل package.json بسیار شلوغ خواهیم رسید (اینبار ارجاعات به Babel، Webpack و تمام ابزارهای دیگر نیز ظاهر میشوند). همچنین این عملیات نیز یک طرفهاست. یعنی از این پس قرار است کنترل تمام این پشت صحنه، در اختیار ما باشد و به روز رسانیهای بعدی create-react-app را با مشکل مواجه میکند. این گزینه صرفا مختص توسعه دهندگان پیشرفتهی React است. به همین جهت نیاز به روشی را داریم تا بتوانیم تنظیمات Webpack و کامپایلر Babel را بدون اجرای دستور npm run eject، تغییر دهیم تا در نتیجه، decorators را در آن فعال کنیم و خوشبختانه پروژهی react-app-rewired دقیقا برای همین منظور طراحی شدهاست.
بنابراین ابتدا بستههای زیر را نصب میکنیم:
> npm i --save-dev customize-cra react-app-rewired
پس از نصب این پیشنیازها، فایل جدید config-overrides.js را به ریشهی پروژه، جائیکه فایل package.json قرار گرفتهاست، با محتوای زیر اضافه کنید تا پشتیبانی ازlegacy" decorators spec" فعال شوند:
const { override, addDecoratorsLegacy, disableEsLint } = require("customize-cra"); module.exports = override( // enable legacy decorators babel plugin addDecoratorsLegacy(), // disable eslint in webpack disableEsLint() );
"scripts": { "start": "react-app-rewired start", "build": "react-app-rewired build", "test": "react-app-rewired test", "eject": "react-app-rewired eject" },
تنظیمات ESLint مخصوص کار با decorators
فایل ویژهی eslintrc.json. که در ریشهی پروژه قرار میگیرد (این فایل بدون نام است و فقط از پسوند تشکیل شده)، برای پروژههای MobX، باید حداقل تنظیم زیر را داشته باشد تا ESLint بتواند legacyDecorators را نیز پردازش کند:
{ "extends": "react-app", "parserOptions": { "ecmaFeatures": { "legacyDecorators": true } } }
{ "env": { "node": true, "commonjs": true, "browser": true, "es6": true, "mocha": true }, "settings": { "react": { "version": "detect" } }, "parserOptions": { "ecmaFeatures": { "jsx": true, "legacyDecorators": true }, "ecmaVersion": 2018, "sourceType": "module" }, "plugins": [ "babel", "react", "react-hooks", "react-redux", "no-async-without-await", "css-modules", "filenames", "simple-import-sort" ], "rules": { "no-const-assign": "warn", "no-this-before-super": "warn", "constructor-super": "warn", "strict": [ "error", "safe" ], "no-debugger": "error", "brace-style": [ "error", "1tbs", { "allowSingleLine": true } ], "no-trailing-spaces": "error", "keyword-spacing": "error", "space-before-function-paren": [ "error", "never" ], "spaced-comment": [ "error", "always" ], "vars-on-top": "error", "no-undef": "error", "no-undefined": "warn", "comma-dangle": [ "error", "never" ], "quotes": [ "error", "double" ], "semi": [ "error", "always" ], "guard-for-in": "error", "no-eval": "error", "no-with": "error", "valid-typeof": "error", "no-unused-vars": "error", "no-continue": "warn", "no-extra-semi": "warn", "no-unreachable": "warn", "no-unused-expressions": "warn", "max-len": [ "warn", 80, 4 ], "react/prefer-es6-class": "warn", "react/jsx-boolean-value": "warn", "react-hooks/rules-of-hooks": "error", "react-hooks/exhaustive-deps": "warn", "react/prop-types": "off", "react-redux/mapDispatchToProps-returns-object": "off", "react-redux/prefer-separate-component-file": "off", "no-async-without-await/no-async-without-await": "warn", "css-modules/no-undef-class": "off", "filenames/match-regex": [ "off", "^[a-zA-Z]+\\.*\\b(typescript|module|locale|validate|test|action|api|reducer|saga)?\\b$", true ], "filenames/match-exported": "off", "filenames/no-index": "off", "simple-import-sort/sort": "error" }, "extends": [ "react-app", "eslint:recommended", "plugin:react/recommended", "plugin:react-redux/recommended", "plugin:css-modules/recommended" ], "globals": { "Atomics": "readonly", "SharedArrayBuffer": "readonly", "process": true } }
>npm i --save-dev eslint babel-eslint eslint-config-react-app eslint-loader eslint-plugin-babel eslint-plugin-react eslint-plugin-css-modules eslint-plugin-filenames eslint-plugin-flowtype eslint-plugin-import eslint-plugin-no-async-without-await eslint-plugin-react-hooks eslint-plugin-react-redux eslint-plugin-redux-saga eslint-plugin-simple-import-sort eslint-loader typescript
const { override, addDecoratorsLegacy, useEslintRc } = require("customize-cra"); module.exports = override( addDecoratorsLegacy(), useEslintRc(".eslintrc.json") );
رفع اخطار مرتبط با decorators در VSCode
تا اینجا کار تنظیم کامپایلر babel، جهت پردازش decorators انجام شد. اما خود VSCode نیز چنین اخطاری را در پروژههایی که از decorates استفاده میکنند، نمایش میدهد:
Experimental support for decorators is a feature that is subject to change in a future release. Set the 'experimentalDecorators' option in your 'tsconfig' or 'jsconfig' to remove this warning.ts(1219)
{ "compilerOptions": { "experimentalDecorators": true, "allowJs": true } }
کدهای کامل این قسمت را میتوانید از اینجا دریافت کنید: state-management-with-mobx-part3.zip
<Routes> <Route path="/product/list" element={<ProductList/>} /> <Route path="/product/new" element={<NewProduct/>} /> </Routes>
<Routes> <Route path="/post" element={<Posts/>}> <Route path="images" element={<Images />} /> <Route path="text" element={<Text />} /> <Route path="/post" element={<Text />} /> </Route> </Routes>
<div className="list-group"> <Link to="/post/images" className="list-group-item list-group-item-action active" aria-current="true"> Images Post </Link> <Link to="/post/text" className="list-group-item list-group-item-action">Text Post</Link> </div> <Outlet />
<div className="list-group"> <Link to="images" className="list-group-item list-group-item-action active" aria-current="true"> Images Post </Link> <Link to="text" className="list-group-item list-group-item-action">Text Post</Link> </div> <Outlet />
<Routes> <Route path="/post" element={<Posts/>}> <Route path="images" element={<Images />} /> <Route path="text" element={<Text />} /> <Route path="/post" element={<Text />} /> </Route> </Routes>
<Routes> <Route path="/product/list" element={<ProductList/>} /> <Route path="/product/new" element={<NewProduct/>} /> <Route path="/post" element={<Posts/>}> <Route path="images" element={<Images />} /> <Route path="text" element={<Text />} /> <Route path="/post" element={<Text />} /> </Route> <Route path="*" element={<NotFound />} /> </Routes>
آموزش ایجاد برنامه های چند زبانه در WPF
در کل تفاوت Performance در کاربردهای این چنین آنقدر نیست که موجب نگرانی باشد.
ASP.NET MVC #21
بررسی روش اجرای MS SQL Server Express درون یک Container
اگر مخازن Imageهای رسمی مایکروسافت را در داکرهاب بررسی کنیم، به مخازنی مانند mssql-server-windows-express ، mssql-server و یا mssql-server-linux نیز خواهیم رسید. در اینجا آخرین نگارش Image مربوط به SQL Server Express آن، حدود 7GB حجم دارد. برای دریافت آن ابتدا به Windows Containers سوئیچ کنید و سپس دستور زیر را صادر نمائید:
docker pull microsoft/mssql-server-windows-express
docker run -d -p 1433:1433 -e sa_password=<SA_PASSWORD> -e ACCEPT_EULA=Y microsoft/mssql-server-windows-express
- سوئیچ d سبب میشود تا پس از اجرای این دستور، بلافاصله به command prompt بازگشت داده شویم و SQL Server Express در background اجرا شود.
- سپس پورت 1433 میزبان به پورت 1433 کانتینر، نگاشت شدهاست که پورت استاندارد SQL Server است.
- سوئیچ e، امکان تنظیم متغیرهای محیطی را میسر میکند؛ برای مثال ورود کلمهی عبور کاربر SA و یا پذیرش مجوز آن. برای نمونه، این کلمهی عبور را مساوی password وارد کنید؛ هرچند کار نخواهد کرد، اما بررسی خطاهای به همراه آن مفید است.
- و در آخر نام image مرتبط ذکر شدهاست.
پس از اجرای این دستور، کانتینر SQL Server Express، در پس زمینه شروع به کار خواهد کرد و بلافاصله به خط فرمان بازگشت داده میشویم. در اینجا ممکن است آغاز SQL Server اندکی طول بکشد. برای اینکه دریابیم در این لحظه وضعیت پروسهی آن به چه صورتی است، دستور docker logs id را صادر کنید. پس از آن خطایی مانند password validation failed را مشاهده خواهیم کرد. عنوان میکند که پیچیدگی کلمهی عبور وارد شده کافی نیست.
یک نکته: زمانیکه دستور docker run را اجرا میکنیم، یک هش طولانی را نمایش میدهد و پس از آن به خط فرمان بازگشت داده میشویم. این هش طولانی، همان id کانتینر در حال اجرا است. برای مثال در دستور docker logs id میتوان 3 حرف ابتدای این هش را بجای id وارد کرد. البته این id را توسط دستور docker ps نیز میتوان بدست آورد.
بنابراین با توجه به اینکه دستور docker logs id، خطایی را گزارش کردهاست، توسط دستور docker stop id، این کانتینر را متوقف کرده و آنرا مجددا با کلمهی عبوری مانند pass!w0rd1 اجرا میکنیم:
docker run -d -p 1433:1433 -e sa_password=pass!w0rd1 -e ACCEPT_EULA=Y microsoft/mssql-server-windows-express
همانطور که در قسمت سوم نیز عنوان شد، اگر این کانتینر را بر روی ویندوز سرور، در حالت Windows Containers اجرا کنیم (و نه در حالت Hyper-V)، پروسههای اجرای شدهی داخل یک Container را میتوان با Job Object Idهای یکسانی که دارند، در Task Manager ویندوز، در کنار سایر پروسههای سیستم، شناسایی کرد.
اتصال به SQL Server Express اجرا شدهی داخل یک Container توسط SQL Server Management Studio
پس از اجرای SQL Server Express دخل کانتینر، مطابق تنظیمات آن، چه در سیستم میزبان و چه در داخل کانتینر، به پورت 1433 گوش فرا داده میشود. به همین منظور نیاز است IP این کانتینر را نیز بدست آوریم. برای اینکار دستور ipconfig را در سیستم میزبان صادر کنید تا بر اساس مشخصات کارت شبکهی مجازی آن، بتوان IP آنرا بدست آورد (دستور docker inspect id نیز چنین اطلاعاتی را به همراه دارد). اکنون میتوان از داخل سیستم راه دور دیگری که SQL Server Management Studio بر روی آن نصب است، توسط این IP و پورت، به SQL Server Express متصل شد.
البته در اینجا نیازی به ذکر پورت نیست؛ چون پورت 1433، شماره پورت پیشفرض است. بعد از اتصال، میتوان کارهای متداولی مانند ایجاد یک بانک اطلاعاتی جدید را انجام داد.
برای آزمایش، یکبار دستور docker ps را صادر کنید تا id این کانتینر مشخص شود. سپس دستور docker stop id را صادر کنید تا پروسه SQL Server Express خاتمه یابد. اکنون اگر در SQL Server Management Studio قصد کار با آنرا داشته باشیم، پیام عدم اتصال مشاهده میشود. اکنون برای اجرای مجدد کانتینر، دستور docker start id را صادر کنید.
بررسی روش اجرای MySQL داخل یک Container
برای اجرای MySQL نیاز است به Linux Containers سوئیچ کنیم. حجم tag ویژهی latest آن نیز حدود 138MB است که نسبت به SQL Server Express هفت گیگابایتی، بسیار کمتر است!
در همان صفحهی مستندات آن در داکرهاب، دستور اجرایی آن نیز ذکر شدهاست:
docker run --name some-mysql -e MYSQL_ROOT_PASSWORD=my-secret-pw -d mysql:tag
docker run --name some-mysql -e MYSQL_ROOT_PASSWORD=my-secret-pw -d mysql
یک نکته: میتوان یک command prompt جدید را باز کرد و سپس دستور docker logs -f id را در آن صادر کرد. به این صورت لاگهای لحظهای یک کانتینر را نیز میتوان مشاهده کرد (f در اینجا به معنای follow است).
اکنون میخواهیم MySQL Client موجود در همین Container در حال اجرا را، اجرا کنیم (اجرای پروسهای درون یک کانتینر در حال اجرا). برای اینکار از دستور docker exec استفاده میشود:
docker ps docker exec -it id mysql --user=root --password=my-secret-pw
در اینجا توسط دستور docker exec ابتدا یک interactive shell را درخواست کردهایم (اجرای foreground یک برنامهی شل). سپس id این کانتینر باید ذکر شود. پس از آن نام فایل اجرایی MySQL Client قید شده و در پایان، نام کاربری و کلمهی عبور اتصال به آن که در دستور docker run تنظیم شدهاند، ذکر میشوند.
با اجرای این دستور، به خط فرمان MySQL Client داخل این کانتینر دسترسی پیدا میکنیم. در اینجا میتوان دستورات مختلفی را برای کار با پروسهی mysql اجرا کرد؛ مانند اجرای دستور show databases که لیست بانکهای اطلاعاتی موجود را نمایش میدهد:
mysql> show databases; use mysql; show tables; select * from user; exit;
روش مدیریت دادههای بانکهای اطلاعاتی توسط Docker
در قسمت قبل دریافتیم که لایهی رویی یک container، دارای قابلیت read/write است و برای مثال میتوان فایلهای یک وب سایت استاتیک را در آنجا کپی و سپس هاست کرد. اما این لایه، لایهی مناسبی برای ذخیره سازی دادههای یک بانک اطلاعاتی نیست. در اینجا برای مدیریت بهتر این نوع دادهها، از مفهومی به نام volume استفاده میشود.
برای درک روش مدیریت دادهها توسط داکر، دستور docker volume ls را اجرا کنید. مشاهده خواهید کرد که docker یک volume پیشفرض را نیز ایجاد کردهاست. البته با volumes پیشتر در قسمت چهارم، در بخش «روش به اشتراک گذاری فایل سیستم میزبان با کانتینرها» نیز آشنا شدهایم. این volume پیشفرض، کار ذخیره سازی اطلاعات را حتی اگر کانتینری در حال اجرا نباشد نیز انجام میدهد. وجود یک چنین قابلیتی جهت از دست نرفتن اطلاعات ارزشمند ذخیره شدهی در بانکهای اطلاعاتی بسیار ضروری است.
البته لازم به ذکر است، این volume ای را که در اینجا مشاهده میکنید، توسط Dockerfile خود mysql به صورت خودکار ایجاد میشود. برای مثال در داکرهاب، در قسمت full description این image، در ابتدای توضیحات قسمتی است به نام supported tags and respective dockerfile links. در اینجا هر tag نامبرده شده، در حقیقت لینکی است به یک Dockerfile. اگر یکی از آنها را باز کنید، چنین سطری را در آن مشاهده خواهید کرد:
VOLUME /var/lib/mysql
اینکار نه فقط برای بالابردن کارآیی اعمال read/write انجام شدهی توسط container انجام میشود، بلکه حتی اگر این کانتینر را توسط دستور docker rm id حذف کنیم، دستور docker volume ls، هنوز همان volume ای را که در حین نصب mysql به صورت خودکار ایجاد شده بود، نمایش میدهد. علت اینجا است که طول عمر این volume، وابستهی به طول عمر کانتینر آن نیست. به این ترتیب حذف تصادفی یک کانتینر، سبب از دست رفتن اطلاعات ارزشمند داخل بانک اطلاعاتی آن نمیشود.
روش تعیین صریح یک volume برای یک کانتینر بانک اطلاعاتی، توسط volumeهای نامدار
دستور docker run ای را که برای اجرای mysql صادر کردیم، یک volume خودکار را ایجاد کردهاست و اگر آنرا با دستور docker volume ls بررسی کنیم، دارای یک نام هش مانند است که به آن anonymous volume هم گفته میشود. در ادامه قصد داریم یک volume نامدار را ایجاد کنیم و سپس از آن جهت ذخیره سازی اطلاعات چندین وهله از کانتینر mysql استفاده نمائیم.
پیش از ادامه بحث، ابتدا توسط دستور docker rm id، کانتینر mysql ای را که پیشتر ایجاد کردیم حذف کنید؛ هرچند این دستور، volume متناظر با آنرا حذف نمیکند.
سپس برای اینکه یک کانتینر جدید mysql را با ذکر صریح volume آن ایجاد و اجرا کنیم، میتوان از دستور زیر استفاده کرد:
docker run --name some-mysql -e MYSQL_ROOT_PASSWORD=my-secret-pw -d -v db:/var/lib/mysql mysql
اکنون اگر دستور docker volume ls را صادر کنیم، در لیست خروجی آن، نام db قابل مشاهدهاست.
در ادامه پروسهی MySQL Client داخل این کانتینر را اجرا کرده:
docker exec -it some-mysql mysql --user=root --password=my-secret-pw
mysql> show databases; create database pets; show databases; exit;
اکنون در ابتدا این کانتینر را متوقف کرده و سپس آنرا حذف میکنیم:
docker ps docker stop id docker rm id
در ادامه مجددا همان دستور قبلی را که توسط آن volume نامداری، ایجاد کردیم، اجرا میکنیم:
docker run --name some-mysql -e MYSQL_ROOT_PASSWORD=my-secret-pw -d -v db:/var/lib/mysql mysql
docker exec -it some-mysql mysql --user=root --password=my-secret-pw
mysql> show databases;
روش حذف volumes اضافی
با توجه به اینکه volumeها، طول عمر متفاوتی را نسبت به کانتینرها دارند، ممکن است پس از مدتی فضای دیسک سخت شما را پر کنند. برای مثال به ازای هربار اجرای دستور docker run مربوط با MYSQL با نامی متفاوت، یک volume جدید نیز ایجاد میشود.
خروجی دستور docker inspect id به همراه قسمتی است به نام mounts که خاصیت name آن، دقیقا مساوی نام volume متناظر با کانتینر بررسی شدهاست. همچنین خاصیت source آن، محل دقیق ذخیره سازی این volume را بر روی فایل سیستم میزبان مشخص میکند.
برای حذف آنها، ابتدا نیاز است کانتینرها را متوقف کرد. دستور زیر تمام کانتینرهای در حال اجرا را متوقف میکند. در اینجا دستور docker ps -q، لیست id تمام کانتینرهای در حال اجرا را باز میگرداند (در این دستورات، افزودن پارامتر q، سبب بازگشت صرفا idها میشود):
docker stop $(docker ps -q)
docker rm $(docker ps -aq)
docker rm -f $(docker ps -aq)
docker volume rm $(docker volume ls -q)
docker rm -fv id
docker volume ls -f dangling=true
docker volume rm $(docker volume ls -qf dangling=true)