آنچه که در خصوص کتابخانه های مخصوص اعتبارسنجی فرمودید آیا با ارجاع به بانک اطلاعاتی هم پیاده سازی میشوند؟ چون مفهوم اعتبارسنجی را در سطح کلاس های برنامه بلد هستم. برای سناریوی عنوان شده در پست اولیه، یک لایه اعتبارسنجی توسط FluentValidation پیاده سازی شده و null بودن کلیدها بررسی می شوند ولی وجود چنین شناسه ای به عنوان کلید اصلی در بانک اطلاعاتی چطور باید بررسی شود؟ آیا با همان FluentValidation امکانپذیر است یا اینکه در همان لایه Application باید تمام کلیدها بررسی و پیغام مناسب به کاربر برگردانده شود؟
[TestMethod] public void SearchForWatiNOnGoogle() { using (var browser = new IE("http://www.google.com")) { browser.TextField(Find.ByName("q")).TypeText("WatiN"); browser.Button(Find.ByName("btnG")).Click(); Assert.IsTrue(browser.ContainsText("WatiN")); } }
[TestMethod] public void SearchForWatiNOnGoogle() { using (var browser = new IE("http://www.google.com")) { browser.TextField(Find.ByName("q")).Value="WatiN"; browser.Button(Find.ByName("btnG")).ClickNoWait(); Thread.Sleep(3000); Assert.IsTrue(browser.ContainsText("WatiN")); } }
ASP.NET MVC #23
- این سؤال خارج از بحث است. بازگرداندن View هیچ ارتباطی به مسیریابی ندارد. فقط کافی است بنویسید:
return View("~/Views/....مسیر کامل فایل", model);
- تمام خطاهای مدیریت نشدهی برنامههای وب در لاگ ویندوز ثبت میشوند. آنها را بررسی کنید. همچنین ELMAH را هم نصب کنید تا خطاها را برای بررسی بیشتر لاگ کند.
- روشهای قدیمی را با MVC کار نکنید. صفحهی اول سایت، همان صفحهای است که در مسیریابی پیش فرض تعریف شدهاست. یعنی همان اکشن متد Index در کنترلر Home، به همراه View ایی که مد نظر شما است.
روش نگاشت محتوای یک سایت استاتیک در یک Container که وب سرور است
فرض کنید یک سایت استاتیک بوت استرپی را تهیه کردهاید و قصد دارید آنرا توسط وب سرور nginx، هاست کنید. برای اینکار، چندین گزینه پیش روی ما هستند:
گزینهی اول: دریافت image مربوط به nginx، سپس ایجاد یک container از آن و در آخر با استفاده از «روش به اشتراک گذاری فایل سیستم میزبان با کانتینرها» که در قسمت قبل بررسی کردیم، این وب سایت را آمادهی اجرا و دسترسی میکنیم.
گزینهی دوم: کپی کردن فایلهای وب سایت از سیستم میزبان، به درون فایل سیستم خود container.
گزینهی سوم: ایجاد یک image سفارشی که از ابتدا به همراه فایلهای وب سایت استاتیک ما است و در این حالت تنها کافی است این image را تبدیل به container اجرایی کنیم.
روش اول: به اشتراک گذاری فایل سیستم میزبان با کانتینر وب سرور جهت هاست آن
در قسمت قبل، یک فایل tar ایجاد شدهی در سیستم میزبان ویندوزی را با یک کانتینر لینوکسی به اشتراک گذاشتیم تا بتوانیم محتویات آنرا استخراج کنیم. در اینجا قصد داریم پوشهی وب سایت استاتیک خود را که در سیستم میزبان ویندوزی قرار دارد، با وب سرور nginx که توسط یک container در حال اجرا است، به اشتراک بگذاریم تا آنرا هاست کند.
فرض کنید وب سایت استاتیک ما در مسیر c:\users\vahid\mysite سیستم میزبان قرار دارد که داخل آن یک فایل index.html و تعدادی فایل css و js آمادهی برای هاست شدن، وجود دارند. برای هاست آن توسط nginx، از دستور زیر استفاده خواهیم کرد:
docker run --rm -it -p 8080:80 -v c:\users\vahid\mysite:/usr/share/nginx/html nginx
- سوئیچ rm سبب میشود تا پس از خاتمهی کار nginx، این container نیز حذف شود.
- از سوئیچ it استفاده شدهاست تا با فشردن ctrl+c، بتوانیم پروسهی container را خاتمه دهیم و پس از آن، برنامهی nginx دیگر در background در حال اجرا نباشد (اجرای آن در foreground).
- سپس پورت 8080 سیستم میزبان، به پورت 80 وب سرور nginx نگاشت شدهاست. چون containerها دارای network stack خاص خودشان هستند (که آنرا در قسمت سوم بررسی کردیم)، پورت 80 آنها با پورت 80 سیستم میزبان تداخل نمیکند و اگر برای مثال بر روی پورت 80 سیستم جاری، IIS در حال اجرا باشد، سبب عدم اجرا شدن وب سرور nginx به دلیل تداخل پورتها نمیشود.
- در ادامه روش volume mount را مشاهده میکنید که در قسمت قبل بررسی کردیم. مسیر c:\users\vahid\mysite سیستم میزبان، به مسیر ویژهی /usr/share/nginx/html داخل container نگاشت شدهاست. این مسیر، یک مسیر استاندارد بوده و در مستندات docker hub این وب سرور، ذکر شدهاست.
- در آخر هم نام image این وب سرور را ذکر کردهایم.
پس از اجرای این دستور، اگر nginx پیشتر دریافت نشده باشد، image آن دریافت شده، یک container بر اساس آن ساخته میشود و سپس با پارامترهایی که توضیح دادیم، اجرا خواهد شد. اکنون اگر در سیستم میزبان، مسیر http://localhost:8080 را در مرورگر باز کنید، وب سایت استاتیک خود را مشاهده خواهید کرد.
روش دوم: کپی کردن فایلهای وب سایت از سیستم میزبان، به درون فایل سیستم خود container
همانطور که در قسمت سوم نیز بررسی کردیم، فایل سیستم مربوط به هاست، به طور کامل از فایل سیستم container، جدا و ایزوله است و بدون volume mount، یک container نمیتواند به فایلهای میزبان خود دسترسی پیدا کند. بنابراین گزینهی دیگری که در اینجا وجود خواهد داشت، کپی کردن فایلهای میزبان و انتقال آنها به container میباشد؛ شبیه به کپی کردن فایلها از یک کامپیوتر موجود در شبکه به کامپیوتر دیگری در آن.
برای این منظور ابتدا nginx را در پسزمینه اجرا میکنیم:
docker run -d -p 8080:80 --name nginx nginx
پس از اجرای این دستور و بازگشت به command prompt، جهت اطمینان حاصل کردن از اجرای آن در پس زمینه، دستور docker ps را صادر میکنیم که لیست آن، حاوی گزارشی از containerهای در حال اجرا است.
اکنون توسط دستور ویژهی docker exec، میخواهیم درون یک container در حال اجرا، پروسهای را اجرا کنیم. یعنی با اینکه پروسهی nginx داخل این container در حال اجرا است، برای مثال میخواهیم یک shell را نیز داخل آن اجرا کنیم:
docker exec -it nginx bash
cd /usr/share/nginx/html
ls mv index.html index2.html exit
docker cp c:\users\vahid\mysite nginx:/usr/share/nginx/html
docker exec nginx ls /usr/share/nginx/html
روش سوم: ایجاد یک image سفارشی که از ابتدا به همراه فایلهای وب سایت استاتیک ما است
در روش دوم، موفق شدیم که فایلهای مدنظر خود را به درون container در حال اجرا کپی کنیم. اکنون میخواهیم یک snapshot را از آن تهیه کنیم؛ شبیه به کاری که با ماشینهای مجازی نیز انجام میشود و این روشی است که از آن برای ساخت یک image سفارشی استفاده میشود. برای این منظور از دستور docker commit استفاده میشود تا تصویری را از وضعیت یک container در حال اجرا، در آن لحظه تهیه کنیم:
docker commit nginx mysite:nginx
اکنون پس از اجرای این دستور، با استفاده از فرمان docker images میتوان مشاهده کرد که image جدید mysite، با tag ای معادل nginx، ایجاد شدهاست.
در ادامه برای اجرای این image جدید، میتوان از دستور زیر استفاده کرد:
docker run -d -p 8090:80 --name mysite mysite:nginx
اکنون اگر در سیستم میزبان، مسیر http://localhost:8090 را در مرورگر باز کنید، وب سایت استاتیک خود را مشاهده خواهید کرد و یا توسط دستور زیر میتوانید فایلهای موجود در پوشهی html وب سرور nginx این container جدید در حال اجرا را ملاحظه نمائید:
docker exec mysite ls /usr/share/nginx/html
نگاهی به لایههای یک Image در مقایسه با یک Container
زمانیکه خواستیم image جدید و سفارشی خاص خود را ایجاد کنیم، با image اصلی nginx شروع کردیم. اولین لایهی موجود در این image، سیستم عاملی است که میتواند آنرا اجرا کند. برفراز این لایه، لایهی خود nginx قرار گرفتهاست. اگر خواستید تاریخچهی ایجاد یک image را مشاهده کنید، از دستور docker history nginx استفاده نمائید. خروجی آن لیست دستوراتی را نمایش میدهد که برای ساخت این image مورد استفاده قرار گرفتهاند. البته دستور docker history nginx --no-trunc، اطلاعات بیشتری را با نمایش لیست کامل و خلاصه نشدهی دستورات، ارائه میدهد. این دستورات را در صفحهی docker hub هر image نیز میتوان مشاهده کرد. در قسمت full description هر image، در ابتدای توضیحات، قسمتی است به نام supported tags and respective dockerfile links. در اینجا هر tag نامبرده شده، در حقیقت لینکی است به یک فایل که دقیقا همین دستورات را لیست کردهاست. به این فایل، docker file گفته میشود که روش ساخت یک image را توضیح میدهد. هدف آن، خودکار سازی اجرای دستوراتی است که سبب ساخت یک image میشوند.
در ادامه اگر از این image، یک container را ایجاد کنیم، این container هر دو لایهی OS و Framework را به همراه خواهد داشت؛ به علاوهی لایهی دیگری به نام Container/Run که میتوان فایلهای آنرا خواند و یا در آن نوشت. بنابراین لایهای که فایلهای وب سایت استاتیک ما در آن کپی شدند، دقیقا همین لایهاست.
و زمانیکه از یک container تصویری تهیه میشود، تغییراتی را که به فایل سیستم آن ایجاد کردهایم، به صورت یک لایهی جدید بر روی لایههای قبلی آن image، ظاهر و ثبت میشود. برای اثبات این موضوع، میتوان از دستور docker diff nginx استفاده کرد. در اینجا nginx نام container ای است که میخواهیم تغییرات آنرا با image قبلی که بر پایهی آن ایجاد شدهاست، مشاهده کنیم.
تبدیل دستورات docker به یک docker file
تا اینجا یک چنین دستوراتی را برای اجرای کانتینر nginx، کپی فایلها به آن و سپس تهیهی یک تصویر از آن، اجرا کردیم:
docker run -d -p 8080:80 --name nginx nginx docker cp c:\users\vahid\mysite nginx:/usr/share/nginx/html docker commit nginx mysite:nginx
بجای سطر اول، تنها نام image ای را که میخواهیم کار را بر مبنای آن انجام دهیم، ذکر میکنیم:
FROM nginx
COPY mysite /usr/share/nginx/html
سپس برای اجرای این فایل، بجای دستور docker commit آخر، از دستور زیر استفاده میکنیم:
docker build -f Dockerfile -t mysite:nginx-df .
docker build -t mysite:nginx-df .
تگ این image را نیز متفاوت با قبلیها انتخاب کردهایم؛ nginx-df بجای مقدار قبلی.
در این حالت اگر دستور آخر را اجرا کنیم، دستور docker images گزارش اضافه شدن این image جدید را ارائه خواهد داد.
مرجع کامل ساخت Dockerfileها را در اینجا میتوانید مطالعه کنید.
ساخت یک image سفارشی برای هاست یک وب سایت استاتیک در IIS
تا اینجا از وب سرور لینوکسی nginx برای هاست وب سایت استاتیک خود استفاده کردیم. در ادامه میخواهیم از وب سرور IIS برای اینکار استفاده نمائیم. بنابراین ابتدا نیاز است یا از ویندوز سرور استفاده کنیم و یا میتوان با کلیک راست بر روی آیکن Docker در قسمت Tray Icons ویندوز، به Windows Containers سوئیچ کرد و سپس به صورت زیر عمل نمود.
اینبار محتوای Dockerfile ای که کنار پوشهی mysite قرار میگیرد، به صورت زیر خواهد بود:
FROM microsoft/iis:nanoserver COPY mysite c:/inetpub/wwwroot
در ادامه با استفاده از دستور زیر و اجرای فایل Dockerfile، این image جدید را با tag ای به نام iis ایجاد میکنیم:
docker build -t mysite:iis .
به اشتراک گذاری imageهای سفارشی در Docker Hub
برای به اشتراک گذاری imageهای سفارشی خود در Docker Hub، نیاز است tag آنها را توسط دستور docker tag مطابق فرمت ویژهی docker hub ویرایش کرد:
docker tag mysite:nginx-df my_user_name/some_name:new_tag_name
و در آخر برای انتشار آن میتوان از دستور docker push استفاده کرد:
docker push my_user_name/some_name:new_tag_name
پس از پایان کار اگر به سایت docker hub و مخازن خود مراجعه کنید، این image جدید قابل مشاهده خواهد بود.
class HandlerChain
{
setNextObj(nextObjInChain){}
processMultiple(req){
console.log("No multiple for: " + req.getMultiple());
}
}
class Multiple
{
constructor(multiple){
this.multiple = multiple;
}
getMultiple(){
return this.multiple;
}
}
class MultipleofTwoHandler extends HandlerChain
{
constructor(){
super()
this.nextObjInChain = new HandlerChain()
}
setNextObj(nextObj){
this.nextObjInChain = nextObj;
}
processMultiple(req) {
if ((req.getMultiple() % 2) == 0) {
console.log("Multiple of 2: " + req.getMultiple());
}else{
this.nextObjInChain.processMultiple(req);
}
}
}
class MultipleofThreeHandler extends HandlerChain
{
constructor(){
super()
this.nextObjInChain = new HandlerChain()
}
setNextObj(nextObj){
this.nextObjInChain = nextObj;
}
processMultiple(req)
{
if ((req.getMultiple() % 3) == 0) {
console.log("Multiple of 3: " + req.getMultiple());
}else{
this.nextObjInChain.processMultiple(req);
}
}
}
class MultipleofFiveHandler extends HandlerChain
{
constructor(){
super()
this.nextObjInChain = new HandlerChain()
}
setNextObj(nextObj){
this.nextObjInChain = nextObj;
}
processMultiple(req) {
if ((req.getMultiple() % 5) == 0) {
console.log("Multiple of 5: " + req.getMultiple());
}else{
this.nextObjInChain.processMultiple(req);
}
}
}
//configuring the chain of handler objects
var c1 = new MultipleofTwoHandler();
var c2 = new MultipleofThreeHandler();
var c3 = new MultipleofFiveHandler();
c1.setNextObj(c2);
c2.setNextObj(c3);
//the chain handling different cases
c1.processMultiple(new Multiple(95)); // Multiple of 5: 95
c1.processMultiple(new Multiple(50)); // Multiple of 2: 50
c1.processMultiple(new Multiple(9)); // Multiple of 3: 9
c1.processMultiple(new Multiple(4)); // Multiple of 2: 4
c1.processMultiple(new Multiple(21)); // Multiple of 3: 21
c1.processMultiple(new Multiple(23)); // No multiple for: 23
- MultipleofTwoHandler: بررسی میکند که آیا عدد وارد شده، مضربی از 2 است یا نه.
- MultipleofThreeHandler: بررسی میکند که آیا عدد وارد شده، مضربی از 3 است یا نه
- MultipleofFiveHandler: بررسی میکند که آیا عدد وارد شده، مضربی از 5 است یا نه.
class HandlerChain { setNextObj(nextObjInChain){} processMultiple(req){ console.log("No multiple for: " + req.getMultiple()); } }
- تنظیم کردن handler بعدی در زنجیره
- پردازش عدد وارد شده به این منظور که آیا در مضرب مورد نظر قرار دارد یا نه.
class MultipleofTwoHandler extends HandlerChain { constructor(){/*code*/} setNextObj(nextObj){/*code*/} processMultiple(req){/*code*/} } class MultipleofThreeHandler extends HandlerChain { constructor(){/*code*/} setNextObj(nextObj){/*code*/} processMultiple(req){/*code*/} } class MultipleofFiveHandler extends HandlerChain { constructor(){/*code*/} setNextObj(nextObj){/*code*/} processMultiple(req){/*code*/} }
constructor(){ super() this.nextObjInChain = new HandlerChain() }
مقدار دهی اولیه میشود که تعیین کنندهی شیء بعدی در زنجیره میباشد.
setNextObj(nextObj){ this.nextObjInChain = nextObj; }
var c1 = new MultipleofTwoHandler(); var c2 = new MultipleofThreeHandler(); var c3 = new MultipleofFiveHandler(); c1.setNextObj(c2); c2.setNextObj(c3);
class Multiple { constructor(multiple){ this.multiple = multiple } getMultiple(){ return this.multiple; } }
processMultiple(req) { if ((req.getMultiple() % 2) == 0) { console.log("Multiple of 2: " + req.getMultiple()); }else{ this.nextObjInChain.processMultiple(req); } }
c1.processMultiple(new Multiple(95)) // Multiple of 5: 95
تفاوت بین متدهای app.Use و app.Run در چیست؟
Middlewareها به همان ترتیبی که در متد Configure کلاس آغازین برنامه معرفی میشوند، اجرا خواهند شد؛ اما نکتهی مهم اینجا است که middleware ایی که توسط متد app.Use تعریف میشود، میتواند middleware بعدی ثبت شدهرا، فراخوانی کند؛ اما app.Run خیر. برای درک بهتر این مفهوم، به مثال زیر دقت کنید:
using Microsoft.AspNetCore.Http; public class Startup { public void Configure(IApplicationBuilder app) { app.Use(async (context, next) => { await context.Response.WriteAsync("<div>from middleware-1, inside app.Use, before next()</div>"); await next(); await context.Response.WriteAsync("<div>from middleware-1, inside app.Use, after next()</div>"); }); app.Run(async context => { await context.Response.WriteAsync("<div>Inside middleware-2 defined using app.Run</div>"); }); app.Use(async (context, next) => { await context.Response.WriteAsync("<div>from middleware-3, inside app.Use, before next()</div>"); await next(); await context.Response.WriteAsync("<div>from middleware-3, inside app.Use, after next()</div>"); });
همانطور که در تصویر نیز مشخص است، ابتدا کدهای پیش از فراخوانی دلیگیت next میانافزار اول اجرا شدهاست. سپس باتوجه به فراخوانی دلیگیت next، کدهای دومین میانافزار ثبت شده، فراخوانی گردیدهاست و سپس کدهای پس از فراخوانی دلیگیت next میانافزار اول، اجرا شدهاند.
این دلیگیت در اصل یک چنین امضایی را دارد:
public delegate Task RequestDelegate(HttpContext context);
باید دقت داشت که فراخوانی دلیگیت next در میانافزارهای از نوع app.Use الزامی نبوده و اگر اینکار انجام نشود، بین app.Run و app.Use تفاوتی نخواهد بود و هر دو terminal به حساب میآیند.
تفاوت بین متدهایapp.Map و app.MapWhen در چیست؟
متد app.Map در صورت برآورده شدن شرطی، سبب اجرای میانافزاری مشخص میشود (امکان اجرای غیر خطی میانافزارها).
فرض کنید قطعه کد زیر را پس از اولین app.Use مثال فوق قرار دادهایم:
app.Map("/dnt", appBuilder => { appBuilder.Run(async context => { await context.Response.WriteAsync(@"<div>Inside Map(/dnt) --> Run</div>"); }); });
در اینجا چون app.Run داخلی فراخوانی شده، از نوع terminal است، دیگر میان افزارهای پس از آن اجرا نشدهاند. بدیهی است در اینجا نیز میتوان به هر تعدادی که نیاز است میان افزارهای جدیدی را به appBuilder متد app.Map اضافه کرد.
پارامتر اول متد Map برای تطابق با الگوهایی خاص و مشخص، مناسب است. اما در اگر در اینجا نیاز به اطلاعات بیشتری از HttpContext جاری داشته باشیم، میتوانیم از متد app.MapWhen استفاده کنیم که اولین پارامتر آن یک دلیگیت است که HttpContext را در اختیار استفاده کننده قرار میدهد و اگر در نهایت true را دریافت کند، سبب اجرای میان افزارهای قسمت appBuilder آن خواهد شد:
app.MapWhen(context => { return context.Request.Query.ContainsKey("dnt"); }, appBuilder => { appBuilder.Run(async context => { await context.Response.WriteAsync(@"<div>Inside MapWhen(?dnt) --> Run</div>"); }); });
http://localhost:7742/?dnt=true
نظم بخشیدن به تعاریف میانافزارها
متدهای app.Run و app.Use و امثال آنها برای تعریف سریع میان افزارها مناسب هستند. اما اگر بخواهیم کدهای کلاس آغازین برنامه را اندکی خلوت کرده و به تعاریف میانافزارها نظم ببخشیم، میتوان کدهای آنها را به کلاسهایی با امضایی خاص منتقل کرد:
using System.Threading.Tasks; using Microsoft.AspNetCore.Http; namespace Core1RtmEmptyTest.StartupCustomizations { public class MyMiddleware1 { private readonly RequestDelegate _next; public MyMiddleware1(RequestDelegate next) { _next = next; } public async Task Invoke(HttpContext context) { context.Response.ContentType = "text/html"; context.Response.StatusCode = 200; await context.Response.WriteAsync("<div>Hello from MyMiddleware1.</div>"); await _next.Invoke(context); await context.Response.WriteAsync("<div>End of action.</div>"); } } }
در اینجا نیز اگر دلیگیت next_ فراخوانی نشود، این میانافزار سبب خاتمهی اجرای پردازش درخواست جاری میگردد.
مرحلهی بعد، روش معرفی این میانافزار تعریف شده، به لیست میانافزارهای موجود است. برای این منظور میتوان متد app.UseMiddleware را به صورت مستقیم در کلاس آغازین برنامه فراخوانی کرد و یا مرسوم است اگر کتابخانهای را طراحی کردهاید، به نحو ذیل متد الحاقی خاصی را برای آن تدارک دید:
using Microsoft.AspNetCore.Builder; public static class MyMiddlewareExtensions { public static IApplicationBuilder UseMyMiddleware(this IApplicationBuilder app) { app.UseMiddleware<MyMiddleware1>(); return app; } }
public void Configure(IApplicationBuilder app) { app.UseMyMiddleware();
ارزیابی و تفسیر مدل در داده کاوی
دانشی که در مرحله یادگیری مدل تولید میشود، میبایست در مرحله ارزیابی مورد تحلیل قرار گیرد تا بتوان ارزش آن را تعیین نمود و در پی آن کارائی الگوریتم یادگیرنده مدل را نیز مشخص کرد. این معیارها را میتوان هم برای مجموعه دادههای آموزشی در مرحله یادگیری و هم برای مجموعه رکوردهای آزمایشی در مرحله ارزیابی محاسبه نمود. همچنین لازمه موفقیت در بهره مندی از علم داده کاوی تفسیر دانش تولید و ارزیابی شده است.
ارزیابی در الگوریتمهای دسته بندی
برای سادگی معیارهای ارزیابی الگوریتمهای دسته بندی، آنها را برای یک مسئله با دو دسته ارائه خواهیم نمود. در ابتدا با مفهوم ماتریس درهم ریختگی (Classification Matrix) آشنا میشویم. این ماتریس چگونگی عملکرد الگوریتم دسته بندی را با توجه به مجموعه داده ورودی به تفکیک انواع دستههای مساله دسته بندی، نمایش میدهد.
هر یک از عناصر ماتریس به شرح ذیل میباشد:
TN: بیانگر تعداد رکوردهایی است که دسته واقعی آنها منفی بوده و الگوریتم دسته بندی نیز دسته آنها را بدرستی منفی تشخیص داده است.
TP: بیانگر تعداد رکوردهایی است که دسته واقعی آنها مثبت بوده و الگوریتم دسته بندی نیز دسته آنها را بدرستی مثبت تشخیص داده است.
FP: بیانگر تعداد رکوردهایی است که دسته واقعی آنها منفی بوده و الگوریتم دسته بندی دسته آنها را به اشتباه مثبت تشخیص داده است.
FN: بیانگر تعداد رکوردهایی است که دسته واقعی آنها مثبت بوده و الگوریتم دسته بندی دسته آنها را به اشتباه منفی تشخیص داده است.
مهمترین معیار برای تعین کارایی یک الگوریتم دسته بندی دقت یا نرخ دسته بندی (Classification Accuracy - Rate) است که این معیار دقت کل یک دسته بند را محاسبه میکند. در واقع این معیار مشهورترین و عمومیترین معیار محاسبه کارایی الگوریتمهای دسته بندی است که نشان میدهد، دسته بند طراحی شده چند درصد از کل مجموعه رکوردهای آزمایشی را بدرستی دسته بندی کرده است.
دقت دسته بندی با استفاده از رابطه I بدست میآید که بیان میکند دو مقدار TP و TN مهمترین مقادیری هستند که در یک مسئله دودسته ای باید بیشینه شوند. (در مسائل چند دسته ای مقادیر قرار گرفته روی قطر اصلی این ماتریس - که در صورت کسر محاسبه CA قرار میگیرند - باید بیشینه باشند.)
معیار خطای دسته بندی (Error Rate) دقیقاً برعکس معیار دقت دسته بندی است که با استفاده از رابطه II بدست میآید. کمترین مقدار آن برابر صفر است زمانی که بهترین کارایی را داریم و بطور مشابه بیشترین مقدار آن برابر یک است زمانی که کمترین کارائی را داریم.
ذکر این نکته ضروری است که در مسائل واقعی، معیار دقت دسته بندی به هیچ عنوان معیار مناسبی برای ارزیابی کارایی الگوریتمهای دسته بندی نمیباشد، به این دلیل که در رابطه دقت دسته بندی، ارزش رکوردهای دستههای مختلف یکسان در نظر گرفته میشوند. بنابراین در مسائلی که با دستههای نامتعادل سروکار داریم، به بیان دیگر در مسائلی که ارزش دسته ای در مقایسه با دسته دیگر متفاوت است، از معیارهای دیگری استفاده میشود.
همچنین در مسائل واقعی معیارهای دیگری نظیر DR و FAR که به ترتیب از روابط III و IV بدست میآیند، اهمیت ویژه ای دارند. این معیارها که توجه بیشتری به دسته بند مثبت نشان میدهند، توانایی دسته بند را در تشخیص دسته مثبت و بطور مشابه تاوان این توانایی تشخیص را تبیین میکنند. معیار DR نشان میدهد که دقت تشخیص دسته مثبت چه مقدار است و معیار FAR نرخ هشدار غلط را با توجه به دسته منفی بیان میکند.
معیار مهم دیگری که برای تعیین میزان کارایی یک دسته بند استفاده میشود معیار (AUC (Area Under Curve است.
AUC نشان دهنده سطح زیر نمودار (ROC (Receiver Operating Characteristic میباشد که هر چه مقدار این عدد مربوط به یک دسته بند بزرگتر باشد کارایی نهایی دسته بند مطلوبتر ارزیابی میشود. نمودار ROC روشی برای بررسی کارایی دسته بندها میباشد. در واقع منحنیهای ROC منحنیهای دو بعدی هستند که در آنها DR یا همان نرخ تشخیص صحیح دسته مثبت (True Positive Rate - TPR) روی محور Y و بطور مشابه FAR یا همان نرخ تشخیص غلط دسته منفی (False Positive Rate - FPR) روی محور X رسم میشوند. به بیان دیگر یک منحنی ROC مصالحه نسبی میان سودها و هزینهها را نشان میدهد.
بسیاری از دسته بندها همانند روشهای مبتنی بر درخت تصمیم و یا روشهای مبتنی بر قانون، به گونه ای طراحی شده اند که تنها یک خروجی دودویی (مبنی بر تعلق ورودی به یکی از دو دسته ممکن) تولید میکنند. به این نوع دسته بندها که تنها یک خروجی مشخص برای هر ورودی تولید میکنند، دسته بندهای گسسته گفته میشود که این دسته بندها تنها یک نقطه در فضای ROC تولید میکنند.
بطور مشابه دسته بندهای دیگری نظیر دسته بندهای مبتنی بر روش بیز و یا شبکههای عصبی نیز وجود دارند که یک احتمال و یا امتیاز برای هر ورودی تولید میکنند، که این عدد بیانگر درجه تعلق ورودی به یکی از دو دسته موجود میباشد. این دسته بندها پیوسته نامیده میشوند و بدلیل خروجی خاص این دسته بندها یک آستانه جهت تعیین خروجی نهایی در نظر گرفته میشود.
یک منحنی ROC اجازه مقایسه تصویری مجموعه ای از دسته بندی کنندهها را میدهد، همچنین نقاط متعددی در فضای ROC قابل توجه است. نقطه پایین سمت چپ (0,0) استراتژی را نشان میدهد که در یک دسته بند مثبت تولید نمیشود. استراتژی مخالف، که بدون شرط دسته بندهای مثبت تولید میکند، با نقطه بالا سمت راست (1,1) مشخص میشود. نقطه (0,1) دسته بندی کامل و بی عیب را نمایش میدهد. بطور کلی یک نقطه در فضای ROC بهتر از دیگری است اگر در شمال غربیتر این فضا قرار گرفته باشد. همچنین در نظر داشته باشید منحنیهای ROC رفتار یک دسته بندی کننده را بدون توجه به توزیع دستهها یا هزینه خطا نشان میدهند، بنابراین کارایی دسته بندی را از این عوامل جدا میکنند. فقط زمانی که یک دسته بند در کل فضای کارایی به وضوح بر دسته دیگری تسلط یابد، میتوان گفت که بهتر از دیگری است. به همین دلیل معیار AUC که سطح زیر نمودار ROC را نشان میدهد میتواند نقش تعیین کننده ای در معرفی دسته بند برتر ایفا کند. برای درک بهتر نمودار ROC زیر را مشاهده کنید.
مقدار AUC برای یک دسته بند که بطور تصادفی، دسته نمونه مورد بررسی را تعیین میکند برابر 0.5 است. همچنین بیشترین مقدار این معیار برابر یک بوده و برای وضعیتی رخ میدهد که دسته بند ایده آل بوده و بتواند کلیه نمونههای مثبت را بدون هرگونه هشدار غلطی تشخیص دهد. معیار AUC برخلاف دیگر معیارهای تعیین کارایی دسته بندها مستقل از آستانه تصمیم گیری دسته بند میباشد. بنابراین این معیار نشان دهنده میزان قابل اعتماد بودن خروجی یک دسته بند مشخص به ازای مجموعه دادههای متفاوت است که این مفهوم توسط سایر معیارهای ارزیابی کارایی دسته بندها قابل محاسبه نمیباشد. در برخی از مواقع سطح زیر منحنیهای ROC مربوط به دو دسته بند با یکدیگر برابر است ولی ارزش آنها برای کاربردهای مختلف یکسان نیست که باید در نظر داشت در این گونه مسائل که ارزش دستهها با یکدیگر برابر نیست، استفاده از معیار AUC مطلوب نمیباشد. به همین دلیل در این گونه مسائل استفاده از معیار دیگری به جزء هزینه (Cost Matrix) منطقی به نظر نمیرسد. در انتها باید توجه نمود در کنار معیارهای بررسی شده که همگی به نوعی دقت دسته بند را محاسبه میکردند، در دسته بندهای قابل تفسیر نظیر دسته بندهای مبتنی بر قانون و یا درخت تصمیم، پیچیدگی نهایی و قابل تفسیر بودن مدل یاد گرفته شده نیز از اهمیت بالایی برخوردار است.
از روشهای ارزیابی الگوریتمهای دسته بندی (که در این الگوریتم روال کاری بدین صورت است که مدل دسته بندی توسط مجموعه داده آموزشی ساخته شده و بوسیله مجموعه داده آزمایشی مورد ارزیابی قرار میگیرد.) میتوان به روش Holdout اشاره کرد که در این روش چگونگی نسبت تقسیم مجموعه دادهها (به دو مجموعه داده آموزشی و مجموعه داده آزمایشی) بستگی به تشخیص تحلیگر دارد که معمولاً دو سوم برای آموزش و یک سوم برای ارزیابی در نظر گرفته میشود. مهمترین مزیت این روش سادگی و سرعت بالای عملیات ارزیابی است ولیکن روش Holdout معایب زیادی دارد از جمله اینکه مجموعه دادههای آموزشی و آزمایشی به یکدیگر وابسته خواهند شد، در واقع بخشی از مجموعه داده اولیه که برای آزمایش جدا میشود، شانسی برای حضور یافتن در مرحله آموزش ندارد و بطور مشابه در صورت انتخاب یک رکورد برای آموزش دیگر شانسی برای استفاده از این رکورد برای ارزیابی مدل ساخته شده وجود نخواهد داشت. همچنین مدل ساخته شده بستگی فراوانی به چگونگی تقسیم مجموعه داده اولیه به مجموعه دادههای آموزشی و آزمایشی دارد. چنانچه روش Holdout را چندین بار اجرا کنیم و از نتایج حاصل میانگین گیری کنیم از روشی موسوم به Random Sub-sampling استفاده نموده ایم. که مهمترین عیب این روش نیز عدم کنترل بر روی تعداد دفعاتی که یک رکورد به عنوان نمونه آموزشی و یا نمونه آزمایشی مورد استفاده قرار میگیرد، است. به بیان دیگر در این روش ممکن است برخی رکوردها بیش از سایرین برای یادگیری و یا ارزیابی مورد استفاده قرار گیرند.
چنانچه در روش Random Sub-sampling به شکل هوشمندانهتری عمل کنیم به صورتی که هر کدام از رکوردها به تعداد مساوی برای یادگیری و تنها یکبار برای ارزیابی استفاده شوند، روش مزبور در متون علمی با نام Cross Validation شناخته میشود.
همچنین در روش جامع k-Fold Cross Validation کل مجموعه دادهها به k قسمت مساوی تقسیم میشوند. از k-1 قسمت به عنوان مجموعه دادههای آموزشی استفاده میشود و براساس آن مدل ساخته میشود و با یک قسمت باقی مانده عملیات ارزیابی انجام میشود. فرآیند مزبور به تعداد k مرتبه تکرار خواهد شد، به گونه ای که از هر کدام از k قسمت تنها یکبار برای ارزیابی استفاده شده و در هر مرتبه یک دقت برای مدل ساخته شده، محاسبه میشود. در این روش ارزیابی دقت نهایی دسته بند برابر با میانگین k دقت محاسبه شده خواهد بود. معمولترین مقداری که در متون علمی برای k در نظر گرفته میشود برابر با 10 میباشد. بدیهی است هر چه مقدار k بزرگتر شود، دقت محاسبه شده برای دسته بند قابل اعتمادتر بوده و دانش حاصل شده جامعتر خواهد بود و البته افزایش زمان ارزیابی دسته بند نیز مهمترین مشکل آن میباشد. حداکثر مقدار k برابر با تعداد رکوردهای مجموعه داده اولیه است که این روش ارزیابی با نام Leaving One Out شناخته میشود.
در روش هایی که تاکنون به آن اشاره شده، فرض بر آن است که عملیات انتخاب نمونههای آموزشی بدون جایگذاری صورت میگیرد. به بیان دیگر یک رکورد تنها یکبار در یک فرآیند آموزشی مورد توجه واقع میشود. چنانچه هر رکورد در صورت انتخاب شدن برای شرکت در عملیات یادگیری مدل بتواند مجدداً برای یادگیری مورد استفاده قرار گیرد روش مزبور با نام Bootstrap و یا 0.632 Bootstrap شناخته میشود. (از آنجا که هر Bootstrap معادل 0.632 مجموعه داده اولیه است)
ارزیابی در الگوریتمهای خوشه بندی
به منظور ارزیابی الگوریتمهای خوشه بندی میتوان آنها به دو دسته تقسیم نمود:
شاخصهای ارزیابی بدون ناظر، که گاهی در متون علمی با نام معیارهای داخلی شناخته میشوند، به آن دسته از معیارهایی گفته میشود که تعیین کیفیت عملیات خوشه بندی را با توجه به اطلاعات موجود در مجموعه داده بر عهده دارند. در مقابل، معیارهای ارزیابی با ناظر با نام معیارهای خارجی نیز شناخته میشوند، که با استفاده از اطلاعاتی خارج از حیطه مجموعه دادههای مورد بررسی، عملکرد الگوریتمهای خوشه بندی را مورد ارزیابی قرار میدهند.
از آنجا که مهمترین وظیفه یک الگوریتم خوشه بندی آن است که بتواند به بهترین شکل ممکن فاصله درون خوشه ای را کمینه و فاصله بین خوشه ای را بیشینه نماید، کلیه معیارهای ارزیابی بدون ناظر سعی در سنجش کیفیت عملیات خوشه بندی با توجه به دو فاکتور تراکم خوشه ای و جدائی خوشه ای دارند. برآورده شدن هدف کمینه سازی درون خوشه ای و بیشینه سازی میان خوشه ای به ترتیب در گرو بیشینه نمودن تراکم هر خوشه و نیز بیشینه سازی جدایی میان خوشهها میباشد. طیف وسیعی از معیارهای ارزیابی بدون ناظر وجود دارد که همگی در ابتدا تعریفی برای فاکتورهای تراکم و جدائی ارائه میدهند سپس توسط تابع (F(Cohesion, Separation مرتبط با خود، به ترکیب این دو فاکتور میپردازند. ذکر این نکته ضروری است که نمیتوان هیچ کدام از معیارهای ارزیابی خوشه بندی را برای تمامی کاربردها مناسب دانست.
ارزیابی با ناظر الگوریتمهای خوشه بندی، با هدف آزمایش و مقایسه عملکرد روشهای خوشه بندی با توجه به حقایق مربوط به رکوردها صورت میپذیرد. به بیان دیگر هنگامی که اطلاعاتی از برچسب رکوردهای مجموعه داده مورد بررسی در اختیار داشته باشیم، میتوانیم از آنها در عملیات ارزیابی عملکرد الگوریتمهای خوشه بندی بهره بریم. لازم است در نظر داشته باشید در این بخش از برچسب رکوردها تنها در مرحله ارزیابی استفاده میشود و هر گونه بهره برداری از این برچسبها در مرحله یادگیری مدل، منجر به تبدیل شدن روش کاوش داده از خوشه بندی به دسته بندی خواهد شد. مشابه با روشهای بدون ناظر طیف وسیعی از معیارهای ارزیابی با ناظر نیز وجود دارد که در این قسمت با استفاده از روابط زیر به محاسبه معیارهای Rand Index و Jaccard می پردازیم به ترتیب در رابطه I و II نحوه محاسبه آنها نمایش داده شده است:
Rand Index را میتوان به عنوان تعداد تصمیمات درست در خوشه بندی در نظر گرفت.
TP: به تعداد زوج داده هایی گفته میشود که باید در یک خوشه قرار میگرفتند، و قرار گرفته اند.
TN: به تعداد زوج داده هایی گفته میشود که باید در خوشههای جداگانه قرار داده میشدند و به درستی در خوشههای جداگانه جای داده شده اند.
FN: به تعداد زوج داده هایی گفته میشود که باید در یک خوشه قرار میگرفتند ولی در خوشههای جداگانه قرار داده شده اند.
FP: به تعداد زوج داده هایی اشاره دارد که باید در خوشههای متفاوت قرار میگرفتند ولی در یک خوشه قرار گرفته اند.
ارزیابی در الگوریتمهای کشف قوانین انجمنی
به منظور ارزیابی الگوریتمهای کشف قوانین انجمنی از آنجایی که این الگوریتمها پتانسیل این را دارند که الگوها و قوانین زیادی تولید نمایند، جهت ارزیابی این قوانین به عواملی همچون شخص استفاده کننده از قوانین و نیز حوزه ای که مجموعه داده مورد بررسی به آن تعلق دارد، وابستگی زیادی پیدا میکنیم و بدین ترتیب کار پیدا کردن قوانین جذاب، به آسانی میسر نیست. فرض کنید قانونی با نام R داریم که به شکل A=>B میباشد، که در آن A و B زیر مجموعه ای از اشیاء میباشند.
پیشتر به معرفی دو معیار Support و Confidence پرداختیم. میدانیم از نسبت تعداد تراکنش هایی که در آن اشیاء A و B هر دو حضور دارند، به کل تعداد رکوردها Support بدست میآید که دارای مقداری عددی بین صفر و یک میباشد و هر چه این میزان بیشتر باشد، نشان میدهد که این دو شیء بیشتر با هم در ارتباط هستند. کاربر میتواند با مشخص کردن یک آستانه برای این معیار، تنها قوانینی را بدست آورد که Support آنها بیشتر از مقدار آستانه باشد، بدین ترتیب میتوان با کاهش فضای جستجو، زمان لازم جهت پیدا کردن قوانین انجمنی را کمینه کرد. البته باید به ضعف این روش نیز توجه داشت که ممکن است قوانین با ارزشی را بدین ترتیب از دست دهیم. در واقع استفاده از این معیار به تنهایی کافی نیست. معیار Confidence نیز مقداری عددی بین صفر و یک میباشد، که هر چه این عدد بزرگتر باشد بر کیفیت قانون افزوده خواهد شد. استفاده از این معیار به همراه Support مکمل مناسبی برای ارزیابی قوانین انجمنی خواهد بود. ولی مشکلی که همچنان وجود دارد این است که امکان دارد قانونی با Confidence بالا وجود داشته باشد ولی از نظر ما ارزشمند نباشد.
از معیارهای دیگر قوانین انجمنی میتوان به معیار Lift که با نامهای Intersect Factor یا Interestingness نیز شناخته میشود اشاره کرد، که این معیار میزان استقلال میان اشیاء A و B را نشان میدهد که میتواند مقدار عددی بین صفر تا بی نهایت باشد. در واقع Lift میزان هم اتفاقی بین ویژگیها را در نظر میگیرد و میزان رخداد تکی بخش تالی قانون (یعنی شیء B) را در محاسبات خود وارد میکند. (بر خلاف معیار Confidence)
مقادیر نزدیک به عدد یک معرف این هستند که A و B مستقل از یکدیگر میباشند، بدین ترتیب نشان دهنده قانون جذابی نمیباشند. چنانچه این معیار از عدد یک کمتر باشد، نشان دهنده این است که A و B با یکدیگر رابطه منفی دارند. هر چه مقدار این معیار بیشتر از عدد یک باشد، نشان دهنده این است که A اطلاعات بیشتری درباره B فراهم میکند که در این حالت جذابیت قانون A=>B بالاتر ارزیابی میشود. در ضمن این معیار نسبت به سمت چپ و راست قانون متقارن است در واقع اگر سمت چپ و راست قانون را با یکدیگر جابجا کنیم، مقدار این معیار تغییری نمیکند. از آنجائی که این معیار نمیتواند به تنهایی برای ارزیابی مورد استفاده قرار گیرد، و حتماً باید در کنار معیارهای دیگر باشد، باید مقادیر آن بین بازه صفر و یک نرمال شود. ترکیب این معیار به همراه Support و Confidence جزو بهترین روشهای کاوش قوانین انجمنی است. مشکل این معیار حساس بودن به تعداد نمونههای مجموعه داده، به ویژه برای مجموعه تراکنشهای کوچک میباشد. از این رو معیارهای دیگری برای جبران این نقص معرفی شده اند.
معیار Conviction برخی ضعفهای معیارهای Confidence و Lift را جبران مینماید. محدوده قابل تعریف برای این معیار در حوزه 0.5 تا بی نهایت قرار میگیرد که هر چه این مقدار بیشتر باشد، نشان دهنده این است که آن قانون جذابتر میباشد. بر خلاف Lift این معیار متقارن نمیباشد و مقدار این معیار برای دلالتهای منطقی یعنی در جایی که Confidence قانون یک میباشد برابر با بی نهایت است و چنانچه A و B مستقل از هم باشند، مقدار این معیار برابر با عدد یک خواهد بود.
معیار Leverage که در برخی متون با نام Novelty (جدید بودن) نیز شناخته میشود، دارای مقداری بین 0.25- و 0.25+ میباشد. ایده مستتر در این معیار آن است که اختلاف بین میزان هم اتفاقی سمت چپ و راست قانون با آن مقداری که مورد انتظار است به چه اندازه میباشد.
معیار Jaccard که دارای مقداری عددی بین صفر و یک است، علاوه بر اینکه نشان دهنده وجود نداشتن استقلال آماری میان A و B میباشد، درجه همپوشانی میان نمونههای پوشش داده شده توسط هر کدام از آنها را نیز اندازه گیری میکند. به بیان دیگر این معیار فاصله بین سمت چپ و راست قانون را بوسیله تقسیم تعداد نمونه هایی که توسط هر دو قسمت پوشش داده شده اند بر نمونه هایی که توسط یکی از آنها پوشش داده شده است، محاسبه میکند. مقادیر بالای این معیار نشان دهنده این است که A و B تمایل دارند، نمونههای مشابهی را پوشش دهند. لازم است به این نکته اشاره شود از این معیار برای فهمیدن میزان همبستگی میان متغیرها استفاده میشود که از آن میتوان برای یافتن قوانینی که دارای همبستگی بالا ولی Support کم هستند، استفاده نمود. برای نمونه در مجموعه داده سبد خرید، قوانین نادری که Support کمی دارند ولی همبستگی بالایی دارند، توسط این معیار میتوانند کشف شوند.
معیار (Coefficient (φ نیز به منظور اندازه گیری رابطه میان A و B مورد استفاده قرار میگیرد که محدوده این معیار بین 1- و 1+ میباشد.
از دیگر معیارهای ارزیابی کیفیت قوانین انجمنی، طول قوانین بدست آمده میباشد. به بیان دیگر با ثابت در نظر گرفتن معیارهای دیگر نظیر Support، Confidence و Lift قانونی برتر است که طول آن کوتاهتر باشد، بدلیل فهم آسانتر آن.
در نهایت با استفاده از ماتریس وابستگی (Dependency Matrix)، میتوان اقدام به تعریف معیارهای متنوع ارزیابی روشهای تولید قوانین انجمنی پرداخت. در عمل معیارهای متعددی برای ارزیابی مجموعه قوانین بدست آمده وجود دارد و لازم است با توجه به تجارب گذشته در مورد میزان مطلوب بودن آنها تصمیم گیری شود. بدین ترتیب که ابتدا معیارهای برتر در مسئله مورد کاوش پس از مشورت با خبرگان حوزه شناسائی شوند، پس از آن قوانین انجمنی بدست آمده از حوزه کاوش، مورد ارزیابی قرار گیرند.
کلاسهای CSS اعتبارسنجی در Angular
زمانیکه Angular فرمی را تحت نظر قرار میدهد، کلاسهای CSS خاصی را نیز بر اساس حالات عناصر مختلف آن، به آنها متصل خواهد کرد. بر این اساس میتوان ظاهر این المانها را سفارشی سازی نمود. این کلاسها به شرح زیر هستند:
کلاس CSS اعتبارسنجی | توضیحات |
ng-untouched | زمانیکه فرمی برای بار اول رندر میشود، تمام فیلدهای آن با کلاس CSS ایی به نام ng-untouched علامتگذاری میشوند. |
ng-touched | همینقدر که کاربر با یک Tab از فیلدی عبور کند، با کلاس ng-touched مزین خواهد شد. بنابراین مهم نیست که حتما دادهای وارد شده باشد یا خیر. حتی عبور از یک فیلد نیز به معنای لمس آن نیز میباشد. |
ng-pristine | مربوط به زمانیاست که یک فیلد نه تغییر کردهاست و نه لمس شدهاست. |
ng-dirty | همینقدر که کاربر، تغییری را در فیلدی ایجاد کند، آن المان با کلاس ng-dirty مشخص خواهد شد. |
ng-valid | برای حالت موفقیت آمیز بودن اعتبارسنجی، به آن المان انتساب داده میشود. |
ng-invalid | برای حالت غیر موفقیت آمیز بودن اعتبارسنجی، به آن المان انتساب داده میشود. |
برای اینکه بتوانیم این موارد را در عمل مشاهده کنیم، به ابتدای فرم مثال این سری، تغییرات ذیل را اعمال خواهیم کرد:
<div class="form-group"> <label>First Name</label> <input #firstName required type="text" class="form-control" name="firstName" [(ngModel)]="model.firstName"> </div> <h3>Classes</h3> <h4>{{ firstName.className }}</h4>
تصویر فوق کلاسهایی را نمایش میدهد که در اولین بار نمایش فرم، به المان firstName متصل شدهاند. برای مثال در این حالت کلاس ng-pristine قابل مشاهدهاست و هنوز تغییری در آن حاصل نشدهاست.
در ادامه اگر حرفی را به آن اضافه کنیم:
هنوز هم ng-untouched آن برقرار است؛ اما ng-pristine آن به ng-dirty تبدیل شدهاست. در اینجا حتی اگر کل اطلاعات فیلد را نیز حذف کنیم و آنرا خالی کنیم یا به حالت اول بازگردانیم نیز کلاس ng-dirty قابل مشاهدهاست. بنابراین اگر حالت فیلدی dirty شد، همواره به همین حالت باقی میماند.
در این لحظه اگر با Tab به فیلد دیگری در فرم مراجعه کنیم:
در اینجا است که کلاس ng-untouched به ng-touched تبدیل میشود. بنابراین کلاسهای مختلف لمس یک فیلد، ارتباطی به افزوده شدن یا حذف کاراکتری از یک فیلد ندارند و فقط به از دست رفتن focus و مراجعهی به فیلد دیگری مرتبط میشوند.
اگر به المان تغییر یافتهی فوق دقت کنید، ویژگی required نیز به آن اضافه شدهاست (علاوه بر template reference variable ایی که تعریف کردیم). در این حالت کل فیلد را خالی کنید:
همانطور که مشاهده میکنید، اکنون کلاس ng-valid به کلاس ng-invalid تغییر یافتهاست.
ارتباط بین کلاسهای CSS اعتبارسنجی و خواص ngModel
تمام کلاسهای -ng ایی که در بالا معرفی شدند، معادلهای خواص ngModel ایی نیز دارند. فقط کافی است -ng آنها را حذف کنید، باقیماندهی آن، نام خاصیت متناظری در ngModel خواهد بود. برای مثال کلاس ng-untouched به خاصیت untouched نگاشت میشود و به همین ترتیب برای مابقی.
template reference variable ایی را که تا به اینجا به المان اضافه کردیم (firstName#) به خواص همان المان دسترسی دارد (مانند className آن). اما در ادامه میخواهیم این متغیر به ngModel و خواص آن دسترسی داشته باشد و میدان دید آن تغییر کند. به همین جهت تنها کافی است تا ngModel را به این متغیر انتساب دهیم:
<div class="form-group"> <label>First Name</label> <input #firstName="ngModel" required type="text" class="form-control" name="firstName" [(ngModel)]="model.firstName"> </div> <h4>dirty: {{ firstName.dirty }}</h4>
یک نکته: در حالت خواص valid و invalid که مرتبط با اعتبارسنجی هستند، خاصیت سومی نیز به نام errors وجود دارد که حاوی اطلاعات بیشتری در مورد خطای اعتبارسنجی رخ دادهاست. بنابراین وجود این خاصیت و نال نبودن آن نیز دلالت بر وجود یک خطای اعتبارسنجی است. از خاصیت errors در ادامهی بحث در قسمت «مدیریت چندین خطای همزمان اعتبارسنجی» استفاده خواهیم کرد.
نمایش بهتر خطاهای اعتبارسنجی با بررسی خواص ngModel
یکی از مزایای کار با خواص ngModel، امکان استفادهی از آنها در عبارات شرطیاست که نسبت به کلاسهای CSS معرفی شدهی در ابتدای بحث، انعطاف پذیری بیشتری را به همراه خواهند داشت.
<div class="form-group"> <label>First Name</label> <input #firstName="ngModel" #firstNameElement required type="text" class="form-control" name="firstName" [(ngModel)]="model.firstName"> <div *ngIf="firstName.invalid && firstName.touched" class="alert alert-danger"> First Name is required. </div> </div> <h4>className: {{ firstNameElement.className }}</h4> <h4>dirty: {{ firstName.dirty }}</h4> <h4>invalid: {{ firstName.invalid }}</h4>
نمایش بهتر خطاهای اعتبارسنجی با مزین ساختن المانهای ورودی
علاوه بر نمایش یک alert بوت استرپی متناظر با یک فیلد غیرمعتبر، میتوان خود المانهای ورودی را نیز با شیوهنامههایی مزین ساخت.
این کار را در بوت استرپ با افزودن کلاس has-error در کنار form-group انجام میدهند. همچنین label نیز باید به کلاس control-label مزین شود تا hass-error بر روی آن نیز تاثیرگذار شود. برای پیاده سازی پویای آن در Angular به روش ذیل عمل میشود:
<div class="form-group" [class.has-error]="firstName.invalid && firstName.touched"> <label class="control-label">First Name</label>
بنابراین اگر المان firstName خالی باشد و همچنین با یک Tab از روی آن عبور کرده باشیم، کلاس has-error در کنار کلاس form-group اضافه میشود.
روش دوم: همانطور که در ابتدای بحث نیز عنوان شد، Angular بر اساس حالات مختلف یک فیلد، کلاسهای CSS خاصی را به آنها انتساب میدهد. یک چنین کاری را با مقدار دهی این کلاسها در فایل src\styles.css نیز میتوان انجام داد که دقیقا معادل بررسی خواص invalid و touched با کدنویسی است:
.ng-touched.ng-invalid{ border: 1px solid red; }
سایر ویژگیهای اعتبارسنجی HTML 5
تا اینجا ویژگی استاندارد required را به المان ورودی فرم ثبت اطلاعات کاربران، اضافه کردیم.
<input #firstName="ngModel" #firstNameElement required maxlength="3" minlength="2" pattern="^V.*" type="text" class="form-control" name="firstName" [(ngModel)]="model.firstName">
برای نمونه minlength همهجا پشتیبانی نمیشود؛ اما آنرا میتوان برای مثال با الگویی مساوی "+..." جایگزین کرد.
مشکل! ذکر چند ویژگی اعتبارسنجی با هم، تداخل ایجاد میکنند!
در اینجا چون چهار ویژگی مختلف را به صورت یکجا به یک المان متصل کردهایم، اکنون div ذیل به هر کدام از این ویژگیها به صورت یکسانی واکنش نشان خواهد داد؛ زیرا خاصیت invalid را true میکنند:
<div *ngIf="firstName.invalid && firstName.touched" class="alert alert-danger"> First Name is required. </div>
<div class="form-group" [class.has-error]="firstName.invalid && firstName.touched"> <label class="control-label">First Name</label> <input #firstName="ngModel" #firstNameElement required maxlength="3" minlength="2" pattern="^V.*" type="text" class="form-control" name="firstName" [(ngModel)]="model.firstName"> <div *ngIf="firstName.invalid && firstName.touched"> <div class="alert alert-info"> errors: {{ firstName.errors | json }} </div> <div class="alert alert-danger" *ngIf="firstName.errors.required"> Name is required. </div> <div class="alert alert-danger" *ngIf="firstName.errors.minlength"> Name should be minimum {{firstName.errors.minlength.requiredLength}} characters. </div> <div class="alert alert-danger" *ngIf="firstName.errors.maxlength"> Name should be max {{firstName.errors.maxlength.requiredLength}} characters. </div> <div class="alert alert-danger" *ngIf="firstName.errors.pattern"> Name pattern: {{firstName.errors.pattern.requiredPattern}} </div> </div> </div>
همانطور که در تصویر ملاحظه میکنید، محتوای خاصیت errors به صورت JSON، جهت دیباگ نیز درج شدهاست.
بنابراین وجود خاصیت firstName.errors.minlength و یا firstName.errors.pattern به این معنا است که این خطاهای خاص وجود دارند (خاصیت firstName.errors به همراه اضافاتی است) و برعکس نال بودن آنها مؤید عدم وجود خطایی است. به همین جهت میتوان به ازای هر کدام، یک div جداگانه را تشکیل داد.
مرحلهی بعد، استخراج اطلاعات بیشتری از همین شیء و محتوای خاصیت errors است. برای مثال زمانیکه در آن خاصیت minlength ظاهر شد، این خاصیت نیز دارای خاصیتی مانند requiredLength است که از آن میتوان جهت درج عدد واقعی مورد نیاز این اعتبارسنج استفاده کرد.
بهبود اعتبارسنجی drop down
در حالت فعلی تعریف drop down مثال این سری، نیازی به اعتبارسنجی نیست؛ چون لیست مشخصی از طریق کامپوننت در اختیار این المان قرار میگیرد و همواره دقیقا یکی از این عناصر انتخاب خواهند شد. اما اگر گزینهی دیگری را مانند:
<option value="default">Select a Language...</option>
<div class="form-group" [class.has-error]="hasPrimaryLanguageError"> <label class="control-label">Primary Language</label> <select class="form-control" name="primaryLanguage" #primaryLanguage (blur)="validatePrimaryLanguage(primaryLanguage.value)" (change)="validatePrimaryLanguage(primaryLanguage.value)" [(ngModel)]="model.primaryLanguage"> <option value="default">Select a Language...</option> <option *ngFor="let lang of languages"> {{ lang }} </option> </select> </div>
export class EmployeeRegisterComponent { hasPrimaryLanguageError = false; validatePrimaryLanguage(value) { if (value === 'default'){ this.hasPrimaryLanguageError = true; } else{ this.hasPrimaryLanguageError = false; } } }
اعتبارسنجی در سطح کل فرم
تا اینجا بررسیهایی را که انجام دادیم، در سطح فیلدها بودند. اکنون اگر کاربر به طور کامل تمام این فیلدها را تغییر ندهد و بر روی دکمهی ارسال کلیک کند چطور؟
<form #form="ngForm" novalidate>
<h3> form.valid: {{ form.valid }}</h3>
<button class="btn btn-primary" type="submit" [disabled]="form.invalid">Ok</button>
در قسمت بعد نحوهی ارسال این فرم را به سرور، بررسی میکنیم.
کدهای کامل این قسمت را از اینجا میتوانید دریافت کنید: angular-template-driven-forms-lab-04.zip
برای اجرای آن فرض بر این است که پیشتر Angular CLI را نصب کردهاید. سپس از طریق خط فرمان به ریشهی پروژه وارد شده و دستور npm install را صادر کنید تا وابستگیهای آن دریافت و نصب شوند. در آخر با اجرای دستور ng serve -o برنامه ساخته شده و در مرورگر پیش فرض سیستم نمایش داده خواهد شد.
مروری بر کاربردهای Anonymous Types
بازی 2048 برای تلگرام
کاربران ایرانی علی رغم اینکه در تلگرام خیلی فعال هستند اما متاسفانه تعداد بازی هایی که ایرانیها برای این پلتفرم توسعه دادند چندان زیاد نیستند. با اینکه تخصص من برنامه نویسی سمت کلاینت و جاوااسکریپت نیست اما سعی کردم به سادهترین شکل یک بازی معمولی نوشته شده با HTML را برای تلگرام مناسب سازی کنم.
مهمترین نکته اینکه شما دو تا API تلگرام یکی SetScore برای ثبت رکورد و دیگری GetHighScores را برای دریافت آخرین رکوردها را پیاده سازی کنید.