Валидация форм

Простой пример

Валидация форм нативно поддерживается браузерами, но иногда разные браузеры могут обрабатывать вещи таким образом, что полагаться на них будет сложнее. Даже когда поддержка валидация реализована на отлично — могут потребоваться специальные проверки, и более подходящие решения основанные на Vue. Начнём с простого примера.

Есть форма с тремя полями, сделаем два из них обязательными. Давайте посмотрим на HTML для решения этой задачи:

<form
id="app"
@submit="checkForm"
action="https://vuejs.org/"
method="post"
>

<p v-if="errors.length">
<b>Пожалуйста исправьте указанные ошибки:</b>
<ul>
<li v-for="error in errors">{{ error }}</li>
</ul>
</p>

<p>
<label for="name">Имя</label>
<input
id="name"
v-model="name"
type="text"
name="name"
>
</p>

<p>
<label for="age">Возраст</label>
<input
id="age"
v-model="age"
type="number"
name="age"
min="0"
>
</p>

<p>
<label for="movie">Любимый фильм</label>
<select
id="movie"
v-model="movie"
name="movie"
>
<option>Star Wars</option>
<option>Vanilla Sky</option>
<option>Atomic Blonde</option>
</select>
</p>

<p>
<input
type="submit"
value="Отправить"
>
</p>

</form>

Начнём с начала. Тег <form> имеет ID, который мы будем использовать для компонента Vue. Есть обработчик события отправки формы (submit), который мы увидим далее, и также атрибут action с временным URL-адресом, который будет указывать на что-то реальное где-нибудь на сервере (где конечно же будет реализована и валидация на стороне сервера).

После этого расположен абзац, который показывается или скрывается в зависимости от наличия ошибок. Это позволит отобразить простой список ошибок над формой. Также обратите внимание, что мы запускаем проверку на submit формы, а не при изменении каждого поля.

Последнее, что нужно отметить — каждое из трёх полей имеет соответствующую v-model для связи с значениями, с которыми мы будем работать в JavaScript. Теперь давайте посмотрим на код.

const app = new Vue({
el: '#app',
data: {
errors: [],
name: null,
age: null,
movie: null
},
methods: {
checkForm: function (e) {
if (this.name && this.age) {
return true;
}

this.errors = [];

if (!this.name) {
this.errors.push('Требуется указать имя.');
}
if (!this.age) {
this.errors.push('Требуется указать возраст.');
}

e.preventDefault();
}
}
})

Довольно коротко и просто. Мы определяем массив для хранения ошибок и задаём null в качестве значения для всех трёх полей формы. Логика checkForm (которая запускается при отправке формы) проверяет только имя и возраст, оставляя поле выбора фильма опциональным. Если они пусты, то мы проверяем по очереди и устанавливаем конкретную ошибку для каждого поля. И это в принципе всё. Вы можете запустить демо по ссылке ниже. Не забывайте, что при успешной отправке формы будет отправляться POST-запрос на временный URL.

Посмотрите Pen валидация форм 1 от Raymond Camden (@cfjedimaster) на CodePen.

Использование пользовательских проверок

Во втором примере, второе текстовое поле (возраст) было заменено на email, который будет проверяться с помощью специальной логики. Код взят из вопроса на StackOverflow, Как валидировать электронный адрес в JavaScript?. Классный вопрос — на его фоне любые политические/религиозные холивары выглядят как незначительные разночтения насчёт сортов пива. Серьёзно — это безумие. Вот HTML, хотя он действительно близок к первому примеру.

<form
id="app"
@submit="checkForm"
action="https://vuejs.org/"
method="post"
novalidate="true"
>

<p v-if="errors.length">
<b>Пожалуйста исправьте указанные ошибки:</b>
<ul>
<li v-for="error in errors">{{ error }}</li>
</ul>
</p>

<p>
<label for="name">Имя</label>
<input
id="name"
v-model="name"
type="text"
name="name"
>
</p>

<p>
<label for="email">Email</label>
<input
id="email"
v-model="email"
type="email"
name="email"
>
</p>

<p>
<label for="movie">Любимый фильм</label>
<select
id="movie"
v-model="movie"
name="movie"
>
<option>Star Wars</option>
<option>Vanilla Sky</option>
<option>Atomic Blonde</option>
</select>
</p>

<p>
<input
type="submit"
value="Отправить"
>
</p>

</form>

Хотя изменения достаточно малы, обратите внимание на novalidate="true" наверху. Это важно, потому что браузер будет пытаться валидировать email адрес в поле с типом type="email". Честно говоря, в этом случае больше смысла доверять браузеру, но, поскольку нам нужен пример с пользовательской валидацией, мы отключим его. Вот обновлённый JavaScript.

const app = new Vue({
el: '#app',
data: {
errors: [],
name: null,
email: null,
movie: null
},
methods: {
checkForm: function (e) {
this.errors = [];

if (!this.name) {
this.errors.push('Укажите имя.');
}
if (!this.email) {
this.errors.push('Укажите электронную почту.');
} else if (!this.validEmail(this.email)) {
this.errors.push('Укажите корректный адрес электронной почты.');
}

if (!this.errors.length) {
return true;
}

e.preventDefault();
},
validEmail: function (email) {
var re = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
return re.test(email);
}
}
})

Как вы можете видеть, мы добавили validEmail в качестве нового метода и просто вызываем его из checkForm. Вы можете поиграться с этим примером здесь:

Посмотреть Pen валидации форм 2 от Raymond Camden (@cfjedimaster) на CodePen.

Другой пример пользовательской валидации

Для третьего примера, мы создадим то, что вы вероятно видели в приложениях для опросов. Пользователю предлагается потратить “бюджет” на набор возможностей новой модели Star Destroyer. Сумма должна быть равна 100. Во-первых, HTML.

<form
id="app"
@submit="checkForm"
action="https://vuejs.org/"
method="post"
novalidate="true"
>

<p v-if="errors.length">
<b>Пожалуйста исправьте указанные ошибки:</b>
<ul>
<li v-for="error in errors">{{ error }}</li>
</ul>
</p>

<p>
Учитывая бюджет в 100 долларов, укажите сколько
вы хотите потратить на следующие возможности для
Star Destroyer следующего поколения. Ваша сумма не должна превысить 100.
</p>

<p>
<input
type="number"
name="weapons"
v-model.number="weapons"
> Орудия <br/>
<input
type="number"
name="shields"
v-model.number="shields"
> Броня <br/>
<input
type="number"
name="coffee"
v-model.number="coffee"
> Кофе <br/>
<input
type="number"
name="ac"
v-model.number="ac"
> Кондиционирование <br/>
<input
type="number"
name="mousedroids"
v-model.number="mousedroids"
> Мыши-дройды <br/>
</p>

<p>
Итого: {{ total }}
</p>

<p>
<input
type="submit"
value="Отправить"
>
</p>

</form>

Обратите внимание на набор полей охватывающих пять разных функций. Обратите внимание на использование .number в атрибуте v-model. Это говорит Vue преобразовывать значение в число, когда вы его используете. Однако, здесь есть ошибка в это функции — когда значение пустое, то оно будет превращаться в строку. Вы увидите ниже как можно обойти эту проблему. Чтобы сделать это немного проще для пользователя, мы также добавили итоговую сумму внизу, чтобы они могли видеть в реальном времени, на какую сумму выбрано возможностей. Теперь давайте посмотрим на JavaScript.

const app = new Vue({
el: '#app',
data: {
errors: [],
weapons: 0,
shields: 0,
coffee: 0,
ac: 0,
mousedroids: 0
},
computed: {
total: function () {
// необходимо парсить, потому что Vue
// преобразует пустое значение в строку
return Number(this.weapons) +
Number(this.shields) +
Number(this.coffee) +
Number(this.ac + this.mousedroids);
}
},
methods: {
checkForm: function (e) {
this.errors = [];

if (this.total != 100) {
this.errors.push('Итоговая сумму должна быть 100!');
}

if (!this.errors.length) {
return true;
}

e.preventDefault();
}
}
})

Мы указали итоговую сумму как вычисляемое свойство, и это было достаточно просто реализовать для обхода этой ошибки. Мой метод checkForm теперь просто должен проверять является ли значение total равным 100 им всё. Вы можете поиграться с этим примером здесь:

Посмотрите Pen валидации форм 3 от Raymond Camden (@cfjedimaster) на CodePen.

Валидация на стороне сервера

В последнем примере мы построили что-то, что использует Ajax для валидации на сервере. Форма предложит назвать новый продукт и затем проверит, чтобы имя было уникальным. Мы быстро накидали OpenWhisk serverless действие для этой валидации. Хотя это не очень важно, вот эта логика:

function main(args) {
return new Promise((resolve, reject) => {
// плохие имена продуктов: vista, empire, mbp
let badNames = ['vista','empire','mbp'];

if (badNames.includes(args.name)) {
reject({ error: 'Существующий продукт' });
}

resolve({ status: 'ok' });
});
}

В принципе, любое имя кроме “vista”, “empire”, и “mbp” приемлемы. Хорошо, давайте посмотрим на форму.

<form
id="app"
@submit="checkForm"
method="post"
>

<p v-if="errors.length">
<b>Пожалуйста исправьте указанные ошибки:</b>
<ul>
<li v-for="error in errors">{{ error }}</li>
</ul>
</p>

<p>
<label for="name">Имя нового продукта: </label>
<input
id="name"
v-model="name"
type="text"
name="name"
>
</p>

<p>
<input
type="submit"
value="Отправить"
>
</p>

</form>

Здесь нет ничего особенного. Давайте посмотрим на JavaScript.

const apiUrl = 'https://openwhisk.ng.bluemix.net/api/v1/web/rcamden%40us.ibm.com_My%20Space/safeToDelete/productName.json?name=';

const app = new Vue({
el: '#app',
data: {
errors: [],
name: ''
},
methods: {
checkForm: function (e) {
e.preventDefault();

this.errors = [];

if (this.name === '') {
this.errors.push('Укажите имя продукта.');
} else {
fetch(apiUrl + encodeURIComponent(this.name))
.then(res => res.json())
.then(res => {
if (res.error) {
this.errors.push(res.error);
} else {
// перенаправляем на новый URL, или делаем что-либо при успехе
alert('ok!');
}
});
}
}
}
})

Мы начали с переменной, определяющей URL-адрес нашего API, который работает на OpenWhisk. Теперь посмотрим на checkForm. В этой версии мы всегда предотвращаем отправку формы (что, кстати, можно было бы сделать и в HTML вместе с Vue). Вы можете увидеть простую проверку на случай если this.name пуста, а затем мы делаем запрос на API. Если всё плохо — мы добавляем ошибку, как и раньше. Если всё хорошо — пока ничего не делаем (просто показываем alert), но вы можете перенаправить пользователя на новую страницу с именем продукта в URL или выполнять другие действия. Вы можете посмотреть демо по ссылке ниже:

Посмотрите Pen валидация форм 4 от Raymond Camden (@cfjedimaster) на CodePen.

Альтернативы

В то время как cookbook сосредотачивается на том, чтобы сделать валидацию формы своими руками, существует конечно и много отличных библиотек Vue, которые сделают большую часть работы за вас. Переход к использованию подготовленной библиотеки может повлиять на итоговый размер вашего приложения, но преимущества могут быть огромны. У вас есть код, который (скорее всего) основательно протестирован и регулярно обновляется. Некоторые примеры библиотек для валидации смотрите тут: