Repository: js-tasks-ru/vue-20250304_nizarit
Pull request #2: Solves
Author: login: Nizarit
Created: 2025-03-17T19:15:42Z | Merged: 2025-03-27T14:11:34Z



Diff:
diff --git a/02-basics-2/10-counter/CounterApp.js b/02-basics-2/10-counter/CounterApp.js
index e0d3101..e2cda7b 100644
--- a/02-basics-2/10-counter/CounterApp.js
+++ b/02-basics-2/10-counter/CounterApp.js
@@ -1,9 +1,19 @@
-import { defineComponent } from 'vue'
+import { computed, defineComponent, ref } from 'vue'
 
 export default defineComponent({
   name: 'CounterApp',
 
-  setup() {},
+  setup() {
+    const minValue = 0
+    const maxValue = 5
+    const count = ref(minValue)
+
+    return {
+      count,
+      minValue,
+      maxValue
+    }
+  },
 
   template: `
     <div class="counter">
@@ -11,15 +21,18 @@ export default defineComponent({
         class="button button--secondary"
         type="button"
         aria-label="Decrement"
-        disabled
+        :disabled="count === minValue"
+        @click="count--"
       >➖</button>
 
-      <span class="count" data-testid="count">0</span>
+      <span class="count" data-testid="count">{{ count }}</span>
 
       <button
         class="button button--secondary"
         type="button"
         aria-label="Increment"
+        :disabled="count === maxValue"
+        @click="count++"
       >➕</button>
     </div>
   `,
diff --git a/02-basics-2/20-broken-map/MapApp.js b/02-basics-2/20-broken-map/MapApp.js
index 5aa14fc..d3fc756 100644
--- a/02-basics-2/20-broken-map/MapApp.js
+++ b/02-basics-2/20-broken-map/MapApp.js
@@ -1,4 +1,4 @@
-import { defineComponent, ref, watch } from 'vue'
+import { defineComponent, ref} from 'vue'
 
 export default defineComponent({
   name: 'MapApp',
@@ -13,27 +13,21 @@ export default defineComponent({
      * @param {MouseEvent} event
      */
     function handleClick(event) {
-      x = event.offsetX
-      y = event.offsetY
+      x.value = event.offsetX
+      y.value = event.offsetY
     }
 
-    // Следим за X и Y для установки нового положения
-    watch([x, y], () => {
-      // Находим метку и изменяем её положение
-      const map = document.querySelector('.pin')
-      map.style.left = `${x}px`
-      map.style.top = `${y}px`
-    })
-
     return {
       handleClick,
+      x,
+      y,
     }
   },
 
   template: `
     <div class="map" @click="handleClick">
       <img class="map-image" src="./map.png" alt="Map" draggable="false" />
-      <span class="pin">📍</span>
+      <span class="pin" :style="{ left: x + 'px', top: y + 'px' }">📍</span>
     </div>
   `,
 })
diff --git a/02-basics-2/30-calculator/CalculatorApp.js b/02-basics-2/30-calculator/CalculatorApp.js
index 381b03f..3d41e97 100644
--- a/02-basics-2/30-calculator/CalculatorApp.js
+++ b/02-basics-2/30-calculator/CalculatorApp.js
@@ -1,26 +1,51 @@
-import { defineComponent } from 'vue'
+import { computed, defineComponent, ref } from 'vue'
 
 export default defineComponent({
   name: 'CalculatorApp',
 
-  setup() {},
+  setup() {
+    let firstOperand = ref(0)
+    let secondOperand = ref(0)
+    let selectedOperator = ref('sum')
+
+    let calc = computed(() => {
+      let total = 0
+      if (selectedOperator.value === 'sum') {
+        total = firstOperand.value + secondOperand.value
+      } else if (selectedOperator.value === 'subtract') {
+        total = firstOperand.value - secondOperand.value
+      } else if (selectedOperator.value === 'multiply') {
+        total = firstOperand.value * secondOperand.value
+      } else if (selectedOperator.value === 'divide') {
+        total = firstOperand.value / secondOperand.value
+      }
+      return total
+    })
+
+    return {
+      firstOperand,
+      secondOperand,
+      selectedOperator,
+      calc,
+    }
+  },
 
   template: `
     <div class="calculator">
-      <input type="number" aria-label="First operand" />
+      <input type="number" aria-label="First operand" v-model="firstOperand"/>
 
       <div class="calculator__operators">
-        <label><input type="radio" name="operator" value="sum"/>➕</label>
-        <label><input type="radio" name="operator" value="subtract"/>➖</label>
-        <label><input type="radio" name="operator" value="multiply"/>✖</label>
-        <label><input type="radio" name="operator" value="divide"/>➗</label>
+        <label><input type="radio" name="operator" value="sum" v-model="selectedOperator"/>➕</label>
+        <label><input type="radio" name="operator" value="subtract" v-model="selectedOperator"/>➖</label>
+        <label><input type="radio" name="operator" value="multiply" v-model="selectedOperator"/>✖</label>
+        <label><input type="radio" name="operator" value="divide" v-model="selectedOperator"/>➗</label>
       </div>
 
-      <input type="number" aria-label="Second operand" />
+      <input type="number" aria-label="Second operand" v-model="secondOperand" />
 
       <div>=</div>
 
-      <output>0</output>
+      <output>{{ calc }}</output>
     </div>
   `,
 })
diff --git a/02-basics-2/40-marked-emails/MarkedEmailsApp.js b/02-basics-2/40-marked-emails/MarkedEmailsApp.js
index 8de9625..4866e34 100644
--- a/02-basics-2/40-marked-emails/MarkedEmailsApp.js
+++ b/02-basics-2/40-marked-emails/MarkedEmailsApp.js
@@ -32,19 +32,34 @@ export const emails = [
 export default defineComponent({
   name: 'MarkedEmailsApp',
 
-  setup() {},
+  setup() {
+    const filterString = ref('')
+
+    const filteredEmails = computed(() => {
+      const filteredEmails = []
+      
+      for (let i = 0; i < emails.length; i++) {
+        filteredEmails[i] = {email: emails[i], marked: filterString.value !== '' && emails[i].toLowerCase().includes(filterString.value.toLowerCase())}  
+      }
+
+      return filteredEmails
+    })
+
+    return {
+      emails,
+      filterString,
+      filteredEmails,
+    }
+  },
 
   template: `
     <div>
       <div class="form-group">
-        <input type="search" aria-label="Search" />
+        <input type="search" aria-label="Search" v-model="filterString"/>
       </div>
       <ul aria-label="Emails">
-        <li>
-          Eliseo@gardner.biz
-        </li>
-        <li class="marked">
-          Jayne_Kuhic@sydney.com
+        <li v-for="item in filteredEmails" :class="{ marked: item.marked }">
+          {{ item.email }}
         </li>
       </ul>
     </div>
diff --git a/02-basics-2/50-selected-meetup/SelectedMeetupApp.js b/02-basics-2/50-selected-meetup/SelectedMeetupApp.js
index dad23b8..40dda55 100644
--- a/02-basics-2/50-selected-meetup/SelectedMeetupApp.js
+++ b/02-basics-2/50-selected-meetup/SelectedMeetupApp.js
@@ -1,15 +1,31 @@
-import { defineComponent } from 'vue'
-// import { getMeetup } from './meetupsService.ts'
+import { computed, defineComponent, ref, watch } from 'vue'
+import { getMeetup } from './meetupsService.ts'
 
 export default defineComponent({
   name: 'SelectedMeetupApp',
 
-  setup() {},
+  setup() {
+    const minValue = 1
+    const maxValue = 5
+    const selectedId = ref(minValue)
+    const meetupTitle = ref('')
+
+    watch(selectedId, () => {
+      getMeetup(selectedId.value).then(result => meetupTitle.value = result.title)
+    }, { immediate: true })
+
+    return {
+      minValue,
+      maxValue,
+      selectedId,
+      meetupTitle,
+    }
+  },
 
   template: `
     <div class="meetup-selector">
       <div class="meetup-selector__control">
-        <button class="button button--secondary" type="button" disabled>Предыдущий</button>
+        <button class="button button--secondary" type="button" @click="selectedId--" :disabled="+selectedId === minValue">Предыдущий</button>
 
         <div class="radio-group" role="radiogroup">
           <div class="radio-group__button">
@@ -19,6 +35,7 @@ export default defineComponent({
               type="radio"
               name="meetupId"
               value="1"
+              v-model="selectedId"
             />
             <label for="meetup-id-1" class="radio-group__label">1</label>
           </div>
@@ -29,6 +46,7 @@ export default defineComponent({
               type="radio"
               name="meetupId"
               value="2"
+              v-model="selectedId"
             />
             <label for="meetup-id-2" class="radio-group__label">2</label>
           </div>
@@ -39,6 +57,7 @@ export default defineComponent({
               type="radio"
               name="meetupId"
               value="3"
+              v-model="selectedId"
             />
             <label for="meetup-id-3" class="radio-group__label">3</label>
           </div>
@@ -49,6 +68,7 @@ export default defineComponent({
               type="radio"
               name="meetupId"
               value="4"
+              v-model="selectedId"
             />
             <label for="meetup-id-4" class="radio-group__label">4</label>
           </div>
@@ -59,17 +79,18 @@ export default defineComponent({
               type="radio"
               name="meetupId"
               value="5"
+              v-model="selectedId"
             />
             <label for="meetup-id-5" class="radio-group__label">5</label>
           </div>
         </div>
 
-        <button class="button button--secondary" type="button">Следующий</button>
+        <button class="button button--secondary" type="button" @click="selectedId++" :disabled="+selectedId === maxValue">Следующий</button>
       </div>
 
       <div class="meetup-selector__cover">
         <div class="meetup-cover">
-          <h1 class="meetup-cover__title">Some Meetup Title</h1>
+          <h1 class="meetup-cover__title">{{ meetupTitle }}</h1>
         </div>
       </div>
 


Reviews:
user: login: ShGKme
state: CHANGES_REQUESTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-19T18:46:28Z
user: login: ShGKme
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-03-27T14:10:37Z

Review comments:
diff_hunk: @@ -32,19 +32,28 @@ export const emails = [
 export default defineComponent({
   name: 'MarkedEmailsApp',
 
-  setup() {},
+  setup() {
+    const filterString = ref('')
+
+    const filteredEmails = computed(() => {
+      return filterString.value ? emails.filter(email => email.toLowerCase().includes(filterString.value.toLowerCase())) : []
+    })
path: 02-basics-2/40-marked-emails/MarkedEmailsApp.js
user: login: ShGKme
body: На вебинаре мы использовали вычисляемое свойство для подготовки **отфильтрованных** данных и дальнейшего вывода отфильтрованного списка. Но здесь - другая задача. Здесь требуется вывести все данные, но некоторые элементы - **отметить** маркировкой.

В результате в текущем решении вычисляемое свойство не помогает выводить почты, а напротив, даже усложняет и замедляет вывод. При изменении фильтра:

1. Сначала перебираются циклом (`filter`) все почты для создания отфильтрованного списка (то есть каждая почта уже проверена).
2. Затем почты перебираются циклом (`v-for`) уже в шаблоне при рендеринге
3. И во время рендеринга каждой почты - опять перебирается циклом (`includes`) массив уже проверенных на п.1 почт. Чтобы узнать как прошла проверка почты ранее.

Требуется подумать, как сделать такое вычисляемое свойство, которое помогает решать задачу вывода маркированных почт.
created_at: 2025-03-19T18:46:10Z
author_association: CONTRIBUTOR
diff_hunk: @@ -32,19 +32,28 @@ export const emails = [
 export default defineComponent({
   name: 'MarkedEmailsApp',
 
-  setup() {},
+  setup() {
+    const filterString = ref('')
+
+    const filteredEmails = computed(() => {
+      return filterString.value ? emails.filter(email => email.toLowerCase().includes(filterString.value.toLowerCase())) : []
+    })
+
+    return {
+      emails,
+      filterString,
+      filteredEmails,
+    }
+  },
 
   template: `
     <div>
       <div class="form-group">
-        <input type="search" aria-label="Search" />
+        <input type="search" aria-label="Search" v-model="filterString"/>
       </div>
       <ul aria-label="Emails">
-        <li>
-          Eliseo@gardner.biz
-        </li>
-        <li class="marked">
-          Jayne_Kuhic@sydney.com
+        <li v-for="email in emails" :class="{ marked: filteredEmails.includes(email) }">
path: 02-basics-2/40-marked-emails/MarkedEmailsApp.js
user: login: ShGKme
body: Напомню, что, хотя это не запрещено, лучше избегать вызова методов в шаблоне. Это делает его менее декларативным, и заставляет беспокоиться о его производительности или наличию у него побочных эффектов.

Особенно если они не простые утилиты, а методы обработки данных с перебором данных.

Для практики работы с Vue, здесь требуется подготовить данные к рендеру так, чтобы при выводе уже было известно, выводить ли текущий email промаркированным, или нет.
created_at: 2025-03-19T18:46:20Z
author_association: CONTRIBUTOR

PR comments:
user: login: jsru-1
created_at: 2025-03-17T19:16:10Z
body: Добавляю преподавателя (@ShGKme) для код-ревью.
author_association: CONTRIBUTOR
user: login: jsru-1
created_at: 2025-03-27T12:16:57Z
body: Добавляю преподавателя (@ShGKme) для код-ревью.
author_association: CONTRIBUTOR