v-if
Ряд директив позволяет по условию изменять структуру DOM, и одной из таких является директива v-if. Она позволяет отобразить или скрыть элемент HTML по условию. Например:
<!DOCTYPE html>
<html>
<head>
<title>Vue.js</title>
<meta charset="utf-8" />
</head>
<body>
<div id="app">
<p v-if="visible">Первый параграф</p>
<p>Второй параграф</p>
<button v-on:click="visible = !visible">{{ visible ? 'Скрыть' : 'Показать' }}</button>
</div>
<script src="https://unpkg.com/vue"></script>
<script>
const app = new Vue({
el: '#app',
data: {
visible: true
}
})
</script>
</body>
</html>
Директива v-if в качестве параметра принимает условие, которое возвращает значение true или false. Если true, то элемент, к которому применяется директива v-if, отображается. Если false, то, наоборот, скрывается. В данном примере это значение определено в свойстве visible. С помощью кнопки в примере выше мы можем изменить значение свойства visible и соответственно отобразить или скрыть элемент.
В паре с директивой v-if может использоваться директива v-else, которая позволяет отобразить другой элемент, если условие в директиве v-if равно false:
<!DOCTYPE html>
<html>
<head>
<title>Vue.js</title>
<meta charset="utf-8" />
</head>
<body>
<div id="app">
<p v-if="visible">Первый параграф</p>
<p v-else>Второй параграф</p>
<button v-on:click="visible = !visible">{{ visible ? 'К параграфу 2' : 'К параграфу 1' }}</button>
</div>
<script src="https://unpkg.com/vue"></script>
<script>
const app = new Vue({
el: '#app',
data: {
visible: true
}
})
</script>
</body>
</html>
В данном случае, если условие, которое представляет свойство visible, равно true, то отображается первый параграф. Если условие возвращает false, то отображается второй параграф.
template
Так как выражение v-if является директивой, то оно может применяться только к одному элементу. Но что если мы хотим применить ее к группе элементов? В этом случае мы можем применить v-if для элемента <template>, который выступает в качестве обертки для группы элементов:
<!DOCTYPE html>
<html>
<head>
<title>Vue.js</title>
<meta charset="utf-8" />
</head>
<body>
<div id="app">
<template v-if="visible">
<h1>Заголовок 1</h1>
<p>Параграф 1</p>
</template>
<template v-else>
<h1>Заголовок 2</h1>
<p>Параграф 2</p>
</template>
<button v-on:click="visible = !visible">{{ visible ? 'К параграфу 2' : 'К параграфу 1' }}</button>
</div>
<script src="https://unpkg.com/vue"></script>
<script>
const app = new Vue({
el: '#app',
data: {
visible: true
}
})
</script>
</body>
</html>
v-else-if
С помощью директивы v-else-if к v-if можно добавить обработку дополнительных условий. Например, в зависимости от введенного числа необходимо отображать тот или иной элемент:
<!DOCTYPE html>
<html>
<head>
<title>Vue.js</title>
<meta charset="utf-8" />
</head>
<body>
<div id="app">
<input type="number" v-model="number" />
<p v-if="number == 1">Один</p>
<p v-else-if="number == 2">Два</p>
<p v-else-if="number > 2 && number < 7">Несколько</p>
<p v-else>Много</p>
</div>
<script src="https://unpkg.com/vue"></script>
<script>
const app = new Vue({
el: '#app',
data: {
number: 1
}
})
</script>
</body>
</html>
Аналогично можно использовать v-else-if вместе с template:
<!DOCTYPE html>
<html>
<head>
<title>Vue.js</title>
<meta charset="utf-8" />
</head>
<body>
<div id="app">
<input type="number" v-model="number" />
<template v-if="number == 1">
<p>Один</p>
</template>
<template v-else-if="number == 2">
<p>Два</p>
</template>
<template v-else-if="number > 2 && number < 7">
<p>Несколько</p>
</template>
<template v-else>
<p>Много</p>
</template>
</div>
<script src="https://unpkg.com/vue"></script>
<script>
const app = new Vue({
el: '#app',
data: {
number: 1
}
})
</script>
</body>
</html>
v-show
Директива v-show аналогично v-if позволяет скрывать или отображать элементы по определенному условию:
<!DOCTYPE html>
<html>
<head>
<title>Vue.js</title>
<meta charset="utf-8" />
</head>
<body>
<div id="app">
<div v-show="visible">
<h2>Заголовок</h2>
<p>Текст</p>
</div>
<button v-on:click="visible = !visible">{{ visible ? 'Скрыть' : 'Отобразить' }}</button>
</div>
<script src="https://unpkg.com/vue"></script>
<script>
const app = new Vue({
el: '#app',
data: {
visible: true
}
})
</script>
</body>
</html>
Здесь в зависимости от значения условия, которое представляет свойство visible, будет скрываться или отображаться блок div. А с помощью кнопки мы можем переключить значение этого свойства с true на false и наоборот.
Но в отличие от v-if директива v-show не изменяет структуру DOM, а манипулирует значением стилевого свойства display. То есть если условие в v-show возвращает false, то для элемента устанавливается стиль display:none; и тем самым данный элемент скрывается на веб-странице.
В то же время манипуляции с DOM через v-if увеличивают накладные расходы и снижают производительность. Поэтому в тех ситуациях, когда возможно частое переключение видимости элемента, следует предпочитать v-show.
v-for
Для рендеринга коллекций предназначена директива v-for. Она имеет следующий синтаксис:
v-for="item in items"
Где items представляет массив, а item псевдоним для текущего перебираемого элемента из массива items.
Например, выведем массив элементов на страницу:
<!DOCTYPE html>
<html>
<head>
<title>Vue.js</title>
<meta charset="utf-8" />
</head>
<body>
<div id="app">
<ul>
<li v-for="phone in phones">{{ phone }}</li>
</ul>
</div>
<script src="https://unpkg.com/vue"></script>
<script>
const app = new Vue({
el: '#app',
data: {
phones: ['iPhone', 'Samsung', 'Nokia', 'Xiaomi']
}
})
</script>
</body>
</html>
Таким образом, для каждого элемента в массиве phones будет создаваться html-элемент <li>.
Подобным образом можно выводить и более сложные объекты из массива:
<!DOCTYPE html>
<html>
<head>
<title>Vue.js</title>
<meta charset="utf-8" />
</head>
<body>
<div id="app">
<ul>
<li v-for="user in users">
<p>Имя: {{ user.name }}</p>
<p>Возраст: {{ user.age }}</p>
</li>
</ul>
</div>
<script src="https://unpkg.com/vue"></script>
<script>
const app = new Vue({
el: '#app',
data: {
users: [
{ name: 'Tom', age: 22 },
{ name: 'Bob', age: 31 },
{ name: 'Sam', age: 28 }
]
}
})
</script>
</body>
</html>
Индексы
При переборе элементов с помощью дополнительного параметра в v-for мы можем получить индекс элемента в массиве, используя следующий синтаксис:
v-for="(element, index) in array"
Где element - это текущий перебираемый элемент в массиве array, а index - индекс этого элемента в массиве. Например:
<!DOCTYPE html>
<html>
<head>
<title>Vue.js</title>
<meta charset="utf-8" />
</head>
<body>
<div id="app">
<ul>
<li v-for="(phone, index) in phones">
{{ index + 1 }}. {{ phone }}
</li>
</ul>
</div>
<script src="https://unpkg.com/vue"></script>
<script>
const app = new Vue({
el: '#app',
data: {
phones: ['iPhone', 'Samsung', 'Nokia', 'Xiaomi']
}
})
</script>
</body>
</html>
Перебор объектов
Подобно тому, как мы перебираем массив, мы можем перебирать и все свойства одного объекта с помощью синтаксиса:
v-for="(value, property) in obj"
Где property - название свойства объекта, а value - его значение.
<!DOCTYPE html>
<html>
<head>
<title>Vue.js</title>
<meta charset="utf-8" />
</head>
<body>
<div id="app">
<ul>
<li v-for="user in users">
<p v-for="(value, key) in user">
{{ key }} : {{ value }}
</p>
</li>
</ul>
</div>
<script src="https://unpkg.com/vue"></script>
<script>
const app = new Vue({
el: '#app',
data: {
users: [
{ name: 'Tom', age: 22 },
{ name: 'Bob', age: 31 },
{ name: 'Sam', age: 28 }
]
}
})
</script>
</body>
</html>
template
Директиву v-for можно применить только к одному html-элементу. Если необходимо, чтобы для каждого объекта из массива создавалось несколько html-элементов, то блок этих элементов следует обертывать элементом template:
<!DOCTYPE html>
<html>
<head>
<title>Vue.js</title>
<meta charset="utf-8" />
</head>
<body>
<div id="app">
<ul>
<template v-for="user in users">
<li>Name: {{ user.name }}</li>
<li>Age: {{ user.age }}</li>
</template>
</ul>
</div>
<script src="https://unpkg.com/vue"></script>
<script>
const app = new Vue({
el: '#app',
data: {
users: [
{ name: 'Tom', age: 22 },
{ name: 'Bob', age: 31 },
{ name: 'Sam', age: 28 }
]
}
})
</script>
</body>
</html>
Перебор чисел
С помощью v-for можно перебрать все числа от 1 до определенного значения. Например, перебор всех чисел от 1 до 10:
<!DOCTYPE html>
<html>
<head>
<title>Vue.js</title>
<meta charset="utf-8" />
</head>
<body>
<div id="app">
<div>
<span v-for="n in 10">{{ n }}</span>
</div>
</div>
<script src="https://unpkg.com/vue"></script>
<script>
const app = new Vue({
el: '#app'
})
</script>
</body>
</html>
Управление массивами
Во Vue.js для каждого массива определено ряд методов, с помощью которых можно управлять элементами массива:
Эти методы являются обертками над одноименными стандартными методами JavaScript для управления массивами и работают точно также. Единственное отличие их от стандартных методов состоит в том, что эти методы-обертки информируют систему Vue.js о том, что с массивом были произведены некоторые действия, и соответственно для этого массива может быть произведен повторный рендеринг на веб-странице.
Но кроме выше описанных методов, которые изменяют отдельные элементы массива, есть ряд методов в JavaScript, которые возвращают новый массив, типа filter(), concat(), slice(). Результат таких функций лучше привязывать к вычисляемому свойству, которое позволит произвести повторный рендеринг элементов веб-страницы.
Добавление и удаление
Определим код для добавления нового элемента в массив и удаления из массива:
<!DOCTYPE html>
<html>
<head>
<title>Vue.js</title>
<meta charset="utf-8" />
</head>
<body>
<div id="app">
<p>
<input type="text" v-model="newPhone" />
<button v-on:click="phones.push(newPhone)">Добавить</button>
</p>
<ul>
<li v-for="(phone, index) in phones">
<p>{{ phone }} <button v-on:click="phones.splice(index, 1)">Удалить</button></p>
</li>
</ul>
</div>
<script src="https://unpkg.com/vue"></script>
<script>
const app = new Vue({
el: '#app',
data: {
newPhone: '',
phones: ['iPhone', 'Samsung', 'Nokia', 'Xiaomi']
}
})
</script>
</body>
</html>
При нажатии на кнопку в массив будет добавлять введенное в текстовое поле значение, которое доступно через свойство newPhone. И в итоге произойдет обновление списка на веб-странице, и мы увидим добавленный элемент.
И кроме того, для каждого элемента предусмотрена кнопка, через которую по индексу можно удалить элемент из массива.
Установка элемента в массиве
При работе с массивами мы можем столкнуться с некоторыми ограничениями. В частности, мы не можем просто по индексу переустановить элемент массива, присвоив ему новое значение:
this.phones[1] = 'Samsung Galaxy S8'
Для установки значения нам надо использовать метод Vue.set():
Vue.set(массив, индекс_элемента, новое_значение)
Например, обновим второй элемент массива:
<!DOCTYPE html>
<html>
<head>
<title>Vue.js</title>
<meta charset="utf-8" />
</head>
<body>
<div id="app">
<ul>
<li v-for="phone in phones">
<p>{{ phone }}</p>
</li>
</ul>
<button v-on:click="updateList">Обновить</button>
</div>
<script src="https://unpkg.com/vue"></script>
<script>
const app = new Vue({
el: '#app',
data: {
phones: ['iPhone', 'Samsung', 'Nokia', 'Xiaomi']
},
methods: {
updateList: function() {
Vue.set(this.phones, 1, 'Samsung Galaxy S8')
}
}
})
</script>
</body>
</html>
Возвращение нового массива
Ряд методов, такие как slice, concat, filter не изменяют текущий массив, а возвращают новый. Одно из решений может заключаться в переустановке массива. Например, используем метод slice, который возвращает часть массива:
<!DOCTYPE html>
<html>
<head>
<title>Vue.js</title>
<meta charset="utf-8" />
</head>
<body>
<div id="app">
<ul>
<li v-for="phone in phones">
<p>{{ phone }}</p>
</li>
</ul>
<button v-on:click="updateList">Обновить</button>
</div>
<script src="https://unpkg.com/vue"></script>
<script>
const app = new Vue({
el: '#app',
data: {
phones: ['iPhone', 'Samsung', 'Nokia', 'Xiaomi']
},
methods: {
updateList: function() {
this.phones = this.phones.slice(1, 3)
}
}
})
</script>
</body>
</html>
Для фиксации нового массива присваиваем полученный массив свойству phones.
Однако данный способ не всегда является оптимальным. Особенно если мы хотим сохранить старый массив и изменять лишь его визуальное представление. И более идеальным вариантом, как правило, является разделение данных и представления этих данных. Для представления данных обычно определяется вычисляемое свойство-список, элементы которого выводятся на веб-страницу:
<!DOCTYPE html>
<html>
<head>
<title>Vue.js</title>
<meta charset="utf-8" />
</head>
<body>
<div id="app">
<input v-model="start" type="number" />
<input v-model="end" type="number" />
<ul>
<li v-for="phone in visibleList">
<p>{{ phone }}</p>
</li>
</ul>
</div>
<script src="https://unpkg.com/vue"></script>
<script>
const app = new Vue({
el: '#app',
data: {
start: 0,
end: 3,
phones: ['iPhone', 'Samsung', 'Nokia', 'Xiaomi']
},
computed: {
visibleList: function() {
return this.phones.slice(this.start, this.end);
}
}
})
</script>
</body>
</html>
Фильтрация и сортировка массива
Фильтрация массива
Стандартной ситуацией при работе с коллекциями объектов является фильтрация. Как правило, при фильтрации есть некоторый начальный список, а пользователю же возвращается некоторый временный результат. Начальный же список при этом не изменяется. И для этого во Vue.js лучше определить привязку к вычисляемому свойству:
<!DOCTYPE html>
<html>
<head>
<title>Vue.js</title>
<meta charset="utf-8" />
</head>
<body>
<div id="app">
<p>
<input type="text" v-model="company" />
</p>
<ul>
<li v-for="phone in filteredList">
<p>{{ phone.title }} - {{ phone.company }}</p>
</li>
</ul>
</div>
<script src="https://unpkg.com/vue"></script>
<script>
const app = new Vue({
el: '#app',
data: {
company: '',
phones: [
{ title: 'iPhone 7', company: 'Apple' },
{ title: 'iPhone 6S', company: 'Apple' },
{ title: 'Galaxy S8', company: 'Samsung' },
{ title: 'Galaxy S7 Edge', company: 'Samsung' },
{ title: 'Nokia N8', company: 'HMD Global' }
]
},
computed: {
filteredList: function() {
const company = this.company
return this.phones.filter(function (element) {
if (company === '') {
return true
}
return element.company.indexOf(company) > -1
})
}
}
})
</script>
</body>
</html>
В данном случае фильтрация работает по принципу живого поиска. При вводе значения в текстовое поле происходит повторное вычисление свойства filteredList. Это свойство представляет результат функции, которая возвращает те объекты, у которых поле company соответствует введенному значению. То есть идет фильтрация телефонов по компании производителя. Если же значение не введено, то возвращаем все элементы из массива phones. В итоге можем динамически фильтровать элементы списка.
Сортировка списка
Для сортировки списка применяется похожая техника, что и для фильтрации:
<!DOCTYPE html>
<html>
<head>
<title>Vue.js</title>
<meta charset="utf-8" />
<style>
a:hover {
cursor: pointer;
}
</style>
</head>
<body>
<div id="app">
<table>
<tr>
<td><a @click="sortParam = 'title'">Модель</a></td>
<td><a @click="sortParam = 'company'">Компания</a></td>
<td><a @click="sortParam = 'price'">Цена</a></td>
</tr>
<tr v-for="phone in sortedList">
<td>{{ phone.title }}</td>
<td>{{ phone.company }}</td>
<td>{{ phone.price }}</td>
</tr>
</table>
</div>
<script src="https://unpkg.com/vue"></script>
<script>
const app = new Vue({
el: '#app',
data: {
sortParam: '',
phones: [
{ title: 'Galaxy S8', company: 'Samsung', price: 45000 },
{ title: 'iPhone 7', company: 'Apple', price: 49000 },
{ title: 'Nokia N8', company: 'HMD Global', price: 25000 },
{ title: 'Galaxy Note 8', company: 'Samsung', price: 50000 },
{ title: 'iPhone 8', company: 'Apple', price: 60000 }
]
},
computed: {
sortedList () {
switch (this.sortParam) {
case 'title': return this.phones.sort(sortByTitle)
case 'company': return this.phones.sort(sortByCompany)
case 'price': return this.phones.sort(sortByPrice)
default: return this.phones
}
}
}
})
const sortByTitle = function (d1, d2) {
return (d1.title.toLowerCase() > d2.title.toLowerCase()) ? 1 : -1
}
const sortByCompany = function (d1, d2) {
return (d1.company.toLowerCase() > d2.company.toLowerCase()) ? 1 : -1
}
const sortByPrice = function (d1, d2) {
return (d1.price > d2.price) ? 1 : -1
}
</script>
</body>
</html>
Как и в случае с фильтрацией, привязка устанавливается к вычисляемому свойству. При нажатии на заголовок столбца в таблице происходит переустановка значения свойства sortParam, которое представляет критерий фильтрации. При его изменении повторно вычисляется свойство sortedList, которое сортирует массив phones в соответствии со значением в sortParam. Для фильтрации по трем критериям определены три вспомогательные функции sortByTitle, sortByCompany и sortByPrice.