- در این مورد در پرسشهای قبلی بحث شده. کمی جستجو کنید. fix weak characters هست متدش.
- بله. باید با canvas کار کنید در iTextSharp. این مورد با شیء PdfWriter و ContentByte آن شروع میشود.
<table id="browsers-grid"> <thead> <tr> <th width="20%">موتور رندرگیری</th> <th width="25%">مرورگر</th> <th width="25%">پلتفرم (ها)</th> <th width="15%">نسخه موتور</th> <th width="15%">نمره css</th> </tr> </thead> <tbody> </tbody> <tfoot> <tr> <th>موتور رندرگیری</th> <th>مرورگر</th> <th>پلتفرم (ها)</th> <th>نسخه موتور</th> <th>نمره css</th> </tr> </tfoot> </table>
{ "aaData": [ {"engine":"Trident", "browser":"Internet Explorer 4.0", "platform":"Win95+", "version":"4", "grade":"X"}, {"engine":"Trident", "browser":"Internet Explorer 5.0", "platform":"Win95+", "version":"5", "grade":"C"}, {"engine":"Trident", "browser":"Internet Explorer 5.5", "platform":"Win95+", "version":"5.5", "grade":"A"} ] }
$(document).ready(function () { $('#browsers-grid').dataTable({ "sAjaxSource": "datasource/objects.txt", "bProcessing": true, "aoColumns": [ { "mDataProp": "engine" }, { "mDataProp": "browser" }, { "mDataProp": "platform" }, { "mDataProp": "version" }, { "mDataProp": "grade" } ] }); });
{ "data": [ {"engine":"Trident", "browser":"Internet Explorer 4.0", "platform":"Win95+", "version":"4", "grade":"X"}, {"engine":"Trident", "browser":"Internet Explorer 5.0", "platform":"Win95+", "version":"5", "grade":"C"}, {"engine":"Trident", "browser":"Internet Explorer 5.5", "platform":"Win95+", "version":"5.5", "grade":"A"} ] }
"sAjaxDataProp": "data"
{ "data": { "inner": [...] } }
"sAjaxDataProp": "data.inner"
"aLengthMenu": [25, 50, 100, -1],
"aLengthMenu": [[25, 50, 100, -1], ["همه", "صد", "پنجاه", "بیست و پنج"]],
'<"top"iflp<"clear">>rt<"bottom"iflp<"clear">>'
همچنین بین علامتهای کوچکتر (>) و بزرگتر (<) یعنی اگر چیزی بیاید در یک تگ div قرار خواهد گرفت. اگر بخواهیم div ی بسازیم و به آن کلاس بدهیم از نحو زیر استفاده خواهیم کرد:
'<"class" and '>'
'<"#id" and '>'
تا به اینجا مثالهایی که زدهایم تاثیر کامپوننتهای React را بر روی UI، نشان دادند. در این بخش به رویدادهای سمت UI و ورودیهای کاربر میپردازیم.
React روش مدیریت رویدادهای خودش را دارد و به آنها رویدادهای Synthetic یا ترکیبی گفته میشود. در زیر مقایسهای داریم از رویدادهای معمول در JavaScript و رویدادهای React و تفاوتها را بررسی میکنیم.
<!-- HTML Buttons --> <button type="button" onclick="console.log('Button Clicked')">Click Me</button> // React Buttons <button type="button" onClick={console.log("Button Clicked")}>Click Me</button>
رفتار رویدادهای React در مرورگرهای مختلف یکسان است. برای مثال رویداد onChange هر تغییری را برای هر نوع تگ ورودی اعمال میکند. هر کلیدی که در یک input یا textarea زده شود، اگر یک check box را انتخاب یا از انتخاب خارج کنیم و یا اگر موردی را از یک drop-down انتخاب کنیم، React رویداد onChange را اجرا میکند. React اکثر رویدادهای مرسوم را پوشش میدهد و همچنین رویدادهایی را برای کار با کلیپبرد، رسانههای مختلف و تصاویر دارد. برای اطلاعات بیشتر به مستندات آن رجوع کنید.
وقتی با کتابخانه React کار میکنیم، همه چیز مجازی اتفاق میافتد؛ مانند ساخت تگ و نمایش آنها، همچنین مدیریت تگها و رویدادها. اما به این معنا نیست که ارتباط React با HTML DOM در مرورگر قطع است. اگر لازم باشد به HTML DOM در کامپوننتها دسترسی داشته باشیم میتوانیم از خاصیت ref در React استفاده کنیم. برای مثال فرض کنید یک ورودی را برای ایمیل بهصورت <input type="email" /> تعریف کردهایم. میخواهیم پیش از ذخیره بدانیم آیا داده وارد شده به فرمت ایمیل هست یا نه.
const EmailForm = React.createClass({ clickHandler() { if (this.inputEmail.checkValidity()) console.log("Email is OK to save it."); else console.log("Email is not in right format."); }, render() { return ( <div> <input type="email" ref={inputEmail => this.inputEmail = inputEmail} /> <button type="submit" onClick={this.clickHandler}>Save</button> </div> ) }
در مثال بالا clickHandler وظیفه مدیریت رویداد کلیک دکمه را به عهده دارد. در ادامه، وقتی از خاصیت ref در تگ input استفاده میکنیم و مقدار آن را یک تابع قرار میدهیم، React این تابع را زمانیکه کامپوننت به طور کامل در HTML DOM ساخته شد، اجرا میکند. React همچنین ارجاعی را به عنوان پارامتر این تابع به DOM همراه با تابع ارسال میکند (inputEmail). داخل تابع ref میتوانیم به نمونه ساخته شده از کامپوننت در DOM دسترسی داشته باشیم. inputEmail که به صورت ارجاع به تابع فرستاده شده، تگ ساخته شده input را برمیگرداند، در نتیجه میتوانیم در کامپوننت به آن دسترسی داشته باشیم.
اگر از کامپوننتهای Sateful که دارای وضعیت هستند استفاده میکنیم، میتوانیم وضعیت کامپوننت را بر اساس ورودیهای کاربر تغییر دهیم. مثال بالا را به این شکل تغییر میدهیم که در ابتدا وضعیت کامپوننت، یک ایمیل پیشفرض باشد و اگر کاربر آدرس متفاوتی را وارد کرد، آدرس جدید به عنوان وضعیت جدید کامپوننت در نظر گرفته شود.
const EmailForm = React.createClass({ getInitialState() { return { currentEmail: this.props.currentEmail } }, setCurrentEmailState(se) { this.setState({ currentEmail: se.target.value }); }, clickHandler() { if (this.inputEmail.checkValidity()) console.log("Email is OK to save it."); else console.log("Email is not in right format."); }, render() { return ( <div> <input type="email" ref={inputEmail => this.inputEmail = inputEmail} value={this.state.currentEmail} onChange={this.setCurrentEmailState} /> <button type="submit" onClick={this.clickHandler}>Save</button> </div> ) } })
در خط 20 از مثال بالا با قرار دادن مقدار value برابر با ایمیل جاری (وضعیت کامپوننت)، کاربر آدرس پیشفرض را در input میبیند، اما هیچ تغییری را نمیتواند در آن ایجاد کند و input عملا تبدیل به یک تگ فقط خواندنی میشود. علت این است که React دو وضعیت را ایجاد کرده، یکی در حافظه به عنوان وضعیت پیشفرض و دیگری وضعیتی که در DOM ساخته. وقتی در سطح DOM تغییری را ایجاد میکنیم، React به صورت خودکار متوجه آن نمیشود و ما باید با روشی React را در جریان این تغییرات قرار دهیم! برای این کار رویداد onChange را برای تگی که قرار است تغییر کند پیادهسازی میکنیم. در مثال بالا متد setCurrentEmailState و رویداد onChange برای همین منظور به کار گرفته شدهاند.
در قسمت بعد که آخرین قسمت است، به مسئله چرخه زندگی (Lifecycle) کامپوننتهای React میپردازیم.
namespace ExplorerPCal.Hooks { public class GetDateTimeFormatInjection : IEntryPoint { public GetDateTimeFormatInjection(RemoteHooking.IContext context, string channelName) { // connect to host... _interface = RemoteHooking.IpcConnectClient<MessagesReceiverInterface>(channelName); _interface.Ping(); } public void Run(RemoteHooking.IContext context, string channelName) { } } }
public void Run(RemoteHooking.IContext context, string channelName) { GetDateFormatHook = LocalHook.Create( InTargetProc: LocalHook.GetProcAddress("kernel32.dll", "GetDateFormatW"), InNewProc: new GetDateFormatDelegate(getDateFormatInterceptor), InCallback: this);
[DllImport("kernel32.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Auto, SetLastError = true)] public static extern int GetDateFormatW( uint locale, uint dwFlags, // NLS_DATE_FLAGS SystemTime lpDate, [MarshalAs(UnmanagedType.LPWStr)] string lpFormat, StringBuilder lpDateStr, int sbSize);
[UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Auto, SetLastError = true)] private delegate int GetDateFormatDelegate( uint locale, uint dwFlags, SystemTime lpDate, [MarshalAs(UnmanagedType.LPWStr)] string lpFormat, StringBuilder lpDateStr, int sbSize);
private int getDateFormatInterceptor( uint locale, uint dwFlags, SystemTime lpDate, string lpFormat, StringBuilder lpDateStr, int sbSize) { }
public class MessagesReceiverInterface : MarshalByRefObject { public void Ping() { } }
var channel = RemoteHooking.IpcCreateServer<MessagesReceiverInterface>(ref _channelName, WellKnownObjectMode.SingleCall);
RemoteHooking.Inject( explorer.Id, InjectionOptions.Default | InjectionOptions.DoNotRequireStrongName, "ExplorerPCal.Hooks.dll", // 32-bit version (the same, because of using AnyCPU) "ExplorerPCal.Hooks.dll", // 64-bit version (the same, because of using AnyCPU) _channelName );
.ctor(IContext, %ArgumentList%) void Run(IContext, %ArgumentList%)
try { while (true) { Thread.Sleep(500); _interface.Ping(); } } catch { _interface = null; // .NET Remoting will raise an exception if host is unreachable }
Install-Package mongocsharpdriver
public class Author { public ObjectId Id { get; set; } public string Name { get; set; } }
public class Language { public ObjectId Id { get; set; } public string Name { get; set; } }
public class Book { public ObjectId Id { get; set; } public string Title { get; set; } public string ISBN { get; set; } public int Price { get; set; } public List<Author> Authors { get; set; } public Language Language { get; set; } }
var book =new Book() { Title = "Gone With Wind", ISBN = "43442424", Price = 50000, Language = new Language() { Name = "Persian" }, Authors = new List<Author>() { new Author() { Name = "Margaret Mitchell" }, new Author() { Name = "Ali Mahboobi (Translator)" }, } };
var client = new MongoClient();
string connectionString = "mongodb://localhost:27017"; MongoClientSettings settings = MongoClientSettings.FromUrl(new MongoUrl(connectionString)); var client = new MongoClient(settings);
var db = client.GetDatabase("publisher");
var collection = db.GetCollection<Book>("books");
collection.InsertOneAsync(book);
فعلا موجودیتهای مؤلفان و زبان به دلیل اینکه سند اختصاصی برای خود ندارند، با صفر پر شدهاند؛ ولی شناسه یکتای سند، مقدار خود را گرفته است.
عملیات خواندن
var client = new MongoClient(); var db = client.GetDatabase("publisher"); db.DropCollection("books"); var collection = db.GetCollection<Book>("books"); var book =new Book() { Title = "Gone With Wind", ISBN = "43442424", Price = 50000, Year = 1936, LastStock = DateTime.Now.AddDays(-13), Language = new Language() { Name = "Persian" }, Authors = new List<Author>() { new Author() { Name = "Margaret Mitchell" }, new Author() { Name = "Ali Mahboobi (Translator)" }, } }; var book2 = new Book() { Title = "Jane Eyre", ISBN = "87897897", Price = 60000, Year = 1847, LastStock = DateTime.Now.AddDays(-5), Language = new Language() { Name = "English" }, Authors = new List<Author>() { new Author() { Name = "Charlotte Brontë" }, } }; var book3 = new Book() { Title = "White Fang", ISBN = "43442424", Price = 50000, Year = 1936, LastStock = DateTime.Now.AddDays(-13), Language = new Language() { Name = "English" }, Authors = new List<Author>() { new Author() { Name = "Jack London" }, new Author() { Name = "Philippe Mignon" }, } }; var book4 = new Book() { Title = "The Lost Symbol", ISBN = "43442424", Price = 3500000, Year = 2009, LastStock = DateTime.Now.AddDays(-17), Language = new Language() { Name = "Persian" }, Authors = new List<Author>() { new Author() { Name = "Dan Brown" }, new Author() { Name = "Mehrdad" }, } }; var book7 = new Book() { Title = "The Lost Symbol", ISBN = "43442424", Price = 47000000, Year = 2009, LastStock = DateTime.Now.AddDays(-56), Language = new Language() { Name = "Persian" }, Authors = new List<Author>() { new Author() { Name = "Dan Brown" }, new Author() { Name = "Mehrdad" }, } }; var book5= new Book() { Title = "The Help", ISBN = "45345e3er3", Price = 9000000, Year = 2009, LastStock = DateTime.Now.AddDays(-2), Language = new Language() { Name = "Enlish" }, Authors = new List<Author>() { new Author() { Name = "Kathryn Stockett" }, } }; var book6 = new Book() { Title = "City of Glass", ISBN = "454534545", Price = 500000, Year = 2009, LastStock = DateTime.Now, Language = new Language() { Name = "Persian" }, Authors = new List<Author>() { new Author() { Name = "Cassandra Clare" }, new Author() { Name = "Ali" }, } }; var books = new List<Book> {book, book2, book3, book4, book5, book6,book7}; collection.InsertManyAsync(books);
var client = new MongoClient(); var db = client.GetDatabase("publisher"); var collection = db.GetCollection<Book>("books"); var filter=new BsonDocument(); var docs = collection.Find(filter).ToList(); foreach (var book in docs) { Console.WriteLine(book.Title + " By "+ book.Authors[0].Name); }
Gone With Wind By Margaret Mitchell Jane Eyre By Charlotte Brontë White Fang By Jack London The Lost Symbol By Dan Brown The Help By Kathryn Stockett City of Glass By Cassandra Clare The Lost Symbol By Dan Brown
var filter = Builders<Book>.Filter.Eq("Year", 2009); var docs = collection.Find(filter).ToList(); foreach (var book in docs) { Console.WriteLine(book.Title + " By "+ book.Authors[0].Name); }
The Lost Symbol By Dan Brown The Help By Kathryn Stockett City of Glass By Cassandra Clare The Lost Symbol By Dan Brown
// var filter=new BsonDocument(); var filterBuilder = Builders<Book>.Filter; var filter= filterBuilder.Eq("Year", 2009) | filterBuilder.Gte("Price",700000); var docs = collection.Find(filter).ToList(); foreach (var book in docs) { Console.WriteLine(book.Title + " By "+ book.Authors[0].Name); }
Gone With Wind By Margaret Mitchell White Fang By Jack London The Lost Symbol By Dan Brown The Help By Kathryn Stockett City of Glass By Cassandra Clare The Lost Symbol By Dan Brown
var docs = collection.AsQueryable().Where(x => x.Year == 2009 || x.Price <= 50000).ToList();
var sort = Builders<Book>.Sort.Ascending("Title").Descending("Price"); var docs = collection.Find(filter).Sort(sort).ToList(); foreach (var book in docs) { Console.WriteLine(book.Title + " By "+ book.Authors[0].Name); }
City of Glass By Cassandra Clare Gone With Wind By Margaret Mitchell The Help By Kathryn Stockett The Lost Symbol By Dan Brown The Lost Symbol By Dan Brown White Fang By Jack London
اگر در حال تهیه یک سایت چند زبانه هستید و همچنین سری مقالات Globalization در ASP.NET MVC رو دنبال کرده باشید میدانید که با تغییر Culture فایلهای Resource مورد نظر بارگذاری و نوشتههای سایت تغییر میابند ولی با تغییر Culture رفتار اعتبارسنجی در سمت سرور نیز تغییر و اعتبارسنجی بر اساس Culture فعلی سایت انجام میگیرد. بررسی این موضوع را با یک مثال شروع میکنیم.
یک پروژه وب بسازید سپس به پوشه Models یک کلاس با نام ValueModel اضافه کنید. تعریف کلاس به شکل زیر هست:
public class ValueModel { [Required] [Display(Name = "Decimal Value")] public decimal DecimalValue { get; set; } [Required] [Display(Name = "Double Value")] public double DoubleValue { get; set; } [Required] [Display(Name = "Integer Value")] public int IntegerValue { get; set; } [Required] [Display(Name = "Date Value")] public DateTime DateValue { get; set; } }
به سراغ کلاس HomeController بروید و کدهای زیر را اضافه کنید:
[HttpPost] public ActionResult Index(ValueModel valueModel) { if (ModelState.IsValid) { return Redirect("Index"); } return View(valueModel); }
Culture را به fa-IR تغییر میدهیم، برای اینکار در فایل web.config در بخش system.web کد زیر اضافه نمایید:
<globalization culture="fa-IR" uiCulture="fa-IR" />
و در نهایت به سراغ فایل Index.cshtml بروید کدهای زیر رو اضافه کنید:
@using (Html.BeginForm()) { <ol> <li> @Html.LabelFor(m => m.DecimalValue) @Html.TextBoxFor(m => m.DecimalValue) @Html.ValidationMessageFor(m => m.DecimalValue) </li> <li> @Html.LabelFor(m => m.DoubleValue) @Html.TextBoxFor(m => m.DoubleValue) @Html.ValidationMessageFor(m => m.DoubleValue) </li> <li> @Html.LabelFor(m => m.IntegerValue) @Html.TextBoxFor(m => m.IntegerValue) @Html.ValidationMessageFor(m => m.IntegerValue) </li> <li> @Html.LabelFor(m => m.DateValue) @Html.TextBoxFor(m => m.DateValue) @Html.ValidationMessageFor(m => m.DateValue) </li> <li> <input type="submit" value="Submit"/> </li> </ol> }
پرژه را اجرا نمایید و در ٢ تکست باکس اول ٢ عدد اعشاری را و در ٢ تکست باکس آخر یک عدد صحیح و یک تاریخ وارد نمایید و سپس دکمه Submit را بزنید. پس از بازگشت صفحه از سمت سرور در در ٢ تکست باکس اول با این پیامها روبرو میشوید که مقادیر وارد شده نامعتبر میباشند.
اگر پروژه رو در حالت دیباگ اجرا کنیم و نگاهی به داخل ModelState بیاندازیم، میبینیم که کاراکتر جدا کننده قسمت اعشاری برای fa-IR '/' میباشد که در اینجا برای اعداد مورد نظر کاراکتر '.' وارد شده است.
برای فایق شدن بر این مشکل یا باید سمت سرور اقدام کرد یا در سمت کلاینت. در بخش اول راه حل سمت کلاینت را بررسی مینماییم.
در سمت کلاینت برای اینکه کاربر را مجبور به وارد کردن کاراکترهای مربوط به Culture فعلی سایت نماییم باید مقادیر وارد شده را اعتبارسنجی و در صورت معتبر نبودن مقادیر پیام مناسب نشان داده شود. برای اینکار از کتابخانه jQuery Globalize استفاده میکنیم. برای اضافه کردن jQuery Globalize از طریق کنسول nuget فرمان زیر اجرا نمایید:
PM> Install-Package jquery-globalize
پس از نصب کتابخانه اگر به پوشه Scripts نگاهی بیاندازید میبینید که پوشەای با نام jquery.globalize اضافه شده است. درداخل پوشه زیر پوشەی دیگری با نام cultures وجود دارد که در آن Cultureهای مختلف وجود دارد و بسته به نیاز میتوان از آنها استفاده کرد. دوباره به سراغ فایل Index.cshtm بروید و فایلهای جاوا اسکریپتی زیر را به صفحه اضافه کنید:
<script src="~/Scripts/jquery.validate.js"> </script> <script src="~/Scripts/jquery.validate.unobtrusive.js"> </script> <script src="~/Scripts/jquery.globalize/globalize.js"> </script> <script src="~/Scripts/jquery.globalize/cultures/globalize.culture.fa-IR.js"> </script>
در فایل globalize.culture.fa-IR.js کاراکتر جدا کننده اعشاری '.' در نظر گرفته شده است که مجبور به تغییر آن هسیتم. برای اینکار فایل را باز کرده و numberFormat را پیدا کنید و آن را به شکل زیر تغییر دهید:
numberFormat: { pattern: ["n-"], ".": "/", currency: { pattern: ["$n-", "$ n"], ".": "/", symbol: "ریال" } },
و در نهایت کدهای زیر را به فایل Index.cshtml اضافه کنید و برنامه را دوباره اجرا نمایید:
Globalize.culture('fa-IR'); $.validator.methods.number = function(value, element) { if (value.indexOf('.') > 0) { return false; } var splitedValue = value.split('/'); if (splitedValue.length === 1) { return !isNaN(Globalize.parseInt(value)); } else if (splitedValue.length === 2 && $.trim(splitedValue[1]).length === 0) { return false; } return !isNaN(Globalize.parseFloat(value)); }; };
در خط اول Culture را ست مینمایم و در ادامه نحوه اعتبارسنجی را در unobtrusive validation تغییر میدهیم. از آنجایی که برای اعتبارسنجی عدد وارد شده از تابع parseFloat استفاده میشود، کاراکتر جدا کننده قسمت اعشاری قابل قبول برای این تابع '.' است پس در داخل تابع دوباره '/' به '.' تبدیل میشود و سپس اعتبارسنجی انجام میشود از اینرو اگر کاربر '.' را نیز وارد نماید قابل قبول است به همین دلیل با این خط کد if (value.indexOf('.') > 0) وجود نقطه را بررسی میکنیم تا در صورت وجود '.' پیغام خطا نشان داده شود.در خط بعدی بررسی مینماییم که اگر عدد وارد شده اعشاری نباشد از تابع parseInt استفاده نماییم. در خط بعدی این حالت را بررسی مینماییم که اگر کاربر عددی همچون /١٢ وارد کرد پیغام خطا صادر شود.
برای اعتبارسنجی تاریخ شمسی متاسفانه توابع کمکی برای تبدیل تاریخ در فایل globalize.culture.fa-IR.js وجود ندارد ولی اگر نگاهی به فایلهای Culture عربی بیاندازید همه دارای توابع کمکی برای تبدیل تاریج هجری به میلادی هستند به همین دلیل امکان اعتبارسنجی تاریخ شمسی با استفاده از jQuery Globalize میسر نمیباشد. من خودم تعدادی توابع کمکی را به globalize.culture.fa-IR.js اضافه کردەام که از تقویم فارسی آقای علی فرهادی برداشت شده است و با آنها کار اعتبارسنجی را انجام میدهیم. لازم به ذکر است این روش ١٠٠% تست نشده است و شاید راه کاملا اصولی نباشد ولی به هر حال در اینجا توضیح میدهم. در فایل globalize.culture.fa-IR.js قسمت Gregorian_Localized را پیدا کنید و آن را با کدهای زیر جایگزین کنید:
Gregorian_Localized: { firstDay: 6, days: { names: ["یکشنبه", "دوشنبه", "سه شنبه", "چهارشنبه", "پنجشنبه", "جمعه", "شنبه"], namesAbbr: ["یکشنبه", "دوشنبه", "سه شنبه", "چهارشنبه", "پنجشنبه", "جمعه", "شنبه"], namesShort: ["ی", "د", "س", "چ", "پ", "ج", "ش"] }, months: { names: ["ژانویه", "فوریه", "مارس", "آوریل", "می", "ژوئن", "ژوئیه", "اوت", "سپتامبر", "اُکتبر", "نوامبر", "دسامبر", ""], namesAbbr: ["ژانویه", "فوریه", "مارس", "آوریل", "می", "ژوئن", "ژوئیه", "اوت", "سپتامبر", "اُکتبر", "نوامبر", "دسامبر", ""] }, AM: ["ق.ظ", "ق.ظ", "ق.ظ"], PM: ["ب.ظ", "ب.ظ", "ب.ظ"], patterns: { d: "yyyy/MM/dd", D: "yyyy/MM/dd", t: "hh:mm tt", T: "hh:mm:ss tt", f: "yyyy/MM/dd hh:mm tt", F: "yyyy/MM/dd hh:mm:ss tt", M: "dd MMMM" }, JalaliDate: { g_days_in_month: [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31], j_days_in_month: [31, 31, 31, 31, 31, 31, 30, 30, 30, 30, 30, 29] }, gregorianToJalali: function (gY, gM, gD) { gY = parseInt(gY); gM = parseInt(gM); gD = parseInt(gD); var gy = gY - 1600; var gm = gM - 1; var gd = gD - 1; var gDayNo = 365 * gy + parseInt((gy + 3) / 4) - parseInt((gy + 99) / 100) + parseInt((gy + 399) / 400); for (var i = 0; i < gm; ++i) gDayNo += Globalize.culture().calendars.Gregorian_Localized.JalaliDate.g_days_in_month[i]; if (gm > 1 && ((gy % 4 == 0 && gy % 100 != 0) || (gy % 400 == 0))) /* leap and after Feb */ ++gDayNo; gDayNo += gd; var jDayNo = gDayNo - 79; var jNp = parseInt(jDayNo / 12053); jDayNo %= 12053; var jy = 979 + 33 * jNp + 4 * parseInt(jDayNo / 1461); jDayNo %= 1461; if (jDayNo >= 366) { jy += parseInt((jDayNo - 1) / 365); jDayNo = (jDayNo - 1) % 365; } for (var i = 0; i < 11 && jDayNo >= Globalize.culture().calendars.Gregorian_Localized.JalaliDate.j_days_in_month[i]; ++i) { jDayNo -= Globalize.culture().calendars.Gregorian_Localized.JalaliDate.j_days_in_month[i]; } var jm = i + 1; var jd = jDayNo + 1; return [jy, jm, jd]; }, jalaliToGregorian: function (jY, jM, jD) { jY = parseInt(jY); jM = parseInt(jM); jD = parseInt(jD); var jy = jY - 979; var jm = jM - 1; var jd = jD - 1; var jDayNo = 365 * jy + parseInt(jy / 33) * 8 + parseInt((jy % 33 + 3) / 4); for (var i = 0; i < jm; ++i) jDayNo += Globalize.culture().calendars.Gregorian_Localized.JalaliDate.j_days_in_month[i]; jDayNo += jd; var gDayNo = jDayNo + 79; var gy = 1600 + 400 * parseInt(gDayNo / 146097); /* 146097 = 365*400 + 400/4 - 400/100 + 400/400 */ gDayNo = gDayNo % 146097; var leap = true; if (gDayNo >= 36525) /* 36525 = 365*100 + 100/4 */ { gDayNo--; gy += 100 * parseInt(gDayNo / 36524); /* 36524 = 365*100 + 100/4 - 100/100 */ gDayNo = gDayNo % 36524; if (gDayNo >= 365) gDayNo++; else leap = false; } gy += 4 * parseInt(gDayNo / 1461); /* 1461 = 365*4 + 4/4 */ gDayNo %= 1461; if (gDayNo >= 366) { leap = false; gDayNo--; gy += parseInt(gDayNo / 365); gDayNo = gDayNo % 365; } for (var i = 0; gDayNo >= Globalize.culture().calendars.Gregorian_Localized.JalaliDate.g_days_in_month[i] + (i == 1 && leap) ; i++) gDayNo -= Globalize.culture().calendars.Gregorian_Localized.JalaliDate.g_days_in_month[i] + (i == 1 && leap); var gm = i + 1; var gd = gDayNo + 1; return [gy, gm, gd]; }, checkDate: function (jY, jM, jD) { return !(jY < 0 || jY > 32767 || jM < 1 || jM > 12 || jD < 1 || jD > (Globalize.culture().calendars.Gregorian_Localized.JalaliDate.j_days_in_month[jM - 1] + (jM == 12 && !((jY - 979) % 33 % 4)))); }, convert: function (value, format) { var day, month, year; var formatParts = format.split('/'); var dateParts = value.split('/'); if (formatParts.length !== 3 || dateParts.length !== 3) { return false; } for (var j = 0; j < formatParts.length; j++) { var currentFormat = formatParts[j]; var currentDate = dateParts[j]; switch (currentFormat) { case 'dd': if (currentDate.length === 2 || currentDate.length === 1) { day = currentDate; } else { year = currentDate; } break; case 'MM': month = currentDate; break; case 'yyyy': if (currentDate.length === 4) { year = currentDate; } else { day = currentDate; } break; default: return false; } } year = parseInt(year); month = parseInt(month); day = parseInt(day); var isValidDate = Globalize.culture().calendars.Gregorian_Localized.checkDate(year, month, day); if (!isValidDate) { return false; } var grDate = Globalize.culture().calendars.Gregorian_Localized.jalaliToGregorian(year, month, day); var shDate = Globalize.culture().calendars.Gregorian_Localized.gregorianToJalali(grDate[0], grDate[1], grDate[2]); if (year === shDate[0] && month === shDate[1] && day === shDate[2]) { return true; } return false; } },
روال کار در تابع convert به اینصورت است که ابتدا تاریخ وارد شده را بررسی مینماید تا معتبر بودن آن معلوم شود به عنوان مثال اگر تاریخی مثل 1392/12/31 وارد شده باشد و در ادامه برای بررسی بیشتر تاریخ یک بار به میلادی و تاریخ میلادی دوباره به شمسی تبدیل میشود و با تاریخ وارد شده مقایسه میشود و در صورت برابری تاریخ معتبر اعلام میشود. در فایل Index.cshtml کدهای زیر اضافی نمایید:
$.validator.methods.date = function (value, element) { return Globalize.culture().calendars.Gregorian_Localized.convert(value, 'yyyy/MM/dd'); };
برای اعتبارسنجی تاریخ میتوانید از ٢ فرمت استفاده کنید:
١ – yyyy/MM/dd
٢ – dd/MM/yyyy
البته از توابع اعتبارسنجی تاریخ میتوانید به صورت جدا استفاده نمایید و لزومی ندارد آنها را همراه با jQuery Globalize بکار ببرید. در آخر خروجی کار به این شکل است:
در کل استفاده از jQuery Globalize برای اعتبارسنجی در سایتهای چند زبانه به نسبت خوب میباشد و برای هر زبان میتوانید از culture مورد نظر استفاده نمایید. در قسمت دوم این مطلب به بررسی بخش سمت سرور میپردازیم.
@attribute [ProtectedPage(GroupName = "Feature 1", Title = "Page 1", GlyphIcon = "bi bi-dot", GroupOrder = 1, ItemOrder = 1)]
app.UseMvcWithDefaultRoute();
public static IApplicationBuilder UseMvcWithDefaultRoute(this IApplicationBuilder app) { if (app == null) throw new ArgumentNullException("app"); return app.UseMvc((Action<IRouteBuilder>) (routes => routes.MapRoute("default", "{controller=Home}/{action=Index}/{id?}"))); }
[Route("[controller]/[action]")]
namespace Core1RtmEmptyTest.Controllers { public class HomeController { public string Index() { return "Running a POCO controller!"; } } }
app.UseMvc(routes => { routes.MapRoute( name: "default", template: "{controller=Home}/{action=Index}/{id?}"); });
using Microsoft.AspNetCore.Mvc; namespace Core1RtmEmptyTest.Controllers { public class AboutController : Controller { public ActionResult Hello() { return Content("Hello from DNT!"); } public ActionResult SiteName() { return Content("DNT"); } } }
using Microsoft.AspNetCore.Mvc; namespace Core1RtmEmptyTest.Controllers { [Route("About")] public class AboutController : Controller { [Route("")] public ActionResult Hello() { return Content("Hello from DNT!"); } [Route("SiteName")] public ActionResult SiteName() { return Content("DNT"); } } }
AmbiguousActionException: Multiple actions matched. The following actions matched route data and had all constraints satisfied: Core1RtmEmptyTest.Controllers.AboutController.Hello (Core1RtmEmptyTest) Core1RtmEmptyTest.Controllers.AboutController.SiteName (Core1RtmEmptyTest)
using Microsoft.AspNetCore.Mvc; namespace Core1RtmEmptyTest.Controllers { [Route("[controller]")] public class AboutController : Controller { [Route("")] public ActionResult Hello() { return Content("Hello from DNT!"); } [Route("[action]")] public ActionResult SiteName() { return Content("DNT"); } } }
[Route("api/[controller]")]
//[Route("/Users/{userid}")] [Route("Users/{userid}")] public IActionResult GetUsers(int userId) { return Json(new { userId = userId }); }
[Route("/Users/{userid:int?}")]
[Route("Users/{userid:int}")]
[Route("/Users/{userid:int:max(1000):min(10)}")]
[Route("/Users/{userid:int}", Name="GetUserById")]
string uri = Url.Link("GetUserById", new { userid = 1 });
[Route("/Users/{userid:int}", Name = "GetUserById", Order = 1)]
using System; using System.Globalization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Routing; namespace Core1RtmEmptyTest { public class CustomRouteConstraint : IRouteConstraint { public bool Match(HttpContext httpContext, IRouter route, string routeKey, RouteValueDictionary values, RouteDirection routeDirection) { object value; if (!values.TryGetValue(routeKey, out value) || value == null) { return false; } long longValue; if (value is long) { longValue = (long)value; return longValue != 10; } var valueString = Convert.ToString(value, CultureInfo.InvariantCulture); if (long.TryParse(valueString, NumberStyles.Integer, CultureInfo.InvariantCulture, out longValue)) { return longValue != 10; } return false; } } }
public class CustomRouteConstraint : IRouteConstraint
public void ConfigureServices(IServiceCollection services) { services.AddRouting(options =>options.ConstraintMap.Add("Custom", typeof(CustomRouteConstraint)));
[Route("/Users/{userid:int:custom}")]
app.UseMvc(routes => { routes.MapRoute( name: "default", template: "{controller=Home}/{action=Index}/{id?}"); routes.MapRoute( name: "spa-fallback", template: "{*url}", defaults: new { controller = "Home", action = "Index" }); });
{ "dependencies": { //same as before "Microsoft.AspNetCore.SpaServices": "1.0.0-beta-000007" },
app.UseMvc(routes => { routes.MapRoute( name: "default", template: "{controller=Home}/{action=Index}/{id?}"); routes.MapSpaFallbackRoute("spa-fallback", new { controller = "Home", action = "Index" }); });
// Serve wwwroot as root app.UseFileServer(); // Serve /node_modules as a separate root (for packages that use other npm modules client side) app.UseFileServer(new FileServerOptions { // Set root of file server FileProvider = new PhysicalFileProvider(Path.Combine(Directory.GetCurrentDirectory(), "node_modules")), // Only react to requests that match this path RequestPath = "/node_modules", // Don't expose file system EnableDirectoryBrowsing = false });