در Angular روشهای متفاوتی برای انتقال اطلاعات از یک کامپوننت به کامپوننت دیگه وجود داره که انتخاب هر کدوم بستگی به نیاز ما و موقعیت اون دو تا کامپوننتها نسبت به همدیگه داره.
الف) وقتی کامپوننتها در وضعیت «والد/فرزند» هستند
@Component({ selector: 'app-child', template: ` <p> {{ username }}'s code is: {{ userCode }}<p> <p> <button (click)="sendMessage('Hello from {{ username }}')"> Send Message </button> </p> ` }) export class ChildComponent { @Input() userCode: number; @Input('name') username: string; @Output() sendInfo = new EventEmitter<string>(); sendMessage(message: string) { this.sendInfo.emit(message); } }
در قطعه کد بالا، کامپوننت ChildComponnet تعریف شده و دارای دو ویژگی userCode و username هستش که قبل از هر دوی اونها Input@ قرار دادیم تا مشخص کنه انتظار داریم مقدارشون از بیرون از این کامپوننت وارد بشه. یه نکتهی ریز هم در این کد استفاده از Input('name')@ هستش و معنیش اینه که کامپوننتهای دیگه این ویژگی رو با نام name شناسایی و مقداردهی خواهند کرد (همونطور که در کد پایین این کار انجام شده)، هر چند که در داخل خود این کامپوننت از username برای ارجاع بهش استفاده میشه.
در قطعه کد پایین، کامپوننت ParentComponent تعریف شده که در بخش template اش با استفاده از تگ <app-child>، کامپوننتِ قطعه کد قبلی رو در یک حلقهی for فراخوانی کرده. در اینجا نحوهی انتقال داده به ChildComponent با استفاده از [userCode] و [name] واضح و روشنه.
@Component({ selector: 'app-parent', template: ` <app-child *ngFor="let user of users" [userCode]="user.code" [name]="user.username" (sendInfo)="onGetInfo($event)"> </app-child> ` }) export class ParentComponent { users = Users; userMessage: string; onGetInfo(message: string) { this.userMessage = message; } }
اما اگه بخوایم برعکس عمل کنیم چطور؟ یعنی داده رو از ChildComponent به ParentComponent منتقل کنیم. تو ChildComponent داریم:
@Output() sendInfo = new EventEmitter<string>();
ChildComponent هر جا که لازم بود از طریق این EventEmitter میتونه یه رویداد یا event تعریف شده تو کامپوننت والد خودش رو فراخوانی کنه و دادهها رو به شکل پارامتر به اون بفرسته. همونطور که در عبارت <EventEmitter<string مشخص شده، یه پارامتر از نوع string ارسال میکنه. در اینجا این پارامتر، message هستش و همون دادهایه که میخوایم منتقلش کنیم:
sendMessage(message: string) { this.sendInfo.emit(message); }
این پارامترها از هر نوع و به هر تعداد میتونن باشن. حالا برای اینکه تو کامپوننت والد این مقدار دریافت بشه، تو تمپلیت ParentChild داریم:
(sendInfo)="onGetInfo($event)"
که یعنی هر موقع sendInfo تو ChildComponent رو emit کردن، ()onGetInfo توی ParentChild فراخوانی بشه و پارامتر هم از طریق event$ ارسال بشه:
onGetInfo(message: string) { this.userMessage = message; }
در حالت خیلی کلی میشه گفت در این روش، از Input@ برای ورود داده و از Output@ برای خروج داده استفاده میشه.
ب) وقتی کامپوننتها مستقل از هم هستند
تو این حالت هر کامپوننت route خودش رو داره. وقتی میخوایم یک یا چند پارامتر رو منتقل کنیم، خیلی راحت میشه از queryها در route استفاده کرد. مثلا اگه بخوایم دو تا پارامتر به نامهای id و category رو از ProducstListComponent به ProductDetailComponent ارسال کنیم:
this.router.navigate( ['/product-detail'], { queryParams: { id: '3', category = 'mobile'} } );
و اگه بخوایم تو ProductDetailComponent این پارامترها رو دریافت کنیم:
productId: number; productCategory: string; ngOnInit() { this.route.queryParams.subscribe(params => { this.productId= +params['id']; this.productCategory = params['category']; }); }
اما گاهی نیاز داریم که تعداد زیادی پارامتر یا حتی مثلا لیستی از اشیا رو منتقل کنیم به کامپوننت بعدی. در این حالت استفاده از query ها نه راحت هستش نه منطقی. در این حالت میتونیم از ویژگی state استفاده کنیم که از نسخهی 7.2 به Angular اضافه شده. برای این کار در ProductsListComponent برای انتقال لیست sameProductsList خواهیم داشت:
this.router.navigate( ['/product-detail'], {state: {sameProds: sameProductsList}} );
سپس برای دریافت این لیست در ProductDetailComponent:
sameProducts: any[]; ngOnInit() { const currentNavigationState = this.router.getCurrentNavigation().extras.state; if (currentNavigationState) { this.sameProducts = currentNavigationState.sameProds; } }