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')
для отображения для любого значения, кроме "один".