Skip to content

SISE-Web-Development-Environments/LAB11

Repository files navigation

ברוכים הבאים למעבדה השנייה ב Vue.js !!! Vue.js Logo

הכנה

עבור המעבדה הזאת אני מבקש ממכם:

  1. לייבא את הקוד מgithub

  2. להוריד את התוספים הבאים לvscode:

    • Prettier - Code formatter
    • Vetur
    • ESLint

מומליצים בנוסף:

  • vue - של (jcbuisson.vue)
  • Vue 2 Snippets
  • Vue VSCode Snippets
  • Vue Peek
  1. להוסיף את הקובץ env.local. לפי הדוגמא env.local_example.
  2. להריץ את הפקודה npm install
  3. לעיין במסמך של צורת הכתיבה הנכונה של Vue

מבוא

במעבדה הזאת נדבר על קומפוננטות ואיך נשתמש בaxios כדי לקבל מידע מהשרת שלנו ולהציג אותו בקומפוננטה מתאימה.

לעומת מעבדה קודמת שבה דיברנו על עמוד הרשמה, היום נדבר על העמוד הראשי שבו נציג מתכונים רנדומלים שנקבל מהשרת.

הקבצים שאיתם תעבדו במעבדה הם MainPage.vue עבור העמוד הראשי, ו RecipePreview.vue עבור התצוגה המקדימה.

צורת הייבוא של Vue

את Vue אנחנו יכולים לייבא בכמה דרכים.
במעבדה הקודמת דיברנו על ייבוא באמצעות תג script שמכיל לינק למודול:

<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>

היום אנחנו נדבר על שימוש בnpm כך שלאחר שהורדנו את Vue, נייבא אותו על ידי:

import Vue from "vue";

כמו שדיברתם בהרצאה, את הקבצים של המעבדה יצרנו באמצעות הפקודה create של Vue-cli.

כמובן, כמו בNode, אם יבאתם את הrepository של המעבדה שבו הnode_modules לא נמצאים, יש צורך להריץ את הפעולה npm install כדי להוריד את כל המודולים שרשומים בpackage.json.

כחלק מהפרויקט הבסיסי שלנו נוכל למצוא את הקבצים המרכזיים של הפרויקט והם:

  • index.html - הקובץ html הראשי שכולל את האלמנט שיהיה הtemplate של האובייקט Vue הראשי
  • App.vue - הקובץ שמכיל את הקומפוננטה הראשית של הפרויקט
  • main.js - הקובץ שיוצר את אובייקט הVue הראשי שמקשר את index.html לApp.vue
  • assets - תיקייה שבה נכיל עזרים סטטים כמו תמונות ורשימות סטטיות (כמו אוסף המדינות)
על מנת להריץ את קבצי המעבדה, יש להריץ את הפקודה:
  • npm run serve_example - על מנת להריץ את הדוגמא
  • npm run serve_task - על מנת להריץ את המשימה

 

קומפוננטה

קומפוננטה היא אובייקט Vue, עם שם, שניתן לעשות בה שימוש חוזר.

כל קומפוננטה בפרויקט שלנו מכילה לוגיקה ותצוגה של רכיב מתוך הפרויקט.

באמצעות שימוש בקומפוננטות, במקום שכל הלוגיקה והתצוגה יהיו בקובץ אחד (כמו שראינו במעבדה הקודמת), כעת אנחנו יכולים ליצור הפרדה כך שהאובייקט הראשי יוכל להכיל אוסף קומפוננטות וכל אחת יכולה גם היא להכיל אוסף קומפוננטות.

בצורה זו אנו מחלקים את הקוד לרכיבים שנוח יותר ליצור אותם ולבדוק אותם.

In logic section (javascript code):

// Define a new component called ButtonCounter
Vue.component("ButtonCounter", {
  // options
  data: function() {
    return {
      count: 0
    };
  },
  template: '<button v-on:click="count++">\
              You clicked me {{ count }} times.\
            </button>'
});

new Vue({ el: "#components-demo" });

In template:

<div id="components-demo">
  <ButtonCounter></ButtonCounter>
  <!-- can reuse -->
  <ButtonCounter />
</div>

כאשר אנחנו מגדירים בצורה הזאת את הקומפוננטה, היא נוספת לנו בצורה גלובלית ואין צורך לציין שאנחנו משתמשים בה באובייקט שלנו.

עבודה עם Vue.component בצורה הזאת בה אנו מגדירים את הקומפוננטה עם options בתוך הפונקציה component טובה כשאנחנו מדברים על פרויקט בגודל קטן~בינוני שמשתמש בjavascript רק כדי לתמוך מעט בחלק מהתצוגות.

כאשר אנחנו מדברים על פרויקט יותר מסובך שמתבסס הרבה יותר על javascript, לשיטה הזאת יש כל מני חסרונות כגון:

  • הגדרת template בצורת מחרוזת לא נוחה לעבודה, במיוחד שהקומפוננטה יכולה להיות גדולה

  • בצורה כזאת אין לנו אפשרות להגדיר עיצוב באמצעות css עבור הקומפוננטה

הדרך השניה שבה נשתמש כדי להגדיר קומפוננטה היא בתוך קובץ נפרד שהוא רשום בפורמט vue. (זאת הדרך שנשתמש בה במעבדה הזאת).

הקובץ הזה יהיה רשום בצורה הבאה:

ButtonCounter.vue :

<template>
  <!-- inside the template tag, there should be one element as a root -->
  <div>
    <!-- ... -->
  </div>
</template>

<script>
  // exporting our component
  export default {
    name: "ButtonCounter" // The name of the component
  };
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
  /* ... */
</style>

טיפים לכתיבה נכונה:

  • את השם של הקומפוננטה נרשום PascalCase - כל מילה מתחילה באות גדולה אחת הסיבות היא להיוודל מאלמנטים בסייסיים של HTML שכולם באותיות קטנות
  • השם של הקובץ שבו שמורה קומפוננטה יהיה זהה לשם של הקומפוננטה
  • אם נרשום כמה קומפוננטות שקשורות אחת לשניה ננסה לדאוג שprefix יהיה דומה

קישור לדוגמאת הקוד

טיפ: באמצעות הextension Vetur נוכל לרשום בקובץ vue את המילה vbase וככה ליצור את הבסיס של הקובץ בצורה מהירה.

את הקומפוננטה שהגדרנו בקובץ נייבא באמצעות import בזכות ההגדרות שvue-cli הגדיר לנו עבור הרצה של הפרויקט.

לאחר שייבאנו את הקומפוננטה, נצהיר שאנחנו משתמשים בה בתוך פרמרטר components בקומפוננטת האב.

מאחורי הקלעים ישנו שימוש בספריית vue-loader, (שהתקנו דרך השימוש ב Vue-cli), שמאפשר לנו את ייבוא הקמפוננטות בדרך זו.

כאשר אנחנו משתמשים מתוך אובייקט Vue בקומפוננטה נוכל לאמר מבחינה היררכית שאותה קומפוננטה היא child של אותו אובייקט (Child Component).

בכל פרויקט Vue יהיה לנו את הקומפוננטה הראשית שלנו שלרוב תהיה מוגדרת בקובץ App.vue:

<template>
  <div id="app">
    <ButtonCounter />
  </div>
</template>

<script>
  // import component from the components directory
  import ButtonCounter from "./components/ButtonCounter";

  export default {
    name: "App",
    components: {
      ButtonCounter
    }
  };
</script>

<style></style>

קישור לדוגמאת הקוד

אנו יכולים לראות בדוגמא זו, שימוש בקומפוננטה בתוך קומפוננטה אחרת.

כפי שדיברנו בעבר, אפליקציית Vue היא למעשה עץ היררכי של קומפוננטות כך שקיים אובייקט Vue שהוא הroot של העץ.

סוגים של קומפוננטות

קומפוננטות אפשר לחלק לשני סוגים:

  1. קומפוננטות שהתוכן שלהם הוא עמוד, כמו עמוד ראשי או עמוד הרשמה.

    • בקומפוננטות אלה בדרך כלל נשתמש פעם אחת.

    • הקומפונטות האלה בדרך כלל יכילו הרבה תוכן ולוגיקה, לכן יכילו קומפוננטות אחרות שיהוו רכיבים של העמוד.

    • את הקומפוננטות האלה נרשום בתוך תיקייה בשם pages.

  2. קומפוננטות שהתוכן שלהם הוא רכיב של עמוד, כמו קומפוננטת תצוגה מקדימה.

    • בקומפוננטות אלה בדרך כלל נשתמש יותר מפעם אחת (לא תמיד).

    • קומפוננטות מהסוג הזה, אשר נרצה לעשות בהם שימוש חוזר לאורך כל האפליקציה בקומפוננטות שונות, כמו קומפוננטה של כפתור מעוצב או הקומפוננטה של תצוגה מקדימה, יכולות להיות מוגדרות כקומפוננטות גלובליות, כך שאין צורך כל מסמך לייבא אותם (רק פעם אחת).

    • את הקומפוננטות האלה נרשום בתוך תיקייה בשם components.

 

<-- משימה 1 -->

המשימה הראשונה שלכם היא ליצור שתי קומפוננטות:

  1. קומפוננטה של תצוגה מקדימה בקובץ RecipePreview.vue - שכרגע תכיל את הטקסט "recipe preview".

  2. קומפוננטה של עמוד ראשי בקובץ MainPage.vue - שתשתמש בקומפוננטה של תצוגה מקדימה (כרגע נתחיל מלהכיל מופע אחד שלה).

  3. לייבא את הקומפוננטה MainPage.vue בקובץ App_task.vue

קישור למשימה 1 2 3 4 5

 

props של קומפוננטה

inside Child Component logic:

export default {
  name: "MessageLine",
  props: {
    message: {
      type: String,
      required: true
    }
  }
};

Inside Parent's template:

<MessageLine message="hello everyone!!!" />

כאשר אנו רוצים להעביר מידע ל Child-Component, עלינו לטפל בשני חלקים בקוד:

  1. בהגדרת הקומפוננטה - נוסיף שדה בשם props (קיצור לproperties).

    שדה זה הינו אובייקט, אשר בתוכו נגדיר את כל השדות שאנחנו רוצים לקבל מקופוננטת האב.

    אותם שדות יהיו לנו חלק מהשדות של הקומפוננטה כמו השדות של data.

  1. בtemplate, בתוך תגית הקומפוננטה בה אנו עושים שימוש, עבור כל שדה בprops אנחנו צריכים לספק ערך.

עבור כל פרמטר יש לציין:

  • שם התכונה

  • הסוג

  • האם הוא הכרחי

    • אם הוא לא הכרחי, יש לציין פונקציה שמחזירה את הערך הדיפולטי שלו
timestamp: {
  type: Number,
  required: false,
  default: () => undefined
}

שימו לב - במידה וברצנוכם להעביר כמות גדולה של שדות, יש לשקול שליחה של אובייקט במקום שדות נפרדים (אובייקט הוא משתנה מסוג Object).

דוגמאת קוד

עוד על props

 

<-- משימה 2 -->

במשימה זו נגדיר בקובץ RecipePreview.vue עבור הקומפוננטה של תצוגה מקדימה את כל הפרמטרים שאנחנו מצפים לקבל, שהם:

  • id : type = Number , required
  • title : type = String, required
  • readyInMinutes : type = Number, required
  • image : type = String, required
  • aggregateLikes : type = Number, not required (default function return undefined)

בנוסף יש:

  • ליצור תצוגה של המידע בRecipePreview.vue מprops באיזה דרך שתבחרו
  • להעביר בשימוש של הקופוננטה ב MainPage.vue ערכים מסוימים (רק לבינתיים)

טיפים והערות:

  • במידע ואנחנו רוצים להעביר מספר אנחנו צריכים לרשום לפני השדה נקודותיים
  • לצורך ההתנסות, במעבדה תרשמו את כל השדות בנפרד.
  • בעבודה תצטרכו גם להתייחס למידע ספציפי למשתמש כמו האם המתכון הוא favorite שלו או האם הוא צפה בו.

קישור למשימה 1 2 3 4 5

 

v-bind

Inside template:

<button v-bind:disabled="!message.length">
  Add new message
</button>

Inside Vue object:

data(){
  return {
    message: ""
  };
}

מאפשר לנו ליצור one-way binding בין expression ל attribute.

צורת הכתיבה תיהיה:

v-bind:AttributeName="expression"

בצורה מקוצרת במקום לרשום :v-bind , נרשום :

<button :disabled="flag">Click me!</button>

קישור לדוגמאת הקוד

תזכורת: כאשר דיברנו בסוף המעבדה הקודמת על v-for, דיברנו שיש אפשרות להצמיד לelement באיטרציה property בשם key שייחד אותו משאר האלמנטים.
הkey בעצם binded לפרמטר של המשתנה שעליו אנחנו עוברים ולכן נרשום אותו בצורה הבאה:

<div v-for="m in messages" :key="m.time">
  {{ message }}
</div>

עוד על v-bind

 

<-- משימה 3 -->

כעת עם מה שלמדנו נוכל ליצור מעבר על רשימת המתכונים וליצור עבור כל אחד אלמנט מסוג קומפוננטת התצוגה המוקדמת.

לשם כך אתם צריכים:

  1. רשימת מתכונים בעמוד הראשי שלנו - MainPage.vue (נתחיל מרשימה שאנחנו ניצור ידנית)
recipes: [
  {
    id: 641726,
    title: "Dulce De Leche Brownies",
    readyInMinutes: 45,
    image: "https://spoonacular.com/recipeImages/641726-556x370.jpg",
    aggregateLikes: 29
  },
  {
    id: 651389,
    title: "Medenjaci - Croatian Honey Spice Cookies",
    readyInMinutes: 45,
    image: "https://spoonacular.com/recipeImages/651389-556x370.jpg",
    aggregateLikes: 6
  }
];
  1. בקובץ MainPage.vue להוסיף את הdirective שלמדנו במעבדה קודמת v-for לאלמנט של הקומפוננט על מנת ליצור את קומפוננטת התצוגה המקדימה עבור כל מתכון ברשימה.

#הערה: כאשר אתם עושים v-for לקומפוננטה, אתם צריכים לציין key (במקרה הזה id)

קישור למשימה 1 2 3 4 5

 

mounted (and) beforeDestroy

export default {
  name: "ComponentName",
  mounted() {
    console.log("mounted");
  },
  beforeDestroy() {
    console.log("beforeDestroy");
  }
};

במחזור החיים של אובייקט Vue, ישנם שני eventים שמעניינים אותנו:

  • mounted: מסמל את הרגע בו האובייקט נוצר ונקשר לtemplate
    מתי נשתמש --> שליחת בקשה לשרת והשמת התוצאה למשתנה של האובייקט ברגע התגובה
  • beforeDestroy: מסמל את הרגע לפני שהאובייקט נהרס ומאפשר לנו להריץ קוד
    מתי נשתמש --> ניקוי נתונים שאנחנו לא צריכים מהזיכרון

link to life cycle image

 

<-- משימה 4 -->

כעת עליכם להוסיף לקובץ MainPage.vue את הפונקציה mounted שתכיל קריאה לconsole.log עם הערה לבחירתכם.

קישור למשימה 1 2 3 4 5

 

axios

axios היא ספרייה שהשתמשנו בה בצד השרת כדי ליצור בקשות לapi החיצוני של spooncular.

כעת בצד הלקוח נשתמש בה על מנת לפנות לשרת שיצרנו בעבודה 3.2 על מנת ליצור בקשות כמו קבלת מתכונים רנדומלים או בקשות כמו בקשת התחברות והרשמה.

 

<-- משימה 5 -->

המשימה שלכם כעת היא להחליף את המתכונים שהגדרנו ממקודם למתכונים רנדומלים שנקבל בעת כניסה לדף הראשי.

בשביל ליישם את זה, ,תצטרכו:

  • ליצור פונקציה אסינכרונית בשם updateRecipes שיוצרת בקשה באמצעות axios וכאשר התגובה חוזרת, במידה ולא הייתה שגיאה, תעדכן את השדה recipes למתכונים החדשים
  • בפונקציה mounted לקרוא לפונקציה הזאת (updateRecipes)

בעבודה 3.3 אתם תפנו לשרת שיצרתם בעבודה 3.2, אך כעת נפנה לspooncular ישירות (מה שלא יקרה ב3.3).

הבקשה דרך axios תראה כך:

try {
  const response = await this.axios.get(
    "https://api.spoonacular.com/recipes/random",
    {
      params: {
        limitLicense: true,
        number: 3,
        apiKey: process.env.VUE_APP_SPOONCULAR_API_KEY
      }
    }
  );
  console.log(response);
  /*const recipes = response.data.recipes.map((r) => {
    return {
      id: r.id,
      title: r.title,
      readyInMinutes: r.readyInMinutes,
      image: r.image,
      aggregateLikes: r.aggregateLikes
    };
  });*/

  // insert to this.recipes
} catch (error) {
  console.log(error);
}

קישור למשימה 1 2 3 4 5

 

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors