ارتباط بین کامپوننت‌های خواهر و برادر

Relationship Between Sibling Components

Vue.JS 2: ارتباط بین کامپوننت های خواهر و برادر - قسمت 36

نکته جالبی در رابطه با نحوه انتقال اطلاعات در Vue وجود دارد. انتقال اطلاعات در Vue به صورت unidirectional یا یک طرفه است، یعنی فقط از پدر به فرزند و از فرزند به پدر. بنابراین کامپوننت های sibling یا برادر و خواهر نمی توانند مستقیما به هم داده ارسال کنند. راه حل این مشکل این است که داده ها ابتدا توسط متدی از فرزند 1 به کامپوننت پدر ارسال شده و سپس کامپوننت پدر آن اطلاعات را به فرزند 2 ارسال کند.

جریان داده ها بین کامپوننت ها در Vue.js
جریان داده ها بین کامپوننت ها در Vue.js

برای چنین حالتی ابتدا باید با مبحث callback ها آشنا شوید. ما در جلسه قبل با رویدادهای شخصی سازی شده (custom event ها) کار کرده ایم اما اگر آن را دوست نداشتید، روش دیگری نیز وجود دارد که همان callback ها هستند. به فایل user.vue (کامپوننت پدر) رفته و مثل من تابعی به نام resetName بنویسید:

  methods: {
    changeName() {
      this.name = "Roxo";
    },
    resetName() {
      this.name = "Amir";
    }
  },

این همان متدی است که در کامپوننت فرزند نوشته بودیم اما این بار آن را در کامپوننت پدر تعریف کرده ایم. حالا این تابع را به صورت یک prop به فرزند خود پاس می دهیم:

<app-user-detail :myName="name" @nameWasReset="name = $event" :resetFn="resetName"></app-user-detail>

همانطور که می بینید prop ای با نام resetFn را پاس داده ایم که یک pointer به تابع resetName ما است. حالا به کامپوننت فرزند userDetail.vue می رویم و prop جدید را دریافت می کنیم:

   props: {
    myName: {
      type: String
    },
    resetFn: Function
  }

حالا از دکمه resetName در همین فایل کپی می گیرم و می گوییم در صورت کلیک شدن تابع resetFn را اجرا کن:

<template>
  <div class="component">
    <h3>You may view the User Details here</h3>
    <p>Many Details</p>
    <p>User Name : {{ switchName() }}</p>
    <button @click="resetName">Reset Name</button>
    <button @click="resetFn()">Reset Name</button>
  </div>
</template>

این کد دقیقا مانند کد جلسه قبل برای custom event ها کار می کند.

ارسال اطلاعات بین کامپوننت های برادر و خواهر

برای انجام این کار 3 روش مختلف وجود دارد. در ابتدا وارد فایل user.vue شده و خصوصیت جدیدی به نام age را اضافه کنید:

  data: function() {
    return {
      name: "Amir",
      age: 27
    };
  }

حالا این مقدار را به عنوان یک prop به کامپوننت userEdit.vue ارسال می کنیم. برای این کار در همین فایل (user.vue) تگ های مربوط به userEdit.vue را پیدا کرده و می گوییم:

<app-user-edit :userAge="age"></app-user-edit>

سپس وارد فایل userEdit.vue شده و این prop را دریافت کنید:

<script>
export default {
  props: ["userAge"],
};
</script>

من می خواهم کارمان ساده باشد بنابراین هیچ کاری برای validation (اعتبارسنجی) انجام نمی دهم و از همان آرایه ساده استفاده می کنم. حالا دکمه ای را تعریف کنید که کارش ویرایش سن باشد:

<template>
    <div class="component">
        <h3>You may edit the User here</h3>
        <p>Edit me!</p>
        <button @click="editAge">Edit Age</button>
    </div>
</template>

سپس در قسمت script همین فایل متد آن (editAge) را تعریف می کنیم:

<script>
export default {
  props: ["userAge"],
  methods: {
    editAge() {
      this.userAge = 30;
    }
  }
};
</script>

در نهایت آن را به صورت یک تگ p نمایش می دهم تا مقدار Age را ببینیم:

<template>
  <div class="component">
    <h3>You may edit the User here</h3>
    <p>Edit me!</p>
    <p>User Age: {{ userAge }}</p>
    <button @click="editAge">Edit Age</button>
  </div>
</template>

همانطور که مشخص است این متد سن را به 30 سال تغییر می دهد. تا این قسمت از کار خصوصیت age را درون کامپوننت userEdit.vue خواهیم داشت و در همان کامپوننت هم قابل تغییر است. حالا برای نمایش این Age به فایل user.vue می رویم و می گوییم:

<div class="col-xs-12 col-sm-6">
  <app-user-detail
    :myName="name"
    @nameWasReset="name = $event"
    :resetFn="resetName"
    :userAge="age"
  ></app-user-detail>
</div>

سپس به userDetail.vue می رویم و این prop را دریافت می کنیم:

export default {
  props: {
    myName: {
      type: String
    },
    resetFn: Function,
    userAge: Number
  },
// بقیه کدها //

حالا می خواهیم به روز رسانی Age را از کامپوننت برادر، یعنی userEdit.vue، دریافت کنیم. من در همین فایل (userDetail.vue) به قسمت Template می روم و یک پاراگراف دیگر را اضافه می کنم تا age را در آن نمایش بدهم. بدین صورت زمانی که Age تغییر کند آن را در مرورگر مشاهده می کنیم:

    <p>Many Details</p>
    <p>User Name : {{ switchName() }}</p>
    <p>User Age: {{ userAge }}</p>

حالا به مرورگر رفته و روی دکمه Edit age در مستطیل سبز (کامپوننت userEdit.vue) تغییر می کند اما در مستطیل قرمز (کامپوننت userDetail.vue) هیچ تغییری نمی بینیم:

دو کامپوننت به صورت مستقل از هم عمل کرده و عدد تغییر نمی کند
دو کامپوننت به صورت مستقل از هم عمل کرده و عدد تغییر نمی کند

دلیل آن هم واضح است. چنین وضعیتی را در جلسه قبل نیز دیده بودیم. از آنجایی که مقدار ما یک عدد است بنابراین reference typed نیست و مقدار آن در کامپوننت های مختلف مستقل است.

راه های تغییر داده در هر دو کامپوننت

اولین راه ایجاد یک custom event است. وارد فایل userEdit.vue شده و هنگام تغییر age یک رویداد را نیز emit کنید:

<script>
export default {
  props: ["userAge"],
  methods: {
    editAge() {
      this.userAge = 30;
      this.$emit("ageWasEdited", this.userAge);
    }
  }
};
</script>

حالا وارد فایل user.vue شده و روی کامپوننت app-user-edit یک listener می گذاریم:

<app-user-edit :userAge="age" @ageWasEdited="age = $event"></app-user-edit>

حالا که age در این فایل به روز رسانی شده است می توانیم به مرورگر رفته و با کلیک روی دکمه edit age شاهد تغییر age در هر دو کامپوننت باشیم:

دو کامپوننت مقادیر را از کامپوننت پدر می گیرند و مستقل نیستند
دو کامپوننت مقادیر را از کامپوننت پدر می گیرند و مستقل نیستند

روش دوم همان روش callback است که در ابتدای این قسمت در مورد آن توضیح دادم. برای این کار متدی را درون user.vue (کامپوننت پدر) تعریف می کردیم و آن متد را به صورت props به کامپوننت userEdit پاس می دهیم. از آنجایی که انجام آن بسیار ساده و تکراری است (برای resetName نیز همین کار را کردیم) دیگر کدهای آن را دوباره نمی نویسم.

روش سوم، همان روشی است که معمولا برنامه نویسان از آن استفاده می کنند. چرا؟ به دلیل اینکه پاس برقراری ارتباط بین فرزند و پدر و دوباره بین پدر و فرزند برای یک خصوصیت کار سختی است و در برنامه های واقعی بسیار پیچیده می شود. مثلا فرض کنید یک کامپوننت فرزند را درون یک کامپوننت فرزند دیگر داشته باشید که درون یک کامپوننت پدر باشد و بخواهید اطلاعاتش را به یک کامپوننت فرزند درون یک کامپوننت فرزند درون یک کامپوننت پدر دیگر ارسال کنید!! قطعا کار سختی خواهد بود. در قسمت بعد با این روش آشنا خواهیم شد.

تمام فصل‌های سری ترتیبی که روکسو برای مطالعه‌ی دروس سری آموزش رایگان Vue js از صفر تا صد توصیه می‌کند:
نویسنده شوید
دیدگاه‌های شما

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.