در مقالهی قبل توانستیم یک سری
از مدلهای مربوط به وبلاگ را آماده کنیم. در ادامه به تکمیل آن و همچین
آغاز تهیهی مدلهای مربوط به اخبار و پیغام خصوصی میپردازیم.
همکاران این قسمت:
سلمان معروفی
مدل گزارش دهی
/// <summary> /// Repersents a Report template for every cms section /// </summary> public class Report { #region Ctor /// <summary> /// Create one instance for <see cref="Report"/> /// </summary> public Report() { ReportedOn = DateTime.Now; Id = SequentialGuidGenerator.NewSequentialGuid(); } #endregion #region Properties /// <summary> /// gets or sets identifier for Report /// </summary> public virtual Guid Id { get; set; } /// <summary> /// gets or sets reason of report /// </summary> public virtual string Reason { get; set; } /// <summary> /// gets or sets section that is reported /// </summary> public virtual ReportSection Section { get; set; } /// <summary> /// gets or sets sectionid that is reported /// </summary> public virtual long SectionId { get; set; } /// <summary> /// gets or sets type of report /// </summary> public virtual ReportType Type{ get; set; } /// <summary> /// gets or sets report's datetime /// </summary> public virtual DateTime ReportedOn { get; set; } /// <summary> /// indicate this report is read by admin /// </summary> public virtual bool IsRead { get; set; } #endregion #region NavigationProperties /// <summary> /// gets or sets id of user that is reporter /// </summary> public virtual long ReporterId { get; set; } /// <summary> /// gets or sets id of user that is reporter /// </summary> public virtual User Reporter { get; set; } #endregion } /// <summary> /// Represents Report Section /// </summary> public enum ReportSection { News, Poll, Announcement, ForumTopic, BlogComment, BlogPost, NewsComment, PollComment, AnnouncementComment, ForumPost, User, ... } /// <summary> /// Represents Type of Report /// </summary> public enum ReportType { Spam, Abuse, Advertising, ... }
قصد داریم در این سیستم به کاربران خاصی دسترسی گزارش دادن در بخشهای مختلف را بدهیم. این دسترسیها در بخش تنظیمات سیستم قابل تغییر خواهند بود (برای مثال براساس امتیاز ، براساس تعداد پست و ... ) . این امکان میتواند برای مدیریت سیستم مفید باشد.
برای سیستم گزارش دهی به مانند سیستم امتیاز دهی عمل خواهیم کرد. در کلاس Report، خصوصیت ReportSection از نوع دادهی شمارشی میباشد که در بالا تعریف آن نیز آماده است و مشخص کنندهی بخشهایی میباشد که لازم است امکان گزارش دهی داشته باشند. خصوصیت Type هم که از نوع شمارشی ReportType میباشد، مشخص کنندهی نوع گزارشی است که داده شده است.
علاوه بر نوع گزارش، میتوان دلیل گزارش را هم ذخیره کرد که برای این منظور خصوصیت Reason در نظر گرفته شدهاست. خصوصیت IsRead هم برای مدیریت این گزارشات در پنل مدیریت در نظر گرفته شده است. اگر در مقالهی قبل دقت کرده باشید، متوجه وجود خصوصیتی به نام ReportsCount در کلاس BaseContent و BaseComment خواهید شد که برای نشان دادن تعداد گزارشهایی است که برای آن مطلب یا نظر داده شده است، استفاده میشود.
کلاس پایه فایلهای ضمیمه
/// <summary> /// Represents a base class for every attachment /// </summary> public abstract class BaseAttachment { #region Ctor public BaseAttachment() { Id = SequentialGuidGenerator.NewSequentialGuid(); AttachedOn = DateTime.Now; } #endregion #region Properties /// <summary> /// sets or gets identifier for attachment /// </summary> public virtual Guid Id { get; set; } /// <summary> /// sets or gets name for attachment /// </summary> public virtual string FileName { get; set; } /// <summary> /// sets or gets type of attachment /// </summary> public virtual string ContentType { get; set; } /// <summary> /// sets or gets size of attachment /// </summary> public virtual long Size { get; set; } /// <summary> /// sets or gets Extention of attachment /// </summary> public virtual string Extension { get; set; } /// <summary> /// sets or gets bytes of data /// </summary> //public byte[] Data { get; set; } /// <summary> /// sets or gets Creation Date /// </summary> public virtual DateTime AttachedOn { get; set; } /// <summary> /// gets or sets counts of download this file /// </summary> public virtual long DownloadsCount { get; set; } /// <summary> /// gets or sets datetime that is modified /// </summary> public virtual DateTime ModifiedOn { get; set; } /// <summary> /// gets or sets section that this file attached there /// </summary> public virtual AttachmentSection Section { get; set; } /// <summary> /// gets or sets information of user agent /// </summary> public virtual string Agent { get; set; } #endregion #region NavigationProperties /// <summary> /// sets or gets identifier of attachment's owner /// </summary> public virtual long OwnerId { get; set; } /// <summary> /// sets or gets identifier of attachment's owner /// </summary> public virtual User Owner { get; set; } #endregion } public enum AttachmentSection { News, Announcement, ForumTopic, Conversation, BlogComment, NewsComment, PollComment, AnnouncementComment, ForumPost, BlogPost, Group, ... }
کلاس بالا اکثر خصوصیات لازم برای مدل Attachment ما را در خود دارد. قصد داریم از ارث بری TPH برای مدیریت فایلهای ضمیمه استفاده کنیم. در سیستم بستهی ما، تنها کاربران احراز هویت شده میتوانند فایل ضمیمه کنند و برای همین منظور OwnerId را که همان ارسال کنندهی فایل میباشد، به صورت Nullable در نظر نگرفتهایم.
یک سری از مشخصات که نیاز به توضیح اضافی ندارند، ولی خصوصیت AttachmentSection که از نوع شمارشی AttachmentSection است، برای دسترسی راحت کاربر به فایلهای ارسالی خود در پنل کاربری در نظر گرفته شده است. برای بخشهای (وبلاگ - اخبار - نظرسنجیها - آگهیها - انجمن) که نیاز به Privacy خاصی نیست و احراز هویت کفایت میکند، مدل زیر را در نظر گرفته ایم:
مدل فایلهای ضمیمه عمومی
/// <summary> /// Repersent the attachment for file /// </summary> public class Attachment : BaseAttachment { }
/// <summary> /// Represents one news item /// </summary> public class NewsItem : BaseContent { #region Ctor /// <summary> /// create one instance of <see cref="NewsItem"/> /// </summary> public NewsItem() { Rating = new Rating(); PublishedOn = DateTime.Now; } #endregion #region Properties /// <summary> /// indicating that this news show on sidebar /// </summary> public virtual bool ShowOnSideBar { get; set; } /// <summary> /// indicate this NewsItem is approved by admin if NewsItem.Moderate==true /// </summary> public virtual bool IsApproved { get; set; } #endregion #region NavigationProperties /// <summary> /// gets or sets newsitem's Reviews /// </summary> public ICollection<NewsComment> Comments { get; set; } #endregion }
کلاس بالا نشان دهندهی اشتراکهای ما خواهند بود. این مدل ما هم از کلاس پایهی BaseContent بحث شده در مقالهی قبل، ارث بری کرده و علاوه بر آن دو خصوصیت دیگر تحت عنوان IsApproved برای اعمال مدیریتی در نظر گرفته شده است (اگر در بخش تنظیمات سیستم اخبار، مدیریت تصمیم گرفته باشد تا اخبار جدید به اشتراک گذاشته شده با تأیید مدیریتی منتشر شوند) و خصوصیت ShowOnSideBar هم به عنوان یک تنظیم مدیریتی برای خبر خاصی در نظر گرفته شده که لازم است به صورت sticky در سایدبار نمایش داده شود.
برای اخبار نیز امکان ارسال نظر خواهیم داشت که برای این منظور لیستی از مدل زیر (NewsComment) در مدل بالا تعریف شده است .
مدل نظرات اخبار
public class NewsComment : BaseComment { #region Ctor public NewsComment() { Rating = new Rating(); CreatedOn = DateTime.Now; } #endregion #region NavigationProperties /// <summary> /// gets or sets body of blog NewsItem's comment /// </summary> public virtual long? ReplyId { get; set; } /// <summary> /// gets or sets body of blog NewsItem's comment /// </summary> public virtual NewsComment Reply { get; set; } /// <summary> /// gets or sets body of blog NewsItem's comment /// </summary> public virtual ICollection<NewsComment> Children { get; set; } /// <summary> /// gets or sets NewsItem that this comment sent to it /// </summary> public virtual NewsItem NewsItem { get; set; } /// <summary> /// gets or sets NewsItem'Id that this comment sent to it /// </summary> public virtual long NewsItemId { get; set; } #endregion }
/// <summary> /// Indicate one conversation /// </summary> public class Conversation { #region Ctor /// <summary> /// create one instance of <see cref="Conversation"/> /// </summary> public Conversation() { Id = SequentialGuidGenerator.NewSequentialGuid(); SentOn = DateTime.Now; } #endregion #region Properties /// <summary> /// gets or sets identifier of record /// </summary> public virtual Guid Id { get; set; } /// <summary> /// represents this conversaion is seen /// </summary> public virtual bool IsRead { get; set; } /// <summary> /// gets or sets subject of this conversation /// </summary> public virtual string Subject { get; set; } /// <summary> /// gets or sets Date that this record added /// </summary> public virtual DateTime SentOn { get; set; } /// <summary> /// indicate this record deleted by sender /// </summary> public virtual bool DeletedBySender { get; set; } /// <summary> /// indicate this record deleted by receiver /// </summary> public virtual bool DeletedByReceiver { get; set; } /// <summary> /// gets or sets Messagescount that Unread by sender of this conversation /// </summary> public virtual int UnReadSenderMessagesCount { get; set; } /// <summary> /// gets or sets Messagescount that Unread by receiver of this conversation /// </summary> public virtual int UnReadReceiverMessagesCount { get; set; } /// <summary> /// gets or sets Messagescount of this conversation for increase performance /// </summary> public virtual int MessagesCount { get; set; } #endregion #region NavigationProperties /// <summary> /// gets or sets if of user that start this conversation /// </summary> public virtual long SenderId { get; set; } /// <summary> /// gets or sets user that start this conversation /// </summary> public virtual User Sender { get; set; } /// <summary> /// gets or sets id of user that is recipient /// </summary> public virtual long ReceiverId { get; set; } /// <summary> /// gets or sets user that is recipient /// </summary> public virtual User Receiver { get; set; } /// <summary> /// get or set Messages of this conversation /// </summary> public virtual ICollection<ConversationReply> Messages { get; set; } /// <summary> /// get or set Attachments that attached in this conversation /// </summary> public virtual ICollection<ConversationAttachment> Attachments { get; set; } #endregion
مدل بالا نشان دهندهی گفتگوی بین دو کاربر میباشد. هر گفتگو امکان دارد با موضوع خاصی ایجاد شود و مسلما یک کاربر بهعنوان دریافت کننده و کاربر دیگری بعنوان ارسال کننده خواهد بود. برای این منظور خصوصیات Receiver و Sender که از نوع User هستند را در این کلاس در نظر گرفتهایم.
خصوصیات DeletedBySender و DeletedByReceiver هم برای این در نظر گفته شدهاند که اگر یک طرف این گفتگو خواهان حذف آن باشد، برای آن کاربر حذف نرم انجام دهیم و فعلا برای کاربر مقابل قابل دسترسی باشد.
UnReadSenderMessagesCount و UnReadReceiverMessagesCount هم برای بالا بردن کارآیی سیستم در نظر گفته شدهاند و در واقع تعداد پیغامهای خوانده نشده در یک گفتگو به صورت متمایز برای هر دو طرف، ذخیره میشود. هر گفتگو شامل یکسری پیغام رد و بدل شده خواهد بود که بدین منظور لیستی از ConversationReplyها را در مدل بالا تعریف کردهایم.
در هر گفتگو یکسری فایل هم ممکن است ضمیمه شود ، برای این منظور هم یک لیستی از کلاس ConversationAttachment در مدل گفتگو تعریف شده است که در ادامه پیاده سازی کلاس ConversationAttachment را هم خواهیم دید.
مدل ConversationReply به شکل زیر میباشد:
/// <summary> /// Represents One Reply to Conversation /// </summary> public class ConversationReply { #region Ctor /// <summary> /// create one instance of <see cref="ConversationReply"/> /// </summary> public ConversationReply() { Id = SequentialGuidGenerator.NewSequentialGuid(); SentOn = DateTime.Now; } #endregion #region Properties /// <summary> /// gets or sets identifier of record /// </summary> public virtual Guid Id { get; set; } /// <summary> /// represents this conversaionReply is seen /// </summary> public virtual bool IsRead { get; set; } /// <summary> /// gets or sets body of this conversationReply /// </summary> public virtual string Body { get; set; } /// <summary> /// gets or sets Date that this record added /// </summary> public virtual DateTime SentOn { get; set; } #endregion #region NavigationProperties /// <summary> /// gets or sets Parent's Id Of this ConversationReply /// </summary> public virtual Guid? ParentId { get; set; } /// <summary> /// gets or sets Parent Of this ConversationReply /// </summary> public virtual ConversationReply Parent { get; set; } /// <summary> /// get or set Children Of this ConversationReply /// </summary> public virtual ICollection<ConversationReply> Children { get; set; } /// <summary> /// gets or sets if of user that start this conversationReply /// </summary> public virtual long SenderId { get; set; } /// <summary> /// gets or sets user that start this conversationReply /// </summary> public virtual User Sender { get; set; } /// <summary> /// gets or sets Conversation that this message sent in it /// </summary> public virtual Conversation Conversation{ get; set; } /// <summary> /// gets or sets Id of Conversation that this message sent in it /// </summary> public virtual Guid ConversationId { get; set; } #endregion }
مدل بالا نشان دهندهی پیغامهای داده شده در یک گفتگو با موضوعی خاص میباشد. ساختار درختی آن هم برای ایجاد امکان جواب دهی برای پیغامها در نظر گرفته شده است (الزامی نیست). هر پیغام در یک گفتگو ارسال شده و یک ارسال کننده نیز دارد که برای این منظور به ترتیب دو خصوصیت Conversation از نوع کلاس Conversation و Sender از نوع User در نظر گرفتهایم.
با توجه به وجود Privacy در گفتگو نیاز است تا مدل فایل ضمیمه بخش گفتگوها به شکل زیر باشد:
/// <summary> /// Represents the attachment That attached in Conversation /// </summary> public class ConversationAttachment : BaseAttachment { #region NavigationProperties public virtual Conversation Conversation { get; set; } public virtual Guid? ConversationId { get; set; } #endregion }
همانطور که کمی بالاتر بحث شد، قصد اعمال ارث بری TPH را برای مدیریت فایلهای ضمیمه داریم. برای این منظور مدل بالا نیز از کلاس BaseAttachment ارث بری کرده و دو خصوصیت اضافه هم برای اعمال ارتباط یک به چند با گفتگو خواهد داشت. توجه کنید که ConversationId به صورت Nullable تعریف شدهاست.
نتیجه این قسمت
Install-package Microsoft.FeatureManagement.AspNetCore
[FeatureGate("chat")] public class ChatController : Controller { public IActionResult Index() { // do sth } }
[FeatureGate("feature1", "feature2")] public class ChatController : Controller { public IActionResult Index() { // do sth } }
"FeatureManagement": { "feature1": true, "feature2": false },
public enum RequirementType { // // Summary: // The enabled state will be attained if any feature in the set is enabled. Any = 0, // // Summary: // The enabled state will be attained if all features in the set are enabled. All = 1 }
[FeatureGate(RequirementType.Any,"feature1", "feature2","feature3")] public class ChatController : Controller { public IActionResult Index() { // do sth } }
@addTagHelper *, Microsoft.FeatureManagement.AspNetCore // put this line in _ViewImports <feature name="feature1,feature2,feature3"> <li> <a asp-area="" asp-controller="Chat" asp-action="index">Stay in contact</a> </li> </feature>
public class RedirectDisabledFeatureHandler : IDisabledFeaturesHandler { public Task HandleDisabledFeatures(IEnumerable<string> features, ActionExecutingContext context) { context.Result = new RedirectResult("url"); return Task.CompletedTask; } }
public void ConfigureServices(IServiceCollection services) { services.AddFeatureManagement().UseDisabledFeaturesHandler(new RedirectDisabledFeatureHandler()); ; }
مشاهدهی جزئیات اطلاعات سرور و بستههای نصب شدهی بر روی آن
در نگارشهای قبل از RTM، با فراخوانی app.UseRuntimeInfoPage در متد Configure کلاس Startup، ریز اطلاعاتی از وضعیت سرور و بستههای موجود در آن با مراجعهی به آدرس http://site/runtimeinfo نمایش داده میشدند. این مورد خاص از نگارش RTM حذف شدهاست (احتمالا به دلایل امنیتی). البته اگر علاقمند به بررسی کدهای آن باشید، هنوز تاریخچهی آن در GitHub موجود است .
مدیریت خطاها در برنامههای ASP.NET Core 1.0
به متد Configure کلاس Startup مراجعه کرد و یک سطر استثناء را به ابتدای کدهای Middleware انتهایی آن اضافه کنید:
public void Configure(IApplicationBuilder app) { app.Run(async context => { throw new Exception("Generic Error"); await context.Response.WriteAsync("Hello DNT!"); }); }
در این حالت اگر برنامه را اجرا کنیم، این خروجی را دریافت خواهیم کرد:
و اگر به وضعیت بازگشت داده شدهی از طرف سرور دقت کنیم، فقط internal server error است:
در اینجا برخلاف نگارشهای قبلی ASP.NET، دیگر حتی صفحهی زرد رنگ معروف نمایش خطاها (yellow screen of death) نیز فعال نیستند. برای فعال سازی آن نیاز است Middleware مرتبط با آنرا به نحو ذیل به برنامه معرفی کنیم:
public void Configure(IApplicationBuilder app) { app.UseDeveloperExceptionPage();
به دلایل امنیتی و عدم نشت اطلاعات سمت سرور و خصوصا عدم امکان دیباگ از راه دور برنامه توسط مهاجمین، این Middleware به صورت پیش فرض فعال نیست.
بنابراین این سؤال مطرح میشود که چگونه میتوان این صفحه را تنها در حین توسعهی برنامه نمایش داد؟
پاسخ آن به نحوهی طراحی متد Configure در کلاس Startup بر میگردد. این متد امضای ثابتی را ندارد. هر تعداد سرویسی را که نیاز داشتید، میتوانید به عنوان پارامتر این متد معرفی کنید و کار تزریق وابستگیها و نمونه سازی آنها، توسط امکانات توکار ASP.NET Core به صورت خودکار انجام میشود. برای مثال سرویس IApplicationBuilder، یکی از سرویسهای توکار ASP.NET Core است و برای تنظیم آن نیازی نیست تا کار خاصی را انجام دهیم. به همین جهت است که صرفا معرفی اینترفیس آن در این متد، وهلهای را از سازندهی برنامه در اختیار ما قرار میدهد. سرویسها را در مطلبی جداگانه مورد بررسی قرار خواهیم داد، اما فعلا جهت تکمیل بحث باید درنظر داشت که یکی دیگر از سرویسهای توکار ASP.NET Core، به نام IHostingEnvironment، اطلاعاتی را در مورد محیطی که برنامه را در آن اجرا میکنیم در اختیار ما قرار میدهد:
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
public void Configure(IApplicationBuilder app, IHostingEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); }
این متغیر محیطی میتواند سه مقدار Development, Staging و Production را داشته باشد و بر اساس این متغیر و مقدار آن است که یکی از سه متد ذیل مفهوم پیدا میکنند و true یا false را باز میگردانند:
if(env.IsDevelopment()){ } if(env.IsProduction()){ } if(env.IsStaging()){ }
نمایش و مدیریت خطاها در حالت Production
از app.UseDeveloperExceptionPage صرفا در حالت توسعه استفاده کنید؛ چون اطلاعات نمایش داده شدهی توسط آن، بیش از اندازه برای مهاجمین مفید است. اما در حالت توزیع نهایی بر روی سرور چه باید کرد؟
public void Configure(IApplicationBuilder app, IHostingEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } else { app.UseExceptionHandler(errorHandlingPath: "/MyControllerName/SomeActionMethodName"); }
به علاوه در اینجا (در این قسمت خاص برنامه که توسط پارامتر errorHandlingPath مشخص شدهاست) با استفاده از قطعه کد ذیل، دسترسی کاملی را به اطلاعات خطای رخ داده، جهت ثبت و لاگ آن دارید:
var feature = HttpContext.Features.Get<IExceptionHandlerFeature>(); var error = feature?.Error;
بررسی میانافزار StatusCode
این میان افزار برای مدیریت responseهایی که status code آنها بین 400 تا 600 هستند، طراحی شدهاست. بر اساس این شمارهها، میتوان خطای خاصی را بازگشت داده و یا کاربر را به یک صفحه یا کنترلر خاصی در برنامه، هدایت کرد.
در حالت عادی ثبت آن
public void Configure(IApplicationBuilder app, IHostingEnvironment env) { if (env.IsDevelopment()) { app.UseStatusCodePages(); app.UseDeveloperExceptionPage(); } else { app.UseExceptionHandler(errorHandlingPath: "/MyControllerName/SomeActionMethodName"); } }
برای نمونه در اینجا مسیری درخواست داده شدهاست که توسط برنامه پردازش نمیشود و وجود ندارد.
اگر خواستید تا status code واقعی، به کاربر بازگشت داده شود، اما خروجی نمایش داده شده را سفارشی سازی کنید، میتوانید از متد UseStatusCodePagesWithReExecute استفاده نمائید:
app.UseStatusCodePagesWithReExecute("/MyControllerName/SomeActionMethodName/{0}");
UseSubmitBehavior="false" OnClientClick="if ((typeof(Page_ClientValidate) == 'function') && (Page_ClientValidate()==false)) { return false;} this.disabled=true;"
در این پست قصد دارم به نحوه بارگزاری یک PartialView با استفاده از ASP.NET MVC بپردازم.
ابتدا یک پروژه جدید ایجاد کنید. حال میخواهیم زمانیکه صفحه اصلی سایت بارگزاری میشود، لیست تمام محصولات را نمایش دهد. برای نمایش محصولات یک PartialView جدید را ایجاد میکنیم؛ همانند شکل ذیل:
حال برای ساده کردن مثال، متن ثابتی را درون این PartialView مینویسیم:
product List Partial
$(function () { $.ajax({ //مشخص کردن اکشنی که باید فراخوانی شود url: '/Home/Details', contentType: 'application/html; charset=utf-8', type: 'GET', //نوع نتیجه بازگشتی dataType: 'html' }) .success(function (result) { //زمانی که کدهای سمت سرور بدون خطا اجرا شده اند //این قسمت فراخوانی میشود و نتیجه اکشن درون متغیر //result //قرار میگیرد $('#sectionContents').html(result); }) .error(function (xhr, status) { alert(xhr.responseText); }); });
<div id="sectionContents"></div>
public ActionResult Details() { return PartialView("partial/_ProductList"); }
نتیجه اجرا به صورت زیر است:
(function () { var itemCtx = {}; itemCtx.Templates = {}; itemCtx.Templates.Header = "<div><b title=\"اطلاعات فیلم ها\">Movie Data</b></div><ul>"; itemCtx.Templates.Item = MyOverrideTemplate; itemCtx.Templates.Footer = "</ul>"; itemCtx.BaseViewID = 1; itemCtx.ListTemplateType = 100; //For Generic List (More : http://msdn.microsoft.com/en-us/library/ms462947(v=office.12).aspx) SPClientTemplates.TemplateManager.RegisterTemplateOverrides(itemCtx); })(); function GT(val , index) { // example of val : 60 % var temp = val.split(' ')[0]; var v = Number(temp); return v > index; } function LT(val, index) { var temp = val.split(' ')[0]; var v = Number(temp); return v < index; } function EQ(val, index) { var temp = val.split(' ')[0]; var v = Number(temp); return v == index; } function MyOverrideTemplate(ctx) { if (LT(ctx.CurrentItem.PopularityPercent ,25)) { return "<li title='خیلی کم بازدید' style='color:white;background-color: red;width: 300px;height: 24px;'>" + ctx.CurrentItem.Title + " – " + ctx.CurrentItem.PopularityPercent + "</li>"; } else if (LT(ctx.CurrentItem.PopularityPercent ,50)) { return "<li title='کم بازدید' style='color:maroon;background-color: #ffcc00;width: 300px;height: 24px;'>" + ctx.CurrentItem.Title + " – " + ctx.CurrentItem.PopularityPercent + "</li>"; } else if (LT(ctx.CurrentItem.PopularityPercent ,75)) { return "<li title='بازدید معمولی' style='color:#ffcc00;background-color: maroon;width: 300px;height: 24px;'>" + ctx.CurrentItem.Title + " – " + ctx.CurrentItem.PopularityPercent + "</li>"; } else if (LT(ctx.CurrentItem.PopularityPercent ,95)) { return "<li title='پر بازدید' style='color:yellow;background-color: blue;width: 300px;height: 24px;'>" + ctx.CurrentItem.Title + " – " + ctx.CurrentItem.PopularityPercent + "</li>"; } else if (EQ(ctx.CurrentItem.PopularityPercent, 100)) { return "<li title='بالاترین بازدید' style='color:black;background-color: green;width: 300px;height: 24px;'>" + ctx.CurrentItem.Title + " – " + ctx.CurrentItem.PopularityPercent + "</li>"; } else { return "<li title='نامعلوم' style='color:navy;background-color: yellow;width: 300px;height: 24px;'>" + ctx.CurrentItem.Title + " – " + ctx.CurrentItem.PopularityPercent + "</li>"; } }
~site/_catalogs/masterpage/MyJsLinkSample.js
[HttpPost,AjaxOnly, ValidateAntiForgeryToken] public virtual JsonResult Create(AddDeviceGroupViewModel deviceGroupViewModel) { if (ModelState.IsNotValid()) { Response.StatusCode = (int)HttpStatusCode.BadRequest; return Json(new { success = false, message = ModelState.FirstErrorMessage(), notificationType = NotificationType.Error }, JsonRequestBehavior.AllowGet); } var result = _deviceGroupService.Add(deviceGroupViewModel); // other codes }
@using (Ajax.BeginForm(MVC.Admin.DeviceGroup.Create(), new AjaxOptions { HttpMethod = "POST", OnSuccess = "saveAjaxForm", OnFailure = "SaveFailure" })) { // form content }
function SaveFailure(data) { $("button[type=submit]").prop('disabled', false); var result = $.parseJSON(data.responseText); showMessage(result.message, result.notificationType); }
مشکل به وجود آمده حاصل اضافه شدن تگهای زیر به وب کانفیگ بود :
<httpErrors errorMode="Custom"> <remove statusCode="404"/> <error statusCode="404" path="/Error/NotFound" responseMode="ExecuteURL"/> </httpErrors>
Response.StatusCode = (int)HttpStatusCode.BadRequest;
و در خروجی تنها مقداری که به سمت کاربر برگشت داده میشد، مقدار BadRequest بود و خط زیر باعث خطا و توقف برنامه میشد:
var result = $.parseJSON(data.responseText);
همچنین در صورتیکه قصد داشتید تگهای فوق را در web.config داشته باشید (جهت هندل کردن صفحات پیدا نشده) میتوانید از مقدار دهی TrySkipIISCustomError با true این مشکل را رفع کنید.
Response.TrySkipIisCustomErrors = true;
روشی دیگر:
<system.webServer> <httpErrors errorMode="DetailedLocalOnly" existingResponse="PassThrough" />
بهوسیلهی order میتوان ترتیب قرارگیری آیتمهای Grid را مشخص کرد. فرض کنید یک ردیف سه ستون داریم و میخواهیم زمانیکه این آیتمها در مروگر نمایش داده میشوند، ترتیب قرارگیری آنها متناسب با نیاز ما باشد که با استفاده از دستور order این کار صورت میپذیرد.
نکته: این کار برای بحث seo مورد استفاده قرار میگیرد.
در پایین با یک مثال، چگونگی انجام اینکار شرح داده شدهاست:<div id="app"> <v-app id="inspire"> <v-container fluid> <v-flex xs4 order-md2> <v-card dark tile flat color="red lighten-1"> <v-card-text>#1</v-card-text> </v-card> </v-flex> <v-flex xs4 order-md3> <v-card dark tile flat color="red lighten-2"> <v-card-text>#2</v-card-text> </v-card> </v-flex> <v-flex xs4 order-md1> <v-card dark tile flat color="red darken-1"> <v-card-text>#3</v-card-text> </v-card> </v-flex> </v-layout> <v-layout row wrap> <v-flex xs12 sm6 md3 order-md4 order-sm2> <v-card dark tile flat color="red darken-2"> <v-card-text>#1</v-card-text> </v-card> </v-flex> <v-flex xs12 sm6 md3 order-md3 order-sm1> <v-card dark tile flat color="deep-orange lighten-1"> <v-card-text>#2</v-card-text> </v-card> </v-flex> <v-flex xs12 sm6 md3 order-md2 order-sm4> <v-card dark tile flat color="deep-orange darken-3"> <v-card-text>#3</v-card-text> </v-card> </v-flex> <v-flex xs12 sm6 md3 order-md1 order-sm3> <v-card dark tile flat color="deep-orange"> <v-card-text>#4</v-card-text> </v-card> </v-flex> </v-layout> </v-container> </v-app> </div>
<div id="app"> <v-app id="inspire"> <v-container fluid grid-list-xl> <v-layout row justify-space-between> <v-flex xs2> <v-card dark color="primary"> <v-card-text>one</v-card-text> </v-card> </v-flex> <v-flex xs2> <v-card dark color="secondary"> <v-card-text>two</v-card-text> </v-card> </v-flex> <v-flex xs2> <v-card dark color="accent"> <v-card-text>three</v-card-text> </v-card> </v-flex> </v-layout> <v-layout row justify-space-around> <v-flex xs2> <v-card dark color="primary"> <v-card-text>one</v-card-text> </v-card> </v-flex> <v-flex xs2> <v-card dark color="secondary"> <v-card-text>two</v-card-text> </v-card> </v-flex> <v-flex xs2> <v-card dark color="accent"> <v-card-text>three</v-card-text> </v-card> </v-flex> </v-layout> <v-layout row justify-center> <v-flex xs2> <v-card dark color="primary"> <v-card-text>one</v-card-text> </v-card> </v-flex> <v-flex xs2> <v-card dark color="secondary"> <v-card-text>two</v-card-text> </v-card> </v-flex> <v-flex xs2> <v-card dark color="accent"> <v-card-text>three</v-card-text> </v-card> </v-flex> </v-layout> </v-container> </v-app> </div>
<div id="app"> <v-app id="inspire"> <v-container fluid> <v-layout row> <v-flex grow pa-1> <v-card dark color="green darken-3"> <v-card-text>#1 Im a Grow Flex</v-card-text> </v-card> </v-flex> <v-flex shrink pa-1> <v-card dark color="green darken-1"> <v-card-text>#2 Im a Shrink Flex</v-card-text> </v-card> </v-flex> </v-layout> <v-layout row> <v-flex grow pa-1> <v-card dark color="green darken-1"> <v-card-text>#1 Im a Grow Flex</v-card-text> </v-card> </v-flex> <v-flex shrink pa-1> <v-card dark color="green lighten-1"> <v-card-text>#2 Im a Shrink Flex</v-card-text> </v-card> </v-flex> <v-flex grow pa-1> <v-card dark color="green darken-4"> <v-card-text>#3 Im a Grow Flex</v-card-text> </v-card> </v-flex> </v-layout> <v-layout row> <v-flex shrink pa-1> <v-card dark color="green darken-3"> <v-card-text>#1 Im a Shrink Flex</v-card-text> </v-card> </v-flex> <v-flex grow pa-1> <v-card dark color="green"> <v-card-text>#2 Im a Grow Flex</v-card-text> </v-card> </v-flex> <v-flex shrink pa-1> <v-card dark color="green lighten-1"> <v-card-text>#3 Im a Shrink Flex</v-card-text> </v-card> </v-flex> </v-layout> </v-container> </v-app> </div>
<div id="app"> <v-app id="inspire"> <v-container fluid grid-list-md> <v-layout row wrap> <v-flex d-flex xs12 sm6 md4> <v-card color="purple" dark> <v-card-title primary>Lorem</v-card-title> <v-card-text>{{ lorem }}</v-card-text> </v-card> </v-flex> <v-flex d-flex xs12 sm6 md3> <v-layout row wrap> <v-flex d-flex> <v-card color="indigo" dark> <v-card-text>{{ lorem.slice(0, 70) }}</v-card-text> </v-card> </v-flex> <v-flex d-flex> <v-layout row wrap> <v-flex v-for="n in 2":key="n" d-flex xs12 > <v-card color="red lighten-2" dark > <v-card-text>{{ lorem.slice(0, 40) }}</v-card-text> </v-card> </v-flex> </v-layout> </v-flex> </v-layout> </v-flex> <v-flex d-flex xs12 sm6 md2 child-flex> <v-card color="green lighten-2" dark> <v-card-text>{{ lorem.slice(0, 90) }}</v-card-text> </v-card> </v-flex> <v-flex d-flex xs12 sm6 md3> <v-card color="blue lighten-2" dark> <v-card-text>{{ lorem.slice(0, 100) }}</v-card-text> </v-card> </v-flex> </v-layout> </v-container> </v-app> </div>
// student-model.ts export interface Student { id: number; name: string; }
// app.component.ts public model: Student; ngOnInit(): void { this.setDefaultValueForModel(); } saveForm(form: NgForm, evetn: Event) { evetn.preventDefault(); if (form.valid) { alert('everything is ok'); } } setDefaultValueForModel() { this.model = { id: 1, name: '' }; }
<form #form="ngForm" novalidate (submit)="saveForm(form,$event)"> <div> <label>Name</label> <input type="text" required name="name" autocomplete="off" [(ngModel)]="model.name" #name="ngModel"> <p [hidden]="name.valid || (name.pristine && !form.submitted)"> Name is required and should be minimum 4 characters. </p> </div> <div> <input type="submit" value="submit"> </div> </form>