var specifiedCommentId = 4; //عدد 4 در اینجا شماره رکورد کامنتی است که دارای یک سری زیر کامنت است var list = ctx.BlogComments .Include(x => x.Reply) .Where(x => x.Id >= specifiedCommentId && (x.ReplyId == x.Reply.Id || x.Reply == null)) .ToList() // fills the childs list too .Where(x => x.Reply == null) // for TreeViewHelper .ToList();
ASP.NET MVC #18
ضمن اینکه به نظر این redirectها نیاز به یک شمارنده هم دارد که در یک چنین مواردی زیاده از حد تکرار نشود.
EF Code First #1
نیازی هم نیست در سراسر پروژه تکرار شود. یکبار باید در فایل کانفیگ برنامه تعریف شود.
اطلاع داشتن از این قراردادهای توکار از اتلاف وقت جلوگیری میکند.
آموزش LINQ بخش دوم
// The Three Parts of a LINQ Query: // 1. منبع داده int[] numbers = new int[7] { 0, 1, 2, 3, 4, 5, 6 }; // 2. ایجاد پرس و جو // numQuery is an IEnumerable<int> var numQuery = from num in numbers where (num % 2) == 0 select num; // 3. اجرای پرس و جو foreach (int num in numQuery) { Console.Write("{0,1} ", num); }
class Ingredient { public string Name { get; set; } public int Calories { get; set; } }
Ingredient[] ingredients = { new Ingredient {Name = "Suger", Calories = 500}, new Ingredient {Name = "Egg", Calories = 100 }, new Ingredient {Name = "Milk", Calories = 150 }, new Ingredient {Name = "Flour", Calories = 50 }, new Ingredient {Name = "Butter", Calories = 200 } };
Ingredient[] ingredients = { new Ingredient {Name = "Suger", Calories = 500}, new Ingredient {Name = "Egg", Calories = 100 }, new Ingredient {Name = "Milk", Calories = 150 }, new Ingredient {Name = "Flour", Calories = 50 }, new Ingredient {Name = "Butter", Calories = 200 } }; IEnumerable<string> highCalories = ingredients.Where(x => x.Calories >= 150) .OrderBy(x => x.Name) .Select(x => x.Name); foreach (var item in highCalories) { Console.WriteLine(item); }
Butter Milk Suger
عبارت Lambda نوشته شدهی در بخش Select مشخص میکند که خروجی بر اساس چه خصوصیتی از توالی ورودی باشد. در اینجا نام عناصر به صورت رشته در خروجی ظاهر میشوند.
سبک Query Expression (عبارتهای پرس و جو)
Query Expression یک گرامر زیبا و روان برای نوشتن پرس و جوها را ارائه میدهد. در مثال زیر از سبک Query Expression استفاده کردهایم:
Ingredient[] ingredients = { new Ingredient {Name = "Suger", Calories = 500}, new Ingredient {Name = "Egg", Calories = 100}, new Ingredient {Name = "Milk", Calories = 150}, new Ingredient {Name = "Flour", Calories = 50}, new Ingredient {Name = "Butter", Calories = 200} }; IEnumerable<string> highCalories = from i in ingredients where i.Calories >= 150 orderby i.Name select i.Name; foreach (var item in highCalories) { Console.WriteLine(item); }
خروجی کد بالا با خروجی کد به سبک Fluent یکسان است:
Butter Milk Suger
همانطور که میبینید ترتیب عملیات همانند روش قبل است. عبارتهای پرس و جوی (from,where,orderby,select) به ترتیب با اصلاح توالی ورودی و تحویل آن به عبارت جستجوی بعدی کار را انجام میدهند.
عبارت جستجوی بالا با کلمهی کلیدی from آغاز شده است. هدف from دو چیز است:
1- مشخص کردن توالی ورودی (منبع داده)
2- معرفی متغیر Range (مشخص کردن عنصر مورد نظر در منبع داده)
متغیر Range همچون متغیر شمارنده در حلقه هاست.
در ادامه این سری آموزشی درباره متغیر Range بصورت کاملتری بحث خواهیم کرد.
متغیرهای سراسری در Postman
برای تعریف متغیرهای سراسری که در تمام برگههای Postman قابل دسترسی باشند، میتوان از متد pm.globals.set در قسمت Tests هر درخواست که پس از پایان درخواست جاری اجرا میشود، استفاده کرد. دراینجا فرصت خواهیم داشت تا مقدار دریافتی از سرور را در یک متغیر ذخیره کنیم. سپس میتوان از این متغیر، در حین ارسال درخواستی دیگر، استفاده کرد که نمونهای از آنرا در قسمت دوم، با تبدیل شیء response به یک شیء جاوا اسکریپتی و استخراج خاصیت uuid آن، مشاهده کردید:
let jsonResponse = pm.response.json(); pm.globals.set("uuid", jsonResponse.uuid);
که سبب باز شدن صفحهی دیالوگ زیر میشود که در آن میتوان کلید/مقدارهای جدیدی را به صورت دستی و بدون کدنویسی، تعریف و مقدار دهی کرد:
عملکرد | متد |
تعریف و مقدار دهی یک متغیر سراسری | pm.globals.set("varName", "VALUE"); |
دریافت مقدار یک متغیر سراسری | pm.globals.get("varName"); |
پاک کردن یک متغیر سراسری مشخص | pm.globals.unset("varName"); |
حذف تمام متغیرهای سراسری | pm.globals.clear(); |
یکی از کاربردهای مهم متغیرهای سراسری، دریافت توکنهای دسترسی پس از لاگین، در یک درخواست و استفادهی از این توکنها در درخواستهای دیگر میباشد.
روش استفادهی از متغیرهای تعریف شده
پس از تعریف این متغیرها، برای دسترسی به آنها میتوان از روش {{variableName}} در قسمتهای مختلف postman استفاده کرد:
Request URL: http://{{domain}}/users/{{userId}} Headers (key:value): X-{{myHeaderName}}:foo Request body: {"id": "{{userId}}", "name": "John Doe"}
متغیرهای محیطی در Postman
متغیرهای محیطی نیز بسیار شبیه به متغیرهای سراسری هستند، اما میدان دید آنها کمتر است. برای مثال فرض کنید که قصد دارید اطلاعات پایهی تنظیمات سرور و پورت آنها را در بین درخواستهای مختلف تغییر دهید؛ بدون اینکه بخواهید اصل درخواستها تغییری کنند؛ یا اینکه قسمت تعریف متغیرهای سراسری بیش از حد شلوغ شدهاست و قصد دارید آنها را گروه بندی کرده و مورد استفاده قرار دهید.
در ابتدای کار، هیچ محیط خاصی تعریف نشدهاست:
برای تعریف یک محیط جدید میتوان بر روی دکمهای با آیکن چشم، در بالای سمت راست صفحه و کلیک بر روی گزینهی Add آن، یک محیط جدید را ایجاد کرد:
در صفحهی باز شده ابتدا باید نامی را برای این محیط جدید انتخاب کرد و سپس میتوان key/valueهایی را مخصوص این محیط، تعریف نمود:
پس از تعریف متغیرهای جدید محیطی و مقادیر آنها، نحوهی استفادهی از این متغیرها دقیقا همانند روشی است که از متغیرهای سراسری استفاده کردیم و توسط روش {{variableName}} قابل دسترسی هستند.
برای ویرایش اطلاعات منتسب به یک محیط، ابتدا باید آنرا از dropdown محیطهای بالای صفحه انتخاب کرد. اکنون با کلیک بر روی دکمهای با آیکن چشم، در بالای سمت راست صفحه، لینک ویرایش این محیط انتخاب شده ظاهر میشود:
API کار با متغیرهای محیطی از طریق کد نویسی
عملکرد | متد |
تعریف و مقدار دهی یک متغیر محیطی | pm.environment.set("varName", "VALUE"); |
دریافت مقدار یک متغیر محیطی | pm.environment.get("varName"); |
پاک کردن یک متغیر محیطی مشخص | pm.environment.unset("varName"); |
حذف تمام متغیرهای محیطی | pm.environment.clear(); |
تفاوت میدان دید متغیرهای محیطی و متغیرهای سراسری
باید دقت داشت که هر دوی متغیرهای سراسری و محیطی، در تمام برگههای تعریف شده قابل دسترسی میباشند و از این لحاظ تفاوتی بین آنها نیست. اما فرض کنید یک متغیر سراسری را با نام port1 تعریف کردهاید و از آن برای ساخت آدرسی مانند https://localhost:{{port1}} استفاده کردهاید. همچنین دقیقا همین متغیر port1 را در محیط جدیدی به نام Env1 نیز تعریف کردهاید. اگر محیطی انتخاب نشده باشد، port1 به همان متغیر سراسری تعریف شده اشاره میکند.
اما اگر محیط انتخابی را به Env1 تغییر دهیم، اینبار port1، از طریق اطلاعات Env1 تامین شده و مقدار متغیر سراسری تعریف شده را بازنویسی (یا مخفی) میکند. بنابراین در حین کارکردن با محیطی مشخص، متغیرهای محیطی، بر متغیرهای سراسری مقدم هستند.
یک نکته: با نزدیک کردن اشارهگر ماوس، به یک متغیر تعریف شدهی در postman، میتوان میدان دید آنرا به سادگی مشاهده کرد و در این حالت دیگر جای حدس و گمانی باقی نمیماند.
عدم انتشار مقادیر اولیهی حساس، در حین گرفتن خروجیها
اگر به تصاویر فوق دقت کنید، حین تنظیم مقادیر متغیرها، ستون اول، initial value نام دارد و ستون دوم، current value. هنگام گرفتن خروجی از یک مجموعهی Postman، تنها این مقدار اولیه در خروجی وجود خواهد داشت و با دیگران به اشتراک گذاشته میشود. مقدار جاری همانی است که در حین ارسال درخواستها مورد استفاده قرار میگیرد. بنابراین تنها کاربرد initial value، در تهیهی خروجیها است که در انتهای قسمت سوم آنرا بررسی کردیم.
مشکل اینجا است که اگر از متدهای به روز رسانی مقادیر متغیرها استفاده کنیم، هر دو مقدار را تغییر میدهند که ممکن است علاقمند نباشید آنها را به اشتراک بگذارید. برای رفع این مشکل میتوان به منوی File->Settings آن مراجعه و گزینهی Automatically persist variable values را خاموش کرد:
با اینکار تغییر current value توسط متدهای API، سبب تغییر initial value که در exports ظاهر میشوند، نخواهد شد.
متغیرها در ES 6
واژهی کلیدی let
تاکنون به کمک واژهی کلیدی var امکان تعریف متغیرها در جاوا اسکریپت مهیا بودند. برای نمونه در مثال زیر، متغیر x داخل بدنهی if با استفاده از var تعریف شدهاست:
var doWork = function(flag){ if(flag){ var x = 3; } return x; };
زمانیکه از var استفاده میشود، برای یک متغیر دو نوع میدان دید را میتوان متصور شد:
- اگر خارج از بدنهی تابع تعریف شود، این متغیر عمومی خواهد بود.
- اگر داخل بدنهی تابع تعریف شود، میدان دید آن محدود به همان بدنهی تابع میشود. در این حالت چیزی به نام block scope بیمفهوم است. در متد doWork فوق، هرچند متغیر x داخل بدنهی بلاک if تعریف شدهاست، اما این x در کل بدنهی تابع در دسترس است و نه صرفا داخل بلاک if. این مورد تا پیش از ES 6 منشاء بسیاری از باگها بودهاست.
بنابراین در اینجا چون x تعریف شده، میدان دیدی در سطح متد دارد، return x معتبر بوده و در حالت دریافت پارامتر true، مقدار 3 را بر میگرداند و در حالت false هم همچنان مقداری را دریافت خواهیم کرد و این مقدار undefined است (اما پیام خطای عدم دسترسی به x را دریافت نمیکنیم).
به این رفتار اصطلاحا hoisting میگویند. در این حالت موتور جاوا اسکریپت، تمام متغیرهای تعریف شدهی توسط var را به صورت ضمنی به ابتدای تعریف متد منتقل کرده و آنها را در آنجا تعریف میکند. به همین جهت است که return x تعریف شدهی در انتهای متد، قابلیت دسترسی به x داخل بدنهی if را دارد.
در ES 6 برای رفع این مشکل، واژهی کلیدی جدیدی به نام let معرفی شدهاست و هدف آن مهیا کردن block scoping تعریف متغیرها است:
var doWork = function(flag){ if(flag){ let x = 3; } return x; };
بله. همانطور که مشاهده میکنید، اینبار میدان دید x به if block تعریف شدهی در آن محدود گشته و دیگر خارج از آن مفهومی ندارد و تعریف نشدهاست. به همین جهت زمانیکه به return x میرسیم، پیام تعریف نشده بودن x را دریافت خواهیم کرد. برای اینکه قطعه کد فوق کار کند، نیاز است return x را به داخل بدنهی قطعهی if تعریف شده، انتقال داد.
این block scoping مهیا شدهی توسط let، با حلقهی for نیز کار میکند:
var doWork = function(){ for(let i = 0; i< 10; i++){ } /* return i won't work */ return 0; };
یک نکته
مفهوم block scoping با تعریف {} معنا پیدا میکند. بنابراین میتوانید یک قطعهی دلخواه را با تعریف {} نیز مشخص کنید:
و یا در مثال ذیل چندین قطعهی تو در تو را مشاهده میکنید:
let outer = 'I am so eccentric!' { let inner = 'I play with neighbors in my block and the sewers' { let innermost = 'I only play with neighbors in my block' } // accessing innermost here would throw } // accessing inner here would throw // accessing innermost here would throw
نمونهی دیگر آن تعریف یک متد داخل یک بلاک است:
{ let _nested = 'secret' function nested () { return _nested } } console.log(nested())
در ES 6 نمیتوان به متغیرهای تعریف شدهی توسط let داخل یک بلاک، در خارج از آن دسترسی یافت. اگر میخواهید سطح دسترسی به متد را افزایش دهید، نیاز است به شکل ذیل عمل کنید و متد را خارج از بدنهی بلاک با سطح دسترسی بیشتری تعریف نمائید:
var nested; { let _nested = 'secret' nested = function () { return _nested } } console.log(nested()) // <- 'secret'
واژهی کلیدی const
در ES 6 برای ایجاد و مقدار دهی متغیرهای فقط خواندنی، واژهی کلیدی const افزوده شدهاست. در اینجا const نیز مانند let دارای block scoping است.
doWork = function() { const value = 10; value = 11; return value; }
در ES 6، انتساب یک مقدار به یک const، پس از تعریف آن، منجر به بروز خطای syntax error خواهد شد. همچنین تعریف مجدد آن نیز چنین خطایی را سبب خواهد شد.
یک نکته
هر چند const سبب read only شدن یک متغیر میشود، اما آنرا immutable نمیکند:
const items = { people: ['you', 'me'] } items.people.push('test') console.log(items)
همانطور که مشاهده میکنید، هنوز هم میتوان به شیء تعریف شده، آیتمی را اضافه کرد (در اینجا test به آرایهی people اضافه شدهاست).
آشنایی با مفهوم shadowing
همان مثال ابتدای بحث را در نظر بگیرید:
var doWork = function(flag){ if(flag){ let x = 10; var x = 3; return x; } };
let x = 10; var doWork = function(flag){ if(flag){ var x = 3; return x; } };
مثال ذکر شده، با مثال ذیل که یک بلاک را توسط {} ایجاد کردهایم، یکی است:
let x = 10; { let x = 3; console.log(x); } console.log(x);
در اینجا نیز ابتدا مقدار 3 که مرتبط با بلاک داخلی است چاپ خواهد شد و سپس مقدار 10 که مرتبط است به بلاک خارجیتر.
ایجاد گزارش با داده های ثابت و متغیر
تمام بازخوردها رو مطالعه کردم اما با نیاز من پیدا نشد. همانطور که میبینید ستون موضوع رو باید به صورت دستی وارد کنم و در واقع من فقط مقادیر رو در دیتابیس ذخیره کردم. برای مثال کلمهی فروش بهداشتی مثل نام کالا نیست که در دیتابیس ذخیره شده باشه و بخوام اینجا نمایشش بدم .
تنها مقادیری که در دیتابیس ذخیره شده اند ستون مبلغ است و تمام . بقیه موارد از قبیل هدر و ستون موضوع را باید به صورت ثابت در گزارش بنویسم.
آیا امکان ایجاد این چنین گزارشی وجود دارد؟
اگر وجود دارد ممنون میشوم راهنمایی کنید.
- ایجاد یک پروژهی جدید ASP.NET Core در VS 2017
- تنظیمات یک برنامهی ASP.NET Core خالی برای اجرای یک برنامهی Angular CLI
- تنظیمات فایل آغازین یک برنامهی ASP.NET Core جهت ارائهی برنامههای Angular
- ایجاد ساختار اولیهی برنامهی Angular CLI در داخل پروژهی جاری: این مورد را تاکنون انجام دادهایم و تکمیل کردهایم. بنابراین تنها کاری که نیاز است انجام شود، cut و paste محتوای پوشهی angular-template-driven-forms-lab (پروژهی این سری) به ریشهی پروژهی ASP.NET Core است.
- تنظیم محل خروجی نهایی Angular CLI به پوشهی wwwroot
- روش اول و یا دوم اجرای برنامههای مبتنی بر ASP.NET Core و Angular CLI
البته سورس کامل تمام این تنظیمات را از انتهای بحث نیز میتوانید دریافت کنید.
ضمن اینکه هیچ نیازی هم به استفاده از VS 2017 نیست و هر دوی برنامهی Angular و ASP.NET Core را میتوان توسط VSCode به خوبی مدیریت و اجرا کرد.
ایجاد ساختار مقدماتی سرویس ارسال اطلاعات به سرور
در برنامههای Angular مرسوم است جهت کاهش مسئولیتهای یک کلاس و امکان استفادهی مجدد از کدها، منطق ارسال اطلاعات به سرور، به درون کلاس یک سرویس منتقل شود و سپس این سرویس به کلاسهای کامپوننتها، برای مثال یک فرم ثبت اطلاعات، برای ارسال و یا دریافت اطلاعات، تزریق گردد. به همین جهت، ابتدا ساختار ابتدایی این سرویس و تنظیمات مرتبط با آنرا انجام میدهیم.
ابتدا از طریق خط فرمان به پوشهی ریشهی برنامه وارد شده (جائیکه فایل Startup.cs قرار دارد) و سپس دستور ذیل را اجرا میکنیم:
>ng g s employee/FormPoster -m employee.module
installing service create src\app\employee\form-poster.service.spec.ts create src\app\employee\form-poster.service.ts update src\app\employee\employee.module.ts
ساختار ابتدایی این سرویس را نیز به نحو ذیل تغییر میدهیم:
import { Injectable } from '@angular/core'; import { Http } from '@angular/http'; import { Employee } from './employee'; @Injectable() export class FormPosterService { constructor(private http:Http) { } postEmployeeForm(employee: Employee) { } }
چون این کلاس از ماژول توکار Http استفاده میکند، نیاز است این ماژول را نیز به قسمت imports فایل src\app\app.module.ts اضافه کنیم:
import { HttpModule } from "@angular/http"; @NgModule({ imports: [ BrowserModule, FormsModule, HttpModule, EmployeeModule, AppRoutingModule ]
import { FormPosterService } from "../form-poster.service"; export class EmployeeRegisterComponent implements OnInit { constructor(private formPoster: FormPosterService) {} }
در ادامه برای آزمایش برنامه، به ریشهی پروژه وارد شده و دو پنجرهی کنسول مجزا را باز کنید. در اولی، دستورات:
>npm install >ng build --watch
>dotnet restore >dotnet watch run
به همین جهت برای آزمایش ابتدایی آن، آدرس http://localhost:5000 را در مرورگر باز کنید. برگهی developer tools مرورگر را نیز بررسی کنید تا خطایی در آن ظاهر نشده باشد. برای مثال اگر فراموش کرده باشید تا HttpModule را به app.module اضافه کنید، خطای no provider for HttpModule را مشاهده خواهید کرد.
مدیریت رخداد submit فرم در Angular
تا اینجا کار برپایی تنظیمات اولیهی کار با سرویس Http را انجام دادیم. مرحلهی بعد مدیریت رخداد submit فرم است. به همین جهت فایل src\app\employee\employee-register\employee-register.component.html را گشوده و سپس رخدادگردان submit را به فرم آن اضافه کنید:
<form #form="ngForm" (submit)="submitForm(form)" novalidate>
export class EmployeeRegisterComponent implements OnInit { submitForm(form: NgForm) { console.log(this.model); console.log(form.value); } }
در همین حال اگر بر روی دکمهی ok کلیک کنیم، چنین خروجی را در کنسول developer مروگر میتوان مشاهده کرد:
اولین مورد، محتوای this.model است و دومی محتوای form.value را گزارش کردهاست. همانطور که مشاهده میکنید، مقدار form.value بسیار شبیه است به وهلهای از مدلی که در سطح کلاس تعریف کردهایم و این مقدار همواره توسط Angular نگهداری و مدیریت میشود. بنابراین حتما الزامی نیست تا مدلی را جهت کار با فرمهای مبتنی بر قالبها به صورت جداگانهای تهیه کرد. توسط شیء form نیز میتوان به تمام اطلاعات فیلدها دسترسی یافت.
تکمیل سرویس ارسال اطلاعات به سرور
در ادامه میخواهیم اطلاعات مدل فرم را به سرور ارسال کنیم. برای این منظور سرویس FormPoster را به صورت ذیل تکمیل میکنیم:
import { Injectable } from "@angular/core"; import { Http, Response, Headers, RequestOptions } from "@angular/http"; import { Observable } from "rxjs/Observable"; import "rxjs/add/operator/do"; import "rxjs/add/operator/catch"; import "rxjs/add/observable/throw"; import "rxjs/add/operator/map"; import "rxjs/add/observable/of"; import { Employee } from "./employee"; @Injectable() export class FormPosterService { private baseUrl = "api/employee"; constructor(private http: Http) {} private extractData(res: Response) { const body = res.json(); return body.fields || {}; } private handleError(error: Response): Observable<any> { console.error("observable error: ", error); return Observable.throw(error.statusText); } postEmployeeForm(employee: Employee): Observable<Employee> { const body = JSON.stringify(employee); const headers = new Headers({ "Content-Type": "application/json" }); const options = new RequestOptions({ headers: headers }); return this.http .post(this.baseUrl, body, options) .map(this.extractData) .catch(this.handleError); } }
در متد postEmployeeForm، ابتدا توسط JSON.stringify محتوای شیء کارمند encode میشود. البته متد post اینکار را به صورت توکار نیز میتواند مدیریت کند. سپس ذکر هدر مناسب در اینجا الزامی است تا در سمت سرور بتوانیم اطلاعات دریافتی را به شیء متناظری نگاشت کنیم. در غیراینصورت model binder سمت سرور نمیداند که چه نوع فرمتی را دریافت کردهاست و چه نوع decoding را باید انجام دهد.
در قسمت map، کار بررسی اطلاعات دریافتی از سرور را انجام خواهیم داد و اگر در این بین خطایی وجود داشت، توسط متد handleError در کنسول developer مرورگر نمایش داده میشود.
خروجی متد postEmployeeForm یک Observable است. بنابراین تا زمانیکه یک subscriber نداشته باشد، اجرا نخواهد شد. به همین جهت به کلاس EmployeeRegisterComponent مراجعه کرده و متد submitForm را به نحو ذیل تکمیل میکنیم:
submitForm(form: NgForm) { console.log(this.model); console.log(form.value); // validate form this.validatePrimaryLanguage(this.model.primaryLanguage); if (this.hasPrimaryLanguageError) { return; } this.formPoster .postEmployeeForm(this.model) .subscribe( data => console.log("success: ", data), err => console.log("error: ", err) ); }
یک نکته: اگر علاقمند باشید تا ساختار واقعی شیء NgForm را مشاهده کنید، در ابتدای متد فوق، console.log(form.form) را فراخوانی کنید و سپس شیء حاصل را در کنسول developer مرورگر بررسی نمائید.
تکمیل Web API برنامهی ASP.NET Core جهت دریافت اطلاعات از کلاینتها
در ابتدای سرویس formPoster، یک چنین تعریفی را داریم:
export class FormPosterService { private baseUrl = "api/employee";
ابتدا مدل زیر را به پروژهی ASP.NET Core جاری، معادل نمونهی تایپاسکریپتی سمت کلاینت آن اضافه میکنیم. البته در اینجا یک Id نیز اضافه شدهاست:
namespace AngularTemplateDrivenFormsLab.Models { public class Employee { public int Id { set; get; } public string FirstName { get; set; } public string LastName { get; set; } public bool IsFullTime { get; set; } public string PaymentType { get; set; } public string PrimaryLanguage { get; set; } } }
سپس کنترلر جدید EmployeeController را با محتوای ذیل اضافه خواهیم کرد:
using Microsoft.AspNetCore.Mvc; using AngularTemplateDrivenFormsLab.Models; namespace AngularTemplateDrivenFormsLab.Controllers { [Route("api/[controller]")] public class EmployeeController : Controller { public IActionResult Post([FromBody] Employee model) { //todo: save model model.Id = 100; return Created("", new { fields = model }); } } }
در اینجا پس از ثبت فرضی مدل، Id آن به همراه اطلاعات مدل، به نحوی که ملاحظه میکنید، بازگشت داده شدهاست. این نوع خروجی، یک چنین JSON ایی را تولید میکند:
{"fields":{"id":100,"firstName":"Vahid","lastName":"N","isFullTime":true,"paymentType":"FullTime","primaryLanguage":"Persian"}}
private extractData(res: Response) { const body = res.json(); return body.fields || {}; }
نمایش success به همراه شیءایی که از سمت سرور دریافت شدهاست؛ که حاصل اجرای سطر ذیل در متد submitForm است:
data => console.log("success: ", data)
بارگذاری اطلاعات drop down از سرور
تا اینجا اطلاعات drop down نمایش داده شده از یک آرایهی مشخص سمت کلاینت تامین شدند. در ادامه قصد داریم تا آنها را از سرور دریافت کنیم. به همین جهت اکشن متد ذیل را به کنترلر سمت سرور برنامه اضافه کنید:
[HttpGet("/api/[controller]/[action]")] public IActionResult Languages() { string[] languages = { "Persian", "English", "Spanish", "Other" }; return Ok(languages); }
پس از آن در سمت کلاینت این تغییرات نیاز هستند:
ابتدا به سرویس FormPosterService دو متد ذیل را اضافه میکنیم که کار آنها دریافت و پردازش اطلاعات از api/employee/languages سمت سرور هستند:
private extractLanguages(res: Response) { const body = res.json(); return body || {}; } getLanguages(): Observable<any> { return this.http .get(`${this.baseUrl}/languages`) .map(this.extractLanguages) .catch(this.handleError); }
پس از آن دو تغییر ذیل را نیاز است به EmployeeRegisterComponent اعمال کنیم:
languages = []; ngOnInit() { this.formPoster .getLanguages() .subscribe( data => this.languages = data, err => console.log("get error: ", err) ); }
مشکل! ممکن است مدت زمانی طول بکشد تا این اطلاعات از سمت سرور دریافت شوند. در این حالت میتوان به شکل زیر در فایل employee-register.component.html فرم را تا زمان پر شدن دراپ داون آن مخفی کرد:
<h3 *ngIf="languages.length == 0">Loading...</h3> <div class="container" *ngIf="languages.length > 0">
کدهای کامل این قسمت را از اینجا میتوانید دریافت کنید: angular-template-driven-forms-lab-05.zip
برای اجرای آن فرض بر این است که پیشتر Angular CLI را نصب کردهاید. سپس به ریشهی پروژه وارد شده و دو پنجرهی کنسول مجزا را باز کنید. در اولی دستورات:
>npm install >ng build --watch
>dotnet restore >dotnet watch run
خواندنیهای 16 اردیبهشت
به خاطر کتاب خوبتون در رابطه با asp ممنونم
شرمنده از اینکه مزاحم شدم . یه مشکلی پیدا کردم که نمی تونم حلش کنم و هرچقدر هم سرچ کردم و توی دولوپرسنتر هم گذاشتم جوابمو پیدا نکردم برای همین هم مزاحم شما شدم
اگه لطف کنین و راهنمایی ام کنین ممنون میشم
من دارم یه فروشگاه الکترونیکی مینویسم و این فروشگاه دیتابیسی با جدول های زیر داره:
user
product
category
order
و در قسمت user علاوه بر مشخصات عمومی کاربر، username , pass , email شخص هم هست
از طرف دیگه میخوام از form_base security که توسط ویژوال استودیو فراهم میشه (asp.net configuration موجود در سلوشن اکسپلورر) استفاده کنم و این خودش باعث ایجاد یه دیتابیس دیگه میشه و برنامه من در حقیقت دارای دو تا دیتابیس میشه
حالا من باید چیکار کنم؟
منظورم اینه که pass , email در جدول مربوط به دیتابیس اصلی آیا باید حذف بشه و از اونایی که در دیتابیس دوم هست استفاده کنم و فیلد مربوط به username باید در هر دوتا باقی بمونه و با مقایسه اونا با هم متوجه بشم که کدام پسورد و ایمیل مال کدوم کاربره؟
یا فقط از دیتابیس دوم استفاده کنم و چهار تا جدول مربوط به دیتابیس اصلی را هم در همین دیتابیس دومی ادغام کنم(اگه بشه)؟
ممنون
آموزش LINQ بخش سوم
• علملگرهای LINQ
• کلمات کلیدی Keyword
• متغیرهای Range
Range Variable : متغیر تعریف شدهی در یک محدوده خاص.
عبارت پرس و جوی زیر را در نظر بگیرد:
var query = from word in list where word.StartsWith("a") select word;
شناسهی list یک متغیر محلی است و تنها موردی که باقی میماند شناسهی word است که به آن متغیر Range میگوییم. متغیرهای Range همانند متغیرهای مرسوم مورد استفادهی در برنامهها هستند که بصورت فقط خواندنی مهیا شده اند. با این اوصاف متغیرهای Range در ابتدا کمی عجیب به نظر میرسند. به این علت که در وسط عبارت پرس و جو معرفی میشوند و نیازی به تعریف شدن به روش مرسوم به شکل زیر را ندارند:
String word;
متغیر word در دو حالت ممکن است قابل دسترس نباشد :
- پایان پرس و جو
- مواجه شدن با کلمه کلیدی into . این کلمهی کلیدی برای اتصال دو Query استفاده میشود.
نکته:در بعضی مواقع باید وضعیت متغیر Range را صریحا مشخص کنیم؛ بطور مثال کد زیر با خطا مواجه خواهد شد:
object[] ints = new object[] { 1, 2, 3 }; var query = from num in ints where num < 3 select num;
var query = from int num in ints where num < 3 select num;
تذکر : بهتر است از تعریف صریح متغیر Range پرهیز کنیم؛ مگر در شرایطی مثل کد بالا .
قطعه کد زیر بهراحتی کامپایل میشود و نیازی به اعلان صریح نوع متغیر range نیست. زیر از طریق مکانیزیم Type Inference نوع متغیر مشخص شده است.
List<string> list = new List<string> {"LINQ","Query","adventure"}; var query = from string word in list where word.Contains("r") orderby word ascending select word;
string word="test"; List<string> list = new List<string> {"LINQ","Query","adventure"}; var query = from string word in list where word.Contains("r") orderby word ascending select word;
تا اینجا از طریق کلمهی کلیدی from، متغیری را تعریف کردیم. با استفاده از کلمات کلیدی let ،into و join نیز میتوان متغیرهای Range تعریف کرد.
عبارت let
کلمهی کلیدی let این امکان را فراهم میکند تا یک متغیر Range جدید را ایجاد کرده و در عبارتهای بعدی از آن استفاده کنیم. در کد زیر از طریق کلمهی کلیدی let، یک متغیر Range جدید را بنام IsDairy تعریف میکنیم که از نوع bool میباشد:
Ingredient[] ingredients = { new Ingredient {Name = "Sugar", Calories = 500}, new Ingredient {Name = "Egg", Calories = 100}, new Ingredient {Name = "Milk", Calories = 150}, new Ingredient {Name = "Flour", Calories = 50}, new Ingredient {Name = "Butter", Calories = 200} }; IEnumerable<Ingredient> highCalDairyQuery = from i in ingredients let isDairy = i.Name == "Milk" || i.Name == "Butter" where i.Calories >= 150 && isDairy select i; foreach (var ingredient in highCalDairyQuery) { Console.WriteLine(ingredient.Name); }
در کد زیر قصد داریم عملیاتهای زیر را بر روی توالی ورودی اعمال کنیم:
1- جدا کردن عناصر توالی ورودی بر اساس جدا کنندهی " ,"
2- تبدیل همهی حروف عناصر توالی ایجاد شده به حروف بزرگ
3- جدا کردن عناصر توالی حاصل از مرحلهی 2، به شرط برابر بودن با MILK,BUTTER,CHEESE
4- نمایش توالی ایجاد شده
string[] csvRecipes = { "milk,sugar,eggs", "flour,BUTTER,eggs", "vanilla,ChEEsE,oats" }; var dairyQuery = from csvRecipe in csvRecipes let ingredients = csvRecipe.Split(',') from ingredient in ingredients let uppercaseIngredient = ingredient.ToUpper() where uppercaseIngredient == "MILK" || uppercaseIngredient == "BUTTER" || uppercaseIngredient == "CHEESE" select uppercaseIngredient; foreach (var dairyIngredient in dairyQuery) { Console.WriteLine($"{dairyIngredient} is dairy"); }
عبارت Into
متغیر جدیدی که توسط این دستور ایجاد میشود، میتواند نتیجهی حاصل از دستور Select را در خود ذخیره کند. در کد زیر یک نوع بینام ایجاد کرده و در ادامهی پرس و جو از آن استفاده میکنیم:
Ingredient[] ingredients = { new Ingredient {Name = "Sugar", Calories = 500}, new Ingredient {Name = "Egg", Calories = 100}, new Ingredient {Name = "Milk", Calories = 150}, new Ingredient {Name = "Flour", Calories = 50}, new Ingredient {Name = "Butter", Calories = 200} }; IEnumerable<Ingredient> highCalDairyQuery = from i in ingredients select new //نوع بی نام { OriginalIngredient = i, IsDairy = i.Name == "Milk" || i.Name == "Butter", IsHighCalorie = i.Calories >= 150 } into temp where temp.IsDairy && temp.IsHighCalorie select temp.OriginalIngredient; foreach (var ingredient in highCalDairyQuery) { Console.WriteLine(ingredient.Name); }
در ادامهی این سری آموزشی، به بررسی عبارت join میپردازیم.