تکمیل پروژه Quoteها

Complete the Quotes Project

Vue.JS 2: تکمیل پروژه ی quote ها - قسمت 45

تا اینجای کار خوب پیش آمده ایم اما هنوز quote های اضافه شده قابل حذف شدن نیستند. برای انجام این کار باید کاری کنیم که quote ها قابل کلیک شدن باشند. احتمالا اکثر شما می خواهید از click@ برای تشخیص کلیک استفاده کنید اما متاسفانه در اینجا چنین کاری جواب نمی دهد. مثلا اگر به QuoteGrid.vue برویم و کد زیر را بنویسیم:

<template>
  <div class="row">
    <app-quote v-for="quote in quotes" @click="">{{ quote }}</app-quote>
  </div>
</template>

اضافه کردن click@ مشکل را حل نمی کند اما یک modifier وجود دارد که باعث حل مشکل می شود:

<app-quote v-for="quote in quotes" @click.native="">{{ quote }}</app-quote>

native به Vue می گوید اگر کلیک روی یکی از عناصر native کامپوننت app-quote کلیک شد، واکنش نشان بده. native یعنی «بومی» یا «مادری» و منظور از عناصر native یعنی عناصری که واقعا مربوط به خود آن کامپوننت هستند (div های داخل آن و غیره). در حالت عادی این کامپوننت در DOM وجود خارجی ندارد بلکه کدهای آن هستند که نهایتا در DOM قرار خواهند گرفت (در HTML که کامپوننت نداریم) بنابراین native کاری می کند که به جای قرار گرفتن listener روی این کامپوننت (خود کامپوننت که روی DOM قرار نخواهد گرفت) روی عناصر داخل آن قرار بگیرد. در این مثال می شود همان div اولیه داخل app-quote.

حالا می توانیم دستور زیر را بنویسیم:

<app-quote v-for="quote in quotes" @click.native="deleteQuote">{{ quote }}</app-quote>

در همان فایل (QuoteGrid.vue) می خواهیم قسمت methods را اضافه کنیم تا متد deleteQuote را تعریف کنیم اما از کجا بدانیم که روی کدام عنصر کلیک شده است؟ احتمالا تا این قسمت vue در مورد این موضوع به شما خطا داده است که حلقه ها در vue باید دارای مشخص کننده خاصی باشند. من از index آرایه quotes استفاده می کنم تا آن را به عنوان مشخص کننده قرار دهم:

<app-quote v-for="(quote, index) in quotes" @click.native="deleteQuote(index)">{{ quote }}</app-quote>

ما در قسمت های قبلی توضیح داده بودیم که چطور می توان در حلقه های for مقدار index را نیز دریافت کرد. همانطور که در کد بالا مشاهده می کنید فقط کافی است آن را به همراه مقدار اصلی درون یک پرانتز قرار بدهید. از طرف دیگر این مقدار را به صورت یک پارامتر به deleteQuote پاس می دهم تا این متد بداند کدام عنصر را باید پاک کند. حالا متد deleteQuote را در همین فایل تعریف می کنیم:

<script>
import Quote from "./Quote.vue";

export default {
  props: ["quotes"],
  components: {
    appQuote: Quote
  },
  methods: {
    deleteQuote (index) {
      this.$emit('quoteDeleted', index);
    }
  }
};
</script>

من در اینجا یک custom event را emit کرده ام تا آن را در کامپوننت اصلی پدر حذف کنم اما می دانید که راه های مختلفی برای حذف quote ها وجود دارد و این فقط یک راه است. حالا وارد فایل app.vue می شویم و به این event گوش می دهیم:

<template>
  <div class="container">
    <app-new-quote @quoteAdded="newQuote"></app-new-quote>
    <app-quote-grid :quotes="quotes" @quoteDeleted="deleteQuote"></app-quote-grid>
    <div class="row">
      <div class="col-sm-12 text-center">
        <div class="alert alert-info">Info: Click on a Quote to delete it!</div>
      </div>
    </div>
  </div>
</template>

یعنی زمانی که رویدادی به نام quoteDeleted اتفاق افتاد، متد deleteQuote را اجرا کن. توجه داشته باشید که نام متدهای هر کامپوننت فقط درون آن کامپوننت تعریف می شوند بنابراین این متد با متد deleteQuote در کامپوننت QuoteGrid هیچ ربطی به هم ندارند، چه نام ها یکسان باشند و چه متفاوت. حالا نوبت آن است که این متد را برای این کامپوننت نیز تعریف کنیم:

  methods: {
    newQuote(quote) {
      this.quotes.push(quote);
    },
    deleteQuote(index) {
      this.quotes.splice(index, 1);
    }
  },

همانطور که می دانید index به صورت خودکار به ما پاس داده می شود (چرا که یک event این مقادیر را ارسال می کند) بنابراین فقط کافی است آن را دریافت کنیم. سپس با متد splice می گوییم از قسمت index، یک عنصر را حذف کن.

تنها کاری که باقی مانده است، اضافه کردن progress bar و تعیین حداکثر تعداد برای quote ها است. برای این کار یک فایل جدید در پوشه components ایجاد کرده و نام آن را Header.vue می گذارم. Template این فایل را از قبل برایتان آماده کرده ام:

<template>
  <div class="row">
    <div class="col-sm-12">
      <h3>Quotes Added</h3>
      <div class="progress">
        <div class="progress">
          <div
            class="progress-bar"
            role="progressbar"
            aria-valuenow="60"
            aria-valuemin="0"
            aria-valuemax="100"
          ></div>
        </div>
      </div>
    </div>
  </div>
</template>

همانطور که می بینید این کدها کدهای ساده HTML هستند که در بوت استرپ برای نوشتن یک progress bar استفاده می شوند. سریعا به فایل App.vue بروید و این فایل را در آنجا import کنید:

<script>
import QuoteGrid from "./components/QuoteGrid.vue";
import NewQuote from "./components/NewQuote.vue";
import Header from "./components/Header";
// بقیه کدها //

سپس آن را به صورت محلی ثبت می کنیم:

  components: {
    appQuoteGrid: QuoteGrid,
    appNewQuote: NewQuote,
    appHeader: Header
  }
};
</script>

و نهایتا آن را درون template خودمان قرار می دهیم:

<template>
  <div class="container">
    <app-header></app-header>
    <app-new-quote @quoteAdded="newQuote"></app-new-quote>
    <app-quote-grid :quotes="quotes" @quoteDeleted="deleteQuote"></app-quote-grid>
    <div class="row">
      <div class="col-sm-12 text-center">
        <div class="alert alert-info">Info: Click on a Quote to delete it!</div>
      </div>
    </div>
  </div>
</template>

حالا به فایل Header.vue برگردید. در حال حاضر progress bar را در مرورگر مشاهده می کنیم اما خالی است چرا که div درونی (Div با کلاس progress-bar) مسئول پر کردن این نوار است. ما باید عرض آن را کنترل کنیم و عرض آن مشخص می کند که چقدر از نوار پر شود. بنابراین قدم اول دریافت مقادیر حداکثر تعداد quote و تعداد فعلی quote ها است. ما این مقادیر را به صورت prop دریافت می کنیم بنابراین در همان Header.vue تگ های script را نیز اضافه کرده و می گویم:

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

قبل از اینکه به فایل های دیگر برویم تا آن ها را ارسال کنیم، بیایید از آن ها استفاده کنیم. در ابتدا باید تعداد quote ها را در progress bar نشان دهیم بنابراین:

<div class="progress">
    <div
    class="progress-bar"
    role="progressbar"
    aria-valuenow="60"
    aria-valuemin="0"
    aria-valuemax="100"
    :style="{width: (quoteCount / maxQuotes) * 100 + '%'}"
    >{{ quoteCount }} / {{ maxQuotes}}</div>
</div>

یعنی در ابتدا style را با v-bind به این div چسبانده ام و گفته ام خصوصیت width (عرض نوار) برابر باشد با تعداد quote های فعلی تقسیم بر تعداد حداکثر quote مجاز ضرب در 100 (درصد گیری ساده) و در نهایت علامت درصد را نیز به صورت رشته به آن اضافه کرده ایم. در نهایت مقادیر quoteCount و maxQuotes را نیز درون خود Div نمایش می دهیم (توجه کنید که علامت / درون div عملیات خاصی را انجام نمی دهد بلکه یک علامت ساده برای جدا کردن دو مقدار است). حالا وارد App.vue شوید تا این مقادیر را به header پاس بدهیم:

<template>
  <div class="container">
    <app-header :quoteCount="quotes.length" :maxQuotes="maxQuotes"></app-header>
    <app-new-quote @quoteAdded="newQuote"></app-new-quote>
// بقیه کدها //

به همین سادگی مقادیر مورد نظر را پاس داده ایم. تنها ویرایشی که باید انجام دهیم در متد newQuote در همین فایل است تا در صورتی که به 10 quote رسیدیم به ما هشدار داده شود:

  methods: {
    newQuote(quote) {
      if (this.quotes.length >= this.maxQuotes) {
        return alert("Please delete Quotes first!");
      }
      this.quotes.push(quote);
    },
    deleteQuote(index) {
      this.quotes.splice(index, 1);
    }
  },

یعنی اگر تعداد quote ها از maxQuotes بیشتر شد یک alert به کاربر نمایش بده که بگوید «باید ابتدا quote ها را حذف کنید» برنامه ما تکمیل شده و حالا می توانیم به فصل بعد برویم. شما می توانید سورس کد این برنامه را از این لینک دانلود کنید.

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

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