رویدادهای شخصی‌سازی‌شده

Custom Events

Vue.JS 2: رویداد های شخصی سازی شده - قسمت 35

تا این قسمت از کار در مورد پاس دادن اطلاعات بین کامپوننت ها صحبت کردیم و حالا می دانیم که چطور اطلاعات را از کامپوننت پدر به کامپوننت فرزند پاس بدهیم اما اگر بخواهیم برعکس این کار را انجام دهیم چه کار باید بکنیم؟ مثلا اگر بخواهیم یک رویداد (event) را به کامپوننت پدر ارسال کنیم تا در صورت بروز اتفاق مورد نظر، کامپوننت پدر مقدار جدیدی بگیرد یا عکس العمل خاصی نشان دهد.

ابتدا وارد فایل userDetail.vue شده و دکمه ای را تعریف می کنیم که با کلیک روی آن نام مشاهده شده در برنامه ریست شود:

<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>
  </div>
</template>

ما هنوز متد resetName را تعریف نکرده ایم بنابراین به قسمت methods می رویم:

<script>
export default {
  props: {
    myName: {
      type: String
    }
  },
  methods: {
    switchName() {
      return this.myName
        .split("")
        .reverse()
        .join("");
    },
    resetName() {
      this.myName = "Amir";
    }
  }
};
</script>

یعنی زمانی که روی دکمه change my name کلیک شد و rimA به oxoR تبدیل شد، بتوانیم روی دکمه Reset Name کلیک کرده و نام را دوباره به rimA برگردانیم. کد بالا برای مقادیر primitive بدون مشکل کار می کند (گرچه Vue در کنسول مرورگر به شما هشدار می دهد) اما اگر مقدار شما آرایه یا شیء باشد دچار مشکل می شویم. چرا؟ اشیاء و آرایه ها reference typed هستند، یعنی فقط یک بار و یک جا در مموری حضور دارند بنابراین اگر آن ها را در 3 متغیر جداگانه ذخیره کنید، همه متغیرها به یک مکان در مموری اشاره می کنند (متغیرها یک pointer یا نشانگر را به سمت مکان آن شیء یا آرایه در مموری ذخیره می کنند نه مقدار خود شیء یا آرایه را). بنابراین اگر آرایه یا شیء ای را از کامپوننت پدر به فرزند ارسال کنید، در اصل همان pointer یا نشانگر را پاس داده اید نه مقدار اصلی آن شیء و آرایه را، بنابراین اگر آن را در کامپوننت فرزند تغییر دهید آن را در کامپوننت پدر نیز تغییر داده اید. چرا؟ به دلیل اینکه آرایه ها و اشیاء فقط در یک مکان در مموری حضور دارند بنابراین شما همان یک مکان را ویرایش می کنید و کامپوننت پدر نیز که از همان یک مکان استفاده می کند، شاهد ویرایش خواهد بود.

البته در برنامه ما این قابلیت به درد بخور است چرا که می خواهیم یک نام را ریست کنیم بنابراین بهتر است که در همه جا ریست شود تا برنامه یک دست کار کند اما چنین اتفاقی نخواهد افتاد چرا که خصوصیت ما یک رشته است و reference typed نیست (خود مقدار رشته واقعا در مموری کپی می شود نه اینکه یک نشانگر به آن اشاره کند – مقدار در کامپوننت های پدر و فرزند از یکدیگر مستقل هستند). البته در اکثر برنامه های واقعی که می نویسید، نمی خواهید چنین قابلیتی را داشته باشید.

برنامه ما در حال حاضر یک مشکل دارد. آیا می دانید مشکل چیست؟ همانطور که گفتم خصوصیت myName یک رشته است و reference typed نیست بنابراین مقدار آن در پدر و فرزند از یکدیگر مستقل هستند که باعث خرابکاری می شود. اگر الان برنامه را باز کنید مقدار rimA را می بینید. سپس روی change my name کلیک کنید تا به oxoR تغییر پیدا کند. سپس روی reset name کلیک کنید تا به rimA تغییر پیدا کند. حالا انتظار داریم که اگر دوباره روی change my name کلیک کنیم، به oxoR برسیم اما هیچ اتفاقی نمی افتد. چرا؟ به دلیل اینکه myName در کامپوننتِ پدر هنوز oxoR است و اصلا تغییر نکرده است. ما این مقدار را فقط در کامپوننت فرزند تغییر داده ایم. بنابراین باید با روشی به کامپوننت پدر بگوییم که نام تغییر کرده است. برای این کار از یک custom event (رویداد شخصی سازی شده) استفاده می کنیم.

در هر نمونه شیء Vue که به انگلیسی به Vue instance شناخته می شود به متدی به نام emit دسترسی داریم (کامپوننت ها شیء Vue را extend می کنند، بنابراین در آن ها نیز به emit دسترسی داریم). این متد دو پارامتر می گیرد که اولین آن ها، نام همان event شخصی ما خواهد بود و دومین پارامتر مقداری است که دوست داریم با event پاس داده شود (فایل userDetail.vue):

  methods: {
    switchName() {
      return this.myName
        .split("")
        .reverse()
        .join("");
    },
    resetName() {
      this.myName = "Amir";
      this.$emit("nameWasReset", this.myName);
    }
  }

حالا به کامپوننت پدر (user.vue) می رویم و به این رویداد شخصی، گوش می کنیم:

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

همانطور که می دانید @ حالت مخفف v-on است و مسئول گوش دادن به رویداد ها است بنابراین با همین علامت به رویداد nameWasReset گوش می دهیم و در صورت اجرا، گفته ایم name (خصوصیت ما در data) را برابر event$ بگذارد. event$ همان مقدار است که با رویداد ارسال کردیم (آرگومان دوم متد emit).

همچنین برای اینکه تغییرات name را مشخصا مشاهده کنیم یک پاراگراف را با مقدار name نیز اضافه می کنیم:

// بقیه کد ها //
    <button @click="changeName">Change my Name</button>
    <p>Name is {{ name }}</p>
    <hr />
    <div class="row">
      <div class="col-xs-12 col-sm-6">
        <app-user-detail :myName="name" @nameWasReset="name = $event"></app-user-detail>
// بقیه کد ها //

حالا اگر به مرورگر رفته و با دکمه های برنامه بازی کنیم، تغییرات را در مرورگر مشاهده خواهیم کرد.

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

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

حسین
15 فروردین 1400
سلام یک سوالی برای من پیش امده چرا این پیغام خطارو میبینم؟راه حلی هست؟ Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop's value. Prop being mutated:

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