نظرات مطالب
نحوه استفاده از افزونه Firebug برای دیباگ برنامه‌های ASP.NET مبتنی بر jQuery
امکان قرار دادن کدهای سمت سرور داخل اسکریپت‌ها هم هست؛ مثلا:
  data: "{'username': '" + $('#<%= TextBox1.ClientID %>').val() + "'}",
به این شرط که این اسکریپت داخل صفحه runat=server دار باشد یا داخل head ایی با این مشخصات.
مطالب
توسعه برنامه‌های Cross Platform با Xamarin Forms & Bit Framework - قسمت هشتم
تا اینجا می‌دانیم که View که با Xaml نوشته می‌شود؛ مسئولیت ظاهر صفحات را به عهده داشته و View Model که با CSharp نوشته می‌شود نیز منطق هر صفحه را مدیریت می‌کند.
حال اگر بخواهیم در مثال Login، در صورتی که UserName و یا Password خالی بودند، به کاربر هشدار دهیم چه؟ برای این کار شما می‌توانید با توجه به دسترسی کد CSharp به صد در صد امکانات هر سیستم عامل، مثلا در Android از MakeToast استفاده کنید، ولی این کار باعث می‌شود مجبور شوید برای Android - iOS - Windows کدی متفاوت بنویسید که البته همه CSharp ای هستند، ولی به هر حال سه بار نوشتن یک چیز اصلا جالب نیست!
توجه: اگر پروژه XamApp را ندارید، آن را Clone کنید و اگر دارید، آخرین تغییرات را Pull کنید. مواردی که در ادامه گفته شده‌اند، در آخرین سورس‌های پروژه XamApp وجود دارند.
یک کتابخانه که این کار را برای ما ساده سازی می‌کند Acr User Dialogs است که قابلیت نمایش دادن Toast - Alert - Confirm - Action Sheet - Loading و ... را با یک کد و برای هر سه پلتفرم دارد. برای استفاده از این کتابخانه، ابتدا روی پروژه XamApp راست کلیک کرده و در Manage Nuget Packages پکیج Acr User Dialogs را نصب کنید.
برای نصب Package مربوطه، دقت کنید که Package Source در گوشه سمت راست-بالا روی All قرار گرفته باشد:

سپس در پروژه XamApp.Android، در کلاس Main Activity، کد زیر را قرار دهید:
 UserDialogs.Init(this); // Before Forms.Init
ممکن است ویژوال استودیو کلاس UserDialogs را نشناسد و کمکی برای افزودن using مربوطه در بالای کلاس MainActivity نکند. در این صورت ویژوال استودیو را باز و بسته کنید تا روال Restore کردن Nuget Package‌ها این بار به صورت کامل انجام شود و بتوانید این کلاس را ببینید و استفاده کنید.
نکته مهم: در آموزش خیلی از کتابخانه‌های Xamarin Forms، به شما گفته می‌شود که Nuget مربوطه را در پروژه Android-iOS-UWP نیز نصب کنید. در نسخه‌های اخیر Visual Studio نیازی به این کار نیست و بیهوده پروژه را شلوغ نکنید!

بعد از نصب، می‌توانیم از UserDialogs.Instance و متدهای آن برای نمایش هشدار و ... در هر جای پروژه استفاده کنیم؛ چون که اینها  static هستند. اما اگر اهل استفاده از Dependency injection و تست خودکار و سایر موارد ایده آل باشید، می‌دانید که استفاده از هر آنچه که static باشد، در اکثر مواقع ایده خوبی نیست.
کانفیگ کردن Dependency injection برای این کتابخانه کار ساده‌ای است. فقط کافی است کد زیر را در فایل App.xaml.cs در پروژه XamApp، به متد RegisterTypes اضافه کنید:
containerBuilder.RegisterInstance(UserDialogs.Instance);
RegisterInstance یکی از متدهای کتابخانه معروف و محبوب Autofac است که برای Dependency injection ساخته شده است.
در متد Login در LoginViewModel برای هشدار دادن خالی بودن نام کاربری یا رمز عبور، به جای استفاده مستقیم از UserDialogs.Instance می‌توانیم IUserDialogs را به صورت یک Property تعریف نموده و از آن استفاده کنیم. وظیفه پر کردن آن Property به عهده Autofac است و ما کار بیشتری نداریم!
public IUserDialogs UserDialogs { get; set; }

public async Task Login()
{
      if (string.IsNullOrWhiteSpace(UserName) || string.IsNullOrWhiteSpace(Password))
           await UserDialogs.AlertAsync(message: "Please provide UserName and Password!", title: ")-:", okText: "Ok!");
}
به سادگی نصب یک Nuget Package در پروژه XamApp، فراخوانی یک متد Init در پروژه XamApp.Android و یک خط کانفیگ برای Autofac، می‌توانید از IUserDialogs در تمامی View Model‌های خود استفاده کنید.
فرض کنید بعد از این که مطمئن شدید نام کاربری و رمز عبور خالی نیستند، می‌خواهید یک Request به سرور بفرستید و نام کاربری و رمز عبور را اعتبار سنجی کنید. ممکن است به خاطر کندی اینترنت یا سرور یا هر چیز دیگری، این پروسه کمی طول بکشد و نشان دادن یک Loading ایده خوبی است. چون فعلا نمی‌خواهیم درگیر فراخوانی سرور شویم، این طول کشیدن را من با Task.Delay شبیه سازی می‌کنم و Loading مربوطه را نمایش می‌دهم:
using (UserDialogs.Loading("Logging in...", maskType: MaskType.Black))
{
     // Login implementation ...
     await Task.Delay(TimeSpan.FromSeconds(3));
}

بررسی Navigation در Xamarin Forms
اگر در متد OnInitializedAsync در App.xaml.cs کد
await NavigationService.NavigateAsync("/Login", animated: false);
را داشته باشیم، وقتی برنامه اجرا می‌شود، ما به صفحه لاگین می‌رویم؛ این از تکلیف اولین صفحه برنامه! حال اگر در LoginViewModel بخواهیم در صورت موفقیت آمیز بودن فرآیند لاگین، مثلا به صفحه HelloWorld برویم چه؟ در این صورت در متد Login داریم:
await NavigationService.NavigateAsync("/Nav/HelloWorld");
چون کلاس LoginViewModel از BitViewModelBase ارث بری کرده است، به صورت پیش فرض دارای NavigationService هست. در رشته (string) استفاده شده، یعنی "/Nav/HelloWorld" چند نکته وجود دارد:
1- آن / اول اگر وجود داشته باشد، یعنی اینکه بعد از باز کردن صفحه HelloWorld، صفحه یا صفحات قبلی (در این مثال یعنی صفحه Login) از بین برده می‌شوند و امکان برگشت به آنها وجود ندارد. طبیعی است که بعد از لاگین موفق، فرد انتظار ندارد با زدن Back به صفحه لاگین باز گردد! ولی مثالی را فرض کنید که در یک صفحه، لیست محصولات فروشگاه را نمایش داده‌ایم و روی هر محصول که کلیک کنیم، به صفحه نمایش جزئیات آن محصول می‌رویم. در این صورت انتظار داریم با زدن Back، به صفحه لیست محصولات برگردیم، در این مثال از / در ابتدا استفاده نمی‌کنیم.

2- آن Nav/ به معنی این است که ابتدا Navigation Page را ایجاد و HelloWorld را درون Navigation Page باز کن. Navigation Page خود دارای امکانات زیادی است. عموما در برنامه‌ها، Title صفحه و دکمه Back نرم افزاری و Search bar را در Nav Bar مربوط به Navigation Page قرار می‌دهند. در Xamarin Forms حتی می‌توانید با Xaml، کل Nav Bar را خودتان Customize کنید و یا اینکه از امکان Large titles در iOS 11 استفاده کنید! درخواست بودن Nav Bar لازم است فقط یک بار انجام شود. لازم نیست و نباید ابتدای رفتن به هر صفحه از Nav/ استفاده کنید.


3- ممکن است بخواهید هنگام رفتن از صفحه‌ای به صفحه دیگر، پارامتر نیز ارسال کنید. اگر برای مثال صفحه اول لیست محصولات را نمایش می‌دهد و با زدن روی هر محصول قرار است به صفحه‌ای برویم که جزئیات آن محصول را ببینیم، بهتر است Id آن محصول به صورت پارامتر به صفحه دوم ارسال شود. برای این کار داریم:

await NavigationService.NavigateAsync("ProductDetail", new NavigationParameters
{
      { "productId", productId }
});

حال سؤال این است که در صفحه جزئیات یک محصول، چگونه productId را بگیریم؟ فرض کنید دو صفحه ProductsList و ProductDetail را داریم. هر صفحه دارای View و View Model است. در ViewModel مربوط به ProductDetail، یعنی ProductDetailViewModel که از BitViewModelBase ارث بری کرده‌است، می‌توانیم متد OnNavigatedToAsync را override کنیم. در آنجا به پارامترهای ارسال شده دسترسی داریم:

public async override Task OnNavigatedToAsync(INavigationParameters parameters)
{
      await base.OnNavigatedToAsync(parameters);
      Guid productId = parameters.GetValue<Guid>("productId");
}

هر ViewModel علاوه بر OnNavigatedTo می تواند دارای OnNavigatedFrom هم باشد که زمانیکه داریم از صفحه مربوطه خارج می‌شویم، فراخوانی می‌شود.


4- برای نمایش صفحه به صورت Popup کافی است بجای اینکه View ما یک Content Page باشد، یک PopupPage باشد (برای درک بهتر، فایل IntroView.xaml را در فولدر Views باز کنید).

حتی می‌توانید Animation مربوط به باز شدن پاپ آپ را هم کاملا Customize کنید. مثلا زمان باز شدن، از سمت راست صفحه وارد شود و زمان خارج شدن، Fade out شود. باز کردن Popup در Navigation Page معنی نمی‌دهد، پس با Nav/ در اینجا کاری نداریم. در مثال ما، بعد از لاگین می‌خواهیم یک صفحه Intro شامل هشدارها و راهنمایی‌های اولیه را در قالب Popup به کاربر نمایش دهیم. Popup‌ها می‌توانند همچون Content Page‌ها، دارای View Model باشند و مواردی چون OnNavigatedTo، ارسال پارامتر و هر آنچه که گفته شد، در مورد آنها نیز صدق می‌کند.


5- برای Master/Detail کافی است بجای Nav/HelloWorld/ از MasterDetail/Nav/HelloWorld/ استفاده کنید. این عمل باعث می‌شود HelloWorld در داخل Navigation Page و Navigation Page داخل Master Detail باز شود. از این ساده‌تر امکان ندارد!

برای تغییر UI مربوط به Master که از سمت چپ باز می‌شود، فایل XamAppMasterDetailView.xaml را تغییر دهید.


در قسمت بعدی به جزئیات Binding خواهیم پرداخت.

نظرات مطالب
نحوه استفاده از ViewModel در ASP.NET MVC
سلاام ...
من تاجاییکه میتونستم چیزهایی که درباره viemodel  و automapper توی سایت بود رو خوندم ولی چیزی که میخواستم را پیدا نکردم شاید هم درک درستی ازش نداشتم در کل سوال سوال من از شما دوستان اینه : 
یک viewmodel دارم که از چندین جدول توی db ایجاد شده که در زیر نوشتمش : 
public class ResumeViewModel
    {
        public ResumeViewModel()
        {
            
        }
        public ResumeViewModel(IEnumerable<Resume> resum, IEnumerable<Work_Experience_Job_Seeker> workExperienceJobSeekerOfViewModel, IEnumerable<Job_Expertises> jobExpertisesOfViewModel, IEnumerable<Degrees_Work_Experience_Required> degreesWorkExperienceRequiredOfViewModel, IEnumerable<Specialized_Course> specializedCourseOfViewModel, IEnumerable<Book_Published> bookPublishedOfViewModel, IEnumerable<Basic_Table> basicTable)
        {
            ResumeOfViewModel = resum;
            WorkExperienceJobSeekerOfViewModel = workExperienceJobSeekerOfViewModel;
            JobExpertisesOfViewModel = jobExpertisesOfViewModel;
            DegreesWorkExperienceRequiredOfViewModel = degreesWorkExperienceRequiredOfViewModel;
            SpecializedCourseOfViewModel = specializedCourseOfViewModel;
            BookPublishedOfViewModel = bookPublishedOfViewModel;
            BasicTable = basicTable;
        }
        public IEnumerable<Resume> ResumeOfViewModel { get; set; }
        public IEnumerable<Work_Experience_Job_Seeker> WorkExperienceJobSeekerOfViewModel { get; set; }
        public IEnumerable<Job_Expertises> JobExpertisesOfViewModel { get; set; }
        public IEnumerable<Degrees_Work_Experience_Required> DegreesWorkExperienceRequiredOfViewModel { get; set; }
        public IEnumerable<Specialized_Course> SpecializedCourseOfViewModel { get; set; }
        public IEnumerable<Book_Published> BookPublishedOfViewModel { get; set; }
        public IEnumerable<Basic_Table> BasicTable { get; set; }

        public int NumberForm { get; set; } // EditResumes.chtml & ShowResumes.chtml  ===> baraye select kardan formha :D 

        }
و این viewmodel رو توی متد Edit Resume  استفاده کردم که متد get  بصورت زیر ::
        [HttpGet]
        public ActionResult EditResumes(int id)
        {
            var contex = new Final_My_ProjectEntities2();
            var res1 = contex.Resumes.Where(rec => rec.Resume_ID == id);
            var res2 = contex.Work_Experience_Job_Seeker.Where(rec => rec.Resume_ID == id).ToList();
            var res3 = contex.Job_Expertises.Where(rec => rec.Resume_ID == id).ToList();
            var res4 = contex.Degrees_Work_Experience_Required.Where(rec => rec.Resume_ID == id).ToList();
            var res5 = contex.Specialized_Course.Where(rec => rec.Resume_ID == id).ToList();
            var res6 = contex.Book_Published.Where(rec => rec.Resume_ID == id).ToList();
            var res12 = contex.Basic_Table.ToList();
            var viewModel = new ResumeViewModel(res1, res2, res3, res4, res5, res6,res12);
            var items = new SelectList(
                 new[]
                    {
                        new {Value = "1", Text = "فرم مهارت ها"},
                        new {Value = "2", Text = "فرم کتاب/مقالات منتشر شده"},
                        new {Value = "3", Text = "فرم سابقه کاری"},
                        new {Value = "4", Text = "فرم دوره‌های تخصصی گذرانده"},
                        new {Value = "5", Text = "فرم تخصص‌های شغلی"},
                        new {Value = "6", Text = "فرم مدارک تحصیلی"}
                    },
             "Value", "Text");

            ViewBag.Form = new SelectList(items, "Value", "Text");

            var res7 = contex.Basic_Table.Where(rec => rec.Domain == "MilitaryStatus").ToList();
            ViewBag.MilitaryStatus = new SelectList(res7, "Value", "Meaning",res1);

            var res8 = contex.Basic_Table.Where(rec => rec.Domain == "Sex");
            ViewBag.Sex = new SelectList(res8, "Value", "Meaning", res1);

            var res9 = contex.Basic_Table.Where(rec => rec.Domain == "MartialStatus").ToList();
            ViewBag.MartialStatus = new SelectList(res9, "Value", "Meaning", res1);

            var res10 = contex.Basic_Table.Where(rec => rec.Domain == "Degree").ToList();
            ViewBag.Degree = new SelectList(res10, "Value", "Meaning");

            var res11 = contex.Basic_Table.Where(rec => rec.Domain == "Ability").ToList();
            ViewBag.Ability = new SelectList(res11, "Value", "Meaning");


            return View(viewModel);
        }
و view  این متد بصورت زیر هست البته قسمتی از آن :: 
@model Final_My_Project.ViewModels.ResumeViewModel

@{
    ViewBag.Title = "ویرایش رزومه";
    ViewBag.PartOne = "فرم مهارت ها";
    ViewBag.PartTwo = "فرم کتاب/مقالات منتشر شده";
    ViewBag.Part3 = "فرم سابقه کاری";
    ViewBag.Part4 = "فرم دوره‌های تخصصی گذرانده";
    ViewBag.Part5 = "فرم تخصص‌های شغلی";
    ViewBag.Part6 = "فرم مدارک تحصیلی";

}
<h2 style="font-family:  Arial;">@ViewBag.Title</h2><br/>
   <script type="text/javascript">
       $(function () {
           $('#Gender').change(function () {
               var selectKind = $(this).find('option:selected').text();
               var divMilitary;
               if (selectKind == "زن") {
                   divMilitary = $('#Military');
                   divMilitary.hide();
                   divMilitary.css('display', 'none');
               }
               else if (selectKind == "مرد") {
                   divMilitary = $('#Military');
                   divMilitary.show();
                   divMilitary.css('display', 'block');

               }
           });
       });
</script>

<script type="text/javascript">
    $(function () {
        $('#SelectForm').change(function () {
            var selectFrom = $(this).find('option:selected').text();

            if (selectFrom == "فرم مهارت ها") {
                $('#PartOne').show();
                $('#PartOne').css('display', 'block');
                $('#PartTwo').hide();
                $('#PartTwo').css('display', 'none');
                $('#Part3').hide();
                $('#Part3').css('display', 'none');
                $('#Part4').hide();
                $('#Part4').css('display', 'none');
                $('#Part5').hide();
                $('#Part5').css('display', 'none');
                $('#Part6').hide();
                $('#Part6').css('display', 'none');


            }
            if (selectFrom == "فرم کتاب/مقالات منتشر شده") {
                $('#PartTwo').show();
                $('#PartTwo').css('display', 'block');
                $('#PartOne').show();
                $('#PartOne').css('display', 'none');
                $('#Part3').hide();
                $('#Part3').css('display', 'none');
                $('#Part4').hide();
                $('#Part4').css('display', 'none');
                $('#Part5').hide();
                $('#Part5').css('display', 'none');
                $('#Part6').hide();
                $('#Part6').css('display', 'none');

            }
            if (selectFrom == "فرم سابقه کاری") {

                $('#Part3').show();
                $('#Part3').css('display', 'block');
                $('#PartTwo').hide();
                $('#PartTwo').css('display', 'none');
                $('#PartOne').show();
                $('#PartOne').css('display', 'none');
                $('#Part4').hide();
                $('#Part4').css('display', 'none');
                $('#Part5').hide();
                $('#Part5').css('display', 'none');
                $('#Part6').hide();
                $('#Part6').css('display', 'none');


            }
            if (selectFrom == "فرم دوره‌های تخصصی گذرانده") {

                $('#Part4').show();
                $('#Part4').css('display', 'block');
                $('#PartTwo').hide();
                $('#PartTwo').css('display', 'none');
                $('#PartOne').show();
                $('#PartOne').css('display', 'none');
                $('#Part3').hide();
                $('#Part3').css('display', 'none');
                $('#Part5').hide();
                $('#Part5').css('display', 'none');
                $('#Part6').hide();
                $('#Part6').css('display', 'none');

            }
            if (selectFrom == "فرم تخصص‌های شغلی") {

                $('#Part5').show();
                $('#Part5').css('display', 'block');
                $('#PartTwo').hide();
                $('#PartTwo').css('display', 'none');
                $('#PartOne').show();
                $('#PartOne').css('display', 'none');
                $('#Part3').hide();
                $('#Part3').css('display', 'none');
                $('#Part4').hide();
                $('#Part4').css('display', 'none');
                $('#Part6').hide();
                $('#Part6').css('display', 'none');

            }

            if (selectFrom == "فرم مدارک تحصیلی") {

                $('#Part6').show();
                $('#Part6').css('display', 'block');
                $('#Part5').show();
                $('#Part5').css('display', 'none');
                $('#PartTwo').hide();
                $('#PartTwo').css('display', 'none');
                $('#PartOne').show();
                $('#PartOne').css('display', 'none');
                $('#Part3').hide();
                $('#Part3').css('display', 'none');
                $('#Part4').hide();
                $('#Part4').css('display', 'none');
            }

        });
    });

</script>

@Html.DropDownListFor(m=>m.NumberForm, (SelectList)ViewBag.Form, new { id = "SelectForm" })


@using (Html.BeginForm())
{
    @Html.ValidationSummary(true)

        <div id="PartOne" >
    <h3 style="font-family: Arial; color: #008080; font-weight: bold; ">@ViewBag.PartOne</h3><br/>
    @foreach (var item in Model.ResumeOfViewModel)
    {
        <table dir="rtl">
            <tr>
                <td>
                    @Html.Label("عنوان رزومه")
                </td>
                <td>
                    @Html.TextBoxFor(model =>item.Title_Of_Resume  , new {@class = "text", style = "width:100 px"})
                    @Html.ValidationMessageFor(model => item.Title_Of_Resume)
                </td>
            </tr>
            <tr>
                <td>
        <div id="Gender" >
        @Html.Label("نوع جنسیت")
         @Html.DropDownList("نوع جنسیت", new SelectList(ViewBag.Sex, 
                       "Value", "Text", item.Sex_ID == 0 ? 0 : item.Sex_ID))
        @Html.ValidationMessageFor(model => item.Sex_ID)
        </div>
    </td>

                <td>
                    <div >
                        @Html.Label("وضعیت تاهل")
         @Html.DropDownList("وضعیت تاهل", new SelectList(ViewBag.MartialStatus, 
                       "Value", "Text", item.Martial_Status_ID == 0 ? 0 : item.Martial_Status_ID))
                        @Html.ValidationMessageFor(model => item.Martial_Status_ID)
                    </div>

                </td>
            </tr>

            <tr id="Military" style="display: none;">
                <td>
                    @Html.Label("وضعیت نظام وظیفه")
                </td>
                <td>
         @Html.DropDownList("وضعیت نظام وظیفه", new SelectList(ViewBag.MilitaryStatus,
                           "Value", "Text", item.Military_Status_ID == 0 ? 0 : item.Military_Status_ID), new { id = "Gender" })
                    @Html.ValidationMessageFor(model => item.Military_Status_ID)
                </td>

            </tr>

            <tr>
                <td>
                    @Html.Label("آشنایی با رایانه")
                </td>
                <td>
                    @Html.DropDownListFor(model => item.Knowledge_Of_Computers_ID, (SelectList)ViewBag.Ability)
                    @Html.ValidationMessageFor(model => item.Knowledge_Of_Computers_ID)
                </td>
            </tr>

            <tr>
                <td>
                    @Html.Label("آشنایی با امور اداری و دفتری")
                </td>
                <td>
                    @Html.DropDownListFor(model => item.Knowledge_Administrative_and_Clerical_ID, (SelectList) ViewBag.Ability)
                    @Html.ValidationMessageFor(model => item.Knowledge_Administrative_and_Clerical_ID)
                </td>
            </tr>

            <tr>
                <td>
                    @Html.Label("آشنایی با زبان انگلیسی")
                </td>
                <td>
                    @Html.DropDownListFor(model => item.Knowledge_Of_English_ID, (SelectList) ViewBag.Ability)
                    @Html.ValidationMessageFor(model => item.Knowledge_Of_English_ID)
                </td>
            </tr>

            <tr>
                <td>
                    @Html.Label("آشنایی با زبان عربی")
                </td>
                <td>
                    @Html.DropDownListFor(model => item.Knowledge_Of_Arabic_ID, (SelectList) ViewBag.Ability)
                    @Html.ValidationMessageFor(model => item.Knowledge_Of_Arabic_ID)
                </td>
            </tr>

            <tr>
                <td>
                    @Html.Label("آشنایی با ماکروسافت آفیس")
                </td>
                <td>
                    @Html.DropDownListFor(model =>item.Knowledge_Of_Office_ID, (SelectList) ViewBag.Ability)
                    @Html.ValidationMessageFor(model => item.Knowledge_Of_Office_ID)
                </td>
            </tr>

            <tr>
                <td>  
                    @Html.Label("آشنایی با امور مالی و حسابداری")
                </td>
                <td>
                    @Html.DropDownListFor(model =>item.Knowledge_Of_Finance_ID, (SelectList) ViewBag.Ability)
                    @Html.ValidationMessageFor(model => item.Knowledge_Of_Finance_ID)
                </td>
            </tr>

            <tr>
                <td>
                    @Html.Label("آشنایی با مدیریت")
                </td>
                <td>
                    @Html.DropDownListFor(model =>item.Knowledge_Of_Manage_ID, (SelectList) ViewBag.Ability)
                    @Html.ValidationMessageFor(model =>item.Knowledge_Of_Manage_ID)
                </td>
            </tr>

            <tr>
                <td>
                    @Html.Label("گواهینامه رانندگی پایه یک")

                </td>
                <td>
                    @Html.DropDownListFor(model =>item.Driving_license_One_ID, (SelectList) ViewBag.Ability)
                    @Html.ValidationMessageFor(model =>item.Driving_license_One_ID)
                </td>
            </tr>

            <tr>
                <td>
                    @Html.Label("گواهینامه رانندگی پایه دو")
                </td>
                <td>
                    @Html.DropDownListFor(model =>item.Driving_license_Two_ID, (SelectList) ViewBag.Ability)
                    @Html.ValidationMessageFor(model =>item.Driving_license_Two_ID)
                </td>
            </tr>

            <tr>
                <td>
                    @Html.Label("گواهینامه رانندگی پایه موتورسیکلت")
                </td>
                <td>
                    @Html.DropDownListFor(model =>item.Certificate_Motor_ID, (SelectList) ViewBag.Ability)
                    @Html.ValidationMessageFor(model =>item.Certificate_Motor_ID)
                </td>
            </tr>

            <tr>
                <td>
                    @Html.Label("ماشین شخصی")
                </td>
                <td>
                    @Html.DropDownListFor(model =>item.Personal_Vehicle_ID, (SelectList) ViewBag.Ability)
                    @Html.ValidationMessageFor(model =>item.Personal_Vehicle_ID)
                </td>
            </tr>

            <tr>
                <td>
                    @Html.Label("روابط عمومی")
                </td>
                <td>
                    @Html.DropDownListFor(model =>item.Public_Relationship_ID, (SelectList) ViewBag.Ability)
                    @Html.ValidationMessageFor(model =>item.Public_Relationship_ID)
                </td>
            </tr>

            <tr>
                <td>
                    @Html.Label("دیگر توانایی ها")

                </td>
                <td>
                    @Html.EditorFor(model =>item.Etc_Ability)
                    @Html.ValidationMessageFor(model =>item.Etc_Ability)
                </td>
            </tr>

        </table>
    }
        </div>

    
        <p>
            <input type="submit" value="Save" onclick="return confirm('از ثبت اطلاعات مطمئن هستید؟')" />
        </p>
}

<div>
    @Html.ActionLink("بازگشت به مدیریت رزومه ها", "ManageOfResumes")
</div>

@section scripts {
    @Scripts.Render("~/bundles/jqueryval")
}
و مشکل اینجاست که بعد از ثبت اطلاعات وقتی به متد post میره مقدارش null  هستش ... درحالیکه فقط در صورت edit  اینجوریه وقتی از همین viewmodel  برای مشاهده رزومه که فقط گزارشگیریه استفاده میکنم نتیجه را میبینم ولی اینجا نه !
متد پست بصورت زیر هستش ... اگر میدونید چطور و چی کار کنم که این درست شه ممنون میشم ... چون دیگه نمیدونم تو متد پست چی بنویسم ... منتظر جوابم ... که چرا null میده و اینکه تو متد پست چطور اینارو ذخیره کنم توی db .../؟
        [HttpPost]
        public ActionResult EditResumes(ResumeViewModel model) // model null mishe ! CHERAA??!
        {
            var contex = new Final_My_ProjectEntities2();
            try
            {
                if (ModelState.IsValid)
                {

                    // Code... che cody?
                    contex.SaveChanges();
                }

            }
            catch (Exception)
            {

                ViewBag.wrong = "لطفا داده‌های ورودی را بررسی نمایید";
            }
            return View(model);

        }

مطالب
متدی برای بررسی صحت کد ملی وارد شده
 در بعضی از سایت‌ها به عنوان داده ورودی کد ملی فرد دریافت می‌شود در این پست می‌خواهیم بررسی کنیم که آیا کد ملی وارد شده از نظر صحت درسا وارد شده است یا خیر.
قبل از نوشتن متد قالب کد ملی را شرح می‌دهیم.

کد ملی شماره ای است 10 رقمی که از سمت چپ سه رقم کد شهرستان ، شش رقم بعدی کد منحصر به فرد برای فرد دارنده و رقم آخر آن هم یک رقم کنترل است که از روی 9 رقم سمت چپ بدست می‌آید. برای بررسی کنترل کد کافی است مجدد از روی 9 رقم سمت چپ رقم کنترل را محاسبه کنیم

از آنجایی که درسیستم کد ملی معمولا قبل از کد  تعدادی صفر وجود دارد.(رقم اول و رقم دوم از سمت چپ کد ملی ممکن است صفر باشد) و در بسیاری از موارد ممکن است کاربر این صفرها را وارد نکرده باشد و یا نرم افزار این صفرها را ذخیره نکرده باشد بهتر است قبل از هر کاری در صورتی که طول کد بزرگتر مساوی 8 و کمتر از 10 باشد  به تعداد لازم (یک تا دو تا صفر) به سمت چپ عدد اضافه کنید. ساختار کد ملی در زیر نشان داده شده است. 

کدهای ملی که همه ارقام آنها مثل هم باشند معتبر نیستند مثل:

۰۰۰۰۰۰۰۰۰۰
۱۱۱۱۱۱۱۱۱۱
۲۲۲۲۲۲۲۲۲۲
۳۳۳۳۳۳۳۳۳۳
۴۴۴۴۴۴۴۴۴۴
۵۵۵۵۵۵۵۵۵۵
۶۶۶۶۶۶۶۶۶۶
۷۷۷۷۷۷۷۷۷۷
۸۸۸۸۸۸۸۸۸۸
۹۹۹۹۹۹۹۹۹۹

روش اعتبار سنجی کد ملی :

دهمین رقم شماره ملی را (از سمت چپ) به عنوان TempAدر نظر می‌گیریم.

یک مقدار TempB در نظر می‌گیریم و آن را برابر با =

(اولین رقم * ۱۰) + ( دومین رقم * ۹ ) + ( سومین رقم * ۸ ) + ( چهارمین رقم * ۷ ) + ( پنجمین رقم * ۶) + ( ششمین رقم * ۵ ) + ( هفتمین رقم * ۴ ) + ( هشتمین رقم * ۳ ) + ( نهمین رقم * ۲ )

قرار می‌دهیم.

مقدار TempC را برابر با = TempB  – (TempB/11)*11 قرار می‌دهیم.

اگر مقدار TempC برابر با صفر باشد و مقدار TempA  برابر TempC  باشد کد ملی صحیح است.

اگر مقدار TempC برابر با ۱ باشد و مقدار TempA  برابر با ۱ باشد کد ملی صحیح است.

اگر مقدار TempC  بزرگتر از ۱ باشد و مقدار TempA برابر با ۱۱ – TempC  باشد کد ملی صحیح است.

در ادامه متد نوشته شده به زبان C#.NET را مشاهده می‌کنید.
public static class Helpers
    {
        /// <summary>
        /// تعیین معتبر بودن کد ملی
        /// </summary>
        /// <param name="nationalCode">کد ملی وارد شده</param>
        /// <returns>
        /// در صورتی که کد ملی صحیح باشد خروجی <c>true</c> و در صورتی که کد ملی اشتباه باشد خروجی <c>false</c> خواهد بود
        /// </returns>
        /// <exception cref="System.Exception"></exception>
        public static Boolean IsValidNationalCode(this String nationalCode)
        {
           //در صورتی که کد ملی وارد شده تهی باشد
if (String.IsNullOrEmpty(nationalCode)) throw new Exception("لطفا کد ملی را صحیح وارد نمایید");
//در صورتی که کد ملی وارد شده طولش کمتر از 10 رقم باشد if (nationalCode.Length != 10) throw new Exception("طول کد ملی باید ده کاراکتر باشد"); //در صورتی که کد ملی ده رقم عددی نباشد var regex = new Regex(@"\d{10}"); if (!regex.IsMatch(nationalCode)) throw new Exception("کد ملی تشکیل شده از ده رقم عددی می‌باشد؛ لطفا کد ملی را صحیح وارد نمایید"); //در صورتی که رقم‌های کد ملی وارد شده یکسان باشد var allDigitEqual = new[]{"0000000000","1111111111","2222222222","3333333333","4444444444","5555555555","6666666666","7777777777","8888888888","9999999999"}; if (allDigitEqual.Contains(nationalCode)) return false;
//عملیات شرح داده شده در بالا var chArray = nationalCode.ToCharArray(); var num0 = Convert.ToInt32(chArray[0].ToString())*10; var num2 = Convert.ToInt32(chArray[1].ToString())*9; var num3 = Convert.ToInt32(chArray[2].ToString())*8; var num4 = Convert.ToInt32(chArray[3].ToString())*7; var num5 = Convert.ToInt32(chArray[4].ToString())*6; var num6 = Convert.ToInt32(chArray[5].ToString())*5; var num7 = Convert.ToInt32(chArray[6].ToString())*4; var num8 = Convert.ToInt32(chArray[7].ToString())*3; var num9 = Convert.ToInt32(chArray[8].ToString())*2; var a = Convert.ToInt32(chArray[9].ToString()); var b = (((((((num0 + num2) + num3) + num4) + num5) + num6) + num7) + num8) + num9; var c = b%11; return (((c < 2) && (a == c)) || ((c >= 2) && ((11 - c) == a))); } }
نحوه استفاده از این متد به این صورت می‌باشد:

if(TextBoxNationalCode.Text.IsValidNationalCode ())
     //some code

//OR

if(Helpers.IsValidNationalCode (TextBoxNationalCode.Text))
     //some code
موفق وموید باشید
مطالب
امکان ساخت برنامه‌های دسکتاپ چندسکویی Blazor در دات نت 6
در این مطلب، روش ساخت یک برنامه‌ی دسکتاپ چندسکویی Blazor 6x را که امکان به اشتراک گذاری کدهای خود را با یک برنامه‌ی WinForms دارد، بررسی خواهیم کرد.


ایجاد برنامه‌های ابتدایی مورد نیاز

در ابتدا دو پوشه‌ی جدید BlazorServerApp و WinFormsApp را ایجاد می‌کنیم. سپس از طریق خط فرمان در اولی دستور dotnet new blazorserver و در دومی دستور dotnet new winforms را اجرا می‌کنیم تا دو برنامه‌ی خالی Blazor Server و همچنین Windows Forms، ایجاد شوند. برنامه‌ی WinForms ایجاد شده مبتنی بر NET Core. و یا همان NET 6x. است؛ بجای اینکه مبتنی بر دات نت فریم‌ورک 4x باشد.


ایجاد یک پروژه‌ی کتابخانه‌ی Razor

چون می‌خواهیم کدهای برنامه‌ی BlazorServerApp ما در برنامه‌ی WinForms قابل استفاده باشد، نیاز است فایل‌های اصلی آن‌را به یک پروژه‌ی razor class library منتقل کنیم. به همین جهت برای این پروژه‌، یک پوشه‌ی جدید را به نام BlazorClassLibrary ایجاد کرده و درون آن دستور dotnet new razorclasslib را اجرا می‌کنیم.


انتقال فایل‌های پروژه‌ی Blazor به پروژه‌ی کتابخانه‌ی Razor

در ادامه این فایل‌ها را از پروژه‌ی BlazorServerApp به پروژه‌ی BlazorClassLibrary منتقل می‌کنیم:
- کل پوشه‌ی Data
- کل پوشه‌ی Pages
- کل پوشه‌ی Shared
- فایل App.razor
- فایل Imports.razor_
- کل پوشه‌ی wwwroot

پس از اینکار، نیاز است فایل csproj کتابخانه‌ی class lib را اندکی ویرایش کرد تا بتواند فایل‌های اضافه شده را کامپایل کند:
<Project Sdk="Microsoft.NET.Sdk.Razor">
  <PropertyGroup>
    <AddRazorSupportForMvc>true</AddRazorSupportForMvc>
  </PropertyGroup>

  <ItemGroup>
    <FrameworkReference Include="Microsoft.AspNetCore.App" />
  </ItemGroup>
</Project>
- چون برنامه از نوع Blazor Server است، ارجاعی به AspNetCore را نیاز دارد و همچنین برای فایل‌های cshtml آن نیز باید AddRazorSupportForMvc را به true تنظیم کرد.
- به علاوه فایل Error.cshtml.cs انتقالی، نیاز به افزودن فضای نام using Microsoft.Extensions.Logging را خواهد داشت.
- در فایل Imports.razor_ انتقالی نیاز است دو using آخر آن‌را که به BlazorServerApp قبلی اشاره می‌کنند، به BlazorClassLibrary جدید ویرایش کنیم:
@using BlazorClassLibrary
@using BlazorClassLibrary.Shared
- این تغییر فضای نام جدید، شامل ابتدای فایل BlazorClassLibrary\Pages\_Host.cshtml انتقالی هم می‌شود:
@namespace BlazorClassLibrary.Pages
- چون wwwroot را نیز به class library منتقل کرده‌ایم، جهت اصلاح مسیر فایل‌های css استفاده شده‌ی در برنامه، فایل BlazorClassLibrary\Pages\_Layout.cshtml را گشوده و تغییر زیر را اعمال می‌کنیم:
<link rel="stylesheet" href="_content/BlazorClassLibrary/css/bootstrap/bootstrap.min.css" />
<link href="_content/BlazorClassLibrary/css/site.css" rel="stylesheet" />
در مورد این مسیر ویژه، در مطلب «روش ایجاد پروژه‌ها‌ی کتابخانه‌ای کامپوننت‌های Blazor» بیشتر بحث شده‌است.


پس از این تغییرات، برای اینکه برنامه‌ی BlazorServerApp موجود، به کار خود ادامه دهد، نیاز است ارجاعی از پروژه‌ی class lib را به فایل csproj آن اضافه کنیم:
<Project Sdk="Microsoft.NET.Sdk.Web">
  <ItemGroup>
    <ProjectReference Include="..\BlazorClassLibrary\BlazorClassLibrary.csproj" />
  </ItemGroup>
</Project>
اکنون جهت آزمایش برنامه‌ی Blazor Server، یکبار دستور dotnet run را در ریشه‌ی آن اجرا می‌کنیم تا مطمئن شویم انتقالات صورت گرفته، سبب کار افتادن آن نشده‌اند.


ویرایش برنامه‌ی WinForms جهت اجرای کدهای Blazor

تا اینجا برنامه‌ی Blazor Server ما تمام فایل‌های مورد نیاز خود را از BlazorClassLibrary دریافت می‌کند و بدون مشکل اجرا می‌شود. در ادامه می‌خواهیم کار هاست این class lib را در برنامه‌ی WinForms نیز انجام دهیم. به همین جهت در ابتدا ارجاعی را به class lib به آن اضافه می‌کنیم:
<Project Sdk="Microsoft.NET.Sdk">
  <ItemGroup>
    <ProjectReference Include="..\BlazorClassLibrary\BlazorClassLibrary.csproj" />
  </ItemGroup>
</Project>
سپس کامپوننت جدید WebView را به پروژه‌ی WinForms اضافه می‌کنیم:
<Project Sdk="Microsoft.NET.Sdk">
  <ItemGroup>
    <PackageReference Include="Microsoft.AspNetCore.Components.WebView.WindowsForms" Version="6.0.101-preview.11.2349" />
  </ItemGroup>
</Project>

در ادامه نیاز است فایل Form1.Designer.cs را به صورت دستی جهت افزودن این WebView اضافه شده، تغییر داد:
namespace WinFormsApp;

partial class Form1
{
    private void InitializeComponent()
    {
      this.blazorWebView1 = new Microsoft.AspNetCore.Components.WebView.WindowsForms.BlazorWebView();

      this.SuspendLayout();

      this.blazorWebView1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
            | System.Windows.Forms.AnchorStyles.Left) 
            | System.Windows.Forms.AnchorStyles.Right)));
      this.blazorWebView1.Location = new System.Drawing.Point(13, 181);
      this.blazorWebView1.Name = "blazorWebView1";
      this.blazorWebView1.Size = new System.Drawing.Size(775, 257);
      this.blazorWebView1.TabIndex = 20;
      this.Controls.Add(this.blazorWebView1);

      this.components = new System.ComponentModel.Container();
      this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
      this.ClientSize = new System.Drawing.Size(800, 450);
      this.Text = "Form1";

      this.ResumeLayout(false);
    }

     private Microsoft.AspNetCore.Components.WebView.WindowsForms.BlazorWebView blazorWebView1;
}
کامپوننت WebView را نمی‌توان از طریق toolbox به فرم اضافه کرد؛ به همین جهت باید فایل فوق را به نحوی که مشاهده می‌کنید، اندکی ویرایش نمود.


هاست برنامه‌ی Blazor در برنامه‌ی WinForm

پس از تغییرات فوق، نیاز است فایل‌های wwwroot را از پروژه‌ی class lib به پروژه‌ی WinForms کپی کرد. از این جهت که این فایل‌ها از طریق index.html جدیدی خوانده خواهند شد. پس از کپی کردن این پوشه، نیاز است فایل csproj پروژه‌ی WinForm را به صورت زیر اصلاح کرد:
<Project Sdk="Microsoft.NET.Sdk.Razor">

  <ItemGroup>
    <PackageReference Include="Microsoft.AspNetCore.Components.WebView.WindowsForms" Version="6.0.101-preview.11.2349" />
  </ItemGroup>

  <ItemGroup>
    <ProjectReference Include="..\BlazorClassLibrary\BlazorClassLibrary.csproj" />
  </ItemGroup>
  
  <ItemGroup>
    <Content Update="wwwroot\**">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </Content>
  </ItemGroup>
  
</Project>
Sdk این فایل تغییر کرده‌است تا بتواند از wwwroot ذکر شده استفاده کند. همچنین به ازای هر Build، فایل‌های واقع در wwwroot به خروجی کپی خواهند شد.
در ادامه داخل این پوشه‌ی wwwroot که از پروژه‌ی class lib کپی کردیم، نیاز است فایل index.html جدیدی را که قرار است blazor.webview.js را اجرا کند، به صورت زیر ایجاد کنیم:
<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
    <title>Blazor WinForms app</title>
    <base href="/" />
    <link rel="stylesheet" href="css/bootstrap/bootstrap.min.css" />
    <link href="css/site.css" rel="stylesheet" />
    <link href="css/app.css" rel="stylesheet" />
    <link href="WinFormsApp.styles.css" rel="stylesheet" />
</head>

<body>
    <div id="app"></div>

    <div id="blazor-error-ui">
        An unhandled error has occurred.
        <a href="">Reload</a>
        <a>🗙</a>
    </div>

    <script src="_framework/blazor.webview.js"></script>
</body>

</html>
- ساختار این فایل بسیار شبیه به ساختار فایل برنامه‌های Blazor WASM است؛ با این تفاوت که در انتهای آن از blazor.webview.js کامپوننت webview استفاده می‌شود.
- همچنین در این فایل باید مداخل css.‌های مورد نیاز را هم مجددا ذکر کرد.

مرحله‌ی آخر کار، استفاده از کامپوننت webview جهت نمایش فایل index.html فوق است:
using System;
using System.Windows.Forms;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.AspNetCore.Components.WebView.WindowsForms;
using Microsoft.Extensions.DependencyInjection;
using BlazorServerApp.Data;
using BlazorClassLibrary;

namespace WinFormsApp;

public partial class Form1 : Form
{
private readonly AppState _appState = new();

    public Form1()
    {
        var serviceCollection = new ServiceCollection();
        serviceCollection.AddBlazorWebView();
        serviceCollection.AddSingleton<AppState>(_appState);
        serviceCollection.AddSingleton<WeatherForecastService>();

        InitializeComponent();

        blazorWebView1.HostPage = @"wwwroot\index.html";
        blazorWebView1.Services = serviceCollection.BuildServiceProvider();
        blazorWebView1.RootComponents.Add<App>("#app");

        //blazorWebView1.Dock = DockStyle.Fill;
    }
}


نکته‌ی مهم! حتما نیاز است WebView2 Runtime را جداگانه دریافت و نصب کرد. در غیر اینصورت در حین اجرای برنامه، با خطای نامفهوم زیر مواجه خواهید شد:
System.IO.FileNotFoundException: The system cannot find the file specified. (0x80070002)

در اینجا یک ServiceCollection را ایجاد کرده و توسط آن سرویس‌های مورد نیاز کامپوننت WebView را تامین می‌کنیم. همچنین مسیر فایل index.html نیز توسط آن مشخص شده‌است. این تنظیمات شبیه به فایل Program.cs برنامه‌ی Blazor هستند.

تا اینجا اگر برنامه را اجرا کنیم، چنین خروجی قابل مشاهده‌است:


اکنون برنامه‌ی کامل Blazor Server ما توسط یک WinForms هاست شده‌است و کاربر برای کار با آن، نیاز به نصب IIS یا هیچ وب سرور خاصی ندارد.


تعامل بین برنامه‌ی WinForm و برنامه‌ی Blazor


می‌خواهیم یک دکمه را بر روی WinForm قرار داده و با کلیک بر روی آن، مقدار شمارشگر حاصل در برنامه‌ی Blazor را نمایش دهیم؛ مانند تصویر فوق.
برای اینکار در کدهای فوق، ثبت سرویس جدید AppState را هم مشاهده می‌کنید:
serviceCollection.AddSingleton<AppState>(_appState);
 که چنین محتوایی را دارد:
 namespace BlazorServerApp.Data;

public class AppState
{
   public int Counter { get; set; }
}
این سرویس را به نحو زیر نیز به فایل Program.cs پروژه‌ی Blazor Server اضافه می‌کنیم:
builder.Services.AddSingleton<AppState>();
سپس در فایل Counter.razor آن‌را تزریق کرده و به نحو زیر به ازای هر بار کلیک بر روی دکمه‌ی افزایش مقدار شمارشگر، مقدار آن‌را اضافه می‌کنیم:
@inject BlazorServerApp.Data.AppState AppState

// ...


@code {

    private void IncrementCount()
    {
       // ...
       AppState.Counter++;
    }
}
با توجه به Singleton بودن آن و هاست برنامه‌ی Blazor توسط WinForms، یک وهله از این سرویس، هم در برنامه‌ی Blazor و هم در برنامه‌ی WinForms قابل دسترسی است. برای نمونه یک دکمه را به فرم برنامه‌ی WinForm اضافه کرده و در روال رویدادگردان کلیک آن، کد زیر را اضافه می‌کنیم:
private void button1_Click(object sender, EventArgs e)
{
   MessageBox.Show(
     owner: this,
     text: $"Current counter value is: {_appState.Counter}",
     caption: "Counter");
}
در اینجا می‌توان با استفاده از وهله‌ی سرویس به اشتراک گذاشته شده، به مقدار تنظیم شده‌ی در برنامه‌ی Blazor دسترسی یافت.

کدهای کامل این مطلب را از اینجا می‌توانید دریافت کنید: BlazorDesktopHybrid.zip
مطالب
Blazor 5x - قسمت 29 - برنامه‌ی Blazor WASM - یک تمرین: رزرو کردن یک اتاق انتخابی


در قسمت قبل که لیست اتاق‌های دریافتی از Web API را نمایش دادیم، هرکدام از آن‌ها، به همراه یک دکمه‌ی Book هم هستند (تصویر فوق) که هدف از آن، فراهم آوردن امکان رزرو کردن آن اتاق، توسط کاربران سایت است. این قسمت را می‌توان به عنوان تمرینی جهت یادآوری مراحل مختلف تهیه‌ی یک Web API و قسمت‌های سمت کلاینت آن، تکمیل کرد.



تهیه موجودیت و مدل متناظر با صفحه‌ی ثبت رزرو یک اتاق

تا اینجا در برنامه‌ی سمت کلاینت، زمانیکه بر روی دکمه‌ی Go صفحه‌ی اول کلیک می‌کنیم، تاریخ شروع رزرو و تعداد روز مدنظر، به صفحه‌ی مشاهده‌ی لیست اتاق‌ها ارسال می‌شود. اکنون می‌خواهیم در این لیست اتاق‌های نمایش داده شده، اگر بر روی لینک Book اتاقی کلیک شد، به صفحه‌ی اختصاصی رزرو آن اتاق هدایت شویم (مانند تصویر فوق). به همین جهت نیاز است موجودیت متناظر با اطلاعاتی را که قرار است از کاربر دریافت کنیم، به صورت زیر به پروژه‌ی BlazorServer.Entities اضافه کنیم:
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace BlazorServer.Entities
{
    public class RoomOrderDetail
    {
        public int Id { get; set; }

        [Required]
        public string UserId { get; set; }

        [Required]
        public string StripeSessionId { get; set; }

        public DateTime CheckInDate { get; set; }

        public DateTime CheckOutDate { get; set; }

        public DateTime ActualCheckInDate { get; set; }

        public DateTime ActualCheckOutDate { get; set; }

        public long TotalCost { get; set; }

        public int RoomId { get; set; }

        public bool IsPaymentSuccessful { get; set; }

        [Required]
        public string Name { get; set; }

        [Required]
        public string Email { get; set; }

        public string Phone { get; set; }

        [ForeignKey("RoomId")]
        public HotelRoom HotelRoom { get; set; }

        public string Status { get; set; }
    }
}
در اینجا مشخصات شروع و پایان رزرو یک اتاق مشخص و مشخصات کاربری که قرار است این فرم را پر کند، مشاهده می‌کنید که Status یا وضعیت آن، در پروژه‌ی مشترک BlazorServer.Common به صورت زیر تعریف می‌شود:
namespace BlazorServer.Common
{
    public static class BookingStatus
    {
        public const string Pending = "Pending";
        public const string Booked = "Booked";
        public const string CheckedIn = "CheckedIn";
        public const string CheckedOutCompleted = "CheckedOut";
        public const string NoShow = "NoShow";
        public const string Cancelled = "Cancelled";
    }
}
پس از این تعاریف، DbSet آن‌را نیز به ApplicationDbContext اضافه می‌کنیم:
namespace BlazorServer.DataAccess
{
    public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
    {
        public DbSet<RoomOrderDetail> RoomOrderDetails { get; set; }

        // ...
    }
}
بنابراین مرحله‌ی بعدی، ایجاد و اجرای Migrations متناظر با این جدول جدید است. برای این منظور با استفاده از خط فرمان به پوشه‌ی BlazorServer.DataAccess وارد شده و دستورات زیر را اجرا می‌کنیم:
dotnet tool update --global dotnet-ef --version 5.0.4
dotnet build
dotnet ef migrations --startup-project ../../BlazorWasm/BlazorWasm.WebApi/ add AddRoomOrderDetails --context ApplicationDbContext
dotnet ef --startup-project ../../BlazorWasm/BlazorWasm.WebApi/ database update --context ApplicationDbContext
این دستورات به پروژه‌ی آغازین WebApi اشاره می‌کنند که قرار است از طریق سرویسی، با بانک اطلاعاتی ارتباط برقرار کند.

پس از تعریف یک موجودیت، یک DTO متناظر با آن‌را که جهت مدلسازی UI از آن استفاده خواهیم کرد، در پروژه‌ی BlazorServer.Models ایجاد می‌کنیم:
using System;
using System.ComponentModel.DataAnnotations;

namespace BlazorServer.Models
{
    public class RoomOrderDetailsDTO
    {
        public int Id { get; set; }

        [Required]
        public string UserId { get; set; }

        [Required]
        public string StripeSessionId { get; set; }

        [Required]
        public DateTime CheckInDate { get; set; }

        [Required]
        public DateTime CheckOutDate { get; set; }

        public DateTime ActualCheckInDate { get; set; }

        public DateTime ActualCheckOutDate { get; set; }

        [Required]
        public long TotalCost { get; set; }

        [Required]
        public int RoomId { get; set; }

        public bool IsPaymentSuccessful { get; set; }

        [Required]
        public string Name { get; set; }

        [Required]
        public string Email { get; set; }

        public string Phone { get; set; }

        public HotelRoomDTO HotelRoomDTO { get; set; }

        public string Status { get; set; }
    }
}
و همچنین در پروژه‌ی BlazorServer.Models.Mappings، نگاشت دوطرفه‌ی AutoMapper آن‌را نیز برقرار می‌کنیم؛ تا در حین تبدیل اطلاعات بین این دو، نیازی به تکرار سطرهای مقدار دهی اطلاعات خواص، نباشد:
namespace BlazorServer.Models.Mappings
{
    public class MappingProfile : Profile
    {
        public MappingProfile()
        {
            // ... 
            CreateMap<RoomOrderDetail, RoomOrderDetailsDTO>().ReverseMap(); // two-way mapping
        }
    }
}


ایجاد سرویسی برای کار با جدول RoomOrderDetails

در برنامه‌ی سمت کلاینت برای کار با بانک اطلاعاتی، دیگر نمی‌توان از سرویس‌های سمت سرور به صورت مستقیم استفاده کرد. به همین جهت آن‌ها را از طریق یک Web API endpoint، در معرض دید استفاده کننده قرار می‌دهیم. اما پیش از اینکار، سرویس سمت سرور Web API باید بتواند با سرویس دسترسی به اطلاعات جدول RoomOrderDetails، کار کند. بنابراین در ادامه این سرویس را تهیه می‌کنیم:
namespace BlazorServer.Services
{
    public interface IRoomOrderDetailsService
    {
        Task<RoomOrderDetailsDTO> CreateAsync(RoomOrderDetailsDTO details);

        Task<List<RoomOrderDetailsDTO>> GetAllRoomOrderDetailsAsync();

        Task<RoomOrderDetailsDTO> GetRoomOrderDetailAsync(int roomOrderId);

        Task<bool> IsRoomBookedAsync(int RoomId, DateTime checkInDate, DateTime checkOutDate);

        Task<RoomOrderDetailsDTO> MarkPaymentSuccessfulAsync(int id);

        Task<bool> UpdateOrderStatusAsync(int RoomOrderId, string status);
    }
}
که به صورت زیر پیاده سازی می‌شود:
namespace BlazorServer.Services
{
    public class RoomOrderDetailsService : IRoomOrderDetailsService
    {
        private readonly ApplicationDbContext _dbContext;
        private readonly IMapper _mapper;
        private readonly IConfigurationProvider _mapperConfiguration;

        public RoomOrderDetailsService(ApplicationDbContext dbContext, IMapper mapper)
        {
            _dbContext = dbContext ?? throw new ArgumentNullException(nameof(dbContext));
            _mapper = mapper ?? throw new ArgumentNullException(nameof(mapper));
            _mapperConfiguration = mapper.ConfigurationProvider;
        }

        public async Task<RoomOrderDetailsDTO> CreateAsync(RoomOrderDetailsDTO details)
        {
            var roomOrder = _mapper.Map<RoomOrderDetail>(details);
            roomOrder.Status = BookingStatus.Pending;
            var result = await _dbContext.RoomOrderDetails.AddAsync(roomOrder);
            await _dbContext.SaveChangesAsync();
            return _mapper.Map<RoomOrderDetailsDTO>(result.Entity);
        }

        public Task<List<RoomOrderDetailsDTO>> GetAllRoomOrderDetailsAsync()
        {
            return _dbContext.RoomOrderDetails
                            .Include(roomOrderDetail => roomOrderDetail.HotelRoom)
                            .ProjectTo<RoomOrderDetailsDTO>(_mapperConfiguration)
                            .ToListAsync();
        }

        public async Task<RoomOrderDetailsDTO> GetRoomOrderDetailAsync(int roomOrderId)
        {
            var roomOrderDetailsDTO = await _dbContext.RoomOrderDetails
                                            .Include(u => u.HotelRoom)
                                                .ThenInclude(x => x.HotelRoomImages)
                                            .ProjectTo<RoomOrderDetailsDTO>(_mapperConfiguration)
                                            .FirstOrDefaultAsync(u => u.Id == roomOrderId);

            roomOrderDetailsDTO.HotelRoomDTO.TotalDays =
                roomOrderDetailsDTO.CheckOutDate.Subtract(roomOrderDetailsDTO.CheckInDate).Days;
            return roomOrderDetailsDTO;
        }

        public Task<bool> IsRoomBookedAsync(int RoomId, DateTime checkInDate, DateTime checkOutDate)
        {
            return _dbContext.RoomOrderDetails
                .AnyAsync(
                    roomOrderDetail =>
                        roomOrderDetail.RoomId == RoomId &&
                        roomOrderDetail.IsPaymentSuccessful &&
                        (
                            (checkInDate < roomOrderDetail.CheckOutDate && checkInDate > roomOrderDetail.CheckInDate) ||
                            (checkOutDate > roomOrderDetail.CheckInDate && checkInDate < roomOrderDetail.CheckInDate)
                        )
                );
        }

        public Task<RoomOrderDetailsDTO> MarkPaymentSuccessfulAsync(int id)
        {
            throw new NotImplementedException();
        }

        public Task<bool> UpdateOrderStatusAsync(int RoomOrderId, string status)
        {
            throw new NotImplementedException();
        }
    }
}
توضیحات:
- از متد CreateAsync برای تبدیل مدل فرم ثبت اطلاعات، به یک رکورد جدول RoomOrderDetails، استفاده می‌کنیم.
- متد GetAllRoomOrderDetailsAsync، لیست تمام سفارش‌های ثبت شده را بازگشت می‌دهد.
- متد GetRoomOrderDetailAsync بر اساس شماره اتاقی که دریافت می‌کند، لیست سفارشات آن اتاق خاص را بازگشت می‌دهد. این لیست به علت استفاده از Include‌های تعریف شده، به همراه مشخصات اتاق و همچنین تصاویر مرتبط با آن اتاق نیز هست.
- متد IsRoomBookedAsync بر اساس شماره اتاق و بازه‌ی زمانی درخواستی توسط یک کاربر مشخص می‌کند که آیا اتاق خالی شده‌است یا خیر؟

پس از تعریف این سرویس، به کلاس آغازین پروژه‌ی Web API مراجعه کرده و آن‌را به سیستم تزریق وابستگی‌ها، معرفی می‌کنیم:
namespace BlazorWasm.WebApi
{
    public class Startup
    {
        // ...

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddScoped<IRoomOrderDetailsService, RoomOrderDetailsService>();
            // ...
 
 
تشکیل سرویس ابتدایی کار با RoomOrderDetails در پروژه‌ی WASM

در ادامه، تعاریف خالی سرویس سمت کلاینت کار با RoomOrderDetails  را به پروژه‌ی WASM اضافه می‌کنیم. تکمیل این سرویس را به قسمت بعدی واگذار خواهیم کرد:
namespace BlazorWasm.Client.Services
{
    public interface IClientRoomOrderDetailsService
    {
        Task<RoomOrderDetailsDTO> MarkPaymentSuccessfulAsync(RoomOrderDetailsDTO details);
        Task<RoomOrderDetailsDTO> SaveRoomOrderDetailsAsync(RoomOrderDetailsDTO details);
    }
}
با این پیاده سازی ابتدایی:
namespace BlazorWasm.Client.Services
{
    public class ClientRoomOrderDetailsService : IClientRoomOrderDetailsService
    {
        private readonly HttpClient _httpClient;

        public ClientRoomOrderDetailsService(HttpClient httpClient)
        {
            _httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient));
        }

        public Task<RoomOrderDetailsDTO> MarkPaymentSuccessfulAsync(RoomOrderDetailsDTO details)
        {
            throw new NotImplementedException();
        }

        public Task<RoomOrderDetailsDTO> SaveRoomOrderDetailsAsync(RoomOrderDetailsDTO details)
        {
            throw new NotImplementedException();
        }
    }
}
که این مورد نیز باید به نحو زیر به سیستم تزریق وابستگی‌های برنامه‌ی سمت کلاینت در فایل Program.cs آن اضافه شود:
namespace BlazorWasm.Client
{
    public class Program
    {
        public static async Task Main(string[] args)
        {
            var builder = WebAssemblyHostBuilder.CreateDefault(args);
            // ...
            builder.Services.AddScoped<IClientRoomOrderDetailsService, ClientRoomOrderDetailsService>();
            // ...
        }
    }
}


تعریف مدل فرم ثبت اطلاعات سفارش

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

namespace BlazorWasm.Client.Models.ViewModels
{
    public class HotelRoomBookingVM
    {
        public RoomOrderDetailsDTO OrderDetails { get; set; }
    }
}


تعریف کامپوننت جدید RoomDetails و مقدار دهی اولیه‌ی مدل آن

در ادامه فایل جدید BlazorWasm.Client\Pages\HotelRooms\RoomDetails.razor را ایجاد کرده و به صورت زیر مقدار دهی اولیه می‌کنیم:
@page "/hotel/room-details/{Id:int}"

@inject IJSRuntime JsRuntime
@inject ILocalStorageService LocalStorage
@inject IClientHotelRoomService HotelRoomService

@if (HotelBooking?.OrderDetails?.HotelRoomDTO?.HotelRoomImages == null)
{
    <div class="spinner"></div>
}
else
{

}

@code {
    [Parameter]
    public int? Id { get; set; }

    HotelRoomBookingVM HotelBooking  = new HotelRoomBookingVM();
    int NoOfNights = 1;

    protected override async Task OnInitializedAsync()
    {
        try
        {
            HotelBooking.OrderDetails = new RoomOrderDetailsDTO();
            if (Id != null)
            {
                if (await LocalStorage.GetItemAsync<HomeVM>(ConstantKeys.LocalInitialBooking) != null)
                {
                    var roomInitialInfo = await LocalStorage.GetItemAsync<HomeVM>(ConstantKeys.LocalInitialBooking);
                    HotelBooking.OrderDetails.HotelRoomDTO = await HotelRoomService.GetHotelRoomDetailsAsync(
                        Id.Value, roomInitialInfo.StartDate, roomInitialInfo.EndDate);
                    NoOfNights = roomInitialInfo.NoOfNights;
                    HotelBooking.OrderDetails.CheckInDate = roomInitialInfo.StartDate;
                    HotelBooking.OrderDetails.CheckOutDate = roomInitialInfo.EndDate;
                    HotelBooking.OrderDetails.HotelRoomDTO.TotalDays = roomInitialInfo.NoOfNights;
                    HotelBooking.OrderDetails.HotelRoomDTO.TotalAmount =
                        roomInitialInfo.NoOfNights * HotelBooking.OrderDetails.HotelRoomDTO.RegularRate;
                }
                else
                {
                    HotelBooking.OrderDetails.HotelRoomDTO = await HotelRoomService.GetHotelRoomDetailsAsync(
                        Id.Value, DateTime.Now, DateTime.Now.AddDays(1));
                    NoOfNights = 1;
                    HotelBooking.OrderDetails.CheckInDate = DateTime.Now;
                    HotelBooking.OrderDetails.CheckOutDate = DateTime.Now.AddDays(1);
                    HotelBooking.OrderDetails.HotelRoomDTO.TotalDays = 1;
                    HotelBooking.OrderDetails.HotelRoomDTO.TotalAmount =
                        HotelBooking.OrderDetails.HotelRoomDTO.RegularRate;
                }
            }
        }
        catch (Exception e)
        {
            await JsRuntime.ToastrError(e.Message);
        }
    }
}
- در ابتدا مسیریابی کامپوننت جدید RoomDetails را مشخص کرد‌ه‌ایم که یک Id را می‌پذیرد که همان Id اتاق انتخاب شده‌ی توسط کاربر است. به همین جهت پارامتر عمومی متناظری با آن هم در قسمت کدهای کامپوننت تعریف شده‌است.
- سپس سرویس توکار IJSRuntime به کامپوننت تزریق شده‌است تا توسط آن و Toastr، بتوان خطاهایی را به کاربر نمایش داد.
- از سرویس ILocalStorageService برای دسترسی به اطلاعات شروع به رزرو شخص و تعداد روز مدنظر او استفاده می‌کنیم که در قسمت قبل آن‌را مقدار دهی کردیم.
- همچنین از سرویس IClientHotelRoomService که آن‌را نیز در قسمت قبل افزودیم، برای فراخوانی متد GetHotelRoomDetailsAsync آن استفاده کرده‌ایم.

در روال آغازین OnInitializedAsync، اگر Id تنظیم شده بود، یعنی کاربر به درستی وارد این صفحه شده‌است. سپس بررسی می‌کنیم که آیا اطلاعاتی از درخواست ابتدایی او در Local Storage مرورگر وجود دارد یا خیر؟ اگر این اطلاعات وجود داشته باشد، بر اساس آن، بازه‌ی تاریخی دقیقی را می‌توان تشکیل داد و اگر خیر، این بازه را از امروز، به مدت 1 روز درنظر می‌گیریم.
پس از پایان کار متد OnInitializedAsync، چون اجزای HotelBooking مقدار دهی کامل شده‌اند، نمایش loading ابتدای کامپوننت، متوقف شده و قسمت else شرط نوشته شده اجرا می‌شود؛ یعنی اصل UI فرم نمایان خواهد شد.

در قسمت قبل، متد GetHotelRoomDetailsAsync را تکمیل نکردیم؛ چون به آن نیازی نداشتیم و فقط قصد داشتیم تا لیست تمام اتاق‌ها را نمایش دهیم. اما در اینجا برای تکمیل کدهای آغازین کامپوننت RoomDetails، متد دریافت اطلاعات یک اتاق را نیز تکمیل می‌کنیم تا توسط آن بتوان در این کامپوننت نیز جزئیات اتاق انتخابی را نمایش داد:
namespace BlazorWasm.Client.Services
{
    public class ClientHotelRoomService : IClientHotelRoomService
    {
        private readonly HttpClient _httpClient;

        public ClientHotelRoomService(HttpClient httpClient)
        {
            _httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient));
        }

        public Task<HotelRoomDTO> GetHotelRoomDetailsAsync(int roomId, DateTime checkInDate, DateTime checkOutDate)
        {
            // How to url-encode query-string parameters properly
            var uri = new UriBuilderExt(new Uri(_httpClient.BaseAddress, $"/api/hotelroom/{roomId}"))
                            .AddParameter("checkInDate", $"{checkInDate:yyyy'-'MM'-'dd}")
                            .AddParameter("checkOutDate", $"{checkOutDate:yyyy'-'MM'-'dd}")
                            .Uri;
            return _httpClient.GetFromJsonAsync<HotelRoomDTO>(uri);
        }

        public Task<IEnumerable<HotelRoomDTO>> GetHotelRoomsAsync(DateTime checkInDate, DateTime checkOutDate)
        {
           // ...
        }
    }
}

اتصال مدل کامپوننت RoomDetails به فرم ثبت سفارش آن

تا اینجا مدل فرم را مقدار دهی اولیه کردیم. اکنون می‌توانیم قسمت else شرط نوشته شده را تکمیل کرده و در قسمتی از آن، مشخصات اتاق جاری را نمایش دهیم و در قسمتی دیگر، فرم ثبت سفارش را تکمیل کنیم.
الف) نمایش مشخصات اتاق جاری
در کامپوننت جاری با استفاده از خواص مقدار دهی اولیه شده‌ی شیء HotelBooking.OrderDetails.HotelRoomDTO، می‌توان جزئیات اتاق انتخابی را نمایش داد که نمونه‌ای از آن‌را در قسمت قبل هم مشاهده کردید:
@if (HotelBooking?.OrderDetails?.HotelRoomDTO?.HotelRoomImages == null)
{
    <div class="spinner"></div>
}
else
{
    <div class="mt-4 mx-4 px-0 px-md-5 mx-md-5">
        <div class="row p-2 my-3 " style="border-radius:20px; ">
            <div class="col-12 col-lg-7 p-4" style="border: 1px solid gray">
                <div class="row px-2 text-success border-bottom">
                    <div class="col-8 py-1"><p style="font-size:x-large;margin:0px;">Selected Room</p></div>
                    <div class="col-4 p-0"><a href="hotel/rooms" class="btn btn-secondary btn-block">Back to Room's</a></div>
                </div>
                <div class="row">
                    <div class="col-6">
                        <div id="" class="carousel slide mb-4 m-md-3 m-0 pt-3 pt-md-0" data-ride="carousel">
                            <div id="carouselExampleIndicators" class="carousel slide" data-ride="carousel">
                                <ol class="carousel-indicators">
                                    <li data-target="#carouselExampleIndicators" data-slide-to="0" class="active"></li>
                                    <li data-target="#carouselExampleIndicators" data-slide-to="1"></li>
                                </ol>
                                <div class="carousel-inner">
                                    <div class="carousel-item active">
                                        <img class="d-block w-100" src="images/slide1.jpg" alt="First slide">
                                    </div>
                                </div>
                                <a class="carousel-control-prev" href="#carouselExampleIndicators" role="button" data-slide="prev">
                                    <span class="carousel-control-prev-icon" aria-hidden="true"></span>
                                    <span class="sr-only">Previous</span>
                                </a>
                                <a class="carousel-control-next" href="#carouselExampleIndicators" role="button" data-slide="next">
                                    <span class="carousel-control-next-icon" aria-hidden="true"></span>
                                    <span class="sr-only">Next</span>
                                </a>
                            </div>
                        </div>
                    </div>
                    <div class="col-6">
                        <span class="float-right pt-4">
                            <span class="float-right">Occupancy : @HotelBooking.OrderDetails.HotelRoomDTO.Occupancy adults </span><br />
                            <span class="float-right pt-1">Size : @HotelBooking.OrderDetails.HotelRoomDTO.SqFt sqft</span><br />
                            <h4 class="text-warning font-weight-bold pt-5">
                                <span style="border-bottom:1px solid #ff6a00">
                                    @HotelBooking.OrderDetails.HotelRoomDTO.TotalAmount.ToString("#,#.00#;(#,#.00#)")
                                </span>
                            </h4>
                            <span class="float-right">Cost for @HotelBooking.OrderDetails.HotelRoomDTO.TotalDays nights</span>
                        </span>
                    </div>
                </div>
                <div class="row p-2">
                    <div class="col-12">
                        <p class="card-title text-warning" style="font-size:xx-large">@HotelBooking.OrderDetails.HotelRoomDTO.Name</p>
                        <p class="card-text" style="font-size:large">
                            @((MarkupString)@HotelBooking.OrderDetails.HotelRoomDTO.Details)
                        </p>
                    </div>

                </div>
            </div>
}
ب) نمایش فرم متصل به مدل کامپوننت
قسمت دوم UI کامپوننت جاری، نمایش فرم زیر است که اجزای مختلف آن به فیلد HotelBooking متصل شده‌اند:
@if (HotelBooking?.OrderDetails?.HotelRoomDTO?.HotelRoomImages == null)
{
    <div class="spinner"></div>
}
else
{
  // ...
             
            <div class="col-12 col-lg-5 p-4 2 mt-4 mt-md-0" style="border: 1px solid gray;">
                <EditForm Model="HotelBooking" class="container" OnValidSubmit="HandleCheckout">
                    <div class="row px-2 text-success border-bottom"><div class="col-7 py-1"><p style="font-size:x-large;margin:0px;">Enter Details</p></div></div>

                    <div class="form-group pt-2">
                        <label class="text-warning">Name</label>
                        <InputText @bind-Value="HotelBooking.OrderDetails.Name" type="text" class="form-control" />
                    </div>
                    <div class="form-group pt-2">
                        <label class="text-warning">Phone</label>
                        <InputText @bind-Value="HotelBooking.OrderDetails.Phone" type="text" class="form-control" />
                    </div>
                    <div class="form-group">
                        <label class="text-warning">Email</label>
                        <InputText @bind-Value="HotelBooking.OrderDetails.Email" type="text" class="form-control" />
                    </div>
                    <div class="form-group">
                        <label class="text-warning">Check in Date</label>
                        <InputDate @bind-Value="HotelBooking.OrderDetails.CheckInDate" type="date" disabled class="form-control" />
                    </div>
                    <div class="form-group">
                        <label class="text-warning">Check Out Date</label>
                        <InputDate @bind-Value="HotelBooking.OrderDetails.CheckOutDate" type="date" disabled class="form-control" />
                    </div>
                    <div class="form-group">
                        <label class="text-warning">No. of nights</label>
                        <select class="form-control" value="@NoOfNights" @onchange="HandleNoOfNightsChange">
                            @for (var i = 1; i <= 10; i++)
                            {
                                if (i == NoOfNights)
                                {
                                    <option value="@i" selected="selected">@i</option>
                                }
                                else
                                {
                                    <option value="@i">@i</option>
                                }
                            }
                        </select>
                    </div>
                    <div class="form-group">
                        <button type="submit" class="btn btn-success form-control">Checkout Now</button>
                    </div>
                </EditForm>
            </div>
        </div>
    </div>
}
در این فرم دو روال رویدادگردان زیر نیز مورد استفاده هستند:
@code {
    // ...

    private async Task HandleNoOfNightsChange(ChangeEventArgs e)
    {
        NoOfNights = Convert.ToInt32(e.Value.ToString());
        HotelBooking.OrderDetails.HotelRoomDTO = await HotelRoomService.GetHotelRoomDetailsAsync(
            Id.Value,
            HotelBooking.OrderDetails.CheckInDate,
            HotelBooking.OrderDetails.CheckInDate.AddDays(NoOfNights));

        HotelBooking.OrderDetails.CheckOutDate = HotelBooking.OrderDetails.CheckInDate.AddDays(NoOfNights);
        HotelBooking.OrderDetails.HotelRoomDTO.TotalDays = NoOfNights;
        HotelBooking.OrderDetails.HotelRoomDTO.TotalAmount =
                NoOfNights * HotelBooking.OrderDetails.HotelRoomDTO.RegularRate;
    }

    private async Task HandleCheckout()
    {
        if (!await HandleValidation())
        {
            return;
        }
    }

    private async Task<bool> HandleValidation()
    {
        if (string.IsNullOrEmpty(HotelBooking.OrderDetails.Name))
        {
            await JsRuntime.ToastrError("Name cannot be empty");
            return false;
        }

        if (string.IsNullOrEmpty(HotelBooking.OrderDetails.Phone))
        {
            await JsRuntime.ToastrError("Phone cannot be empty");
            return false;
        }

        if (string.IsNullOrEmpty(HotelBooking.OrderDetails.Email))
        {
            await JsRuntime.ToastrError("Email cannot be empty");
            return false;
        }
        return true;
    }
}
- کاربر اگر تعداد شب‌های اقامت را از طریق دارپ‌داون فرم تغییر داد، در روال رویدادگردان HandleNoOfNightsChange، محاسبات جدیدی را بر این اساس انجام می‌دهیم؛ چون هزینه و سایر مشخصات جزئیات اتاق نمایش داده شده، باید تغییر کنند.
- همچنین کدهای ابتدایی HandleCheckout را که برای ثبت نهایی اطلاعات فرم است، تهیه کرده‌ایم. البته در این قسمت این مورد را فقط محدود به اعتبارسنجی دستی و سفارشی که در متد HandleValidation مشاهده می‌کنید، کرده‌ایم. این روش دستی را نیز می‌توان برای تعریف منطق اعتبارسنجی یک فرم بکار برد و آن‌را توسط کدهای #C تکمیل کرد. البته باید درنظر داشت که data annotation validator توکار، هنوز از اعتبارسنجی خواص تو در تو، پشتیبانی نمی‌کند. به همین جهت است که در اینجا خودمان این اعتبارسنجی را به صورت دستی تعریف کرده‌ایم.


کدهای کامل این مطلب را از اینجا می‌توانید دریافت کنید: Blazor-5x-Part-29.zip
مطالب
Vue.js - نحوه‌ی ایجاد یک Vue جدید و استفاده از معماری MVVM - قسمت دوم
در قسمت قبلی توضیحاتی جهت معرفی و نصب فریم‌ورک داده شد. در این قسمت قصد داریم کمی بیشتر با ساختار آن آشنا شویم و یک vue جدید را ایجاد کنیم. پس لازم است تا ابتدا درون تگ اسکریپتی که فراخوانی کردیم و آدرس فریم‌ورک در آن قرار داده شده‌است، به صورت زیر یک ویو جدید را ایجاد کنیم:
<html>
    <body>
       <script src="https://cdnjs.cloudflare.com/ajax/libs/vue/1.0.27/vue.min.js">  
 new Vue({ 
               
            }); 
       </script>
    </body>
</html>
ساختار vue.js بدین صورت است که از شما می‌خواهد تعیین کنید این کدها محدود و مربوط به کدام صفحه می‌شوند.
اگر قصد داشته باشید تا درون تگی با یک مشخصه‌ی خاص، دستوری را اجرا کنید، باید تعیین کنید که آن تگ چه خواصی دارد. فرض می‌کنیم که تگ body درون پروژه لازم است مقداری را فراخوانی و اجرا کند. پس لازم است تا یک id، به تگ بادی اختصاص یابد. 
<html>
    <body id="dotnettips">
       <script src="https://cdnjs.cloudflare.com/ajax/libs/vue/1.0.27/vue.min.js">  
 new Vue({ 
               
            }); 
       </script>
    </body>
</html>
در کد بالا یک آیدی با نام dotnettips، به تگ بادی داده شد. حال قصد داریم تا با استفاده از دستور new Vue، رشته یا مقداری مشخص را در خروجی قرار دهیم. پس باید یک مشخصه را برای آن تعریف کرد که ما طبق مستندات فریم‌ورک، مشخصه‌ای را با نام el فراخوانی می‌کنیم:
<html>
    <body id="dotnettips">
       <script src="https://cdnjs.cloudflare.com/ajax/libs/vue/1.0.27/vue.min.js">  
       new Vue({ 
           el: '#dotnettips',
           data: {
                name: 'dotnettips'
               }
            });
       </script>
    </body>
</html>
با استفاده از مشخصه el تعیین کردیم که کدهای برنامه برای کدام id در نظر گرفته شده‌اند. حال با استفاده از مقدار خاصیت name، مقادیر ما در خروجی چاپ خواهند شد.


استفاده از MVVM درون پروژه
به زبان ساده در معماری MVVM، یک مدل، یک view و یک viewmodel داریم که در مدل، کدهای JS و اطلاعاتی که قصد نمایش آن را داریم، قرار می‌دهیم. در واقع viewmodel نقش data building را خواهد داشت.

برای شروع، یک مدل جدید را نیاز داریم که بدین شکل باید فراخوانی کنیم.
 //new model 
var SampleData =( 
        name: 'dotnettips' 
)
در کد بالا ما یک مدل جدید ساخته‌ایم و یک ویژگی را به آن نسبت دادیم. حال لازم است تا ویوی جدیدی را برای نمایش آن ایجاد کنیم.
  • برای نمایش اطلاعات درون ویو جی اس باید از {{ }} استفاده کنید، تا ویژگی ساخته و فراخوانی شده را نمایش دهد.
<div id="dotnettips"> 
 Hello  {{ name }} 
</div>
مثال بالا، ویوی ما خواهد بود که ویژگی مورد نظر را در خروجی نمایش می‌دهد. حال، باید هردو را با viewmodel بهم متصل کنیم.
یک ویوی جدید را ایجاد می‌کنیم و با مشخصه el، ویژگی تعیین شده را به مدل خود متصل می‌کنیم.
new Vue({
  el:'#dotnettips',
  data: SampleData
})
 .یک نمونه از این مثال را از اینجا می‌توانید مشاهده کنید  
.به نمونه کد کامل زیر دقت بفرمائید
<html>  
    <body id="dotnettips">  
          
        <h3 id="dotnettips"> 
            Hello  {{ name }} 
        </h3>  
      
        <script src="https://cdnjs.cloudflare.com/ajax/libs/vue/1.0.27/vue.min.js">
            
        </script>  
        <script type="text/javascript"> 
            new Vue({  
            el: '#dotnettips',  
                data:{  
                    name: 'dotnettips'  
                }  
            });  
        </script>  
        </body>  
</html>