اشتراک‌ها
ورک فلو نسخه راست چین شده بوت استرپ 4

نسخه 4 بوت استرپ راست چین شده با پلاگین gulp-rtlcss .

برای استفاده سریع :

ابتدا مخزن را clone کنید. سپس با رفتن به دایرکتوری آن دستور gulp watch را بزنید. یا فایل index.html را با مرور گر خود باز کنید.

ورک فلو نسخه راست چین شده بوت استرپ 4
مطالب
آموزش Coded UI Test #1
اکثر برنامه نویسان با مباحث Unit Testing  آشنایی دارند و بعضی برنامه نویسان هم، از این مباحث در پروژه‌های خود استفاده می‌کنند. ساختار الگو‌های MVC و MVVM به گونه ای است که به راحتی می‌توان برای این گونه پروژه‌ها Unit Test بنویسیم. در پروژه‌های MVC به دلیل عدم وابستگی بین View و Controller به طور مستقیم، امکان نوشتن Unit Test برای Controller امکان پذیر است و از طرفی در الگوی MVVM به دلیل منطق وجود ViewModel می‌توان برای اینگونه پروژه‌ها نیز Unit Test نوشت. اما ساختار سایر پروژه‌ها به گونه ای است که نوشتن Unit Test برای آن‌ها مشکل و در بعضی مواقع غیر ممکن می‌شود. برای مثال در پروژهای Desktop نظیر Windows Application و حتی وب به صورت Asp.Net Web Forms به دلیل وابستگی مستقیم کنترل‌های UI به منطق اجرای برنامه، طراحی و نوشتن Unit Test بسیار مشکل و در برخی موارد بیهوده است. در VS.Net ابزاری وجود دارد به نام Coded UI Test که برای تست این گونه پروژه‌ها طراحی شده است و همان طور که از نامش پیداست صرفا برای تست کنترل‌های UI و رویداد‌های کنترل‌ها و تست درستی برنامه با توجه به داده‌های ورودی به کار می‌رود. یکی از مزیت‌های اصلی آن تسریع عملیات تست در حجم بالا است و زمان ایجاد unit  test را به حداقل می‌رساند. مزیت دوم آن امکان ایجاد unit test برای پروژه‌های که در مراحل پایانی تولید هستند ولی هنوز اطمینانی به عملکرد صحیح برنامه در حالات مختلف نیست.
 در این پست قصد دارم روش استفاده از این گونه پروژه‌های تست را با ذکر یک مثال بررسی کنیم و در پست‌های بعدی به بررسی امکانات دیگر خواهیم پرداخت.

نکته : فقط در Vs.Net با نسخه‌های Ultimate و Premium می‌تونید از Code UI Test استفاده کنید که البته به دلیل اینکه در ایران پیدا کردن نسخه‌های دیگر Vs.Net به غیر از Ultimate سخت‌تر است به طور قطع این محدودیت برای برنامه نویسان ما وجود نخواهد داشت. برای اینکه از نسخه Vs.Net خود اطمینان حاصل کنید از منوی Help گزینه About Microsoft Visual Studio رو انتخاب کنید. پنجره ای به شکل زیر مشاهده خواهید کرد که در آن مشخصات کامل Vs.Net ذکر شده است.

در این مرحله قصد داریم برای فرم زیر Unit Test طراحی کنیم. پروژه به صورت زیر است:

کاملا واضح است که در این فرم دو عدد به عنوان ورودی دریافت می‌شود و بعد از کلیک بر روی CalculateSum نتیجه در textbox سوم نمایش داده می‌شود. برای تست عملکرد صحیح فرم بالا ایتدا به Solution مورد نظر از منوی test Project یک Coded UI Test Project اضافه می‌کنیم. به دلیل اینکه این قبلا در این Solution پروژه تست از نوع Coded UI Test نبود بلافاصله یک پنجره نمایش داده می‌شود. مطمئن شوید گزینه اول انتخاب شده و بعد بر روی Ok کلیک کنید.(گزینه اول به معنی است که قصد داریم عملیات مورد نظر بر روی UI را رکورد کنیم و گزینه دوم به معنی است که قصد داریم از عملیات رکورد شده قبلی استفاده کنیم).  یک کلاس به نام CodeUITest1 به همراه یک متد تست به نام CodedUITestMethod1 ساخته می‌شود. اولین چیزی که جلب توجه می‌کند این است که این کلاس به جای TestClassAttribute دارای نشان CodeUITestAttrbiute است. در گوشه سمت راست Vs.Net خود یک پنجره کوچک به نام UI Map Test Builder مانند شکل زیر خواهید دید.

دکمه قرمز رنگ به نام Record Button است و عملیات تست را رکورد خواهد کرد. دکمه دایره ای به رنگ مشکی برای تعیین Assertion به کار می‌رود. و در نهایت گزینه آخر کد‌های مورد نظر مراحل قبل را به صورت خودکار تولید خواهد کرد.

#روش کار

روش کار به این صورت است که ابتدا شما مراحل تست خود را شبیه سازی خواهید کرد و بعد از آن Test Builder مراحل تست شما را به صورت کامل به صورت کد‌های قابل فهم تولید خواهد کرد. (دقیقا شبیه به ایجاد UnitTest به روش Arrange/Act/Assert است با این تفاوت که این مراحل توسط UI Map رکورد شده و نیازی به کد نویسی ندارد). در پایان باید یک Data Driven Coded UI Test طراحی کنید تا بتوانید از این مراحل رکورد استفاده نمایید. 

#چگونگی شبیه سازی :

پروژه را اجرا نمایید. زمانی که فرم مورد نظر  ظاهر شد بر روی گزینه Record در TestBuilder کلیک کنید. عملیات ذخیره سازی شروع شده است. در نتیجه به فرم مربوطه رفته و در Textbox اول مقدار 10 و در textbox دوم مقدار 5 را وارد نمایید. با کلیک بر روی دکمه CalculateSum مقدار 15 نمایش داده خواهد شد. از برنامه خارج شوید و بعد بر روی گزینه Generate Code در TestBuilder کلیک کنید با از کلید‌های ترکیبی Alt + G استفاده نمایید.(اگر در این مرحله، از برنامه خارج نشده باشید با خطا مواجه خواهید شد.) در پنجره نمایش داده شده یک نام به متد اختصاص دهید. عملیات تولید کد شروع خواهد شد. بعد کدی مشابه زیر را در متد مربوطه مشاهده خواهید کرد.

   [TestMethod]
        public void CodedUITestMethod1()
        {            
            this.UIMap.CalculateSum();
            this.UIMap.txtSecondValueMustBe10();
        }
بخشی از سورس کد تولید شده برای متد CalculateSum به شکل زیر است:
        public void CodedUITestMethod1 ()
        {
            #region Variable Declarations
            WinEdit uITxtFirstNumberEdit = this.UIدوعددصحیحواردنماییدWindow.UITxtFirstNumberWindow.UITxtFirstNumberEdit;
            WinEdit uITxtSecondNumberEdit = this.UIدوعددصحیحواردنماییدWindow.UITxtSecondNumberWindow.UITxtSecondNumberEdit;
            WinButton uICalculateSumButton = this.UIدوعددصحیحواردنماییدWindow.UICalculateSumWindow.UICalculateSumButton;
            #endregion

            // Type '10' in 'txtFirstNumber' text box
            uITxtFirstNumberEdit.Text = this.CalculateSumParams.UITxtFirstNumberEditText;

            // Type '{Tab}' in 'txtFirstNumber' text box
            Keyboard.SendKeys(uITxtFirstNumberEdit, this.CalculateSumParams.UITxtFirstNumberEditSendKeys, ModifierKeys.None);

            // Type '10' in 'txtSecondNumber' text box
            uITxtSecondNumberEdit.Text = this.CalculateSumParams.UITxtSecondNumberEditText;

            // Click 'Calculate Sum' button
            Mouse.Click(uICalculateSumButton, new Point(83, 12));

            // Type '10' in 'txtFirstNumber' text box
            uITxtFirstNumberEdit.Text = this.CalculateSumParams.UITxtFirstNumberEditText1;

            // Type '{Tab}' in 'txtFirstNumber' text box
            Keyboard.SendKeys(uITxtFirstNumberEdit, this.CalculateSumParams.UITxtFirstNumberEditSendKeys1, ModifierKeys.None);

            // Type '10' in 'txtSecondNumber' text box
            uITxtSecondNumberEdit.Text = this.CalculateSumParams.UITxtSecondNumberEditText1;

            // Type '{Tab}' in 'txtSecondNumber' text box
            Keyboard.SendKeys(uITxtSecondNumberEdit, this.CalculateSumParams.UITxtSecondNumberEditSendKeys, ModifierKeys.None);

            // Click 'Calculate Sum' button
            Mouse.Click(uICalculateSumButton, new Point(49, 11));

            // Type '10' in 'txtFirstNumber' text box
            uITxtFirstNumberEdit.Text = this.CalculateSumParams.UITxtFirstNumberEditText2;

            // Type '{Tab}' in 'txtFirstNumber' text box
            Keyboard.SendKeys(uITxtFirstNumberEdit, this.CalculateSumParams.UITxtFirstNumberEditSendKeys2, ModifierKeys.None);

            // Type '5' in 'txtSecondNumber' text box
            uITxtSecondNumberEdit.Text = this.CalculateSumParams.UITxtSecondNumberEditText2;

            // Type '{Tab}' in 'txtSecondNumber' text box
            Keyboard.SendKeys(uITxtSecondNumberEdit, this.CalculateSumParams.UITxtSecondNumberEditSendKeys1, ModifierKeys.None);

            // Click 'Calculate Sum' button
            Mouse.Click(uICalculateSumButton, new Point(74, 16));
        }
همان طور که می‌بینید تمام مراحل تست شما رکورد شده است و به صورت کد قابل فهم بالا ایجاد شده است.
چگونگی ایجاد Assertion
اگر به کد متد تست CodedUITestMethod1 در بالا دقت کنید یک متد به صورت this.UIMap.txtSecondValueMustBe10 فراخوانی شده است. این در واقع یک Assertion  است که در هنگام عملیات رکورد ایجاد کردم و به این معنی است که مقدار TextBox دوم حتما باید 10 باشد. حال روش تولید Assertion‌ها را بررسی خواهیم کرد.
بعد از شروع شدن مرحله رکورد اگر قصد دارید برای یک کنترل خاص Assert بنویسید، دکمه assertion (به رنگ مشکی و به صورت دایره است) را بر روی کنترل مورد نظر drag&drop کنید. یک border آبی برای کنترل مورد نظر ایجاد خواهد شد:

به محض اتمام عملیات drag&drop منوی زیر ظاهر خواهد شد:

از گزینه Add Assertion استفاده کنید و برای کنترل مورد نظر یک assert بنویسید. در شکل زیر یک assert برای textbox دوم نوشتم به صورتی که مقدار آن باید با 5 برابر باشد.


از گزینه آخر برای نمایش پیغام مورد نظر خودتون در هنگامی که aseert با شکست مواجه می‌شود استفاده کنید.
کد تولید شده زیر برای عملیات assert بالا است:
  public void txtSecondValueMustBe10()
        {
            #region Variable Declarations
            WinEdit uITxtSecondNumberEdit = this.UIدوعددصحیحواردنماییدWindow.UITxtSecondNumberWindow.UITxtSecondNumberEdit;
            #endregion

            // Verify that the 'ControlType' property of 'txtSecondNumber' text box equals '10'
            Assert.AreEqual(this.txtSecondValueMustBe10ExpectedValues.UITxtSecondNumberEditControlType, uITxtSecondNumberEdit.ControlType.ToString());
        }

مرحله اول انجام شد. برای تست این مراحل باید یک Data DrivenTest بسازید که در پست بعدی به صورت کامل شرح داده خواهد شد.
نظرات مطالب
پَرباد - راهنمای اتصال و پیاده‌سازی درگاه‌های پرداخت اینترنتی (شبکه شتاب)
در مورد ثبت اطلاعات همونطور که دوستمون گفتن برای نمونه ثبت مشخصات بانک ملت مثال زده شده.
در مورد امنیت، این پروژه اوپن سورس هست لینک گیت هاب هم در توضیحات موجوده.
حتی در صورتیکه شخصی نسبت به امنیت اطلاعاتشون تردید داشته باشه، باید این رو در نظر داشته باشه که به هیچ وجه شخص دیگری با این اطلاعات کاری نمیتواند انجام دهد، به دو دلیل:
۱) کلیه عملیات بانکی فقط و فقط از جانب آی پی سرور یا وب سایت فروشنده (صاحب وب سایت) قابل دریافت هست که این آی پی در هنگام سفارش درگاه پرداخت توسط صاحب وب سایت، به بانک ارائه شده است.
۲) هیچ عملیاتی مانند انتقال وجه از یک حساب به حساب دیگه وجود نداره و این عملیات صرفا سه حالت اصلی و عمده، ثبت، تایید و برگشت هست.
مطالب
شروع به کار با Aurelia
در این مطلب قصد داریم نحوه‌ی راه اندازی و شروع به کار با Aurelia را توضیح دهیم. برای شروع ابتدا باید Git را نصب کنیم. زیرا می‌خواهیم از jspm برای نصب محتویات Aurelia استفاده کنیم و برای کار کردن با jspm باید Git بر روی سیستم نصب باشد. در صورتیکه Git نصب نباشد، زمان init کردن jspm با خطای زیر رو به رو می‌شویم :

پس از نصب Git اطمینان حاصل کنید که NodeJs ، npm و jspm نیز بر روی سیستم شما نصب باشند. در این قسمت گفته‌ایم که چگونه از این «اطمینان» آگاه شوید.

حال نوبت به ساخت اولین پروژه‌ی MVC ما می‌رسد. یک پروژه MVC جدید با نام دلخواه خودتان در مسیر دلخواه خودتان ایجاد کنید. با خط فرمان، در ریشه‌ی پروژه دستور زیر را اجرا کنید:

jspm init

بدون هیچ تغییری، به هیچ کدام از سوالات پاسخ ندهید و از دکمه‌ی enter استفاده کنید تا مقادیر پیشفرض اعمال شوند. اگر تصویر زیر را در خروجی مشاهده کردید یعنی تا بدین جای کار به درستی پیش رفته‌اید :

حالا نوبت به نصب محتویات Aurelia می‌باشد. برای این کار دستورات زیر را اجرا کنید : 

jspm install aurelia-framework
jspm install aurelia-bootstrapper
با صادر کردن دستورات بالا، jspm تمامی فایل‌های مورد نیاز Aurelia را در پوشه‌ی jspm_packages قرار می‌دهد.

توجه داشته باشید، اگر دستورات بالا به درستی اجرا و تکمیل شوند، باید پس از پایان هر دستور، پیام زیر را در انتهای خروجی مشاهده کنید:
 

پس از این کارها، فایل Layout را باز کنید و کدهای آن را به صورت زیر تغییر دهید:
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Aurelia - www.dotnettips.info</title>
</head>
<body aurelia-app>
    <div>
        @RenderBody()
    </div>
    <script src="~/jspm_packages/system.js"></script>
    <script src="~/config.js"></script>
    <script>
        System.import("aurelia-bootstrapper");
    </script>
</body>
</html>
سپس در ریشه‌ی برنامه یک فایل جاوااسکریپت را با نام app ایجاد کنید و کدهای زیر را در آن قرار دهید:
export class App {
    
}
حالا به یک View، برای این کار نیاز داریم. در همان ریشه‌ی سایت یک فایل html را با نام app ایجاد کنید و کدهای زیر را در آن قرار دهید:
<template>
    <h3>www.dotnettips.info</h3>
</template>
پس از سپری کردن این 3 مرحله، برنامه‌ی خود را اجرا کنید. باید خروجی زیر را مشاهده کنید:

در بخش‌های بعدی در مورد کدهای فوق و همچنین به سایر مباحث دیگر Aurelia می‌پردازیم.

نکته‌ی تکمیلی

زمانیکه می‌خواهید از دستوارت jspm استفاده کنید، باید به ریشه‌ی برنامه مراجعه کنید. حال اگر پوشه‌های تودرتوی زیادی داشته باشید، این رفت و آمد‌ها زمانبر و خسته کننده خواهند شد. برای حل این مشکل کافیست روی پروژه، راست کلیک کنید و گزینه‌ی Open Folder in File Explorer را انتخاب کنید تا ریشه‌ی پروژه باز شود. حالا  کافیست در فضای خالی Windows Explorer کلید Shift را گرفته و راست کلیک کنید. حالا یک آیتم جدید با نام Open command window here ظاهر شده است. کافیست روی آن کلیک کنید. CMD در ریشه‌ی سایت باز خواهد شد.


دانلود پروژه جاری در مخزن گیت
مطالب
شروع کار با Apache Cordova در ویژوال استودیو #5

همانطور که در قسمت قبل گفته شد، در این قسمت با روش کار jQuery Mobile و plugin‌های مربوط به Cordova آشنا خواهیم شد.


تگ متای زیر برای تنظیمات مربوط به viewport است و برای jQuery Mobile توصیه می‌شود.
<!DOCTYPE html> 
<html> 
<head> 
<meta charset="utf-8"> 
<title>Title</title> 
<meta name="viewport" content="width=device-width, initial-scale=1">
device-width  نشان می‌دهد که می‌خواهیم مقیاس محتوای ما به اندازه‌ی عرض دستگاه(device) مورد نظر باشد و initial-scale هم مقدار زوم را برای Web page ما مشخص می‌کند. شما می‌توانید با مقدار دهی user-scalable=no هم امکان تغییر زوم را به کاربر ندهید. این متا تگ را در تمام صفحات html خود بعد از تگ title قرار دهید.

روال کار jQuery Mobile
برای اینکه بتواند سند HTML ما را برای استفاده‌ی در موبایل بهینه کند، ابتدا آن را لود می‌کند و سپس بر  اجزایی که با ویژگیdata-role علامت گذاری شده‌اند، CSS3 بهینه شده برای موبایل را اعمال می‌کند.


از آنجایی که مستندات jQuery Mobile به قدر کافی کامل هست، نیازی نیست تا در مورد تک تک آنها مثال بزنیم و از اصل مطلب دور شویم. در هر مثالی که زده خواهد شد، در صورت استفاده از ویجتی خاص، با آن آشنا خواهیم شد.

لیست کامل اتریبیوت‌های -data به همراه مقادیری که می‌پذیرند 

دموی مربوط به ویجت‌ها  

لیست تمام رخدادها 

شما می‌توانید از امکانات Theme Roller برای شخصی سازی تم‌های مورد نیاز استفاده کنید.

لیست کامل کلاس‌های CSS  



Cordova Plugins

از این قسمت http://plugins.cordova.io/#/viewAll و این قسمت  http://plugreg.com/plugins می‌توانید سراغ پلاگین‌های مورد نیاز خود بگردید. برای مثال وارد بخش کانفیگ پروژه شده و از قسمت plugins  و تب Core یکسری از پلاگین‌هایی را که در Cordova گنجانده شده است، مشاهده می‌کنید. با کلیک بر روی دکمه‌ی Add می‌توانید آن را دانلود کرده و از API‌های آن استفاده کنید.



برای مثال پلاگین Notification را به پروژه اضافه می‌کنم. سپس یک فایل js را با نام custom.js به فولدر scripts در ریشه پروژه اضافه کرده و  محتوای فایل‌های index.html , custome.js را به شکل زیر در نظر می‌گیرم:


$(function() {
    $("#alert").on('tap', function(event) {
        navigator.notification.alert("اطلاعات ذخیره شد",null, "alert", "تایید");
    });

    $("#prompt").on('tap', function(event) {
        navigator.notification.prompt("برای تائید نام خود را وارد کنید", onPrompt, "prompt", "تایید", "لغو"],"نام خود"]);
    });

    function onPrompt(results) {
        navigator.notification.alert(results.buttonIndex + "\n" + results.input1, null);
    }
    $("#confirm").on('tap', function(event) {
        navigator.notification.confirm("حذف انجام شود؟", onConfirm, "confirm", ["بله", "خیر", "نمیدانم"]);
    });

    function onConfirm(buttonIndex) {
        navigator.notification.alert(buttonIndex , null);
    }
    $("#beep").on('tap', function(event) {
        navigator.notification.beep(1);
    });

});

رخداد tap زمانی صادر می‌شود که کاربر، دکمه‌ی مورد نظر را لمس کند و یکی از رخداد‌های jQuery Mobile می‌باشد. بعد از نصب پلاگین Notification، با استفاده از navigator.notification می‌توانید به متد‌های مورد نظر که در بالا مشخص است، دسترسی پیدا کنید.

برای آشنایی با این پلاگین می‌توانید داکیومنت آن را مطالعه کنید.

در کد بالا با استفاده از متد‌های callback توانسته‌ایم اطلاعاتی در مورد نوع عملکرد کاربر با notification ما بدست آوریم.


<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>CordovaApp01</title>
   <meta name="viewport" content="width=device-width, initial-scale=1"/> 
    <!-- CordovaApp01 references -->
    <link href="css/index.css" rel="stylesheet" />
    <link href="jquery.mobile.rtl/css/themes/default/rtl.jquery.mobile-1.4.0.css" rel="stylesheet" />
</head>
<body>
<div data-role="page" id="page1">
    <div data-role="header">
        <h2>
            تست پلاگین Notification
        </h2>
    </div>
    <div data-role="content">
        <a href="#page2" data-transition="pop" data-rel="dialog" data-role="button" data-inline="true" data-icon="back">page 2</a>
       
        <button data-role="button" id="alert" data-inline="true" >alert</button>
        <button data-role="button" id="confirm" data-inline="true">confirm</button>
        <button data-role="button" id="beep" data-inline="true" >beep</button>
        <button data-role="button" id="prompt" data-inline="true" >prompt</button>

    </div>
    <div data-role="footer">
        <h2>من فوتر هستم</h2>
    </div>
</div>
    <div data-role="page" id="page2">
        <div data-role="header">
            <h1>Header</h1>
        </div>
        <div data-role="content">
            Content
        </div>
        <div data-role="footer">
            <h1>Footer</h1>
        </div>
    </div>
<!-- Cordova reference, this is added to your app when it's built. -->
    <script src="scripts/jquery-2.1.3.min.js"></script>
    <script src="cordova.js"></script>
    <script src="scripts/platformOverrides.js"></script>
    <script src="scripts/index.js"></script>
    <script src="jquery.mobile.rtl/js/rtl.jquery.mobile-1.4.0.js"></script>
    <script src="scripts/custom.js"></script>
</body>
</html>

در کد بالا 4 تا button دیده می‌شود که ویژگی data-role آنها مقدار button در نظر گرفته شده‌است تا توسط jQuery Mobile به عنوان button شناخته شوند و استایل‌های لازم بر روی آن‌ها اعمال گردد. قرار است طبق کد js ایی که نوشته‌ایم، با لمس کردن هر کدام از دکمه‌ها، notification هایی نمایش داده شوند.


برای اینکار شبیه ساز YouWave را دانلود کرده و نصب کنید. سپس در قسمت toolbar ویژوال، گزینه‌ی Device را به جای شبیه ساز Ripple انتخاب کنید. نرم افزار youwave را اجرا کنید حال اگر برنامه را اجرا کنید با خطای زیر مواجه خواهید شد:

Error447C:\Users\Administrator\Documents\Visual Studio 2013\Projects\CordovaApp-01\CordovaApp-01\bld\Debug\platforms\android\cordova\node_modules\q\q.js:126CordovaApp-01
Error448throw e;CordovaApp-01
Error449^CordovaApp-01
Error450Error : DEP10201 : Failed to deploy to device, no devices found.CordovaApp-01
مشخصا خطا، مبنی بر پیدا نشدن دستگاه خارجی است. برای رفع این مشکل می‌بایست شبیه ساز youwave را به ویژوال استودیو وصل کنیم. برای این منظور دستور زیر را در cmd اجرا کنید.
adb connect localhost:5558

بعد از آن اگر پروژه را اجرا کنید، فایل apk. پروژه بر روی شبیه ساز نصب شده و اجرا خواهد شد. با کلیک بر روی دکمه‌ی confirm تصویری به شکل زیر قابل مشاهده خواهد بود:


علاوه بر این ما در سند HTML خود در بالا، یک page و یک تگ a قرار داده‌ایم. 
 <a href="#page2" data-transition="pop" data-rel="dialog" data-role="button" data-inline="true" data-icon="back">page 2</a>
data-role: با مقدار button در نظر گرفته شده است؛ لذا به شکل 4 دکمه دیگر رندر خواهد شد.
data-transition: با مقدار pop در نظر گرفته شده است که مشخص کننده‌ی افکت ظاهر شدن صفحه‌ای است که قرار است بار گذاری شود.
data-rel: مشخص می‌کند که صفحه‌ی مورد نظر من به صورت دیالوگ باز شود.
data-icon: با استفاده از این ویژگی می‌توان icon مورد نظر خود را برای المنت در نظر گرفت.
data-inline: برای به خط کردن دکمه‌ها کنار هم استفاده می‌شود.
با لمس کردن این دکمه، نتیجه به شکل زیر خواهد بود:

در مقاله‌ی بعد، به مباحث Database در Cordova خواهیم پرداخت.

ادامه دارد...

اشتراک‌ها
تغییراتی در مجوز jQuery
مجوز قسمت‌های وابسته‌ای که GPL بوده‌اند اکنون به مجوز آزادتری به نام MIT‌ تبدیل شده‌اند
تغییراتی در مجوز jQuery
مطالب
Angular Material 6x - قسمت ششم - کار با فرم‌ها و دیالوگ‌ها
در این قسمت قصد داریم به لیست فعلی کاربران و تماس‌های تعریف شده، تماس‌های جدیدی را اضافه کنیم و می‌خواهیم این‌کار را توسط دیالوگ‌های Popup بسته‌ی Angular Material انجام دهیم.


معرفی سرویس MatDialog

توسط سرویس MatDialog می‌توان modal dialogs بسته‌ی Angular Material را نمایش داد که به همراه طراحی متریال و پویانمایی مخصوص آن است.
 let dialogRef = dialog.open(UserProfileComponent,  { height: '400px’,  width: '600px’  });
در اینجا یک صفحه‌ی دیالوگ، توسط متد open آن باز خواهد شد. پارامتر اول آن کامپوننتی است که باید بارگذاری شود و پارامتر دوم آن یک شیء تنظیمات اختیاری است. خروجی این متد وهله‌ای است از MatDialogRef و توسط آن می‌توان به دیالوگ باز شده دسترسی یافت:
dialogRef.afterClosed().subscribe(result => {
   console.log(`Dialog result: ${result}`);
});
dialogRef.close('value');
از آن می‌توان برای بستن dialog و یا دریافت پیامی پس از بسته شدن دیالوگ، استفاده کرد.
در این مثال اگر dialogRef را با متد close و پارامتر value فراخوانی کنیم، سبب بسته شدن این دیالوگ خواهیم شد. این پارامتر در قسمت Dialog result پیام دریافتی پس از بسته شدن دیالوگ نیز قابل دسترسی است.
کامپوننت‌هایی که توسط سرویس MatDialog نمایش داده می‌شوند، می‌توانند توسط سرویس جنریک MatDialogRef، صفحه‌ی دیالوگ باز شده را ببندند:
@Component({/* ... */})
export class YourDialog {

   constructor(public dialogRef: MatDialogRef<YourDialog>) { }

   closeDialog() {
      this.dialogRef.close('Value….!’);
   }
}
نکته‌ی مهم: چون سرویس MatDialog کار وهله سازی و نمایش کامپوننت‌ها را به صورت پویا انجام می‌دهد، محل تعریف این نوع کامپوننت‌های ویژه در قسمت entryComponents ماژول مرتبط است. به این ترتیب به A head of time compiler اعلام می‌کنیم که component factory این کامپوننت پویا است و باید به نحو ویژه‌ای مدیریت شود.

نحوه‌ی طراحی یک دیالوگ نیز به کمک تعدادی کامپوننت و دایرکتیو میسر است:
<h2 mat-dialog-title>Delete all</h2>
<mat-dialog-content>Are you sure?</mat-dialog-content>
<mat-dialog-actions>
    <button mat-button mat-dialog-close>No</button>
    <!-- Can optionally provide a result for the closing dialog. -->
    <button mat-button [mat-dialog-close]="true">Yes</button>
</mat-dialog-actions>
دایرکتیو mat-dialog-title سبب نمایش عنوان دیالوگ می‌شود. mat-dialog-content محتوای قابل اسکرول این دیالوگ را در بر می‌گیرد. mat-dialog-actions محلی است برای قرارگیری action buttons در پایین صفحه‌ی دیالوگ. در اینجا اگر ویژگی mat-dialog-close به true تنظیم شود، آن دکمه قابلیت بستن دیالوگ را پیدا می‌کند.


ایجاد دکمه‌ی نمایش دیالوگ افزودن تماس‌ها و کاربران جدید

قبل از هر کاری نیاز است دکمه‌ی افزودن یک کاربر جدید را به صفحه اضافه کنیم. برای اینکار یک منوی ویژه را در سمت راست، بالای صفحه ایجاد می‌کنیم. بنابراین ابتدا به مستندات toolbar و menu مراجعه می‌کنیم تا با نحوه‌ی تعریف دکمه‌ها و منوها به toolbar آشنا شویم. سپس فایل قالب toolbar\toolbar.component.html را به صورت زیر تکمیل می‌کنیم:
<mat-toolbar color="primary">
  <button mat-button fxHide fxHide.xs="false" (click)="toggleSidenav.emit()">
    <mat-icon>menu</mat-icon>
  </button>

  <span>Contact Manager</span>

  <span fxFlex="1 1 auto"></span>
  <button mat-button [matMenuTriggerFor]="menu">
    <mat-icon>more_vert</mat-icon>
  </button>
  <mat-menu #menu="matMenu">
    <button mat-menu-item>New Contact</button>
  </mat-menu>
</mat-toolbar>
توسط یک span و سپس fxFlex تعریف شده‌ی آن سبب خواهیم شد تا mat-button بعدی و محتوای پس از آن به گوشه‌ی سمت راست toolbar هدایت شوند؛ در غیراینصورت دقیقا در کنار عبارت Contact manager ظاهر خواهند شد.
سپس ابتدا یک mat-button را با آیکن more_vert (آیکن علامت بیشتر عمودی) تعریف کرده‌ایم:


این دکمه توسط ویژگی matMenuTriggerFor به template reference variable ایی به نام menu متصل شده‌است تا با کلیک بر روی آن، این mat-menu را نمایش دهد:



ایجاد دیالوگ افزودن تماس‌ها و کاربران جدید

پس از تعریف دکمه و منویی که سبب نمایش عبارت افزودن یک تماس جدید می‌شوند، به رخ‌داد کلیک آن متدی را جهت نمایش صفحه‌ی دیالوگ جدید اضافه می‌کنیم:
 <button mat-menu-item (click)="openAddContactDialog()">New Contact</button>
سپس در کدهای کامپوننت toolbar، کار مدیریت آن‌را انجام خواهیم داد. اما پیش از آن بهتر است کامپوننت جدیدی را که قرار است نمایش دهد به برنامه اضافه کنیم:
 ng g c contact-manager/components/new-contact-dialog --no-spec
این دستور علاوه بر تولید کامپوننت جدید new-contact-dialog در پوشه‌ی components، کار تعریف مدخل آن‌را در ماژول این قسمت نیز انجام می‌دهد. اما همانطور که پیشتر نیز عنوان شد، باید آن‌را به لیست entryComponents اضافه کنیم تا بتوان آن‌را به صورت پویا بارگذاری کرد (در غیر اینصورت در زمان نمایش پویای آن، خطای no component factory for را دریافت می‌کنیم). بنابراین فایل contact-manager.module.ts را گشوده و به صورت زیر تکمیل می‌کنیم:
import { NewContactDialogComponent } from "./components/new-contact-dialog/new-contact-dialog.component";

@NgModule({
  declarations: [
    NewContactDialogComponent],
  entryComponents: [
    NewContactDialogComponent
  ]
})
export class ContactManagerModule { }
اکنون می‌توانیم سرویس MatDialog را به سازنده‌ی کامپوننت toolbar تزریق کرده و از آن برای نمایش این کامپوننت جدید استفاده کنیم:
import { Component, EventEmitter, OnInit, Output } from "@angular/core";
import { MatDialog } from "@angular/material";

import { NewContactDialogComponent } from "../new-contact-dialog/new-contact-dialog.component";

@Component({
  selector: "app-toolbar",
  templateUrl: "./toolbar.component.html",
  styleUrls: ["./toolbar.component.css"]
})
export class ToolbarComponent implements OnInit {

  @Output() toggleSidenav = new EventEmitter<void>();

  constructor(private dialog: MatDialog) { }

  ngOnInit() { }

  openAddContactDialog(): void {
    const dialogRef = this.dialog.open(NewContactDialogComponent, { width: "450px" });
    dialogRef.afterClosed().subscribe(result => {
      console.log("The dialog was closed", result);
    });
  }
}
در اینجا سرویس MatDialog به سازنده‌ی کامپوننت تزریق شده و سپس از آن در متد openAddContactDialog که متصل به منوی نمایش «new contact» است، جهت نمایش پویای کامپوننت NewContactDialogComponent، استفاده می‌شود. اکنون اگر برنامه را اجرا کنیم و گزینه‌ی «new contact» را از منو انتخاب کنیم، چنین تصویری حاصل خواهد شد:



تکمیل قالب کامپوننت تماس جدید

در ادامه می‌خواهیم فرم افزودن یک تماس جدید را به همراه فیلدهای ورودی آن، به قالب new-contact-dialog.component.html اضافه کنیم:
<h2 mat-dialog-title>Add new contact</h2>
<mat-dialog-content>
  <div fxLayout="column">

  </div>
</mat-dialog-content>
<mat-dialog-actions>
  <button mat-button color="primary" (click)="save()">
    <mat-icon>save</mat-icon> Save
  </button>
  <button mat-button color="primary" (click)="dismiss()">
    <mat-icon>cancel</mat-icon> Cancel
  </button>
</mat-dialog-actions>
تا اینجا ساختار مقدماتی صفحه دیالوگ را مطابق توضیحاتی که در ابتدای بحث عنوان شد، تکمیل کردیم. یک عنوان توسط mat-dialog-title به آن اضافه شده‌است. سپس mat-dialog-content اضافه شده که در ادامه آن‌را تکمیل می‌کنیم. در آخر هم دکمه‌های action به این دیالوگ استاندارد اضافه شده‌اند. برای تکمیل متدهای save و dismiss این دکمه‌ها، کدهای ذیل را به کامپوننت new-contact-dialog.component.ts اضافه می‌کنیم:
import { Component, OnInit } from "@angular/core";
import { MatDialogRef } from "@angular/material";

@Component()
export class NewContactDialogComponent implements OnInit {

  constructor(
    private dialogRef: MatDialogRef<NewContactDialogComponent>
  ) { }

  ngOnInit() {
  }

  save() {
  }

  dismiss() {
    this.dialogRef.close(null);
  }
}
در اینجا توسط سرویس MatDialogRef از نوع NewContactDialogComponent، می‌توانیم به ارجاعی از سرویس MatDialog باز کننده‌ی آن دسترسی پیدا کنیم و برای نمونه در متد dismiss سبب بسته شدن این دیالوگ شویم.
تا اینجا اگر برنامه را اجرا کنیم، به چنین شکلی خواهیم رسید:



تکمیل فیلدهای ورود اطلاعات فرم ثبت یک تماس جدید

تا اینجا ساختار فرم دیالوگ ثبت اطلاعات جدید را تکمیل کردیم. این فرم، به شیء user متصل خواهد شد. همچنین لیستی از avatars را هم جهت انتخاب، نمایش می‌دهد. به همین جهت این دو خاصیت عمومی را به کدهای کامپوننت آن اضافه می‌کنیم:
import { User } from "../../models/user";

@Component()
export class NewContactDialogComponent implements OnInit {

  avatars = ["user1", "user2", "user3", "user4", "user5", "user6", "user7", "user8"];
  user: User = { id: 0, birthDate: new Date(), name: "", avatar: "", bio: "", userNotes: null };
در ادامه فیلدهای آن‌را به صورت زیر در قسمت mat-dialog-content اضافه خواهیم کرد:


الف) فیلد نمایش و انتخاب avatar کاربر
    <mat-form-field>
      <mat-select placeholder="Avatar" [(ngModel)]="user.avatar">
        <mat-select-trigger>
          <mat-icon svgIcon="{{user.avatar}}"></mat-icon> {{ user.avatar }}
        </mat-select-trigger>
        <mat-option *ngFor="let avatar of avatars" [value]="avatar">
          <mat-icon svgIcon="{{avatar}}"></mat-icon> {{ avatar }}
        </mat-option>
      </mat-select>
    </mat-form-field>


در اینجا از کامپوننت mat-select برای انتخاب avatar کاربر استفاده شده‌است که نتیجه‌ی نهایی انتخاب آن به خاصیت user.avatar متصل شده‌است.
گزینه‌های این لیست (mat-option) بر اساس آرایه‌ی avatars که در کامپوننت تعریف کردیم، تامین می‌شوند که در اینجا از mat-icon برای نمایش آیکن مرتبط نیز استفاده شده‌است. در این مورد در قسمت قبل چهارم، بخش «بارگذاری و معرفی فایل svg نمایش avatars کاربران به Angular Material» بیشتر توضیح داده شده‌است.
کار mat-select-trigger، سفارشی سازی برچسب نمایشی این کنترل است.

ب) فیلد دریافت نام کاربر به همراه اعتبارسنجی آن
    <mat-form-field>
      <input matInput placeholder="Name" #name="ngModel" [(ngModel)]="user.name" required>
      <mat-error *ngIf="name.invalid && name.touched">You must enter a name</mat-error>
    </mat-form-field>


در اینجا فیلد نام کاربر، به user.name متصل و همچنین توسط ویژگی required، پر کردن آن الزامی اعلام شده‌است. به همین جهت در تصویر فوق یک ستاره را نیز کنار آن مشاهده می‌کند که به صورت خودکار توسط Angular Material نمایش داده شده‌است.
از کامپوننت mat-error برای نمایش خطاهای اعتبارسنجی یک فیلد استفاده می‌شود که نمونه‌ای از آن‌را در اینجا با بررسی خواص invalid  و touched فیلد نام که بر اساس ویژگی required فعال می‌شوند، مشاهده می‌کنید. بدیهی است در اینجا به هر تعدادی که نیاز است می‌توان mat-error را قرار داد.

ج) فیلد دریافت تاریخ تولد کاربر توسط یک date picker
    <mat-form-field>
      <input matInput [matDatepicker]="picker" placeholder="Born" [(ngModel)]="user.birthDate">
      <mat-datepicker-toggle matSuffix [for]="picker"></mat-datepicker-toggle>
      <mat-datepicker #picker></mat-datepicker>
    </mat-form-field>


در اینجا از کامپوننت mat-datepicker برای انتخاب تاریخ تولید یک شخص استفاده شده‌است و نتیجه‌ی آن به خاصیت user.birthDate متصل خواهد شد.
برای افزودن آن ابتدا یک mat-datepicker را به mat-form-field اضافه می‌کنیم. سپس یک template reference variable را به آن نسبت خواهیم داد. از آن هم در فیلد ورودی با انتساب آن به ویژگی matDatepicker و هم در کامپوننت mat-datepicker-toggle که سبب نمایش آیکن انتخاب تقویم می‌شود، در ویژگی for آن استفاده خواهیم کرد.

د) فیلد چند سطری دریافت توضیحات و شرح‌حال کاربر
    <mat-form-field>
      <textarea matInput placeholder="Bio" [(ngModel)]="user.bio"></textarea>
    </mat-form-field>


در اینجا برای دریافت توضیحات چندسطری، از یک text area استفاده شده‌است که به خاصیت user.bio متصل است.

بنابراین همانطور که ملاحظه می‌کنید، روش طراحی فرم‌های Angular Material ویژگی‌های خاص خودش را دارد:
- دایرکتیو matInput را می‌توان به المان‌های استاندارد input و textarea اضافه کرد تا داخل mat-form-field نمایش داده شوند. این mat-form-field است که کار اعمال CSS ویژه‌ی طراحی متریال را انجام می‌دهد و امکان نمایش پیام‌های خطای اعتبارسنجی و پویانمایی ورود اطلاعات را سبب می‌شود.
- قسمت mat-dialog-content را توسط fxLayout به حالت ستونی تنظیم کردیم:
<mat-dialog-content>
  <div fxLayout="column">

  </div>
</mat-dialog-content>
این مورد است که سبب خواهد شد المان‌های فرم از بالا به پایین نمایش داده شوند. در غیراینصورت mat-form-fieldها دقیقا در کنار هم قرار می‌گیرند.
برای مثال اگر خواستید المان‌های فرم با فاصله‌ی بیشتری از هم قرار بگیرند، می‌توان از fxLayoutGap استفاده کرد که در مورد آن در قسمت دوم «معرفی Angular Flex layout» بیشتر توضیح داده شد.


تکمیل سرویس کاربران جهت ذخیره‌ی اطلاعات تماس کاربر جدید

در ادامه می‌خواهیم با کلیک کاربر بر روی دکمه‌ی Save، ابتدا این اطلاعات به سمت سرور ارسال و سپس در سمت سرور ذخیره شوند. پس از آن، Id این کاربر جدید به سمت کلاینت بازگشت داده شود، دیالوگ جاری بسته و در آخر این شیء جدید به لیست تماس‌های نمایش داده‌ی شده‌ی در sidenav اضافه گردد.

الف) تکمیل سرویس Web API سمت سرور
در ابتدا متد Post را به Web API برنامه جهت ذخیره سازی اطلاعات User ارسالی از سمت کلاینت اضافه می‌کنیم. کدهای کامل آن‌را از فایل پیوستی انتهای بحث می‌توانید دریافت کنید:
namespace MaterialAspNetCoreBackend.WebApp.Controllers
{
    [Route("api/[controller]")]
    public class UsersController : Controller
    {
        private readonly IUsersService _usersService;

        public UsersController(IUsersService usersService)
        {
            _usersService = usersService ?? throw new ArgumentNullException(nameof(usersService));
        }

        [HttpPost]
        public async Task<IActionResult> Post([FromBody] User user)
        {
            if (!ModelState.IsValid)
            {
                return BadRequest(ModelState);
            }
            await _usersService.AddUserAsync(user);
            return Created("", user);
        }
    }
}

ب) تکمیل سرویس کاربران سمت کلاینت
سپس به فایل user.service.ts مراجعه کرده و دو تغییر زیر را به آن اضافه می‌کنیم:
@Injectable({
  providedIn: "root"
})
export class UserService {

  private usersSource = new BehaviorSubject<User>(null);
  usersSourceChanges$ = this.usersSource.asObservable();

  constructor(private http: HttpClient) { }

  addUser(user: User): Observable<User> {
    const headers = new HttpHeaders({ "Content-Type": "application/json" });
    return this.http
      .post<User>("/api/users", user, { headers: headers }).pipe(
        map(response => {
          const addedUser = response || {} as User;
          this.notifyUsersSourceHasChanged(addedUser);
          return addedUser;
        }),
        catchError((error: HttpErrorResponse) => throwError(error))
      );
  }

  notifyUsersSourceHasChanged(user: User) {
    this.usersSource.next(user);
  }
}
کار متد addUser، ارسال اطلاعات فرم ثبت یک تماس جدید به سمت سرور و Web API برنامه است. پس از ثبت موفقیت آمیز کاربر در سمت سرور، متد return Created آن:
 return Created("", user);
سبب خواهد شد تا بتوانیم در سمت کلاینت، به Id اطلاعات رکورد جدید دسترسی داشته باشیم. مزیت آن امکان افزودن این رکورد به لیست کاربران sidenav و همچنین فعالسازی مسیریابی آن است که بر اساس این Id واقعی کار می‌کند.
بنابراین نیاز است از طریق این سرویس به کامپوننت sidenav، در مورد تغییرات لیست کاربران اطلاعات رسانی کنیم که روش کار آن‌را پیشتر در مطلب «صدور رخدادها از سرویس‌ها به کامپوننت‌ها در برنامه‌های Angular» نیز مرور کرده‌ایم. برای این منظور یک BehaviorSubject از نوع User را تعریف کرده‌ایم که اشتراک به آن از طریق خاصیت عمومی usersSourceChanges میسر است. هر زمانیکه متد next آن فراخوانی شود، تمام مشترکین به آن، از افزوده شدن کاربر جدید، به همراه اطلاعات کامل آن مطلع خواهند شد.

ج) تکمیل متد save کامپوننت new-contact-dialog
پس از تکمیل سرویس کاربران جهت افزودن متد addUser به آن، اکنون می‌توانیم از آن در کامپوننت دیالوگ افزودن اطلاعات تماس جدید استفاده کنیم:
import { UserService } from "../../services/user.service";

@Component()
export class NewContactDialogComponent {

  user: User = { id: 0, birthDate: new Date(), name: "", avatar: "", bio: "", userNotes: null };

  constructor(
    private dialogRef: MatDialogRef<NewContactDialogComponent>,
    private userService: UserService
  ) { }

  save() {
    this.userService.addUser(this.user).subscribe(data => {
      console.log("Saved user", data);
      this.dialogRef.close(data);
    });
  }
}
در اینجا در متد save، ابتدا متد addUser سرویس افزودن اطلاعات جدید فراخوانی می‌شود. سپس در صورت موفقیت آمیز بودن عملیات، توسط سرویس dialogRef، این صفحه‌ی دیالوگ نیز به صورت خودکار بسته خواهد شد. همچنین به متد close آن data دریافتی از سرور ارسال شده‌است. این data در toolbar.component در قسمت dialogRef.afterClosed قابل دسترسی خواهد بود.

د) تکمیل کامپوننت sidenav جهت واکنش نشان دادن به افزوده شدن اطلاعات تماس جدید
اکنون که سرویس کاربران به صفحه دیالوگ افزودن اطلاعات یک تماس جدید متصل شده‌است، نیاز است بتوانیم اطلاعات کاربر جدید را به لیست تماس‌های sidenav اضافه کنیم. به همین جهت به sidenav.component مراجعه کرده و مشترک usersSourceChanges سرویس کاربران خواهیم شد:
import { UserService } from "../../services/user.service";

@Component()
export class SidenavComponent implements OnInit, OnDestroy {

  users: User[] = [];
  subscription: Subscription | null = null;

  constructor(
    private userService: UserService) { }

  ngOnInit() {
    this.subscription = this.userService.usersSourceChanges$.subscribe(user => {
      if (user) {
        this.users.push(user);
      }
    });
  }

  ngOnDestroy() {
    if (this.subscription) {
      this.subscription.unsubscribe();
    }
  }
}
ابتدا در ngOnInit توسط سرویس کاربران، مشترک تغییرات usersSourceChanges خواهیم شد. در اینجا اگر کاربر جدیدی به لیست اضافه شده باشد، آن‌را توسط متد push به لیست کاربران جاری sidenav اضافه می‌کنیم تا بلافاصله در لیست نمایش داده شود.


استفاده از کامپوننت Snackbar جهت نمایش موفقیت آمیز بودن ثبت اطلاعات

متد save کامپوننت دیالوگ یک تماس جدید را به صورت زیر تکمیل کردیم:
  save() {
    this.userService.addUser(this.user).subscribe(data => {
      console.log("Saved user", data);
      this.dialogRef.close(data);
    });
در اینجا data ارسال شده‌ی به متد close در کامپوننت toolbar در قسمت dialogRef.afterClosed قابل دسترسی خواهد بود:
  openAddContactDialog(): void {
    const dialogRef = this.dialog.open(NewContactDialogComponent, { width: "450px" });
    dialogRef.afterClosed().subscribe(result => {
      console.log("The dialog was closed", result);
    });
  }
بنابراین در ادامه قصد داریم از آن جهت نمایش یک snackbar به همراه ارائه لینک هدایت به صفحه‌ی جزئیات تماس جدید، استفاده کنیم:


کدهای کامل این تغییرات را در ذیل مشاهده می‌کنید:
@Component()
export class ToolbarComponent {

  @Output() toggleSidenav = new EventEmitter<void>();

  constructor(private dialog: MatDialog, private snackBar: MatSnackBar,  private router: Router) { }

  openAddContactDialog(): void {
    const dialogRef = this.dialog.open(NewContactDialogComponent, { width: "450px" });
    dialogRef.afterClosed().subscribe((result: User) => {
      console.log("The dialog was closed", result);
      if (result) {
        this.openSnackBar(`${result.name} contact has been added.`, "Navigate").onAction().subscribe(() => {
          this.router.navigate(["/contactmanager", result.id]);
        });
      }
    });
  }

  openSnackBar(message: string, action: string): MatSnackBarRef<SimpleSnackBar> {
    return this.snackBar.open(message, action, {
      duration: 5000,
    });
  }
}
توضیحات:
برای گشودن snackbar که نمونه‌ای از آن‌را در تصویر فوق ملاحظه می‌کنید، ابتدا نیاز است سرویس MatSnackBar را به سازنده‌ی کلاس تزریق کرد. سپس توسط آن می‌توان یک کامپوننت مستقل را همانند دیالوگ‌ها نمایش داد و یا می‌توان یک متن را به همراه یک Action منتسب به آن، به کاربر نمایش داد؛ مانند متد openSnackBar که در کامپوننت فوق از آن استفاده می‌شود. این متد در رخ‌داد پس از بسته شدن dialog، نمایش داده شده‌است.
پارامتر اول آن پیامی است که توسط snackbar نمایش داده می‌شود و پارامتر دوم آن، برچسب دکمه مانندی است کنار این پیام، که سبب انجام عملی خواهد شد و در اینجا به آن Action گفته می‌شود. برای مدیریت آن باید متد onAction را فراخوانی کرد و مشترک آن شد. در این حالت اگر کاربر بر روی این دکمه‌ی action کلیک کند، سبب هدایت خودکار او به صفحه‌ی نمایش جزئیات اطلاعات تماس کاربر خواهیم شد. به همین جهت سرویس Router نیز به سازنده‌ی کلاس تزریق شده‌است تا بتوان از متد navigate آن استفاده کرد.



کدهای کامل این قسمت را از اینجا می‌توانید دریافت کنید: MaterialAngularClient-05.zip
برای اجرای آن:
الف) ابتدا به پوشه‌ی src\MaterialAngularClient وارد شده و فایل‌های restore.bat و ng-build-dev.bat را اجرا کنید.
ب) سپس به پوشه‌ی src\MaterialAspNetCoreBackend\MaterialAspNetCoreBackend.WebApp وارد شده و فایل‌های restore.bat و dotnet_run.bat را اجرا کنید.
اکنون برنامه در آدرس https://localhost:5001 قابل دسترسی است.
مطالب دوره‌ها
بوت استرپ (نگارش 3) چیست؟
بوت استرپ یک فریم ورک CSS واکنشگرا (responsive) است، که جهت ساخت سریع برنامه‌های استاتیک و همچنین پویای وب کاربرد دارد. در حال حاضر این پروژه جزو محبوب‌ترین و فعال‌ترین پروژه‌های سایت Github است. اگر علاقمند هستید که لیستی از سایت‌های استفاده کننده از بوت استرپ را مشاهده کنید، به آدرس‌های ذیل مراجعه نمائید:


تازه‌های بوت استرپ 3 کدامند؟

- بوت استرپ 3 جهت کار با صفحه‌های نمایش کوچک دستگاه‌های موبایل به شدت بهینه سازی شده است و به همین جهت به آن mobile-first CSS framework نیز می‌گویند.
- در نگارش 2 بوت استرپ، حداقل دو نوع گرید واکنشگرا و غیر واکنشگرا قابل تعریف بودند. در نگارش سوم آن، تنها یک نوع گرید جدید واکنشگرا در این فریم ورک وجود دارد که می‌تواند چهار نوع سایز از بزرگ تا کوچک را شامل شود.
- بوت استرپ 3 با IE7 به قبل و همچنین فایرفاکس 3.6 و پایین‌تر دیگر سازگار نیست. البته برای پشتیبانی از IE8، نیاز به اندکی تغییرات نیز وجود خواهد داشت که در قسمت‌های بعد این جزئیات را بیشتر بررسی خواهیم کرد. به عبارت دیگر بدون این تغییرات، بوت استرپ 3 در حالت پیش فرض با IE9 به بعد سازگار است.
- در بوت استرپ 3 برخلاف نگارش قبلی آن که لیستی از آیکن‌های خود را در قالب چند فایل PNG image sprite که آیکن‌ها را به صورت فشرده در کنار هم قرار داده بود، اینبار تنها از Font icons استفاده می‌کند. به این ترتیب تغییر اندازه این آیکن‌ها با توجه به برداری بودن نمایش قلم‌ها و همچنین قابلیت اعمال رنگ به آن‌ها نیز بسیار ساده‌تر می‌گردد.


سؤال: آیا نیاز است از یک فریم ورک CSS واکنشگرا استفاده شود؟

در سال‌های قبل، عموما طراحی وب بر اساس تهیه یا خرید یک سری قالب‌های از پیش آماده شده، شکیل صورت می‌گرفته‌است. این قالب‌ها به سرعت با برنامه، یکپارچه شده و حداکثر قلم یا رنگ‌های آن‌ها‌را اندکی تغییر می‌دادیم و یا اینکه خودمان کل این مسیر را از صفر طی می‌کردیم. این پروسه سفارشی، بسیار سنگین بوده و مشکل مهم آن، عدم امکان استفاده مجدد از طراحی‌های انجام شده می‌باشد که نهایتا در دراز مدت هزینه‌ی بالایی را برای ما به همراه خواهند داشت. اما با استفاده از فریم ورک‌های CSS واکنشگرا به این مزایا خواهیم رسید:
- قسمت عمده‌ای از کار پیشتر برای شما انجام شده است.
برای مثال نیازی نیست تا حتما برای طرحبندی صفحه، سیستم گرید خاص خودتان را طراحی کنید و یا اینکه مانند سال‌های دور، به استفاده از HTML tables پناه ببرید.
- قابلیت سفارشی سازی بسیار بالایی دارند.
برای مثال با استفاده از فناوری‌هایی مانند less می‌توان بوت استرپ را تا حد بسیار زیادی سفارشی سازی کرد. به این ترتیب دیگر یک سایت بوت استرپ، شبیه به بوت استرپ به نظر نخواهد رسید! شاید عده‌ای عنوان کنند که تمام سایت‌های بوت استرپ یک شکل هستند، اما واقعیت این است که این سایت‌ها تنها از قابلیت‌های سفارشی سازی بوت استرپ و less استفاده نکرده‌اند.
 

دریافت بوت استرپ 3

سایت رسمی دریافت بوت استرپ، آدرس ذیل می‌باشد:

البته ما از این نگارش خام استفاده نخواهیم کرد و نیاز است برای کارهای سایت‌های فارسی، از نگارش راست به چپ آن استفاده کنیم. بنابراین اگر از ویژوال استودیو استفاده می‌کنید، می‌توانید به یکی از دو بسته نیوگت ذیل مراجعه نمائید:
و اگر می‌خواهید صرفا به فایل‌های درون این بسته‌ها دسترسی پیدا کنید، از دو آدرس ذیل استفاده کنید:
فایل‌های دریافت شده با پسوند nupkg، در حقیقت یک فایل zip استاندارد هستند.

اگر بوت استرپ اصل را از سایت اصلی آن دریافت کنید، شامل تعداد فایل‌ها و پوشه‌های بسیار بیشتری است نسبت به نمونه RTL فوق. اما فایل‌های نهایی آن که مورد استفاده قرار خواهند گرفت، درون پوشه dist یا توزیع آن قرار گرفته‌اند و آنچنان تفاوتی با نگارش RTL ندارند. فقط در نگارش اصل، فایل‌های min و فشرده شده نیز همراه این بسته هستند که در نگارش RTL لحاظ نشده‌اند. این موضوع در آینده به نفع ما خواهد بود. از این لحاظ که اگر از سیستم bundling & minification مربوط بهASP.NET  استفاده کنید (جهت تولید خودکار فایل‌های min در زمان اجرا)، این سیستم به صورت پیش فرض از فایل‌های min موجود استفاده می‌کند و ممکن است مدتی سردرگم باشید که چرا تغییراتی را که به فایل CSS بوت استرپ اعمال کرده‌ام، در سایت اعمال نمی‌شوند. به علاوه امکان اعمال تغییرات و حتی دیباگ فایل‌های غیرفشرده خصوصا جاوا اسکریپتی آن نیز بسیار ساده‌تر و مفهوم‌تر است.

جهت مطالعه مباحث تکمیلی در مورد نحوه فشرده سازی فایل‌های CSS یا JS می‌توانید به مقالات ذیل، در سایت جاری مراجعه نمائید:

علاوه بر این‌ها در نگارش سوم بوت استرپ، تعدادی فایل CSS جدید به نام قالب یا theme نیز اضافه شده‌اند که همراه نسخه RTL نیست. برای مثال اگر به پوشه bootstrap-3.0.0.zip\bootstrap-3.0.0\dist\css مراجعه کنید، فایل bootstrap-theme.css نیز قابل مشاهده است. به این ترتیب قالبی و لایه‌ای بر روی مقادیر پیش فرض موجود در فایل bootstrap.css اعمال خواهند شد؛ برای مثال اعمال طراحی تخت یا flat مدرن آن به دکمه‌ها و عناصر دیگر این مجموعه.


شروع یک فایل HTML با بوت استرپ

تا اینجا فرض بر این است که فایل‌های بوت استرپ را دریافت کرده‌اید. در ادامه قصد داریم، نحوه معرفی این فایل‌ها را در یک فایل ساده HTML بررسی کنیم.
<!doctype html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>My Website</title>
    <link href="Content/css/bootstrap-rtl.css" rel="stylesheet">    
<link href="Content/css/custom.css" rel="stylesheet">    
</head>
<body>


</body>
</html>
صفحه آغازین کار با بوت استرپ 3 یک چنین شکلی را خواهد داشت و می‌تواند پایه تشکیل فایل masterpage یا layout برنامه‌های ASP.NET قرار گیرد. متا تگ viewport اضافه شده، جهت طراحی‌های واکنشگرا اضافه شده است و در ادامه لینک شدن فایل CSS بوت استرپ 3 را ملاحظه می‌کنید.
اگر سایت شما از تعاریف CSS سفارشی دیگری نیز استفاده می‌کند، تعاریف آن‌ها باید پس از بوت استرپ، ذکر گردند.


افزودن اسکریپت‌های بوت استرپ 3

برای کار با اسکریپت‌های بوت استرپ 3 نیاز است ابتدا jQuery را به صورت جداگانه دریافت کنیم. در حال حاضر اگر به سایت جی‌کوئری مراجعه کنید با دو نگارش 1.x و 2.x این کتابخانه مواجه خواهید شد. اگر نیاز به پشتیبانی از IE 8 را در محل کار خود دارید، باید از نگارش 1.x استفاده کنید. نگارش آخر 1.x کتابخانه جی‌کوئری را از طریق CDN آن همواره می‌توان مورد استفاده قرار داد:
 <script src="http://code.jquery.com/jquery-latest.min.js"></script>
بهتر است تعاریف فایل‌های جاوا اسکریپت را پیش از بسته شدن تگ body قرار دهید. یکی از مزایای مهم آن مشاهده نشدن یک فلش کوتاه مدت سفید رنگ در ابتدای بارگذاری صفحاتی با پس زمینه غیر روشن است. از این جهت که هر المانی که در head صفحه تعریف شود، حتما باید پیش از بارگذاری کل صفحه دریافت گردد. به این ترتیب با سرعت‌های دریافت کمتر، این مساله سبب خالی ماندن صفحه برای مدتی کوتاه خواهد شد و همان فلش سفید رنگ عنوان شده را پدید می‌آورد؛ چون هنوز مابقی صفحه بارگذاری نشده و خالی است.
پس از تعریف جی‌کوئری، تعریف اسکریپت‌های بوت استرپ قرار می‌گیرد (چون وابسته است به جی‌کوئری). فایل bootstrap-rtl.js شامل تمام زیر فایل‌های مورد نیاز نیز می‌باشد:
 <script src="Scripts/bootstrap-rtl.js"></script>
برای سازگار سازی بوت استرپ 3 با IE8 نیاز به یک فایل اسکریپت دیگر نیز داریم. این فایل را از آدرس ذیل دریافت نمائید:
این فایل 4 کیلوبایتی را نیز باید به تعاریف اسکریپت‌های مورد نیاز، اضافه کرد:
 <script src="Scripts/respond.min.js"></script>
البته این اسکریپت خاص، مطابق توضیحات آن باید به head صفحه اضافه شود تا با IE8 بهتر کار کند.
تا اینجا ساختار صفحه HTML تهیه شده جهت استفاده از امکانات بوت استرپ 3، شکل زیر را خواهد داشت:
<!doctype html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>My Website</title>

    <link href="Content/css/bootstrap-rtl.css" rel="stylesheet">    
<link href="Content/css/custom.css" rel="stylesheet">       
<script src="Scripts/respond.min.js"></script>
</head>
<body>


<script src="http://code.jquery.com/jquery-latest.min.js"></script>
<script src="Scripts/bootstrap-rtl.js"></script>
</body>
</html>

فایل‌های نهایی این قسمت را از اینجا نیز می‌توانید دریافت کنید:
bs3-sample01.zip
 
مطالب
بررسی ORM های مناسب جهت استفاده در اندروید
با آمدن ORM‌ها به دنیای برنامه نویسی، کار برنامه نویسی نسبت به قبل ساده‌تر و راحت‌تر شد. عدم استفاده کوئری‌های دستی، پشتیبانی از چند دیتابیس و از همه مهمتر و اصلی‌ترین هدف این ابزار "تنها درگیری با اشیا و مدل شیء گرایی" کار را پیش از پیش آسان‌تر نمود.
در این بین به راحتی می‌توان چندین نمونه از این ORM‌ها را  نام برد مثل IBatis , Hibernate ,Nhibernate و EF که از معروفترین آن‌ها هستند.
من در حال حاضر قصد شروع یک پروژه اندرویدی را دارم و دوست دارم بجای استفاده‌ی از Sqlitehelper، از یک ORM مناسب بهره ببرم که چند سوال برای من پیش می‌آید. آیا ORM ای برای آن تهیه شده است؟ اگر آری چندتا و کدامیک از آن‌ها بهتر هستند؟ شاید در اولین مورد کتابخانه‌ی Hibernate جاوا را نام ببرید؛ ولی توجه به این نکته ضروری است که ما در مورد پلتفرم موبایل و محدودیت‌های آن صحبت می‌کنیم. یک کتابخانه همانند Hibernate مطمئنا برای یک برنامه اندروید چه از نظر حجم نهایی برنامه و چه از نظر حجم بزرگش در اجرا، مشکل زا خواهد بود و وجود وابستگی‌های متعدد و دارا بودن بسیاری از قابلیت‌هایی که اصلا در بانک‌های اطلاعاتی موبایل قابل اجرا نیست، باعث می‌شود این فریمورک انتخاب خوبی برای یک برنامه اندروید نباشد.

معیارهای انتخاب یک فریم ورک مناسب برای موبایل:
  • سبک بودن: مهمترین مورد سبک بودن آن است؛ چه از لحاظ اجرای برنامه و چه از لحاظ حجم نهایی برنامه
  • سریع بودن: مطمئنا ORM‌های طراحی شده‌ی موجود، از سرعت خیلی بدی برخوردار نخواهند بود؛ اگر سر زبان هم افتاده باشند. ولی باز هم انتخاب سریع بودن یک ORM، مورد علاقه‌ی بسیاری از ماهاست.
  • یادگیری آسان و کانفیگ راحت تر.

OrmLight

این فریمورک مختص اندروید طراحی نشده ولی سبک بودن آن موجب شده‌است که بسیاری از برنامه نویسان از آن در برنامه‌های اندرویدی استفاده کنند. این فریم ورک جهت اتصالات JDBC و Spring و اندروید طراحی شده است.

نحوه معرفی جداول در این فریمورک به صورت زیر است:
@DatabaseTable(tableName = "users")
public class User {
    @DatabaseField(id = true)
    private String username;
    @DatabaseField
    private String password;
 
    public User() {
        // ORMLite needs a no-arg constructor
    }
    public User(String username, String password) {
        this.username = username;
        this.password = password;
    }
 
    // Implementing getter and setter methods
    public String getUserame() {
        return this.username;
    }
    public void setName(String username) {
        this.username = username;
    }
    public String getPassword() {
        return this.password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
}
با استفاده از کلمات کلیدی DatabaseTable@ در بالای کلاس و DatabaseField@ در بالای هر پراپرتی به معرفی جدول و فیلدهای جدول می‌پردازیم.
سورس این فریمورک را می‌توان در گیت هاب یافت و مستندات آن در این آدرس قرار دارند.


SugarORM
این فریمورک مختص اندروید طراحی شده است. یادگیری آن بسیار آسان است و به راحتی به یاد می‌ماند. همچنین جداول مورد نیاز را به طور خودکار خواهد ساخت. روابط یک به یک و یک به چند را پشتیبانی می‌کند و عملیات CURD را با سه متد Save,Delete و Find که البته FindById هم جزء آن است، پیاده سازی می‌کند.

برای استفاده از این فریمورک نیاز است ابتدا متادیتا‌های زیر را به فایل manifest اضافه کنید:
<meta-data android:name="DATABASE" android:value="my_database.db" />
<meta-data android:name="VERSION" android:value="1" />
<meta-data android:name="QUERY_LOG" android:value="true" />
<meta-data android:name="DOMAIN_PACKAGE_NAME" android:value="com.my-domain" />
برای تبدیل یک کلاس به جدول هم از کلاس این فریم ورک ارث بری می‌کنیم:
public class User extends SugarRecord<User> {
    String username;
    String password;
    int age;
    @Ignore
    String bio; //this will be ignored by SugarORM
 
    public User() { }
 
    public User(String username, String password,int age){
        this.username = username;
        this.password = password;
        this.age = age;
    }
}
بر خلاف OrmLight که باید فیلد جدول را معرفی می‌کردید، اینجا تمام پراپرتی‌ها به اسم فیلد شناخته می‌شوند؛ مگر اینکه در بالای آن از عبارت Ignore@ استفاده کنید.

باقی عملیات آن از قبیل اضافه کردن یک رکورد جدید یا حذف رکورد(ها) به صورت زیر است:
User johndoe = new User(getContext(),"john.doe","secret",19);
johndoe.save(); //ذخیره کاربر جدید در دیتابیس


//حذف تمامی کاربرانی که سنشان 19 سال است
List<User> nineteens = User.find(User.class,"age = ?",new int[]{19});
foreach(user in nineteens) {
    user.delete();
}
برای اطلاعات بیشتر به مستندات آن رجوع کنید.


GreenDAO
موقعیکه بحث کارآیی و سرعت پیش می‌آید نام GreenDAO هست که می‌درخشد. طبق گفته‌ی سایت رسمی آن این فریمورک میتواند در ثانیه چند هزار موجودیت را اضافه و به روزرسانی و بارگیری نماید. این لیست حاوی برنامه‌هایی است که از این فریمورک استفاده می‌کنند. جدول زیر مقایسه‌ای است بین این کتابخانه و OrmLight که نشان میدهد 4.5 برابر سریعتر از OrmLight عمل می‌کند.

غیر از این‌ها در زمینه‌ی حجم هم حرف‌هایی برای گفتن دارد. حجم این کتابخانه کمتر از 100 کیلوبایت است که در اندازه‌ی APK اثر چندانی نخواهد داشت.

آموزش راه اندازی  آن در اندروید استادیو، سورس آن در گیت هاب و مستندات رسمی آن.


Active Android
این کتابخانه از دو طریق فایل JAR و به شیوه maven قابل استفاده است که می‌توانید روش استفاده‌ی از آن را در این لینک ببینید و سورس اصلی آن هم در این آدرس قرار دارد. بعد از اینکه کتابخانه را به پروژه اضافه کردید، دو متادیتای زیر را که به ترتیب نام دیتابیس و ورژن آن هستند، به manifest اضافه کنید:
<meta-data android:name="AA_DB_NAME" android:value="my_database.db" />
<meta-data android:name="AA_DB_VERSION" android:value="1" />
بعد از آن  عبارت ;()ActiveAndroid.Initialize را در اکتیویتی‌های مدنظر اعمال کنید:
public class MyActivity extends Activity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ActiveAndroid.initialize(this);
 
        //ادامه برنامه
    }
}
برای معرفی کلاس‌ها به جدول هم از دو اعلان Table و Column مانند کد زیر به ترتیب برای معرفی جدول و فیلد استفاده می‌کنیم.
@Table(name = "User")
public class User extends Model {
    @Column(name = "username")
    public String username;
 
    @Column(name = "password")
    public String password;
 
    public User() {
        super();
    }
 
    public User(String username,String password) {
        super();
        this.username = username;
        this.password = password;
    }
}
جهت اطلاعات بیشتر در مورد این کتابخانه به مستندات آن رجوع کنید.


ORMDroid
 از آن دست کتابخانه‌هایی است که سادگی و کم حجم بودن شعار آنان است و سعی دارند تا حد ممکن همه چیز را خودکار کرده و کمترین کانفیگ را نیاز داشته باشد. حجم فعلی آن حدود 20 کیلوبایت بوده و نمی‌خواهند از 30 کیلوبایت تجاوز کند.

برای استفاده‌ی از آن ابتدا دو خط زیر را جهت معرفی تنظیمات به manifest اضافه کنید:
<meta-data
  android:name="ormdroid.database.name"
  android:value="your_database_name" />

<meta-data
  android:name="ormdroid.database.visibility"
  android:value="PRIVATE||WORLD_READABLE||WORLD_WRITEABLE" />
برای آغاز کار این کتابخانه، عبارت زیر را در هرجایی که مایل هستید مانند کلاس ارث بری شده از Application یا در ابتدای هر اکتیویتی که مایل هستید بنویسید. طبق مستندات آن صدا زدن چندباره این متد هیچ اشکالی ندارد.
ORMDroidApplication.initialize(someContext);
معرفی مدل جدول بانک اطلاعاتی هم از طریق ارث بری از کلاس Entity می‌باشد .
public class Person extends Entity {
  public int id;
  public String name;
  public String telephone;
}

//====================

Person p = Entity.query(Person.class).where("id=1").execute();
p.telephone = "555-1234";
p.save();

// یا

Person person = Entity.query(Person.class).where(eql("id", id)).execute();
p.telephone = "555-1234";
p.save();
کد بالا دقیقا یادآوری به EF هست ولی حیف که از Linq پشتیبانی نمی‌شود.
سورس آن در گیت هاب

در اینجا سعی کردیم تعدادی از کتابخانه‌های محبوب را معرفی کنیم ولی تعداد آن به همین جا ختم نمی‌شود. ORM‌های دیگری نظیر AndRom و سایر ORM هایی که در این لیست معرفی شده اند وجود دارند.


نکته نهایی اینکه خوب می‌شود دوستانی که از این ORM‌های مختص اندروید استفاده کرده اند؛ نظراتشان را در مورد آن‌ها بیان کنند و مزایا و معایب آن‌ها را بیان کنند.
مطالب
بارگذاری یک یوزرکنترل با استفاده از جی‌کوئری

مزیت استفاده از یوزر کنترل‌ها، ماژولار کردن برنامه است. برای مثال اگر صفحه جاری شما قرار است از چهار قسمت اخبار، منوی پویا ، سخن روز و آمار کاربران تشکیل شود، می‌توان هر کدام را توسط یک یوزر کنترل پیاده سازی کرده و سپس صفحه اصلی را از کنار هم قرار دادن این یوزر کنترل‌ها تهیه نمود.
با این توضیحات اکنون می‌خواهیم یک یوزکنترل ASP.Net را توسط jQuery Ajax بارگذاری کرده و نمایش دهیم. حداقل دو مورد کاربرد را می‌توان برای آن متصور شد:
الف) در اولین باری که یک صفحه در حال بارگذاری است، قسمت‌های مختلف آن‌را بتوان از یوزر کنترل‌های مختلف خواند و تا زمان بارگذاری کامل هر کدام، یک عبارت لطفا منتظر بمانید را نمایش داد. نمونه‌ی آن‌را شاید در بعضی از CMS های جدید دیده باشید. صفحه به سرعت بارگذاری می‌شود. در حالیکه مشغول مرور صفحه جاری هستید، قسمت‌های مختلف صفحه پدیدار می‌شوند.
ب) بارگذاری یک قسمت دلخواه صفحه بر اساس درخواست کاربر. مثلا کلیک بر روی یک دکمه و امثال آن.

روش کلی کار:
1) تهیه یک متد وب سرویس که یوزر کنترل را بر روی سرور اجرا کرده و حاصل را تبدیل به یک رشته کند.
2) استفاده از متد Ajax جی‌کوئری برای فراخوانی این متد وب سرویس و افزودن رشته دریافت شده به صفحه.
بدیهی است زمانیکه متد Ajax فراخوانی می‌شود می‌توان عبارت یا تصویر منتظر بمانید را نمایش داد و پس از پایان کار این متد، عبارت (یا تصویر) را مخفی نمود.

پیاده سازی:
قسمت تبدیل یک یوزر کنترل به رشته را قبلا در مقاله "تهیه قالب برای ایمیل‌های ارسالی یک برنامه ASP.Net" مشاهده کرده‌اید. در این‌جا برای استفاده از این متد در یک وب سرویس نیاز به کمی تغییر وجود داشت (KeyValuePair ها درست سریالایز نمی‌شوند) که نتیجه نهایی به صورت زیر است. یک فایل Ajax.asmx را به برنامه اضافه کرده و سپس در صفحه Ajax.asmx.cs کد آن به صورت زیر می‌تواند باشد:

using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Text.RegularExpressions;
using System.Web;
using System.Web.Script.Services;
using System.Web.Services;
using System.Web.UI;
using System.Web.UI.HtmlControls;

namespace AjaxTest
{
public class KeyVal
{
public string Key { set; get; }
public object Value { set; get; }
}

/// <summary>
/// Summary description for Ajax
/// </summary>
[ScriptService]
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[System.ComponentModel.ToolboxItem(false)]
public class Ajax : WebService
{
/// <summary>
/// Removes Form tags using Regular Expression
/// </summary>
private static string cleanHtml(string html)
{
return Regex.Replace(html, @"<[/]?(form)[^>]*?>", string.Empty, RegexOptions.IgnoreCase);
}

/// <summary>
/// تبدیل یک یوزر کنترل به معادل اچ تی ام ال آن
/// </summary>
/// <param name="path">مسیر یوزر کنترل</param>
/// <param name="properties">لیست خواص به همراه مقادیر مورد نظر</param>
/// <returns></returns>
/// <exception cref="NotImplementedException"><c>NotImplementedException</c>.</exception>
[WebMethod(EnableSession = true)]
[ScriptMethod(ResponseFormat = ResponseFormat.Json)]
public string RenderUserControl(string path,
List<KeyVal> properties)
{
Page pageHolder = new Page();

UserControl viewControl =
(UserControl)pageHolder.LoadControl(path);

viewControl.EnableViewState = false;

Type viewControlType = viewControl.GetType();

if (properties != null)
foreach (var pair in properties)
{
if (pair.Key != null)
{
PropertyInfo property =
viewControlType.GetProperty(pair.Key);

if (property != null)
{
if (pair.Value != null) property.SetValue(viewControl, pair.Value, null);
}
else
{
throw new NotImplementedException(string.Format(
"UserControl: {0} does not have a public {1} property.",
path, pair.Key));
}
}
}

//Form control is mandatory on page control to process User Controls
HtmlForm form = new HtmlForm();

//Add user control to the form
form.Controls.Add(viewControl);

//Add form to the page
pageHolder.Controls.Add(form);

//Write the control Html to text writer
StringWriter textWriter = new StringWriter();

//execute page on server
HttpContext.Current.Server.Execute(pageHolder, textWriter, false);

// Clean up code and return html
return cleanHtml(textWriter.ToString());
}
}
}
تا این‌جا متد وب سرویسی را داریم که می‌تواند مسیر یک یوزر کنترل را به همراه خواص عمومی آن‌را دریافت کرده و سپس یوزر کنترل را رندر نموده و حاصل را به صورت HTML به شما تحویل دهد. با استفاده از reflection خواص عمومی یوزر کنترل یافت شده و مقادیر لازم به آن‌ها پاس می‌شوند.

چند نکته:
الف) وب کانفیگ برنامه ASP.Net شما اگر با VS 2008 ایجاد شده باشد مداخل لازم را برای استفاده از این وب سرویس توسط jQuery Ajax دارد در غیر اینصورت موفق به استفاده از آن نخواهید شد.
ب) هنگام بازگرداندن این اطلاعات با فرمت json = ResponseFormat.Json جهت استفاده در jQuery Ajax ، گاهی از اوقات بسته به حجم بازگردانده شده ممکن است خطایی حاصل شده و عملیات متوقف شد. این طول پیش فرض را (maxJsonLength) در وب کانفیگ به صورت زیر تنظیم کنید تا مشکل حل شود:

<system.web.extensions>
<scripting>
<webServices>
<jsonSerialization maxJsonLength="10000000"></jsonSerialization>
</webServices>
</scripting>
</system.web.extensions>

برای پیاده سازی قسمت Ajax آن برای اینکه کار کمی تمیزتر و با قابلیت استفاده مجدد شود یک پلاگین تهیه شده (فایلی با نام jquery.advloaduc.js) که سورس آن به صورت زیر است:

$.fn.advloaduc = function(options) {
var defaults = {
webServiceName: 'Ajax.asmx', //نام فایل وب سرویس ما
renderUCMethod: 'RenderUserControl', //متد وب سرویس
ucMethodJsonParams: '{path:\'\'}',//پارامترهایی که قرار است پاس شوند
completeHandler: null //پس از پایان کار وب سرویس این متد جاوا اسکریپتی فراخوانی می‌شود
};
var options = $.extend(defaults, options);

return this.each(function() {
var obj = $(this);
obj.prepend("<div align='center'> لطفا اندکی تامل بفرمائید... <img src=\"images/loading.gif\"/></div>");

$.ajax({
type: "POST",
url: options.webServiceName + "/" + options.renderUCMethod,
data: options.ucMethodJsonParams,
contentType: "application/json; charset=utf-8",
dataType: "json",
success:
function(msg) {
obj.html(msg.d);

// if specified make callback and pass element
if (options.completeHandler)
options.completeHandler(this);
},
error:
function(XMLHttpRequest, textStatus, errorThrown) {
obj.html("امکان اتصال به سرور در این لحظه مقدور نیست. لطفا مجددا سعی کنید.");
}
});
});
};
برای اینکه با کلیات این روش آشنا شوید می‌توان به مقاله "بررسی وجود نام کاربر با استفاده از jQuery Ajax در ASP.Net" مراجعه نمود که از ذکر مجدد آن‌ها خودداری می‌شود. همچنین در مورد نوشتن یک پلاگین جی‌کوئری در مقاله "افزونه جملات قصار jQuery" توضیحاتی داده شده است.
عمده کاری که در این پلاگین صورت می‌گیرد فراخوانی متد Ajax جی‌کوئری است. سپس به متد وب سرویس ما (که در اینجا نام آن به صورت پارامتر نیز قابل دریافت است)، پارامترهای لازم پاس شده و سپس نتیجه حاصل به یک شیء در صفحه اضافه می‌شود.
completeHandler آن اختیاری است و پس از پایان کار متد اجکس فراخوانی می‌شود. در صورتیکه به آن نیازی نداشتید یا مقدار آن را null قرار دهید یا اصلا آن‌را ذکر نکنید.

مثالی در مورد استفاده از این وب سرویس و همچنین پلاگین جی‌کوئری نوشته شده:

الف) یوزر کنترل ساده زیر را به پروژه اضافه کنید:

<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="part1.ascx.cs" Inherits="TestJQueryAjax.part1" %>
<asp:Label runat="server" ID="lblData" ></asp:Label>
بدیهی است یک یوزر کنترل می‌تواند به اندازه یک صفحه کامل پیچیده باشد به همراه انواع و اقسام ارتباطات با دیتابیس و غیره.

سپس کد آن‌را به صورت زیر تغییر دهید:

using System;
using System.Threading;

namespace TestJQueryAjax
{
public partial class part1 : System.Web.UI.UserControl
{
public string Text1 { set; get; }
public string Text2 { set; get; }

protected void Page_Load(object sender, EventArgs e)
{
Thread.Sleep(3000);
if (!string.IsNullOrEmpty(Text1) && !string.IsNullOrEmpty(Text2))
lblData.Text = Text1 + "<br/>" + Text2;
}
}
}
این یوزر کنترل دو خاصیت عمومی دارد که توسط وب سرویس مقدار دهی خواهد شد و نهایتا حاصل نهایی را در یک لیبل در دو سطر نمایش می‌دهد.
عمدا یک sleep سه ثانیه‌ای در اینجا در نظر گرفته شده تا اثر آن‌را بهتر بتوان مشاهده کرد.

ب) اکنون کد مربوط به صفحه‌ای که قرار است این یوزر کنترل را به صورت غیرهمزمان بارگذاری کند به صورت زیر خواهد بود (مهم‌ترین قسمت آن نحوه تشکیل پارامترها و مقدار دهی خواص یوزر کنترل است):

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="TestJQueryAjax._Default" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>

<script src="js/jquery.js" type="text/javascript"></script>
<script src="js/jquery.advloaduc.js" type="text/javascript"></script>
<script src="js/json2.js" type="text/javascript"></script>

<script type="text/javascript">
function showAlert() {
alert('finished!');
}

//تشکیل پارامترهای متد وب سرویس جهت ارسال به آن
var fileName = 'part1.ascx';
var props = [{ 'Key': 'Text1', 'Value': 'سطر یک' }, { 'Key': 'Text2', 'Value': 'سطر 2'}];
var jsonText = JSON.stringify({ path: fileName, properties: props });

$(document).ready(function() {
$("#loadMyUc").advloaduc({
webServiceName: 'Ajax.asmx',
renderUCMethod: 'RenderUserControl',
ucMethodJsonParams: jsonText,
completeHandler: showAlert
});
});

</script>

</head>
<body>
<form id="form1" runat="server">
<div id="loadMyUc">
</div>
</form>
</body>

</html>
نکته:
برای ارسال صحیح و امن اطلاعات json به سرور، از اسکریپت استاندارد json2.js استفاده شد.

ابزار دیباگ:
بهترین ابزار برای دیباگ این نوع اسکریپت‌ها استفاده از افزونه فایرباگ فایرفاکس است. برای مثال مطابق تصویر زیر، یوزر کنترلی فراخوانی شده است که در سرور وجود ندارد:


دریافت مثال فوق