همانطور که در فصل های گذشته مشاهده کردید، به کامپوننتها به عنوان عضو اصلی انگولار نگاهی گذرا داشتیم و تا حدودی شما را با ساختار آن آشنا کردیم طبق وعدهای که خدمت شما عزیزان مطرح کرده بودیم، اینبار بیشتر در عمق فرو رفته و مطالب را در سطح پیشرفته تری دنبال میکنیم. با ما همراه باشید.
برای اینکه تمرینی برای شما عزیزان شده باشد با یک مثال شروع میکنیم تا مروری به مباحث قبل داشته باشیم.
در این مثال دو فرم در اختیار خواهیم داشت که با کلیک کردن روی هر یک از دکمههای این فرم میخواهیم یک سرور به صفحه اضافه شود.
مثلا اگر روی دکمهی «سرور داخلی» کلیک کردیم متن «سرور داخلی با موفقیت اضافه شد» چاپ شود و اگر روی دکمهی سرور خارجی کلیک کردیم نیز متن «سرور خارجی با موفقیت اضافه شد» نمایش داده شود.
بنابراین یک پروژهی جدید ایجاد کرده و سپس درون آن یک کامپوننت به نام servers با استفاده از دستور زیر تولید میکنیم:
ng g c servers
سپس برای رعایت ساختار تو در تو یک کامپوننت دیگر به نام server درون این کامپوننت به صورت زیر تعریف میکنیم:
ng g c servers/server
با اجرای این دستور یک پوشه به نام server درون این پوشهی servers ایجاد میشود.
حال درون فایل server.component.ts که به عنوان کنترلر برنامه به حساب میآید کدهای زیر را لحاظ میکنیم:
import {Component, OnInit} from '@angular/core'; @Component({ selector: 'app-server', templateUrl: './server.component.html', styleUrls: ['./server.component.css'] }) export class ServerComponent implements OnInit { serverName: string = ''; serverContent: string = ''; serverList = []; constructor() { } ngOnInit() { } onCreateLocalServer() { this.serverList.push({ name: this.serverName, content: this.serverContent }); } onCreateExternalServer() { this.serverList.push({ name: this.serverName, content: this.serverContent }); } }
با بررسی این فایل متوجه خواهید شد که دو متد به نامهای onCreateLocalServer و onCreateExternalServer جهت پاسخ به فشردن هر یک از دکمهها توسط کاربر، ایجاد کرده ایم و سپس درون آنها متغییرهای name و content را متناسب با آنچه از ngModel در قالب HTML دریافت میکنیم قرار داده ایم.
نکته: توجه داشته باشید که عبارت serverList بدون نوع تعریف شده است، زیرا مقادیر درون آن به صورت یک جفت key و value بوده که به عنوان شیء جاوا اسکریپت شناخته میشود.
به مثال زیر توجه کنید:
serverList = [{name: string = "roxo", content: string="این سرور در داخل کشور قرار دارد"}]
یک شیء جاوا اسکریپت ساخته شده که مقادیر آن به صورت key:value هستند. بنابراین در فایل server.component.html نیز خواهیم داشت:
<div class="row"> <div class="col-xs-12"> <label for="name">نام سرور</label> <input type="text" id="name" class="form-control" [(ngModel)]="serverName"> <label for="description">توضیحات سرور</label> <input type="text" id="description" class="form-control" [(ngModel)]="serverContent"> </div> </div> <div class="row"> <div class="col-xs-12"> <button class="btn btn-primary" (click)="onCreateLocalServer()">سرور داخلی جدید</button> <button class="btn btn-primary" (click)="onCreateExternalServer()">سرور خارجی جدید</button> </div> </div> <div class="row"> <hr> <div class="col-xs-12"> <div class="panel panel-default" *ngFor="let server of serverList"> <div class="panel-heading">{{server.name}}</div> <div class="panel-body">{{server.content}}</div> </div> </div> </div>
در این فایل از مجموعهی دستورهایی که از فصل ۱ تا کنون مطالعه کردهایم استفاده شده است. روش انتقال داده به صورت two-way databinding (دو طرفه) میباشد و در نهایت با استفاده از دستور ngFor* برای هر مقدار موجود در serverList تکرار را انجام دادهایم.
از طرفی فایل servers.component.html را به صورت زیر ایجاد کردهایم:
<app-server></app-server>
که در آن سکلتورهای فایل server.component.ts در آن قرار دارد و خود این کامپوننت نیز در کامپوننت ریشه (مادر) app-root مورد استفاده قرار میگیرد بنابراین فایل app.component.html به صورت زیر ویرایش میشود:
<div class="container" dir="rtl"> <div class="row"> <div class="col-xs-12"> <app-servers></app-servers> </div> </div> </div>
بسیار عالی در صورتیکه تمام موارد فوق را به درستی انجام داده باشید خروجی شما به شکل زیر خواهد بود: خب تا به اینجای کار در یک مثال دیگر به صورت کاربردی و خلاصه هر آنچه تا کنون فرا گرفتهاید را به رخ کشیدیم.
در واقع در این مثال یاد گرفتید که چگونه از یک کامپوننت درون کامپوننت والد استفاده کنید. یعنی کامپوننت server درون کامپوننت servers و در نهایت کامپوننت servers درون کامپوننت app استفاده شده است.
حال در ادامه به نحوهی ارسال یک ویژگی (Property) و Event از یک کامپوننت فرزند به کامپوننت والد و بالعکس میپردازیم.
گاهی نیاز داریم که یک سری اطلاعات و دادهی پردازش شده را از کامپوننت والد به فرزند ارسال کنیم در این صورت باید آن ویژگی را به تگ سلکتور کامپوننت فرزند پیوست کرده و به صورت Property Binding یا به عبارت ساده تر با استفاده از علامت [ ] به کامپوننت والد منتقل کنیم.
تنها نکتهای که باید توجه کنید این است: در انگولار تمام ویژگیها یا متغییرهایی که درون یک کامپوننت تعریف میشوند تنها درون همان کامپوننت در دسترس هستند و برای دسترسی به آنها در سایر کامپوننتهای باید یک مفسر یا دکوراتور به نام Input@ تعریف شود.
بنابراین ساختار کلی این دستور به صورت زیر است:
@Input() initServer = []
در این حالت ویژگی initServer از کلاس کامپوننتهای دیگر وارد این کامپوننت شده و قابل استفاده است.
توجه داشته باشید که این مفسر از طریق ماژول Input در angular/core قابل استفاده است و باید حتما آن را در ابتدای کدنویسی خود فراخوانی کنید.
بنابراین برای تفهیم این بحث یک مثال کاربردی خدمت شما عزیزان ارائه میدهیم. مثال بالا را در نظر بگیرید.
حال فرض کنید یک متغییر را به صورت شیء درون کلاس فرزند serverComponent تعریف میکنیم و میخواهیم این متغییر را از کامپوننت والد یعنی serversComponent به این کامپوننت ارسال کنیم. در این صورت برای فایل server.component.ts داریم:
import {Component, OnInit, Input} from '@angular/core'; @Component({ selector: 'app-server', templateUrl: './server.component.html', styleUrls: ['./server.component.css'] }) export class ServerComponent implements OnInit { serverName: string = ''; serverContent: string = ''; serverList = []; @Input() initServer = {name: "روکسو", content:"این سرور باید از کامپوننت والد دریافت شود"}; constructor() { } ngOnInit() { } onCreateLocalServer() { this.serverList.push({ name: this.serverName, content: this.serverContent }); } onCreateExternalServer() { this.serverList.push({ name: this.serverName, content: this.serverContent }); } }
تنها یک خط به مجموعه ی کد مثال قبلی اضافه شد و آن تعریف یک متغییر یا ویژگی (Property) به نام initServer بود. حال در قالب HTML کامپوننت والد (servers.component.html) داریم:
<app-server [initServer]="changeServerList"></app-server>
سپس عبارت changeServerList را درون کامپوننت والد servers.component.ts به صورت زیر تعریف میکنیم:
import { Component, OnInit } from '@angular/core'; @Component({ selector: 'app-servers', templateUrl: './servers.component.html', styleUrls: ['./servers.component.css'] }) export class ServersComponent implements OnInit { changeServerList ={name: "roxoChanges", content: "مکان این سرور تغییر کرده است"}; constructor() { } ngOnInit() { } }
در این مثال، ابتدا مقدار initServer برابر یک سری عناوین مشخص بود ولی وقتی آن را در تگ app-server به عنوان یک ویژگی به کامپوننت server.component.ts ارسال کردیم، مقدار changeServerList جایگزین آن شد و در صفحه به نمایش گذاشته میشود. به این روش ارسال ویژگی یک کامپوننت والد به کامپوننتهای فرزند گفته میشود.
همچنین گاهی شاید نیاز داشته باشیم تا یک ویژگی را با تغییر اسم و به صورت نام مستعار به کامپوننت فرزند ارسال کنیم در این صورت باید به مفسر و دکوراتور Input یک آرگومان ورودی به عنوان اسم مستعاری که مد نظر ماست، درج کنیم.
به عنوان فررض کنید میخواهیم به جای ویژگی initServer ویژگی با نام initSe برای ما بایند شود در اینصورت فایل sever.component.ts به صورت زیر تغییر پیدا میکند:
@Input('initS') initServer = {name: "روکسو", content:"این سرور باید از کامپوننت والد دریافت شود"};
همچنین در فایل servers.component.html تغییرات زیر را لحاظ میکنیم:
<app-server [initS]="changeServerList"></app-server>
همانطور که ملاحظه کردید عنوان initS به جای initServer بایند شده است.
در بخش قبلی ملاحظه کردید که چگونه یک متغییر یا ویژگی را از کامپوننت والد به کامپوننت فرزند انتقال دادیم.
حال فرض کنید میخواهیم عکس این کار را انجام داده و اطلاعات را از کامپوننت فرزند به کامپوننت والد ارسال کنیم. در این حالت باید از رویدادها یا event ها بهره ببریم.
یعنی باید یک ویژگی از نوع EventEmitter به صورت جنریک در کامپوننت فرزند تعریف کرده و سپس مقادیری که مد نظر ماست را به کامپوننت والد با رخ دادن یک رویداد ارسال کنیم به عبارت دیگر میخواهیم تغییراتی که در کامپوننت فرزند رخ میدهند را به کامپوننت والد اطلاع دهیم.
برای تعریف یک event یا رویداد دلخواه باید نام آن را درون () قرار داده و سپس متدی که میخواهیم به وقوع بپیوندد را مقابل آن یادداشت کنیم. جهت درک بیشتر این موضوع مثال قبلی را ادامه خواهیم داد.
فرض کنید میخواهیم هنگامیکه روی دکمهی «سرور داخلی جدید» کلیک شد یک ویژگی یا متغییر به کامپوننت والد یعنی servers ارسال شود. در این حالت ابتدا باید در کامپوننت والد event ها را تعریف کنیم.
توجه داشته باشید که این event ها پس از کلیک کردن روی دکمه (که در کامپوننت فرزند است) رخ میدهند. بنابراین برای تعریف eventها در فایل servers.component.ts خواهیم داشت:
import {Component, OnInit} from '@angular/core'; @Component({ selector: 'app-servers', templateUrl: './servers.component.html', styleUrls: ['./servers.component.css'] }) export class ServersComponent implements OnInit { changeServerList = {name: "roxoChanges", content: "مکان این سرور تغییر کرده است"}; serverDataFromChild = []; constructor() { } ngOnInit() { } onLocalServerAdded(localServerData: {serverName: string, serverContent: string}) { this.serverDataFromChild.push({ name: localServerData.serverName, content: localServerData.serverContent }) } onExternalServerAdded(externalServerData: {serverName: string, serverContent: string}) { this.serverDataFromChild.push({ name: externalServerData.serverName, content: externalServerData.serverContent }) } }
توجه داشته باشید در این کامپوننت دو متد به نامهایی که ملاحظه میکنید ایجاد کرده و مقادیری را به عنوان آرگومان به آنها ارسال میکنیم.
در واقع این متدها زمانی بکار گرفته میشود که eventهای مربوطه رخ دهند. این eventها را در قالب HTML کامپوننت servers به صورت زیر تعریف خواهیم کرد:
<app-server [initS]="changeServerList" (localServerCreated)="onLocalServerAdded($event)" (externalServerCreated)="onExternalServerAdded($event)" > </app-server>
همانطور که در فصلهای گذشته اطلاع داده بودیم عبارت event$ حاوی اطلاعاتیست که پس از رخ دادن یک event ذخیره میگردد.
حال در مرحلهی بعدی باید دو رویداد یا event مربوطه را (localServerCreated و externalServerCreated) در فایل server.component.ts که به عنوان کامپوننت فرزند هستند و این اطلاعات را به کامپوننت والد انتقال میدهند، تعریف کنیم. بنابراین داریم:
import {Component, OnInit, Input, EventEmitter, Output} from '@angular/core'; @Component({ selector: 'app-server', templateUrl: './server.component.html', styleUrls: ['./server.component.css'] }) export class ServerComponent implements OnInit { serverName: string = ''; serverContent: string = ''; serverList = []; @Input('initS') initServer = {name: "روکسو", content: "این سرور باید به کامپوننت والد ارسال شود"}; @Output() localServerCreated = new EventEmitter<{ serverName: string, serverContent: string }>(); @Output() externalServerCreated = new EventEmitter<{ serverName: string, serverContent: string }>() constructor() { } ngOnInit() { } onCreateLocalServer() { this.localServerCreated.emit({ serverName: this.serverName, serverContent: this.serverContent }); } onCreateExternalServer() { this.externalServerCreated.emit({ serverName: this.serverName, serverContent: this.serverContent }); } }
در صورتیکه دقت کنید متوجه خواهید شد که برای خارج کردن یک ویژگی یا به عبارت دیگر برای ارسال یک ویژگی از کامپوننت فرزند به والد باید مفسر یا دکوراتوری تحت عنوان Output@ مشابه آنچه در Input@ بود تعریف کنیم.
پس مفسر Output به معنای خارج شدن این ویژگی و ارسال آن به کامپوننت والد و بالاتر است. همچنین چون این اطلاعات پس از رخ دادن یک رویداد ارسال خواهند شد، به عنوان یک شیء از کلاس EventEmitter و به صورت جنریک تعریف شدهاند تا اطلاعات حاوی نام توضیحات سرور را به کامپوننت بالاتر ارسال کنند.
از طرفی درون متدهایی که به هنگام کلیک روی دکمهها اجرا میشوند دستور emit را اعمال میکنیم. با استفاده از این دستور، اطلاعاتی که توسط فرم ارسال میشوند درون ویژگیهای localServerCreated و externalServerCreated ذخیره و در نهایت به عنوان رویداد یا event به کامپوننت والد (servers) ارسال میشوند. در نتیجه اطلاعات به صورت کاملا منظم از کامپوننت فرزند به کامپوننت والد انتقال داده شدند.
همچنین مشابه آنچه در مفسر Input@ ملاحظه کردید در مفسر Output@ نیز میتوان یک نام مستعار را به عنوان آرگومان ارسال کرد تا در طول برنامه از آن استفاده کنیم.
بنابراین در فایل server.component.ts داریم:
@Output('exServerCreated') externalServerCreated = new EventEmitter<{ serverName: string, serverContent: string }>()
در نهایت درون فایل servers.component.html خواهیم داشت:
<app-server [initS]="changeServerList" (localServerCreated)="onLocalServerAdded($event)" (exServerCreated)="onExternalServerAdded($event)" > </app-server> <div class="row"> <hr> <div class="col-xs-12"> <div class="panel panel-default" *ngFor="let serverData of serverDataFromChild"> <div class="panel-heading">{{serverData.name}}</div> <div class="panel-body">{{serverData.content}}</div> </div> </div> </div>
در این مثال از یک نام مستعار برای معرفی ویژگی از نوع رویداد جهت ارسال داده از کامپوننت server به کامپوننت servers استفاده کردهایم.
بسیار عالی! تا به اینجای کار با مفاهیم تخصصی مربوط به ارسال داده از سمت کامپوننت والد به فرزند و بالعکس (ارسال داده از سمت کامپوننت فرزند به والد) را فرا گرفتید. این آموزش یکی از مهمترین مباحث میباشد که به شما عزیزان در صفحهبندی صفحات و سایر مباحث پیشرفته کمک شایانی میکند. در صورتیکه سوالی داشتید داخل نظرات وب سایت مطرح بفرمایید. با ما همراه باشید.
توجه: دوستان عزیز آموزش ویدیویی انگولار 6 از مقدماتی تا پیشرفته به زبان فارسی را میتوانید با کلیک روی اینجا یاد بگیرید.
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.