Vue 3 — Кнопки не обновляются после второго выпадающего списка изменений

#twitter-bootstrap #vue.js #vuejs3 #bootstrap-5

Вопрос:

Я довольно новичок в Vue, и мне трудно понять, как решить эту проблему. Я действительно продвинулся в углубленном проекте, но ломаю голову над тем, что должно быть простой функцией…

Краткая справочная информация/Обзор:

Я получаю данные из API (моего собственного), который извлекает исследование, созданное пользователем. Они могут переключиться на другое из своих исследований, что изменит данные на странице (звезды, указывающие на то, что предмет уже находится в исследовании). Это их «общее исследование просмотра страниц». Они могут добавлять элементы на странице в исследование. Они нажимают на элемент, появляется модальный загрузчик 5 с выпадающим списком их исследований. Текущее исследование страницы выбрано по умолчанию, но они могут выбрать любое из своих других исследований, чтобы добавить его, если захотят. Поэтому, если они добавляют предмет в другое исследование (не то, которое они просматривают), мыпредположим, что они могут добавить его и позволить API вернуть специальный ответ (409), в котором он покажет пользователю сообщение «это уже находится в исследовании» (тост/флэш-сообщение/и т. Д.). Если это их текущее «исследование страницы», то они должны иметь возможность добавить его, если он не существует, или удалить, если он уже есть в исследовании.

Я просто хотел предоставить общий обзор на случай, если что-то в нем может повлиять на решение (Vue3, начальная загрузка 5, модальная загрузка, выпадающий список выбора и т. Д.).

Из-за сложности и уместности я создал общий макет, воспроизводящий проблему, а также демонстрационную версию на CodePen.

Вопрос:

У меня есть выпадающий список (селектор изучения) и 2 кнопки (Добавить и удалить). Если они выбирают исследование из выпадающего списка, которое не является просматриваемым в данный момент «исследованием страницы», мы предполагаем, что «Добавить». Если это просматриваемая в данный момент «страница исследования», нам нужно сделать проверку. Он проверяет, находится ли этот «предмет» в исследовании. Если это так, то покажите кнопку «Удалить», в противном случае покажите кнопку «Добавить».

Когда я выбираю элемент, который уже находится в выбранном «исследовании страницы», на нем отображается кнопка «Удалить» (сначала). Затем, когда я переключаюсь на другое исследование в раскрывающемся списке, он меняет местами кнопки (показывает «Добавить»). Пока все хорошо… Однако, когда я переключаю его обратно на текущее «исследование страницы», кнопка «Добавить» остается, когда она ДОЛЖНА переключиться на кнопку «Удалить»… Как будто он обновился один раз, игнорирует меня и больше не слушает, лол.

I have tried watchers, computed, tried to forceUpdate… I just don’t know enough about Vue to get this working.

Mock App Code with dummy data:

 <div id="app">
  Study ID: {{ currentStudyId }}
  
  <br><br>

  Selected StudyData Item: 
  <select v-model="selectedStudyDataItem">
    <option value="1">1</option>
    <option value="2">2</option>
    <option value="3">3</option>
    <option value="4">4</option>
    <option value="5">5</option>
    <option value="6">6</option>
    <option value="7">7</option>
    <option value="8">8</option>
  </select>

  <br><br>

  <select v-model="selectedStudyModel" name="study_id" class="form-select">
    <option disabled value="">Choose a study...</option>
    <option v-for="item in studies" :value="item.id" :key="item.id">
      {{ item.title }}
    </option>
  </select>

  <button v-show="showAddButton()" type="submit" class="btn btn-primary">Add</button>

  <button v-show="showRemoveButton()" type="submit" class="btn btn-danger">Remove</button>
</div>

 
 const TestApp = {
  data() {
    return {
      currentStudyId: 2,
      selectedStudyDataItem: 1,
      selectedStudyModel: "", // dropdown model
      studies: [
        { id: 1, title: "Study 1" },
        { id: 2, title: "Study 2" },
        { id: 3, title: "Study 3" },
        { id: 4, title: "Study 4" }
      ],
      studyData: {
        title: "My Cool Study",
        user_id: 1,
        items: [1, 3]
      }
    };
  },

  methods: {
    showAddButton() {
      // if it isnt the current study, we assume add and let api handle it
      if (this.selectedStudyModel !== this.currentStudyId) {
        return true;
      }

      // else, if it isn't in the current viewed study, we can add it
      if (!this.isInStudy(this.selectedStudyDataItem)) {
        return true;
      }

      return false;
    },
    showRemoveButton() {
      if (this.selectedStudyModel === this.currentStudyId) {
        if (this.isInStudy(this.selectedStudyDataItem)) {
          return true;
        }
      }

      return false;
    },
    isInStudy(something) {
      if (this.studyData) {
        const match = (item) => item === something;
        return this.studyData.items.some(match);
      }

      return false;
    }
  }
};

Vue.createApp(TestApp).mount("#app");
 

Пример кода

Комментарии:

1. это выглядит так, как будто vue2 new Vue({ это сделано намеренно?

2. Нет, это Vue3. Код скопирован из JSFiddle.

3. Но скрипка говорит Vue 2.2.1 ?

4. Да. Я торопился выйти за дверь, чтобы поиграть в софтбол моей дочери. Я изменил его с их включенной опции vue2, и похоже, что этого не потребовалось. То, как они обрабатывают изменения, странно, и моя ссылка выглядела так, как будто она была непосредственно связана с редакцией. Вместо этого я поставил его на кодовую панель. — Несмотря на это, проблема присутствовала на Vue 2 и 3. — Я собираюсь просмотреть ответ Дэниела и посмотреть, повлияет ли это на мое реальное приложение.

Ответ №1:

У вас возникла проблема из-за приведения типов.

   <select v-model="selectedStudyDataItem">
    <option value="1">1</option>
    <option value="2">2</option>
    <option value="3">3</option>
    <option value="4">4</option>
    <option value="5">5</option>
    <option value="6">6</option>
    <option value="7">7</option>
    <option value="8">8</option>
  </select>
 

👆 будет использовать жало в качестве selectedStudyModel после изменения

Чтобы обойти это, вам нужно обновить showAddButton и включить parseInt для обработки

 showAddButton() {
  // if it isnt the current study, we assume add and let api handle it
  if ( parseInt(this.selectedStudyModel) !== this.currentStudyId ) {
    return true;
  }

  // else, if it isn't in the current viewed study, we can add it
  if ( ! this.isInStudy(parseInt(this.selectedStudyDataItem)) ) {
    return true;
  }

  return false;
},
 

в качестве альтернативы вы также можете использовать :value , который преобразует его

   <select v-model="selectedStudyDataItem">
    <option :value="1">1</option>
    <option :value="2">2</option>
    <option :value="3">3</option>
    <option :value="4">4</option>
    <option :value="5">5</option>
    <option :value="6">6</option>
    <option :value="7">7</option>
    <option :value="8">8</option>
  </select>
 

👆 теперь они будут отлиты как Number вместо String , однако я бы рекомендовал, чтобы в сценариях было лучше быть явным и не полагаться для этого на магию шаблонов.

Некоторые другие соображения

  • используйте вычисляемые вместо методов, их результат кэшируется, поэтому вы вызываете их только по мере необходимости
  • если у вас есть только опции «добавить» или «удалить», не нужно вычислять «удалить», все как раз наоборот (вы можете использовать v-if и v-else )
 new Vue({
  el: '#app',
    data() {
        return {
        'currentStudyId': 2,
      'selectedStudyDataItem': 1,
            'selectedStudyModel': '',       // dropdown model
      'studies': [
        {'id': 1, 'title': 'Study 1'},
        {'id': 2, 'title': 'Study 2'},
        {'id': 3, 'title': 'Study 3'},
        {'id': 4, 'title': 'Study 4'},
      ],
      'studyData': {
        'title': 'My Cool Study',
        'user_id': 1,
        'items': [
            1, 3
        ],
      },
    }
  },
  computed:{
    showAddButton() {
      // if it isnt the current study, we assume add and let api handle it
      if ( parseInt(this.selectedStudyModel) !== this.currentStudyId ) {
        return true;
      }

      // else, if it isn't in the current viewed study, we can add it
      if ( ! this.isInStudy(parseInt(this.selectedStudyDataItem)) ) {
        return true;
      }

      return false;
    },
  },
  methods: {
    isInStudy(something) {
        if ( this.studyData ) {
                const match = (item) => item === something;
                return this.studyData.items.some(match);
            }

            return false;
    }
  }
  
}); 
 <script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
  Study ID: {{ currentStudyId }}
  
  <br><br>

  Selected StudyData Item: 
  <select v-model="selectedStudyDataItem">
    <option value="1">1</option>
    <option value="2">2</option>
    <option value="3">3</option>
    <option value="4">4</option>
    <option value="5">5</option>
    <option value="6">6</option>
    <option value="7">7</option>
    <option value="8">8</option>
  </select>

  <br><br>

  <select v-model="selectedStudyModel" name="study_id" class="form-select">
    <option disabled value="">Choose a study...</option>
    <option v-for="item in studies" :value="item.id" :key="item.id">
      {{ item.title }}
    </option>
  </select>

  <button v-if="showAddButton" type="submit" class="btn btn-primary">Add</button>

  <button v-else type="submit" class="btn btn-danger">Remove</button>

  <p>
    <strong>How to reproduce:</strong> Choose the "Study 2" option from the 2nd dropdown. The button should be "Remove" because "Selected StudyData Item [1]" (dropdown above it) is in the "studyData". 1 and 3 are in the studyData for "Study 2".
  </p>
  <p>
    Then select a "studyData Item" that isn't in the study (not 1 or 3), ie: 8. It should change the button to "Add".
  </p>
  <p>
  Now, <u>here is the problem</u>. Swap the "studyData Item" back to 1 or 3 (items in the studyData). It is supposed to change the button back to "Remove" because 1 and 3 are both in the study (study 2).. However, it just does nothing lol.. 
  </p>
  selectedStudyDataItem: {{JSON.stringify(selectedStudyDataItem)}} | {{ typeof selectedStudyDataItem }}
  <br/>
  selectedStudyModel: {{JSON.stringify(selectedStudyModel)}} | {{ typeof selectedStudyModel}}
</div> 

Комментарии:

1. Спасибо @daniel за помощь в решении моей проблемы! — После твоего ответа это было очевидно и кое-что, что я должен был знать… просто затуманен 1000 строками кода и незнаком с Vue. Ваш ответ позволил мне сосредоточиться на отладке и оценить, как я использовал значения (строка/целое число).