ارسال ایمیل در ASP.NET Core
using (var client = new SmtpClient ()) { client.ServerCertificateValidationCallback = (s,c,h,e) => true; client.CheckCertificateRevocation = false; // ... }
نکته : آشنایی با مفاهیم اولیه WCF برای درک بهتر مطالب الزامی است.
در ابتدا لازم است تا مدل برنامه را تعریف کنیم. ابتدا یک پروژه از نوع WCF Service Application ایجاد کنید و مدل زیر را بسازید.
#Employee
[DataContract] public class Employee { [DataMember] public string Name { get; set; } [DataMember] public Employee Manager { get; set; } }
[DataContract] public class Department { [DataMember] public string DeptName { get; set; } [DataMember] public List<Employee> Staff { get; set; } }
#Contract
[ServiceContract] public interface IDepartmentService { [OperationContract] Department GetOneDepartment(); }
public class DepartmentService : IDepartmentService { public Department GetOneDepartment() { List<Employee> listOfEmployees = new List<Employee>(); var masoud = new Employee() { Name = "Masoud" }; var saeed = new Employee() { Name = "Saeed", Manager = masoud }; var peyman = new Employee() { Name = "Peyman", Manager = saeed }; var mostafa = new Employee() { Name = "Mostafa", Manager = saeed }; return new Department() { DeptName = "IT", Staff = new List<Employee>() { masoud, saeed, peyman, mostafa } }; } }
class Program { static void Main( string[] args ) { DepartmentServiceClient client = new DepartmentServiceClient(); var result = client.GetOneDepartment(); WriteDataToFile( result ); Console.ReadKey(); } private static void WriteDataToFile( Department data ) { DataContractSerializer dcs = new DataContractSerializer( typeof( Department ) ); var ms = new MemoryStream(); dcs.WriteObject( ms, data ); ms.Seek( 0, SeekOrigin.Begin ); var sr = new StreamReader( ms ); var xml = sr.ReadToEnd(); string filePath = @"d:\\data.xml"; if ( !File.Exists( filePath ) ) { File.Create( filePath ); } using ( TextWriter writer = new StreamWriter( filePath ) ) { writer.Write( xml ); } }
<Department xmlns="http://schemas.datacontract.org/2004/07/Service" xmlns:i="http://www.w3.org/2001/XMLSchema-instance"> <Name>IT</Name> <Staff> <Employee> <Manager i:nil="true"/> <Name>Masoud</Name> </Employee> <Employee> <Manager> <Manager i:nil="true"/> <Name>Masoud</Name> </Manager> <Name>Saeed</Name> </Employee> <Employee> <Manager> <Manager> <Manager i:nil="true"/> <Name>Masoud</Name> </Manager> <Name>Saeed</Name> </Manager> <Name>Peyman</Name> </Employee> <Employee> <Manager> <Manager> <Manager i:nil="true"/> <Name>Masoud</Name> </Manager> <Name>Saeed</Name> </Manager> <Name>Mostafa</Name> </Employee> </Staff> </Department>
[DataContract( IsReference = true )] public class Employee { [DataMember] public string Name { get; set; } [DataMember] public Employee Manager { get; set; } }
<Department xmlns="http://schemas.datacontract.org/2004/07/Service" xmlns:i="http://www.w3.org/2001/XMLSchema-instance"> <Name>IT</Name> <Staff> <Employee z:Id="i1" xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/"> <Manager i:nil="true"/> <Name>Masoud</Name> </Employee> <Employee z:Id="i2" xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/"> <Manager z:Ref="i1"/> <Name>Saeed</Name> </Employee> <Employee z:Id="i3" xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/"> <Manager z:Ref="i2"/> <Name>Peyman</Name> </Employee> <Employee z:Id="i4" xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/"> <Manager z:Ref="i2"/> <Name>Mostafa</Name> </Employee> </Staff> </Department>
مزایا :استفاده از این روش در هنگام عمل سریالایز دادههای زیاد و زمانی که تعداد Objectهای موجود در ObjectGraph زیاد باشد باعث افزایش کارایی و سرعت انجام عملیات سریالایز میشود.
constructor() { this.updateStatusOnPageRefresh(); }
خواندنیهای 7 خرداد
Pipe یک متغیر یا عبارت را به عنوان ورودی دریافت کرده و آنرا به شکل دیگری برای نمایش تغییر میدهد. Pipeها معمولا در صفحات HTML مورد استفاده قرار میگیرند. با استفاده از عملگر Pipe (|) به شکل زیر میتوانید Pipe مورد نظر خود را اعمال کنید.
import { Component } from '@angular/core'; @Component({ selector: 'hero-birthday', template: `<p>The hero's birthday is {{ birthday | date }}</p>` }) export class HeroBirthdayComponent { birthday = new Date(1988, 3, 15); // April 15, 1988 }
در این مثال Pipe از قبل ساخته شده date را بر روی متغییر birthday اعمال میکنیم. خروجی کار به شکل زیر خواهد بود.
The hero's birthday is April 15, 1988
لازم به ذکر است Pipe هیچگونه اثری بر روی متغییر birthday نداشته و فقط نحوه نمایش آن را تغییر میدهد.
Pipeهای از پیش ساخته شده
در انگیولار ۲ یکسری Pipe از پیش ساخته شده مانند DatePipe، UpperCasePipe، LowerCasePipe، CurrencyPipe و PercentPipe وجود دارند که شما در تمامی صفحات HTML میتوانید بدون هیچ تنظیم اضافهای از آنها استفاده کنید. لیست Pipeهای از پیش ساخته شده را اینجا مشاهده کنید.
ارسال پارامتر به Pipe
<p>The hero's birthday is {{ birthday | date:"MM/dd/yy" }} </p>
مقدار پارامتر، هر نوع مجازی از Template expressions میتواند باشد (از جمله عبارت رشتهای یا حتی یک خصوصیت از کامپوننت). بنابراین در سرتاسر برنامه و در هر زمان میتوانید با تغییر پارامتر در لحظه، خروجی مدنظر خود را به کاربرنهایی نمایش دهید.
Template expressions: عباراتی (expressions) که بعد از اجرا توسط انگیولار، تبدیل به یک مقدار جهت نمایش در HTML، کامپوننت یا دایرکتیو میشود.
استفاده زنجیرهای از Pipeها
برای اعمال چند Pipe بر روی یک عبارت، میتوان از Pipeها به صورت پشت سر هم استفاده کرد. برای مثال در ادامه میخواهیم علاوه بر اعمال DatePipe با پارامتر fullDate جهت نمایش تاریخ به صورت Friday, April 15, 1988، حروف را نیز به صورت UpperCase نمایش دهیم. لازم به ذکر است برای نمایش حروف به صورت UpperCase از Pipe به همین نام استفاده میکنیم.
<p>The hero's birthday is {{ birthday | date:"fullDate" | uppercase}} </p>
خروجی کار به شکل زیر خواهد بود.
The hero's birthday is FRIDAY, APRIL 15, 1988
استفاده از Pipe در TypeScript
برای کسانیکه با انگیولار یک آشنایی دارند، Pipe در انگولار ۲ معادل filter در انگولار یک است. در انگیولار یک با تزریق سرویس filter$ به کنترلرها یا سرویسها، میتوانستیم filterهای تعریف شده شخصی و از پیش ساخته شده را جهت اعمال بر روی متغیرهای خود، استفاده کنیم. در انگیولار دو نیز این امکان فراهم شدهاست؛ ولی به سادگی تزریق filter$ نیست. یعنی لازم است علاوه بر تزریق Pipe به سرویس یا کامپوننتهای خود، Pipe مورد نظر خود را در لیست providers ماژول خود نیز اضافه کنید. برای نمونه اگر بخواهیم DatePipe را در component خود (نه در template) مورد استفاده قرار دهیم به شکل زیر عمل میکنیم:
import { Component } from '@angular/core'; // اضافه کردن DatePipe از @angular/common import { DatePipe } from '@angular/common'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent { title = 'app works!'; birthDay = new Date(1988, 3, 15); strBirthDay = ""; // تزریق DatePipe constructor(private datePipe: DatePipe) { this.strBirthDay = this.datePipe.transform(this.birthDay, 'yyyy-MM-dd'); } }
پس از وارد کردن DatePipe از angular/common@ به کامپوننت و تزریق آن از طریق سازنده کامپوننت، برای اعمال Pipe بر روی عبارت مورد نظر خود، از متد transform استفاده میکنیم.
import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; import { FormsModule } from '@angular/forms'; import { HttpModule } from '@angular/http'; import { DatePipe } from '@angular/common' import { AppComponent } from './app.component'; @NgModule({ declarations: [ AppComponent ], imports: [ BrowserModule, FormsModule, HttpModule ], // افزودن Pipe به Providers providers: [DatePipe], bootstrap: [AppComponent] }) export class AppModule { }
نکتهای در مورد DatePipe و CurrencyPipe
Pipeهای Date و Currency به دلیل استفاده از شی Intl در داخل خود نیاز به ECMAScript Internationalization API دارند. مرورگرهای قدیمی و همچنین مرورگر Safari به دلیل عدم پشتیبانی از این قضیه به هنگام استفاده از این Pipeها دچار مشکل میشوند. برای حل این مشکل کافی است اسکریپت زیر را به صفحه خود اضافه کنید.
<script src="https://cdn.polyfill.io/v2/polyfill.min.js?features=Intl.~locale.en"></script>
در بخشهای بعدی نحوه ساخت Pipeهای سفارشی و همچنین نکات تکمیلی در مورد Pipeها را بررسی خواهیم کرد.
- canActivate : جهت محافظت دسترسی به یک مسیر
- canActivateChild: برای محافظت دسترسی به یک Child Route
- canDeactivate : برای جلوگیری کردن از ترک مسیر جاری و هدایت به مسیری دیگر (برای مثال جهت نمایش پیام «هنوز اطلاع تغییر یافته را ذخیره نکردهاید»)
- canLoad : برای جلوگیری از مسیریابی غیرهمزمان (async routing) که در قسمت بعدی بررسی خواهد شد
- resolve: برای پیش واکشی اطلاعات، پیش از نمایش مسیر (که آنرا در قسمت چهارم این سری بررسی کردیم)
لزوم استفادهی از محافظهای مسیرها
گاهی از اوقات میخواهیم دسترسی به یک مسیر را محدود به کاربران وارد شدهی به سیستم کنیم و یا مسیرهایی را داشته باشیم که تنها توسط گروه خاصی از کاربران قابل دسترسی باشند. همچنین در بسیاری از اوقات نیاز است به کاربران اخطارهایی را پیش از ترک یک مسیر نمایش دهیم. برای مثال پیش از ترک صفحهی ویرایش اطلاعاتی که دارای اطلاعات ذخیره نشدهاست، بهتر است پیامی را جهت یادآوری این مساله نمایش دهیم. برای پیاده سازی هر کدام از این قابلیتها از یک محافظ مسیر ویژه استفاده میشود.
ترتیب اجرای محافظهای مسیرها
مسیریاب سیستم، ابتدا محافظ canDeactivate را اجرا میکند تا مشخص شود که آیا کاربر میتواند مسیر جاری را ترک کند یا خیر؟ سپس اگر مسیریابی تعریف شده غیرهمزمان باشد، محافظ canLoad اجرا میشود. پس از آن محافظ canActivateChild بررسی میشود. در ادامه محافظ canActivate اجرا میگردد. در پایان کار بررسی محافظهای موجود، کار بررسی محافظ resolve، جهت پیش واکشی اطلاعات مسیر درخواستی، انجام خواهد شد.
در اینجا اگر یکی از محافظها مقدار false را برگرداند، پردازش مابقی آنها لغو خواهد شد و کار هدایت کاربر به مسیر درخواستی، خاتمه مییابد.
مراحل ساخت و اعمال یک محافظ مسیر
ساخت و اعمال یک محافظ مسیر شامل سه مرحله است:
الف) یک محافظ مسیر عموما به صورت یک سرویس جدید پیاده سازی میشود:
import { Injectable } from '@angular/core'; import { CanActivate } from '@angular/router'; @Injectable() export class AuthGuard implements CanActivate { canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean { } }
ب) از آنجائیکه محافظها، سرویس هستند، نیاز است تعریف کلاس آنها را در قسمت providers ماژول مرتبط نیز ذکر کنیم تا در برنامه قابل دسترسی شوند. باید دقت داشت که برخلاف سایر سرویسها، امکان تعریف محافظها صرفا در سطح یک ماژول مسیر است و نه در سطح یک کامپوننت. به این ترتیب مسیریاب میتواند به آن، در طی هدایت کاربر به مسیر درخواستی، دسترسی پیدا کند.
ج) پس از آن برای فعالسازی یک محافظ مسیر، آنرا به عنوان یک خاصیت جدید، به تنظیمات مسیریابی اضافه خواهیم کرد. نام این خاصیت دقیقا مساوی با نوع محافظی است که تعریف شدهاست. برای مثال اگر محافظ تعریف شده از نوع CanActivate است، نام خاصیتی که ذکر خواهد شد، canActivate میباشد. مقدار آن نیز میتواند آرایهای از سرویسهایی از این نوع باشد.
امکان به اشتراک گذاشتن یک محافظ بین چندین مسیر نیز وجود دارد. فرض کنید میخواهیم تمام مسیرهای مربوط به محصولات را محافظت کنیم. در این حالت میتوان محافظ را به تک تک Child routes موجود اعمال کرد و یا میتوان محافظ را به والد آنها نیز اعمال کنیم تا به صورت خودکار سبب محافظت از فرزندان آن نیز شویم.
یک مثال: ساخت محافظ canActivate
جهت بررسی شرط یا شرایطی پیش از فعال سازی یک مسیر درخواستی، از محافظهایی از نوع canActivate میتوان استفاده کرد. این نوع محافظها عموما جهت اعتبارسنجی کاربران و محدود سازی دسترسی آنها به قسمتهای مختلف برنامه استفاده میشوند. این نوع محافظها حتی با تغییر پارامترهای مسیریابی نیز فعال شده و بررسی میشوند.
در ادامهی مثال این سری میخواهیم کاربران را پیش از دسترسی به قسمتهای مختلف مرتبط با محصولات، وادار به لاگین کنیم. برای این منظور دستور ذیل را اجرا کنید:
>ng g guard user/auth -m user/user.module
installing guard create src\app\user\auth.guard.spec.ts create src\app\user\auth.guard.ts update src\app\user\user.module.ts
در ادامه کدهای این محافظ را به صورت ذیل تکمیل کنید:
import { Injectable } from '@angular/core'; import { ActivatedRouteSnapshot, RouterStateSnapshot, CanActivate, Router } from '@angular/router'; import { AuthService } from './auth.service'; @Injectable() export class AuthGuard implements CanActivate { constructor(private authService: AuthService, private router: Router) { } canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean { return this.checkLoggedIn(state.url); } checkLoggedIn(url: string): boolean { if (this.authService.isLoggedIn()) { return true; } this.authService.redirectUrl = url; this.router.navigate(['/login']); return false; } }
export class AuthService { currentUser: IUser; redirectUrl: string;
توضیحات:
این سرویس چون از نوع CanActivate است، این اینترفیس را پیاده سازی کردهاست و همچنین متد canActivate آنرا نیز به همراه دارد:
export class AuthGuard implements CanActivate { canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
یک نکته: هرچند در اینجا میتوان به پارامتر id مسیر، مانند route.params['id'] در صورت نیاز دسترسی یافت، اما امکان دسترسی به اطلاعات از پیش واکشی شده مانند route.data['product'] وجود ندارد. علت آنرا نیز در قسمت «ترتیب اجرای محافظهای مسیرها» ابتدای بحث جاری، بررسی کردیم: محافظ resolve در انتهای کار پردازش تمام محافظهای موجود فراخوانی میشود.
در متد canActivate میخواهیم بررسی کنیم که آیا کاربر، لاگین کردهاست یا خیر؟ اگر بله، تنها کافی است true را بازگشت دهیم تا کار این محافظ پایان یابد. در غیراینصورت false را بازگشت داده و همچنین سبب هدایت کاربر به صفحهی لاگین میشویم.
به همین منظور سرویس AuthService را به سازندهی این کلاس تزریق کردهایم تا بتوانیم به متد isLoggedIn آن دسترسی پیدا کنیم (این سرویس را در قسمت دوم این سری تکمیل کردیم).
این متد نیز به صورت ذیل تعریف شدهاست:
isLoggedIn(): boolean { return !this.currentUser; }
در ادامه برای استفادهی از این محافظ مسیر، به فایل src\app\product\product-routing.module.ts مراجعه کرده و آنرا به نحو ذیل اعمال خواهیم کرد:
import { AuthGuard } from './../user/auth.guard'; const routes: Routes = [ { path: 'products', canActivate: [ AuthGuard ], children: [ ] } ];
اکنون برنامه را با دستور ng s -o ساخته و اجرا کنید. سپس بر روی لینک لیست محصولات و یا افزودن یک محصول جدید کلیک کنید. بلافاصله صفحهی لاگین را مشاهده خواهید کرد.
به خاطر سپاری و بازیابی مسیر درخواستی کاربر پس از لاگین
در اینجا اگر کاربر بر روی لینک افزودن یک محصول جدید کلیک کند، صفحهی لاگین را مشاهده خواهد کرد. اما پس از لاگین، همواره به مسیر لیست محصولات هدایت میشود و در این حالت مسیر درخواستی اولیه فراموش خواهد شد. برای رفع این مشکل نیاز است آدرس درخواستی کاربر را نیز ذخیره و بازیابی کرد. به همین جهت خاصیت this.authService.redirectUrl = url را در متد checkLoggedIn محافظ تعریف شده مقدار دهی کردیم. در اینجا از سرویس Auth، برای به اشتراک گذاری اطلاعات با محافظهای مسیر استفاده کردهایم. طول عمر یک سرویس، singleton است. بنابراین تنها یک وهله از آن در طول عمر برنامه وجود خواهد داشت. به این ترتیب با ذخیرهی اطلاعاتی در آن، این اطلاعات در تمام برنامه قابل دسترسی خواهد شد.
با توجه به این نکته، اکنون به فایل src\app\user\login\login.component.ts مراجعه کرده و قسمت this.router.navigate آنرا به صورت ذیل بهبود خواهیم بخشید:
if (this.authService.login(userName, password)) { if (this.authService.redirectUrl) { this.router.navigateByUrl(this.authService.redirectUrl); } else { this.router.navigate(['/products']); } }
در ادامه برای آزمایش آن، پس از اجرای برنامه، صفحهی افزودن یک محصول جدید را درخواست دهید. سپس لاگین کنید. اکنون مشاهده خواهید کرد که برنامه مسیر درخواستی پیش از لاگین را به خاطر سپردهاست.
بررسی محافظ canActivateChild
این محافظ نیز شبیه به محافظ canActivate است؛ با این تفاوت که تنها زمانی فعالسازی خواهد شد که فرزند یک مسیر قرار است نمایش داده شود و نه خود مسیر اصلی.
محافظ canActivateChild با تغییر قسمت child یک مسیر فعالسازی میشود؛ حتی اگر این تغییر در حد تغییر پارامترهای آن مسیر باشد. اما باید درنظر داشت که اگر تنها قسمت child یک مسیر تغییر کند، دیگر محافظ canActivate مجددا اجرا نخواهد شد.
یک مثال: اگر کاربر در حال مشاهدهی صفحهی لیست محصولات باشد و بر روی لینک مشاهدهی یک محصول کلیک کند، تنها قسمت child مسیر تغییر میکند. در این حالت canActivate مسیر اصلی دیگر اجرا نخواهد شد؛ اما تمام محافظهای canActivateChild مرتبط مجددا اجرا خواهند شد.
بررسی محافظ canDeactivate
محافظ canDeactivate پیش از ترک یک مسیر، فعالسازی و بررسی میشود. عموما از آن جهت بررسی وضعیت اطلاعات ذخیره نشده و اطلاع رسانی به کاربر، پیش از ترک مسیر جاری استفاده استفاده میگردد. این محافظ با هر تغییری در آدرس جاری مسیر، بررسی میشود. بدیهی است این تغییر صرفا درون یک برنامهی Angular معنا پیدا میکند و نه هدایت به سایتی دیگر.
در حال حاضر در مثال جاری این سری، اگر کاربر، تغییری را در صفحهی ویرایش اطلاعات ایجاد کند و بدون کلیک بر روی دکمهی Save به صفحهی دیگری مراجعه کند، این اطلاعات تغییر یافته، از دست خواهند رفت. برای رفع این مشکل میتوان محافظ canDeactivate ایی را برای آن طراحی کرد. به همین جهت دستور ذیل را اجرا کنید:
>ng g guard product/ProductEdit -m product/product.module
installing guard create src\app\product\product-edit.guard.spec.ts create src\app\product\product-edit.guard.ts update src\app\product\product.module.ts
امضای ابتدایی یک محافظ CanDeactivate به صورت ذیل است:
export class ProductEditGuard implements CanDeactivate<ProductEditComponent> { canDeactivate(component: ProductEditComponent): boolean {
اکنون این محافظ نیاز دارد تا بداند که آیا کامپوننت ویرایش محصولات، دارای اطلاعات ذخیره نشدهای هست یا خیر؟ چون کامپوننت ویرایش محصولات، به عنوان پارامتر به متد canDeactivate آن ارسال شدهاست، بنابراین میتواند به خواص و متدهای عمومی آن کلاس نیز دسترسی پیدا کند. به همین جهت تغییرات ذیل را به کامپوننت ویرایش محصولات در فایل src\app\product\product-edit\product-edit.component.ts اعمال میکنیم:
get product(): IProduct { return this.currentProduct; } set product(value: IProduct) { this.currentProduct = value; // Clone the object to retain a copy this.originalProduct = Object.assign({}, value); } get isDirty(): boolean { return JSON.stringify(this.originalProduct) !== JSON.stringify(this.currentProduct); }
برای اینکه این امر میسر شود، خاصیت product به حالت get/set دار تغییر یافتهاست تا بتوان کپی اولیهی محصول را جهت مقایسه، نگهداری کرد. استفاده از متد Object.assign سبب ایجاد یک کپی از شیء اولیه شده و به این صورت دو وهلهی غیرمشترک را خواهیم داشت. اگر value مستقیما به originalProduct انتساب داده میشد، در این حالت هر دوی currentProduct و originalProduct به یک شیء اشاره میکردند.
اکنون میتوان از این خاصیت جدید کامپوننت ویرایش محصولات، در محافظ ترک صفحهی آن استفاده کرد:
import { Injectable } from '@angular/core'; import { CanDeactivate } from '@angular/router'; import { ProductEditComponent } from './product-edit/product-edit.component'; @Injectable() export class ProductEditGuard implements CanDeactivate<ProductEditComponent> { canDeactivate(component: ProductEditComponent): boolean { if (component.isDirty) { let productName = component.product.productName || 'New Product'; return confirm(`Navigate away and lose all changes to ${productName}?`); } return true; } }
در آخر برای استفادهی از این محافظ جدید، باید آنرا به تنظیمات مسیریابی برنامه اضافه کنیم. به همین جهت به فایل src\app\product\product-routing.module.ts مراجعه کرده و این محافظ را به والد مسیریابی ویرایش یک محصول اضافه میکنیم:
import { ProductEditGuard } from './product-edit.guard'; const routes: Routes = [ { path: 'products', canActivate: [ AuthGuard ], children: [ { path: '', component: ProductListComponent }, { path: ':id', component: ProductDetailComponent, resolve: { product: ProductResolverService } }, { path: ':id/edit', component: ProductEditComponent, resolve: { product: ProductResolverService }, canDeactivate: [ ProductEditGuard ], children: [ { path: '', redirectTo: 'info', pathMatch: 'full' }, { path: 'info', component: ProductEditInfoComponent }, { path: 'tags', component: ProductEditTagsComponent } ] } ] } ];
برای آزمایش آن، به صفحهی ویرایش یکی از محصولات مراجعه کرده و تغییری را ایجاد کنید. سپس درخواست مشاهدهی صفحهی دیگری را با کلیک بر روی یکی از لینکهای منوی برنامه ارائه دهید. بلافاصله دیالوگ confirm ظاهر خواهد شد (تصویر فوق).
مشکل! در همین حالت بر روی دکمهی Ok کلیک کنید تا اطلاعات ذخیره نشده را از دست داده و به مسیر دیگری هدایت شویم. مجددا همین پروسه را تکرار کنید. اینبار اگر بر روی دکمهی Save کلیک کنید، باز هم دیالوگ confirm ظاهر میشود. علت اینجا است که شیء محصول اصلی و جاری، پس از ذخیره سازی به حالت اولیه بازگشت داده نشدهاند. برای این منظور متد reset را به کامپوننت ویرایش اطلاعات اضافه کرده:
reset(): void { this.dataIsValid = null; this.currentProduct = null; this.originalProduct = null; }
onSaveComplete(message?: string): void { if (message) { this.messageService.addMessage(message); } this.reset(); // Navigate back to the product list this.router.navigate(['/products']); }
کدهای کامل این قسمت را از اینجا میتوانید دریافت کنید: angular-routing-lab-08.zip
برای اجرای آن فرض بر این است که پیشتر Angular CLI را نصب کردهاید. سپس از طریق خط فرمان به ریشهی پروژه وارد شده و دستور npm install را صادر کنید تا وابستگیهای آن دریافت و نصب شوند. در آخر با اجرای دستور ng s -o برنامه ساخته شده و در مرورگر پیش فرض سیستم نمایش داده خواهد شد.
مزیت مدیریت وابستگیها
public class ShoppingCartController : Controller { public ActionResult GetCart() { //shopping cart service as a concrete dependency ShoppingCartService shoppingCartService = new ShoppingCartService(); ShoppingCart cart = shoppingCartService.GetContents(); return View("Cart", cart); } public ActionResult AddItemToCart(int itemId, int quantity) { //shopping cart service as a concrete dependency ShoppingCartService shoppingCartService = new ShoppingCartService(); ShoppingCart cart = shoppingCartService.AddItemToCart(itemId, quantity); return View("Cart", cart); } }
public class ShoppingCartController : Controller { private ShoppingCartService _shoppingCartService; public ShoppingCartController() { _shoppingCartService = new ShoppingCartService(); } public ActionResult GetCart() { //now using the shared instance of the shoppingCartService dependency ShoppingCart cart = _shoppingCartService.GetContents(); return View("Cart", cart); } public ActionResult AddItemToCart(int itemId, int quantity) { //now using the shared instance of the shoppingCartService dependency ShoppingCart cart = _shoppingCartService.AddItemToCart(itemId, quantity); return View("Cart", cart); } }