تعریف عناصر شخصی‌سازی‌شده

Definition of Custom Elements

Vue.JS 2: تعریف عناصر شخصی سازی شده - قسمت 50

در قسمت قبل با کار با radiobutton ها و همچنین checkbox ها آشنا شدیم و در این قسمت به سراغ تعریف عناصر Custom می رسیم. این عناصر، عناصری هستند که در HTML وجود ندارند و ما خودمان آن ها را تولید کرده ایم. قبل از شروع این جلسه کدهایی را آماده کرده ام که باید به شما بدهم. ابتدا به پوشه src بروید و سپس فایل جدیدی به نام switch.vue را ایجاد کنید. محتوای این فایل به شکل زیر است:

<template>
  <div>
    <div id="on" @click="isOn = true" :class="{active: isOn}">On</div>
    <div id="off" @click="isOn = false" :class="{active: !isOn}">Off</div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      isOn: true
    };
  }
};
</script>

<style scoped>
#on,
#off {
  width: 40px;
  height: 20px;
  background-color: lightgray;
  padding: 2px;
  display: inline-block;
  margin: 10px -2px;
  box-sizing: content-box;
  cursor: pointer;
  text-align: center;
}

#on:hover,
#on.active {
  background-color: lightgreen;
}

#off:hover,
#off.active {
  background-color: lightcoral;
}
</style>

ما در این قسمت دو عدد div داریم که یکی از آنها دارای آیدی on و دیگری دارای آیدی off می باشد. همچنین هر دو div دارای یک listener هستند که با خصوصیت active کار می کنند، سپس بر اساس on یا off بودن خصوصیت active کلاسِ active را به این div ها می چسبانیم. پس از آن کلاس هایی را در قسمت style تعریف کرده ام که کار استایل دهی را بر عهده دارند و ربطی به Vue ندارند.

حالا باید به App.vue برویم و این فایل را به عنوان یک کامپوننت محلی در آن وارد و ثبت کنیم:

<script>
import Swtich from "./Switch.vue";

export default {
  data() {
    return {
      userData: {
        email: "",
        password: "",
        age: 27
      },
      message: "A new text",
      sendMail: [],
      gender: "male",
      selectedPriority: "High",
      priorities: ["High", "Medium", "Low"]
    };
  },
  components: {
    appSwitch: Swtich
  }
};
</script>

سپس در همین فایل و در قسمت template (پایین تر از منوی آبشاری) یک div جدید را برایش در نظر می گیریم:

<div class="row">
  <div class="col-xs-12 col-sm-8 col-sm-offset-2 col-md-6 col-md-offset-3 from-group">
    <label for="priority">Priority</label>
    <select id="priority" class="form-control" v-model="selectedPriority">
      <option v-for="priority in priorities">{{ priority }}</option>
    </select>
  </div>
</div>
// کدهای جدید از این قسمت اضافه شده اند //
<div class="row">
  <div class="col-xs-12 col-sm-8 col-sm-offset-2 col-md-6 col-md-offset-3">
    <app-switch></app-switch>
  </div>
</div>
<hr />

همانطور که می بینید از این کامپوننت به عنوان یک کامپوننت عادی استفاده کرده ایم. در حال حاضر دکمه ها را درون مرورگر خود می بینید اما هنوز منطق اجرایی برای آن ها ننوشته ایم. برای شروع این کار ابتدا یک خصوصیت مخصوص به آن ها در data تعریف می کنیم (هنوز در فایل App.vue هستیم):

data() {
  return {
    userData: {
      email: "",
      password: "",
      age: 27
    },
    message: "A new text",
    sendMail: [],
    gender: "male",
    selectedPriority: "High",
    priorities: ["High", "Medium", "Low"],
    dataSwitch: true
  };

خصوصیت dataSwitch، خصوصیت جدید ماست که به صورت پیش فرض دارای true است اما باید آن را به عنصر سوئیچ خودمان متصل کنیم تا کاربر توانایی تغییر دادن آن را داشته باشد. برای این کار می گوییم:

<div class="row">
  <div class="col-xs-12 col-sm-8 col-sm-offset-2 col-md-6 col-md-offset-3">
    <app-switch v-model="dataSwitch"></app-switch>
  </div>
</div>

همانطور که در جلسه قبل گفتم برای اینکه عناصر و فیلد های دست ساز خود ما توانایی کار با v-model را داشته باشند نیاز به دو فاکتور اصلی است:

  • عنصر ما دارای خصوصیت Value باشد.
  • عنصر ما دارای رویدادی به نام input باشد تا v-model بتواند به آن واکنش نشان دهد (یک رویداد به این نام را emit کند).

چرا به این دو نیاز داریم؟ به دلیل اینکه دستور v-model چیزی به جز value: و input@ نمی باشد. من دوست دارم با value شروع کنم. ما می دانیم که v-model یک value را به صورت prop به ما پاس خواهد داد (در قسمت قبل در این باره صحبت کردیم) بنابراین باید آن را به صورت یک prop دریافت کنیم. برای این کار به فایل Switch.vue می رویم و در قسمت script می گوییم:

<script>
export default {
  data() {
    return {
      isOn: true
    };
  },
  props: ["value"]
};
</script>

حالا که value را به صورت prop گرفته ایم دیگر نیازی به isOn نداریم بنابراین قسمت data را حذف می کنم:

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

من قسمت data را فقط به این خاطر نوشته بودم که پیش نمایش سوئیچ ما در مرورگر نمایش داده شود تا بتوانیم آن را ببینیم. بدین ترتیب می توانیم به جای تغییر isOn در هنگام کلیک شدن روی div ها، مقدار value را که از props گرفته ایم تغییر بدهیم. من به سادگی می گویم:

<template>
  <div>
    <div id="on" @click="value = true" :class="{active: value}">On</div>
    <div id="off" @click="value = false" :class="{active: !value}">Off</div>
  </div>
</template>

بدین ترتیب تمام موارد مربوط به isOn را با value تغییر داده ایم. با این حال کد بالا کافی نیست. چرا؟ به دلیل اینکه برای کار کردن v-model باید یک رویداد را نیز emit کنیم نه اینکه فقط مقدار value را مشخص نماییم. بنابراین بهتر است به جای نوشتن کدها به صورت بالا در قسمت script یک متد جداگانه برای انجام این کار بنویسیم که هم value را مشخص کرده و هم رویداد ما را emit کند:

<script>
export default {
  props: ["value"],
  methods: {
    switched(isOn) {
      this.$emit("input", isOn);
    }
  }
};
</script>

توجه داشته باشید که رویداد emit شده باید input باشد و نمی توانید نام دیگری برایش انتخاب کنید به دلیل اینکه دستور v-model فقط به input و برخی دیگر از رویداد های خاص گوش می دهد و هدف ما در این قسمت input است. البته این متد مقداری را نیز می گیرد که به همراه رویداد ارسال می کند  بنابراین در قسمت template این فایل می گوییم:

<template>
  <div>
    <div id="on" @click="switched(true)" :class="{active: value}">On</div>
    <div id="off" @click="switched(false)" :class="{active: !value}">Off</div>
  </div>
</template>

یعنی در div اول (روشن بودن) مقدار true را پاس داده ام و در div دوم (خاموش بودن) مقدار False را ارسال کرده ام. حالا به App.vue برمی گردیم تا این مقدار را در panel-body نیز نمایش بدهیم:

<div class="panel-body">
  <p>Mail: {{ userData.email }}</p>
  <p>Password: {{ userData.password }}</p>
  <p>Age: {{ userData.age }}</p>
  <p style="white-space: pre">Message: {{ message }}</p>
  <p>
    <strong>Send Mail?</strong>
  </p>
  <ul>
    <li v-for="item in sendMail">{{ item }}</li>
  </ul>
  <p>Gender: {{ gender }}</p>
  <p>Priority: {{ selectedPriority }}</p>
  <p>Switched: {{ dataSwitch }}</p>
</div>

حالا اگر کدها را در مرورگر تست کنیم، بدون خطا اجرا می شوند. به شما تبریک می گویم، برنامه ما تکمیل شده است! در نهایت باید در مورد ثبت یا submit کردن فرم ها نیز توضیحاتی بدهم و این فصل در همینجا به پایان می رسد. در اولین قدم یک listener برای دکمه submit ایجاد می کنم و modifier ای به نام prevent را نیز به آن اضافه خواهم کرد که معادل preventDefault در جاوا اسکریپت عادی است:

<div class="row">
  <div class="col-xs-12 col-sm-8 col-sm-offset-2 col-md-6 col-md-offset-3">
    <button class="btn btn-primary" @click.prevent="submitted">Submit!</button>
  </div>
</div>

با این کار از ثبت شدن فرم به سرور و refresh شدن صفحه جلوگیری کرده ایم، بلکه به جای آن متدی به نام submitted اجرا خواهد شد. برای تعریف این متد هم مثل همیشه از قسمت scripts عمل می کنیم:

<script>
import Swtich from "./Switch.vue";

export default {
  data() {
    return {
      userData: {
        email: "",
        password: "",
        age: 27
      },
      message: "A new text",
      sendMail: [],
      gender: "male",
      selectedPriority: "High",
      priorities: ["High", "Medium", "Low"],
      dataSwitch: true,
      isSubmitted: false
    };
  },
  methods: {
    submitted() {
      this.isSubmitted = true;
    }
  },
  components: {
    appSwitch: Swtich
  }
};
</script>

همانطور که می بینید در ابتدا خصوصیتی به نام isSubmitted را تعریف کرده ام که به صورت پیش فرض روی false است. سپس متد submitted را تعریف کرده ام که تنها کارش تغییر خصوصیت isSubmitted به true است. حالا با استفاده از این ساز و کار قرار است قسمت body-panel را به صورت شرطی نمایش بدهیم که با یک v-if ساده قابل انجام است:

<div class="row" v-if="isSubmitted">
  <div class="col-xs-12 col-sm-8 col-sm-offset-2 col-md-6 col-md-offset-3">
    <div class="panel panel-default">
      <div class="panel-heading">
        <h4>Your Data</h4>
      </div>
      <div class="panel-body">
        <p>Mail: {{ userData.email }}</p>
        <p>Password: {{ userData.password }}</p>
        <p>Age: {{ userData.age }}</p>
        <p style="white-space: pre">Message: {{ message }}</p>
// بقیه کدها //

فصل مربوط به کار با فرم ها در همین قسمت به پایان رسیده است. امیدوارم مطالب ارائه شده مورد پسند شما قرار گرفته باشد.

دانلود سورس کد تمام این فصل

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

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