v-for и v-if не работают вместе в vue.js

Форма используется для отправки текста и двух опций, которые сообщают vue, в каком столбце отображать текст. Если выбрана радиокнопка col2, отправленный текст должен отображаться в столбце 2. Этого не происходит, в столбце 1 отображается текст.

У меня есть две радиокнопки, которые должны передавать значение 'one' или 'two' в newInfo.option. В submnit метод помещает данные формы в массив 'info'.

<input type="radio" id="col1" value="one" v-model="newInfo.col">
<input type="radio" id="col2" value="two" v-model="newInfo.col">

Эти данные правильно помещаются в массив 'info', и я могу перебирать их. Я знаю, что это работает, потому что я могу перебирать массив, console.log все данные в нем. Все представленные данные формы есть.

Далее я дважды перебираю этот массив в шаблоне. Один раз для info.col==="one", а другая итерация должна отображаться только когда info.col==="two". Я использую v-for и v-if вместе, что в документации vue.js разрешено делать,

https://vuejs.org/v2/guide/conditional.html

<div class="row">
            <div class="col-md-6">
                <ol>
                    <li v-for="item in info" v-if="item.col==='one'">
                        text: {{ item.text }}, col: {{ item.col }}
                    </li>
                </ol>
            </div>
            <div class="col-md-6">
                <ol>
                    <li v-for="item in info" v-if="!item.col==='two'">
                        text: {{ item.text }}, col: {{ item.col }}
                    </li>
                </ol>
            </div>
        </div>

Полный код vue.js находится на github здесь

И это работает на GH-страницах здесь

9 ответов

Решение

Удалить ! со второго, если v-if="item.col==='two'"

лучше вы можете сделать это (повторить только один раз):

<div class="row" v-for="item in info">
            <div class="col-md-6">
                <ol>
                    <li v-if="item.col==='one'">
                        text: {{ item.text }}, col: {{ item.col }}
                    </li>
                </ol>
            </div>
            <div class="col-md-6">
                <ol>
                    <li v-if="item.col==='two'">
                        text: {{ item.text }}, col: {{ item.col }}
                    </li>
                </ol>
            </div>
        </div>

Почему бы не использовать силу вычисляемых свойств?

computed: {
  infoOne: function () {
    return this.info.filter(i => i.col === 'one')
  },
  infoTwo: function () {
    return this.info.filter(i => i.col === 'two')
  }
}

Затем в каждом списке просто переберите его соответствующее свойство без проверки. пример

<ol>
   <li v-for="item in infoOne">{{item}}</li>
</ol>

Здесь рабочая скрипка

Из документов Vue :

Когда они существуют на одном узле, v-if имеет более высокий приоритет, чем v-for. Это означает, что условие v-if не будет иметь доступа к переменным из области видимости v-for:

      
<!--
This will throw an error because property "todo"
is not defined on instance.
-->
<li v-for="todo in todos" v-if="!todo.isComplete">
  {{ todo.name }}
</li>

Это можно исправить, переместив v-for в тег-оболочку (что также более явно):

      <template v-for="todo in todos">   
  <li v-if="!todo.isComplete">
     {{ todo.name }}   
  </li> 
</template> 

Если вы не возражаете против того, чтобы ваше представление продолжалось в html с помощью «display: none» , вы можете без проблем использовать v-show вместе с v-for .

Вы также можете использовать JavaScript в своем шаблоне для фильтрации элементов массива v-for. Вместо v-for="item in infos"вы можете сузить информационный массив до v-for="item in infos.filter(info => info.col === 'one')".

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

      <div class="row">
    <div class="col-md-6">
        <ol>
            <li v-for="item in infos.filter(info => info.col === 'one')">
                text: {{ item.text }}, col: {{ item.col }}
            </li>
        </ol>
    </div>
    <div class="col-md-6">
        <ol>
            <li v-for="item in info.filter(info => info.col === 'two')">
                text: {{ item.text }}, col: {{ item.col }}
            </li>
        </ol>
    </div>
</div>

<div class="row">
    <div class="col-md-6">
        <ol>
            <li v-for="item in info">
                <template v-if="item.col==='one'">
                    text: {{ item.text }}, col: {{ item.col }}
                <template>
            </li>
        </ol>
    </div>
    <div class="col-md-6">
        <ol>
            <li v-for="item in info">
                <template v-if="!item.col==='two'">
                    text: {{ item.text }}, col: {{ item.col }}
                <template>
            </li>
        </ol>
    </div>
</div>

Если по какой-то причине фильтрация списка невозможна, вы можете преобразовать элемент с помощью обоих v-for и v-if в компонент и переместите v-if в компонент.

Исходный пример

Исходный цикл

<li v-for="item in info" v-if="item.col==='one'">
  text: {{ item.text }}, col: {{ item.col }}
</li>

Предлагаемый рефакторинг

Реорганизованный цикл

<custom-li v-for="item in info" :visible="item.col==='one'">
  text: {{ item.text }}, col: {{ item.col }}
</custom-li>

Новый компонент

Vue.component('custom-li', {
  props: ['visible'],
  template: '<li v-if="visible"><slot/></li>'
})

Вычислено

В большинстве случаевcomputedСвойство действительно лучший способ сделать это, как сказал user5596717 , но в моем случае у меня есть a внутри другого, поэтому вычисление не имеет смысла для второго v-for.

V-шоу

Поэтому вместо использованияv-ifкоторый имеет более высокий приоритет, чем (как упоминалось user12975352), альтернативой может быть использование, которое не имеет такого более высокого приоритета. Это в основном делает то же самое и работает в вашем случае.

Нет обертки

Использование позволяет избежать добавления бесполезного элемента-оболочки, как я видел в некоторых ответах, что в моем случае было требованием, чтобы не испортить селекторы CSS и структуру html.

Недостатки v-show

Единственным недостатком использования является то, что элемент все равно будет добавлен в ваш HTML, что в моем собственном случае все еще испортит селекторы CSS :first, например. Так что лично я пошел на.filter()решение, упомянутое user13608849. Но в большинстве основных случаев вы определенно можете использоватьv-showДля решения этой проблемы.

      <div class="row">
  <div class="col-md-6">
      <ol>
          <li v-for="item in info" v-show="item.col==='one'">
              text: {{ item.text }}, col: {{ item.col }}
          </li>
      </ol>
  </div>
  <div class="col-md-6">
      <ol>
          <li v-for="item in info" v-show="item.col!=='two'">
              text: {{ item.text }}, col: {{ item.col }}
          </li>
      </ol>
  </div>
</div>

Если вам интересно, какого черта я использую внутри другогоv-for, вот урезанная версия моего варианта использования:

Это список бесед (который является вычисляемым свойством), затем отображаются аватары всех участников каждой беседы, за исключением аватара пользователя, который в данный момент просматривает ее.

      <a v-for="convo in filteredConversations" class="card">
  <div class="card-body">
      <div class="row">
          <div class="col-auto">
            <div class="avatar-group">
                <div class="avatar" v-for="participant in convo.participants.filter(info => !thatsMe(info))">
                    <img :src="userAvatarUrl(participant)" :alt="participant.name" class="avatar-img">
                </div>
            </div>
          </div> ...
<div v-for="item in items">

   <div v-if="checkThis(item.computeThisProperty)" />
   <div v-else />

</div>

methods: {
   checkThis(i) {
      return this[i];
   }
},

computed: {
   myComputedProperty() {
      return this.$store.state.something ? true : false;
   }
}

Ваш второй чек !item.col==='two' и будет отображаться, только если он не равен "два".

РЕДАКТИРОВАТЬ:! Оператор not, скорее всего, связан более тесно, чем ===, поэтому всегда будет возвращать false. Добавьте скобки, чтобы контролировать порядок применения. Я говорю скорее всего, потому что это может быть немного волшебства Vue, с которым я не знаком, а не просто выражение JavaScript.

Я думаю, что вы хотите удалить этот восклицательный знак. Или сделать это !(item.col==='one') для отображения для любого значения, кроме "один".

Другие вопросы по тегам