اشتراکها
اشتراکها
Firefox و امکان ترجمهی صفحات
نظرات مطالب
پشتیبانی توکار از GDPR در ASP.NET Core 2.1
آن مدل دیگر کار با ASP.NET Core هست به نام «صفحات Razor».
پاسخ به بازخوردهای پروژهها
ارتفاع داینامیک
امکان ایجاد فایل PDF ایی که اندازهی صفحات آن با هم متفاوت هست، با خود iTextSharp وجود دارد.
بازخوردهای پروژهها
نحوه دریافت تعداد صفحات گزارش در هدر یا فوتر بصورت عدد
با سلام.
چگونه میتوانم به تعداد صفحات گزارش دسترسی پیدا کنم؟
با تشکر.
صورت مساله:
تعدادی تصویر داریم، میخواهیم اینها را تبدیل به فایل PDF کنیم به این شرط که هر تصویر در یک صفحه مجزا قرار داده شود.
در ادامه برای این منظور از کتابخانهی iTextSharp استفاده خواهیم کرد.
iTextSharp چیست؟
iTextSharp کتابخانهی سورس باز و معروفی جهت تولید فایلهای PDF ، توسط برنامههای مبتنی بر دات نت است. آن را از آدرس زیر میتوان دریافت کرد:
کتابخانه iTextSharp نیز جزو کتابخانههایی است که از جاوا به دات نت تبدیل شدهاند. نام کتابخانه اصلی iText است و اگر کمی جستجو کنید میتوانید کتاب 617 صفحهای iText in Action از انتشارات MANNING را در این مورد نیز بیابید. هر چند این کتاب برای برنامه نویسهای جاوا نوشته شده اما نام کلاسها و متدها در iTextSharp تفاوتی با iText اصلی ندارند و مطالب آن برای برنامه نویسهای دات نت هم قابل استفاده است.
مجوز استفاده از iTextSharp کدام است؟
مجوز این کتابخانه GNU Affero General Public License است. به این معنا که شما موظفید، تغییری در قسمت تهیه کننده خواص فایل PDF تولیدی که به صورت خودکار به نام کتابخانه تنظیم میشود، ندهید. اگر میخواهید این قسمت را تغییر دهید باید هزینه کنید. همچنین با توجه به اینکه این مجوز، GPL است یعنی زمانیکه از آن استفاده کردید باید کار خود را به صورت سورس باز ارائه دهید؛ درست خوندید! بله! مثل مجوز استفاده از نگارش عمومی و رایگان MySQL و اگر نمیخواهید اینکار را انجام دهید، در اینجا تاکید شده که باید کتابخانه را خریداری کنید.
نحوه استفاده از کتابخانه iTextSharp
در ابتدا کد تبدیل تصاویر به فایل PDF را در ذیل مشاهده خواهید کرد. فرض بر این است که ارجاعی را به اسمبلی itextsharp.dll اضافه کردهاید:
using System.Collections.Generic;
using System.Drawing.Imaging;
using System.IO;
using iTextSharp.text;
using iTextSharp.text.pdf;
namespace iTextSharpTests
{
public class ImageToPdf
{
public iTextSharp.text.Rectangle PdfPageSize { set; get; }
public ImageFormat ImageCompressionFormat { set; get; }
public bool FitImagesToPage { set; get; }
public void ExportToPdf(IList<string> imageFilesPath, string outPdfPath)
{
using (var pdfDoc = new Document(PdfPageSize))
{
PdfWriter.GetInstance(pdfDoc, new FileStream(outPdfPath, FileMode.Create));
pdfDoc.Open();
foreach (var file in imageFilesPath)
{
var pngImg = iTextSharp.text.Image.GetInstance(file);
if (FitImagesToPage)
{
pngImg.ScaleAbsolute(pdfDoc.PageSize.Width, pdfDoc.PageSize.Height);
}
pngImg.SetAbsolutePosition(0, 0);
//add to page
pdfDoc.Add(pngImg);
//start a new page
pdfDoc.NewPage();
}
}
}
}
}
استفاده از کتابخانهی iTextSharp همیشه شامل 5 مرحله است. ابتدا شیء Document ایجاد میشود. سپس وهلهای از PdfWriter ساخته شده و Document جهت نوشتن در آن گشوده خواهد شد. در طی یک سری مرحله محتویات مورد نظر به Document اضافه شده و نهایتا این شیء بسته خواهد شد. البته در اینجا چون کلاس Document اینترفیس IDisposable را پیاده سازی کرده، بهترین روش استفاده از آن بکارگیری واژه کلیدی using جهت مدیریت منابع آن است. به این ترتیب کامپایلر به صورت خودکار قطعه try/finally مرتبط را جهت پاکسازی منابع، تشکیل خواهد داد.
اندازه صفحات توسط سازندهی شیء Document مشخص خواهند شد. این شیء از نوع iTextSharp.text.Rectangle است؛ اما مقدار دهی آن توسط کلاس iTextSharp.text.PageSize صورت میگیرد که انواع و اقسام اندازه صفحات استاندارد در آن تعریف شدهاند.
متد iTextSharp.text.Image.GetInstance که در این مثال جهت دریافت اطلاعات تصاویر مورد استفاده قرار گرفت، 15 overload دارد که از آدرس مستقیم یک فایل تا استریم مربوطه تا Uri یک آدرس وب را نیز میپذیرد و از این لحاظ بسیار غنی است.
مثالی در مورد نحوه استفاده از کلاس فوق:
using System.Collections.Generic;
using System.Drawing.Imaging;
namespace iTextSharpTests
{
class Program
{
static void Main(string[] args)
{
new ImageToPdf
{
FitImagesToPage = true,
ImageCompressionFormat = ImageFormat.Jpeg,
PdfPageSize = iTextSharp.text.PageSize.A4
}.ExportToPdf(
imageFilesPath: new List<string>
{
@"D:\3.jpg",
@"D:\4.jpg"
},
outPdfPath: @"D:\tst.pdf"
);
}
}
}
پیش از بحث در مورد «مدیریت حالت»، باید با مفهوم «حالت» آشنا شد. «حالت» در اینجا همان لایهی دادههای برنامه است. زمانیکه بحث React و کتابخانههای مدیریت حالت آن مطرح میشود، میتوان گفت حالت، شیءای است حاوی اطلاعاتی که برنامه با آن سر و کار دارد. برای مثال اگر برنامهای قرار است لیستی از موارد را نمایش دهد، حالت برنامه، حاوی اشیاء متناظری خواهد بود. حالت، بر روی نحوهی رفتار و رندر کامپوننتهای React تاثیر میگذارد. بنابراین مدیریت حالت، روشی است برای ردیابی و مدیریت دادههای مورد استفادهی در برنامه و تقریبا تمام برنامهها به نحوی نیاز به آنرا خواهند داشت.
داشتن یک کتابخانهی مدیریت حالت برای برنامههای React بسیار مفید است؛ خصوصا اگر این برنامه پیچیده باشد و برای مثال در آن نیاز به اشتراک گذاری دادهها، بین دو کامپوننت یا بیشتر که در یک رده سلسه مراتبی قرار نمیگیرند، وجود داشته باشد. اما حتی اگر از یک کتابخانهی مدیریت حالت استفاده شود، شاید راه حلی را که ارائه میکند آنچنان تمیز و قابل انتظار نباشد. با MobX میتوان از ساختارهای پیچیدهی شیءگرا به سادگی استفاده کرد (mutation مستقیم اشیاء در آن مجاز است) و همچنین برای کار با آن به همراه React، نیاز به کدهای کمتری است نسبت به Redux. در اینجا از مفاهیم Reactive programming استفاده میشود؛ اما سعی میکند پیچیدگیهای آنرا مخفی کند. در نام MobX، حرف X به Reactive بودن آن اشاره میکند (مانند RxJS) و ob آن از observable گرفته شدهاست. M هم به حرف ابتدای نام شرکتی اشاره میکند که این کتابخانه را ایجاد کردهاست.
خواص محاسبه شده در جاوا اسکریپت
برای کار با MobX، نیاز است تا ابتدا با یکسری از مفاهیم آن آشنا شد؛ مانند خواص محاسبه شده (computed properties). برای مثال در اینجا یک کلاس متداول جاوا اسکریپتی را داریم:
که در آن از طریق سازنده، دو پارامتر نام و نام خانوادگی دریافت شده و سپس به دو خاصیت جدید، نسبت داده شدهاند. اکنون برای محاسبهی نام کامل، که حاصل جمع این دو است، میتوان متد fullName را به این کلاس اضافه کرد. روش کار با آن نیز به صورت زیر است:
اگر بر اساس متغیر person که بیانگر وهلهای از شیء Person است، سعی در خواندن مقادیر خواص شیء ایجاد شده کنیم، آنها را دریافت خواهیم کرد. اما ذکر person.fullName (بدون هیچ () در مقابل آن)، تنها اشارهگری را به آن متد بازگشت میدهد و نه نام کامل را که البته یکی از ویژگیهای جالب جاوا اسکریپت است و امکان ارسال آنرا به سایر متدها، به صورت پارامتر میسر میکند.
در ES6 برای اینکه تنها با ذکر person.fullName بدون هیچ پرانتزی در مقابل آن بتوان به مقدار کامل fullName رسید، میتوان از روش زیر و با ذکر واژهی کلیدی get، در پیش از نام متد، استفاده کرد:
در اینجا هرچند fullName هنوز یک متد است، اما اینبار فراخوانی person.fullName، حاصل جمع دو خاصیت را بازگشت میدهد و نه اشارهگری به آن متد را.
اگر شبیه به همین قطعه کد را بخواهیم در ES5 پیاده سازی کنیم، روش آن به صورت زیر است:
به این ترتیب میتوان یک خاصیت محاسبه شدهی ویژهی ES5 را تعریف کرد.
اکنون فرض کنید قسمتی از state برنامهی React، قرار است خاصیت ویژهی fullName را نمایش دهد. برای اینکه UI برنامه با تغییرات نام و نام خانوادگی، متوجه تغییرات fullName که یک خاصیت محاسباتی است، شود و آنرا رندر مجدد کند، باید در طی یک حلقهی بینهایت، مدام آنرا فراخوانی کند و نتیجهی جدید را با نتیجهی قبلی محاسبه کرده و تغییرات را نمایش دهد. اینجا است که MobX یک چنین پیاده سازیهایی را به کمک مفهوم decorators، ساده میکند.
Decorators در جاوا اسکریپت
تزئین کنندهها یا decorators در سایر زبانهای برنامه نویسی نیز وجود دارند؛ اما پیاده سازی آنها در جاوا اسکریپت هنوز در مرحلهی آزمایشی است. Decorators در جاوا اسکریپت چیزی نیستند بجز بیان زیبای higher-order functions.
higher-order functions، توابعی هستند که توابع دیگر را با ارائهی قابلیتهای بیشتری، محصور میکنند. به همین جهت هر کاری را که بتوان با تزئین کنندهها انجام داد، همان را با توابع معمولی جاوا اسکریپتی نیز میتوان انجام داد. یک نمونه از این higher-order functions را در سری جاری تحت عنوان higher-order components با متد connect کتابخانهی react-redux مشاهده کردهایم. متد connect، متدی است که متدهای نگاشت state به props و نگاشت dispatch به props را دریافت کرده و سپس یک کامپوننت را نیز دریافت میکند و آنرا به صورت محصور شدهای ارائه میدهد تا بجای کامپوننت اصلی مورد استفاده قرار گیرد؛ به یک چنین کامپوننتهایی، higher-order components گفته میشود.
برای تعریف تزئین کنندهها، به نحوهی پیاده سازی Object.defineProperty در مثال فوق دقت کنید:
در اینجا Person.prototype یک target است. ثابت fullName، یک کلید است. سایر خواص ذکر شده، مانند enumerable، writable و get، تحت عنوان Descriptor شناخته میشوند.
در ذیل روش تعریف یک تزئین کننده را مشاهده میکنید که دقیقا از یک چنین الگویی پیروی میکند:
برای مثال در اینجا روش پیاده سازی تزئین کنندهی readonly را ملاحظه میکنید:
سپس روش اعمال آن به یک خاصیت محاسباتی در کلاس Person به صورت زیر است:
ذکر یک تزئین کننده با @ شروع میشود. سپس متد fullName را دریافت کرده و نگارش جدیدی از آنرا بازگشت میدهد؛ بطوریکه readonly باشد.
مثالهایی از تزئین کنندهها
برای نمونه میتوان تزئین کنندهی bindThis@ را طراحی کرد تا کار bind شیء this را به متدهای کامپوننتهای React انجام دهد و یا کتابخانهای به نام core-decorators وجود دارد که به صورت زیر نصب میشود:
و به همراه این تزئین کنندهها میباشد:
برای مثال autobind آن، همان کار bind شیء this را انجام میدهد. deprecate جهت نمایش یک اخطار، در کنسول توسعه دهندگان مرورگر، جهت گوشزد کردن منسوخ بودن قسمتی از برنامه، استفاده میشود.
نمونهی دیگری از این کتابخانهها lodash-decorators است که تعدادی دیگر از تزئین کنندهها را ارائه میکند.
MobX چگونه کار میکند؟
انجام یکسری از کارها با Redux مشکل است؛ برای مثال تغییر دادن یک شیء تو در توی پیچیده که شامل تهیهی یک کپی از آن، اعمال تغییرات و غیرهاست. اما با MobX میتوان با اشیاء جاوا اسکریپتی به همان صورتی که هستند کار کرد. برای مثال آرایهای را با متدهای push و pop تغییر داد (mutation اشیاء مجاز است) و یا خواص اشیاء را به صورت مستقیم ویرایش کرد، در این حالت MobX اعلام میکند که ... من میدانم که چه تغییری صورت گرفتهاست. بنابراین سبب رندر مجدد UI خواهم شد.
ایجاد یک برنامهی خالی React برای آزمایش MobX
در اینجا برای بررسی MobX، یک پروژهی جدید React را ایجاد میکنیم:
در ادامه کتابخانهی mobx را نیز نصب میکنیم. برای این منظور پس از باز کردن پوشهی اصلی برنامه توسط VSCode، دکمههای ctrl+` را فشرده (ctrl+back-tick) و دستور زیر را در ترمینال ظاهر شده وارد کنید:
البته برای کار با MobX، الزاما نیازی به طی مراحل فوق نیست؛ ولی چون این قالب، یک محیط آمادهی کار با ES6 را فراهم میکند، به سادگی میتوان فایل index.js آنرا خالی کرد و سپس شروع به کدنویسی و آزمایش MobX نمود.
مثالی از MobX، مستقل از React
در اینجا نیز همانند روشی که در بررسی Redux در پیش گرفتیم، ابتدا MobX را به صورت کاملا مستقل از React، با یک مثال بررسی میکنیم و سپس در قسمتهای بعد آنرا به React متصل میکنیم. برای این منظور ابتدا فایل src\index.js را به صورت زیر تغییر میدهیم:
در اینجا یک text-box، به همراه دو div، در صفحه رندر خواهند شد که قرار است با ورود اطلاعاتی در text-box، یکی از آنها (text-display) این اطلاعات را به صورت معمولی و دیگری (text-display-uppercase) آنرا به صورت uppercase نمایش دهد. روش کار انجام شده هم مستقل از React است و به صورت مستقیم، با استفاده از DOM API و document.getElementById عمل شدهاست. همچنین در ابتدای این فایل، دو import را از کتابخانهی mobx مشاهده میکنید.
- با استفاده از observable میخواهیم تغییرات یک شیء جاوا اسکریپتی را تحت نظر قرار داده و هر زمانیکه تغییری در شیء رخ داد، از آن مطلع شویم.
برای مثال شیء سادهی جاوا اسکریپتی زیر را در نظر بگیرید:
این شیء دارای دو خاصیت است که یکی معمولی و دیگری به صورت یک خاصیت محاسباتی، تعریف شدهاست. مشکلی که با این شیء وجود دارد این است که اگر مقدار خاصیت value آن تغییر کند، از آن مطلع نخواهیم شد تا پس از آن برای مثال در مورد رندر مجدد DOM، تصمیم گیری شود. چون از دیدگاه React، مقدار ارجاع به این شیء با تغییر خواص آن، تغییری نمیکند. به همین جهت اگر نحوهی مقایسه، بر اساس مقایسهی ارجاعات به اشیاء باشد (strict === reference check)، چون شیء تغییر یافته نیز به همان شیء اصلی اشاره میکند، بنابراین دارای ارجاع یکسانی خواهند بود و سبب رندر مجدد DOM نمیشوند.
به همین جهت اینبار شیء فوق را توسط یک observable ارائه میدهیم، تا بتوانیم به تغییرات خواص آن گوش فرا دهیم:
در ادامه یک EventListener را به text-box تعریف شده اضافه کرده و در رخداد keyup آن، سبب تغییر خاصیت value شیء text فوق، بر اساس مقدار تایپ شده میشویم:
اکنون چون شیء text، یک observable است، هر زمانیکه که خاصیتی از آن تغییر میکند، میخواهیم بر اساس آن، DOM را به صورت دستی به روز رسانی کنیم. برای اینکار نیاز به متد autorun دریافتی از mobx خواهیم داشت:
هر زمانیکه شیء observable ای که داخل متد autorun تحت نظر قرار گرفته شده، تغییر کند، سبب اجرای callback method ارسالی به آن خواهد شد. برای مثال در اینجا چون text.value را به event.target.value متصل کردهایم، هربار که کلیدی فشرده میشود، سبب بروز تغییری در خاصیت value خواهد شد. در نتیجهی آن، autorun اجرا شده و مقادیر درج شدهی در divهای صفحه را بر اساس خواص value و uppercase شیء text، تغییر میدهد:
برای آزمایش آن، برنامه را اجرا کرده و متنی را داخل textbox وارد کنید:
نکتهی جالب اینجا است که هرچند فقط خاصیت value را تغییر دادهایم (تغییر مستقیم خواص یک شیء؛ بدون نیاز به ساخت یک clone از آن)، اما خاصیت محاسباتی uppercase نیز به روز رسانی شدهاست.
زمانیکه mobx را به یک برنامهی React متصل میکنیم، قسمت autorun، از دید ما مخفی خواهد بود. در این حالت فقط یک شیء معمولی جاوا اسکریپتی را مستقیما تغییر میدهیم و ... در نتیجهی آن رندر مجدد UI صورت خواهد گرفت.
یک observable چگونه کار میکند؟
در اینجا یک شبهکد را که بیانگر نحوهی عملکرد یک observable است، مشاهده میکنید:
یک observable هنگامیکه شیءای را در بر میگیرد. هر زمانیکه مقدار جدیدی را به خاصیتی از آن نسبت دادیم، سبب فراخوانی متد onChange شده و به این صورت است که کتابخانهی MobX متوجه تغییرات میگردد و بر اساس آن امکان ردیابی تغییرات را میسر میکند.
کدهای کامل این قسمت را میتوانید از اینجا دریافت کنید: state-management-with-mobx-part1.zip
داشتن یک کتابخانهی مدیریت حالت برای برنامههای React بسیار مفید است؛ خصوصا اگر این برنامه پیچیده باشد و برای مثال در آن نیاز به اشتراک گذاری دادهها، بین دو کامپوننت یا بیشتر که در یک رده سلسه مراتبی قرار نمیگیرند، وجود داشته باشد. اما حتی اگر از یک کتابخانهی مدیریت حالت استفاده شود، شاید راه حلی را که ارائه میکند آنچنان تمیز و قابل انتظار نباشد. با MobX میتوان از ساختارهای پیچیدهی شیءگرا به سادگی استفاده کرد (mutation مستقیم اشیاء در آن مجاز است) و همچنین برای کار با آن به همراه React، نیاز به کدهای کمتری است نسبت به Redux. در اینجا از مفاهیم Reactive programming استفاده میشود؛ اما سعی میکند پیچیدگیهای آنرا مخفی کند. در نام MobX، حرف X به Reactive بودن آن اشاره میکند (مانند RxJS) و ob آن از observable گرفته شدهاست. M هم به حرف ابتدای نام شرکتی اشاره میکند که این کتابخانه را ایجاد کردهاست.
خواص محاسبه شده در جاوا اسکریپت
برای کار با MobX، نیاز است تا ابتدا با یکسری از مفاهیم آن آشنا شد؛ مانند خواص محاسبه شده (computed properties). برای مثال در اینجا یک کلاس متداول جاوا اسکریپتی را داریم:
class Person { constructor(firstName, lastName) { this.firstName = firstName; this.lastName = lastName; } fullName() { return `${this.firstName} ${this.lastName}`; } }
const person = new Person('Vahid', 'N'); person.firstName; // 'Vahid' person.lastName; // 'N' person.fullName; // function fullName() {…}
در ES6 برای اینکه تنها با ذکر person.fullName بدون هیچ پرانتزی در مقابل آن بتوان به مقدار کامل fullName رسید، میتوان از روش زیر و با ذکر واژهی کلیدی get، در پیش از نام متد، استفاده کرد:
class Person { constructor(firstName, lastName) { this.firstName = firstName; this.lastName = lastName; } get fullName() { return `${this.firstName} ${this.lastName}`; } }
اگر شبیه به همین قطعه کد را بخواهیم در ES5 پیاده سازی کنیم، روش آن به صورت زیر است:
function Person(firstName, lastName) { this.firstName = firstName; this.lastName = lastName; } Object.defineProperty(Person.prototype, 'fullName', { get: function () { return this.firstName + ' ' + this.lastName; } });
اکنون فرض کنید قسمتی از state برنامهی React، قرار است خاصیت ویژهی fullName را نمایش دهد. برای اینکه UI برنامه با تغییرات نام و نام خانوادگی، متوجه تغییرات fullName که یک خاصیت محاسباتی است، شود و آنرا رندر مجدد کند، باید در طی یک حلقهی بینهایت، مدام آنرا فراخوانی کند و نتیجهی جدید را با نتیجهی قبلی محاسبه کرده و تغییرات را نمایش دهد. اینجا است که MobX یک چنین پیاده سازیهایی را به کمک مفهوم decorators، ساده میکند.
Decorators در جاوا اسکریپت
تزئین کنندهها یا decorators در سایر زبانهای برنامه نویسی نیز وجود دارند؛ اما پیاده سازی آنها در جاوا اسکریپت هنوز در مرحلهی آزمایشی است. Decorators در جاوا اسکریپت چیزی نیستند بجز بیان زیبای higher-order functions.
higher-order functions، توابعی هستند که توابع دیگر را با ارائهی قابلیتهای بیشتری، محصور میکنند. به همین جهت هر کاری را که بتوان با تزئین کنندهها انجام داد، همان را با توابع معمولی جاوا اسکریپتی نیز میتوان انجام داد. یک نمونه از این higher-order functions را در سری جاری تحت عنوان higher-order components با متد connect کتابخانهی react-redux مشاهده کردهایم. متد connect، متدی است که متدهای نگاشت state به props و نگاشت dispatch به props را دریافت کرده و سپس یک کامپوننت را نیز دریافت میکند و آنرا به صورت محصور شدهای ارائه میدهد تا بجای کامپوننت اصلی مورد استفاده قرار گیرد؛ به یک چنین کامپوننتهایی، higher-order components گفته میشود.
برای تعریف تزئین کنندهها، به نحوهی پیاده سازی Object.defineProperty در مثال فوق دقت کنید:
Object.defineProperty(Person.prototype, 'fullName', { enumerable: false, writable: false, get: function () { return this.firstName + ' ' + this.lastName; } });
در ذیل روش تعریف یک تزئین کننده را مشاهده میکنید که دقیقا از یک چنین الگویی پیروی میکند:
function decoratorName(target, key, descriptor) { // … }
function readonly(target, key, descriptor) { descriptor.writable = false; return descriptor; }
class Person { constructor(firstName, lastName) { this.firstName = firstName; this.lastName = lastName; } @readonly get fullName() { return `${this.firstName} ${this.lastName}`; } }
مثالهایی از تزئین کنندهها
برای نمونه میتوان تزئین کنندهی bindThis@ را طراحی کرد تا کار bind شیء this را به متدهای کامپوننتهای React انجام دهد و یا کتابخانهای به نام core-decorators وجود دارد که به صورت زیر نصب میشود:
> npm install core-decorators
@autobind @deprecate @readonly @memoize @debounce @profile
نمونهی دیگری از این کتابخانهها lodash-decorators است که تعدادی دیگر از تزئین کنندهها را ارائه میکند.
MobX چگونه کار میکند؟
انجام یکسری از کارها با Redux مشکل است؛ برای مثال تغییر دادن یک شیء تو در توی پیچیده که شامل تهیهی یک کپی از آن، اعمال تغییرات و غیرهاست. اما با MobX میتوان با اشیاء جاوا اسکریپتی به همان صورتی که هستند کار کرد. برای مثال آرایهای را با متدهای push و pop تغییر داد (mutation اشیاء مجاز است) و یا خواص اشیاء را به صورت مستقیم ویرایش کرد، در این حالت MobX اعلام میکند که ... من میدانم که چه تغییری صورت گرفتهاست. بنابراین سبب رندر مجدد UI خواهم شد.
ایجاد یک برنامهی خالی React برای آزمایش MobX
در اینجا برای بررسی MobX، یک پروژهی جدید React را ایجاد میکنیم:
> create-react-app state-management-with-mobx-part1 > cd state-management-with-mobx-part1 > npm start
> npm install --save mobx
مثالی از MobX، مستقل از React
در اینجا نیز همانند روشی که در بررسی Redux در پیش گرفتیم، ابتدا MobX را به صورت کاملا مستقل از React، با یک مثال بررسی میکنیم و سپس در قسمتهای بعد آنرا به React متصل میکنیم. برای این منظور ابتدا فایل src\index.js را به صورت زیر تغییر میدهیم:
import { autorun, observable } from "mobx"; import React from "react"; import ReactDOM from "react-dom"; ReactDOM.render( <div> <input type="text" id="text-input" /> <div id="text-display"></div> <div id="text-display-uppercase"></div> </div>, document.getElementById("root") ); const input = document.getElementById("text-input"); const textDisplay = document.getElementById("text-display"); const loudDisplay = document.getElementById("text-display-uppercase"); console.log({ observable, autorun, input, textDisplay, loudDisplay });
- با استفاده از observable میخواهیم تغییرات یک شیء جاوا اسکریپتی را تحت نظر قرار داده و هر زمانیکه تغییری در شیء رخ داد، از آن مطلع شویم.
برای مثال شیء سادهی جاوا اسکریپتی زیر را در نظر بگیرید:
{ value: "Hello world!", get uppercase() { return this.value.toUpperCase(); } }
به همین جهت اینبار شیء فوق را توسط یک observable ارائه میدهیم، تا بتوانیم به تغییرات خواص آن گوش فرا دهیم:
const text = observable({ value: "Hello world!", get uppercase() { return this.value.toUpperCase(); } });
input.addEventListener("keyup", event => { text.value = event.target.value; });
autorun(() => { textDisplay.textContent = text.value; loudDisplay.textContent = text.uppercase; });
برای آزمایش آن، برنامه را اجرا کرده و متنی را داخل textbox وارد کنید:
نکتهی جالب اینجا است که هرچند فقط خاصیت value را تغییر دادهایم (تغییر مستقیم خواص یک شیء؛ بدون نیاز به ساخت یک clone از آن)، اما خاصیت محاسباتی uppercase نیز به روز رسانی شدهاست.
زمانیکه mobx را به یک برنامهی React متصل میکنیم، قسمت autorun، از دید ما مخفی خواهد بود. در این حالت فقط یک شیء معمولی جاوا اسکریپتی را مستقیما تغییر میدهیم و ... در نتیجهی آن رندر مجدد UI صورت خواهد گرفت.
یک observable چگونه کار میکند؟
در اینجا یک شبهکد را که بیانگر نحوهی عملکرد یک observable است، مشاهده میکنید:
const onChange = (oldValue, newValue) => { // Tell MobX that this value has changed. } const observable = (value) => { return { get() { return value; }, set(newValue) { onChange(this.get(), newValue); value = newValue; } } }
کدهای کامل این قسمت را میتوانید از اینجا دریافت کنید: state-management-with-mobx-part1.zip
در ادامه مطلب تغییر اندازه تصاویر #1 ، در این پست میخواهیم نحوه تغییر اندازه تصاویر را در زمان درخواست کاربر بررسی کنیم.
پس از ایجاد هندلر، فایل کد آن مانند زیر خواهد بود، ما باید کدهای خود را در متد ProcessRequestبنویسیم.
خوب برای نوشتن کد در این مرحله ما باید چند کار انجام دهیم.
1- گرفتن پارامترهای ورودی کاربر جهت تغییر سایز از طریق روشهای انتقال مقادیر بین صفحات (در اینجا استفاده از Query String ).
2-بازیابی تصویر از دیتابیس یا از دیسک به صورت یک آرایه بایتی.
3- تغییر اندازه تصویر مرحله 2 و ارسال تصویر به خروجی.
در این هندلر ما چند متد اضافه کردیم.
پ.ن : هرچند میتوانستیم کد هارا بهبود داده و خیلی بهینهتر بنویسیم اما هدف فقط اشنایی با عمل تغییر اندازه تصویر در زمان اجرا بود، امیدوارم اساتید من ببخشن.
نظرات اقای موسوی تا حدودی اعمال شد و در پست تغییراتی انجام شد.
موفق وموید باشید
در پست قبلی بررسی کردیم که کاربر میتواند در دوحالت تصاویر دریافتی از کاربران سایت را تغییر اندازه دهد، یکی در زمان ذخیره سازی تصاویر بود و دیگری در زمانی که کاربر درخواست نمایش یک تصویر را دارد.
خوب ابتدا فرض میکنیم برای نمایش تصاویر چند حالت داریم مثلا کوچک، متوسط، بزرگ و حالت واقعی (اندازه اصلی).
البته دقت نمایید که این طبقه بندی فرضی بوده و ممکن است برای پروژههای مختلف این طبقه بندی متفاوت باشد. (در این پست قصد فقط اشنایی با تغییر اندازه تصاویر است و شاید کد به درستی refactor نشده باشد).
برای تغییر اندازه تصاویر در زمان اجرا یکی از روش ها، میتواند استفاده از Handler باشد. خوب برای ایجاد Handler ابتدا در پروژه وب خود بروی پروژه راست کلیک کرده، و گزینه New Item را برگزینید، و در پنجره ظاهر شده مانند تصویر زیر گزینه Generic Handler را انتخاب نمایید.
خوب ابتدا فرض میکنیم برای نمایش تصاویر چند حالت داریم مثلا کوچک، متوسط، بزرگ و حالت واقعی (اندازه اصلی).
البته دقت نمایید که این طبقه بندی فرضی بوده و ممکن است برای پروژههای مختلف این طبقه بندی متفاوت باشد. (در این پست قصد فقط اشنایی با تغییر اندازه تصاویر است و شاید کد به درستی refactor نشده باشد).
برای تغییر اندازه تصاویر در زمان اجرا یکی از روش ها، میتواند استفاده از Handler باشد. خوب برای ایجاد Handler ابتدا در پروژه وب خود بروی پروژه راست کلیک کرده، و گزینه New Item را برگزینید، و در پنجره ظاهر شده مانند تصویر زیر گزینه Generic Handler را انتخاب نمایید.
پس از ایجاد هندلر، فایل کد آن مانند زیر خواهد بود، ما باید کدهای خود را در متد ProcessRequestبنویسیم.
using System; using System.Collections.Generic; using System.Linq; using System.Web; namespace PWS.UI.Handler { /// <summary> /// Summary description for PhotoHandler /// </summary> public class PhotoHandler : IHttpHandler { public void ProcessRequest(HttpContext context) { context.Response.ContentType = "text/plain"; context.Response.Write("Hello World"); } public bool IsReusable { get { return false; } } } }
خوب برای نوشتن کد در این مرحله ما باید چند کار انجام دهیم.
1- گرفتن پارامترهای ورودی کاربر جهت تغییر سایز از طریق روشهای انتقال مقادیر بین صفحات (در اینجا استفاده از Query String ).
2-بازیابی تصویر از دیتابیس یا از دیسک به صورت یک آرایه بایتی.
3- تغییر اندازه تصویر مرحله 2 و ارسال تصویر به خروجی.
using System; using System.Data.SqlClient; using System.Diagnostics; using System.Drawing; using System.Drawing.Drawing2D; using System.Drawing.Imaging; using System.Globalization; using System.IO; using System.Web; namespace PWS.UI.Handler { /// <summary> /// Summary description for PhotoHandler /// </summary> public class PhotoHandler : IHttpHandler { /// <summary> /// بازیابی تصویر اصلی از بانک اطلاعاتی /// </summary> /// <param name="photoId">کد تصویر</param> /// <returns></returns> private byte[] GetImageFromDatabase(int photoId) { using (var connection = new SqlConnection("ConnectionString")) { using (var command = new SqlCommand("Select Photo From tblPhotos Where Id = @PhotoId", connection)) { command.Parameters.Add(new SqlParameter("@PhotoId", photoId)); connection.Open(); var result = command.ExecuteScalar(); return ((byte[])result); } } } /// <summary> /// بازیابی فایل از دیسک /// </summary> /// <param name="photoId">با فرض اینکه نام فایل این است</param> /// <returns></returns> private byte[] GetImageFromDisk(string photoId /* or somting */) { using (var sourceStream = new FileStream("Original File Path + id", FileMode.Open, FileAccess.Read)) { return StreamToByteArray(sourceStream); } } /// <summary> /// Streams to byte array. /// </summary> /// <param name="inputStream">The input stream.</param> /// <returns></returns> /// <exception cref="System.ArgumentException"></exception> static byte[] StreamToByteArray(Stream inputStream) { if (!inputStream.CanRead) { throw new ArgumentException(); } // This is optional if (inputStream.CanSeek) { inputStream.Seek(0, SeekOrigin.Begin); } var output = new byte[inputStream.Length]; int bytesRead = inputStream.Read(output, 0, output.Length); Debug.Assert(bytesRead == output.Length, "Bytes read from stream matches stream length"); return output; } /// <summary> /// Enables processing of HTTP Web requests by a custom HttpHandler that implements the <see cref="T:System.Web.IHttpHandler" /> interface. /// </summary> /// <param name="context">An <see cref="T:System.Web.HttpContext" /> object that provides references to the intrinsic server objects (for example, Request, Response, Session, and Server) used to service HTTP requests.</param> public void ProcessRequest(HttpContext context) { // Set up the response settings context.Response.ContentType = "image/jpeg"; context.Response.Cache.SetCacheability(HttpCacheability.Public); context.Response.BufferOutput = false; // مرحله اول int size = 0; switch (context.Request.QueryString["Size"]) { case "S": size = 100; //100px break; case "M": size = 198; //198px break; case "L": size = 500; //500px break; } byte[] changedImage; var id = Convert.ToInt32(context.Request.QueryString["PhotoId"]); byte[] sourceImage = GetImageFromDatabase(id); // یا //byte[] sourceImage = GetImageFromDisk(id.ToString(CultureInfo.InvariantCulture)); //مرحله 2 if (size != 0) //غیر از حالت واقعی تصویر { changedImage = Helpers.ResizeImageFile(sourceImage, size, ImageFormat.Jpeg); } else { changedImage = (byte[])sourceImage.Clone(); } // مرحله 3 if (changedImage == null) return; context.Response.AddHeader("Content-Length", changedImage.Length.ToString(CultureInfo.InvariantCulture)); context.Response.BinaryWrite(changedImage); } public bool IsReusable { get { return false; } } } }
1- متد GetImageFromDatabase: این متد یک کد تصویر را گرفته و آن را از بانک اطلاعاتی بازیابی میکند. (در صورتی که تصویر در بانک ذخیره شده باشد)
2- متد GetImageFromDisk: این متد نام تصویر (با فرض اینکه یک عدد میباشد) را به عنوان پارامتر گرفته و آنرا بازیابی میکند (در صورتی که تصویر در دیسک ذخیره شده باشد.)
3- متد StreamToByteArray: زمانی که تصویر از فایل خوانده میشود به صورت Stream است این متد یک Stream را گرفته و تبدیل به یک آرایه بایتی میکند.
در نهایت در متد ProcessRequestتصویر خوانده شده با توجه به پارامترهای ورودی تغییر اندازه داده شده و در نهایت به خروجی نوشته میشود.
برای استفاده این هندلر، کافی است در توصیر خود به عنوان مسیر رشته ای شبیه زیر وارد نمایید:
2- متد GetImageFromDisk: این متد نام تصویر (با فرض اینکه یک عدد میباشد) را به عنوان پارامتر گرفته و آنرا بازیابی میکند (در صورتی که تصویر در دیسک ذخیره شده باشد.)
3- متد StreamToByteArray: زمانی که تصویر از فایل خوانده میشود به صورت Stream است این متد یک Stream را گرفته و تبدیل به یک آرایه بایتی میکند.
در نهایت در متد ProcessRequestتصویر خوانده شده با توجه به پارامترهای ورودی تغییر اندازه داده شده و در نهایت به خروجی نوشته میشود.
برای استفاده این هندلر، کافی است در توصیر خود به عنوان مسیر رشته ای شبیه زیر وارد نمایید:
PhotoHandler.ashx?PhotoId=10&Size=S مانند <img src='PhotoHandler.ashx?PhotoId=10&Size=S' alt='تصویر ازمایشی' />
نظرات اقای موسوی تا حدودی اعمال شد و در پست تغییراتی انجام شد.
موفق وموید باشید
اشتراکها
معرفی پروژه DNTFrameworkCore
پروژه DNTFrameworkCore که قصد پشتیبانی از آن را دارم، یک زیرساخت سبک وزن و توسعه پذیر با پشتیبانی از طراحی چند مستاجری با کمترین وابستگی به کتابخانههای ثالث میباشد که با تمرکز بر کاهش زمان و افزایش کیفیت توسعه بخش منطق تجاری پروژههای تحت وب، توسعه داده شده است. به مرور زمان مطالب و مستندات آن نیز کامل خواهد شد. برای برخی از امکانات از جمله اعتبارسنجی خودکار، مدیریت تراکنش ها، شماره گذاری خودکار و ... آزمون واحد نیز در نظر گرفته شده است که در آینده نزدیک با تکمیل آزمون واحد بخشهای دیگر، انتشار آنها نیز انجام خواهد شد.
برای نصب و استفاده از بستههای نیوگت آن، دستورات زیر را اجرا کنید:
PM>Install-Package DNTFrameworkCore PM>Install-Package DNTFrameworkCore.EntityFramework PM>Install-Package DNTFrameworkCore.Web PM>Install-Package DNTFrameworkCore.Web.EntityFramework
به منظور بررسی دقیقتر امکانات آن میتوانید پروژه TestAPI موجود در مخزن گیت هاب را بررسی کنید.
نمونه API پیاده سازی شده:
[Route("api/[controller]")] public class TasksController : CrudController<ITaskService, int, TaskReadModel, TaskModel, TaskFilteredPagedQueryModel> { public TasksController(ITaskService service) : base(service) { } protected override string CreatePermissionName => PermissionNames.Tasks_Create; protected override string EditPermissionName => PermissionNames.Tasks_Edit; protected override string ViewPermissionName => PermissionNames.Tasks_View; protected override string DeletePermissionName => PermissionNames.Tasks_Delete; }