хуйу нас не матерятся
Недавно один мой знакомый вайтишник спросил как запилить редактирование данных в бд, ну Вы знаете, так чтобы и страница на каждый чих не перезагружалась и чтобы формочка редактирования объекта появлялась итд. Ну вот этим мы сейчас и займёмся.
У нас есть какая-то отдельная апи которая возвращает список чего-то, пусть это будут, например, котики: GET: /api/cats.json
[
{
"id": 1,
"name": "Барсик",
"breed": "Persian",
"age": 12,
"sex": "male"
},
...
]
и по каждому котику будет ещё дополнительная апи, с дополнительной информацией: GET /api/cats/1.json
{
"id": 1,
"name": "Барсик",
"breed": "Persian",
"age": 12,
"sex": "male",
"owner": {
"phone": "+7888999000",
"name": "Сычёв",
"city": "Мухосранск"
}
}
Нам нужно написать веб страничку, которая ассинхронно загрузит табличку с котиками, и дополнительно, тыкнув по кнопке, будет показывать формочку с расширенной информацией. А тыкнув по другой кнопке, появится форма редактирования, или добавления котика.
Итак, нам нужно подключить, стили, jquery, main.js в который положим весь наш код и зафигачить минимальную index.html:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ru" lang="ru">
<head>
<title>JQUERY interactive example</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta name="viewport" content="initial-scale=1">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" crossorigin="anonymous">
<link href="./css/main.css" type="text/css" rel="stylesheet">
</head>
<body>
<div class="container">
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<a class="navbar-brand" href="#">JQUERY example</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNavAltMarkup" aria-controls="navbarNavAltMarkup" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNavAltMarkup">
<div class="navbar-nav">
<!-- какие-то элементы меню -->
</div>
</div>
</nav>
<br/>
</div>
<div class="container">
... content
</div>
<script src="https://code.jquery.com/jquery-3.4.1.min.js" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js" crossorigin="anonymous"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js" crossorigin="anonymous"></script>
<script src="./js/main.js"></script>
</body>
</html>
Для начала фигачим шаблон:
<table class="table table-hover">
<thead>
<tr>
<th>id</th>
<th>Имя</th>
<th>Порода</th>
<th>Возраст</th>
<th>Пол</th>
<th></th>
</tr>
</thead>
<tbody id="cats_table_body"></tbody>
</table>
id="cats_table_body" нам нужен для лёгкого поиска тела таблицы по ID. Ну и да, делаем нашу первую js функцию, которая будет загружать список котиков с сервера:
function loadCatsList(url) {
$.get(url)
.then( data=> {
console.log('* data', data);
})
}
Функция получает url по которому лежат котики, и если сервер отдаёт правильные заголовки, то jquery автоматически распарсит json в объект.
Теперь нам надо написать функцию, которая сгенерирует тело таблицы, по полученным данным:
function genTableBody(data) {
let result = '';
for(let item of data) {
result += '<tr>';
for(let index in item)
result += `<td>${item[ index ]}</td>`;
result += `<td><a class="btn btn-primary" href="">Owner info</a></td>`;
result += '</tr>';
}
return result;
}
Теперь мы можем вставить сгенерированные строки в таблицу:
function loadCatsList(url) {
$.get(url)
.then( data=> {
$('#cats_table_body').html(genTableBody(data));
});
}
Сейчас если мы кликнем по кнопке "Owner info", то ничего не произойдёт, нужно это исправить. Для этого, вешаем на кнопку вызов функции- обработчика события:
result += `<td><a class="btn btn-primary" href="javascript:catClick(${item.id})">Owner info</a></td>`;
И пишем тестовую функцию- обработчик:
function catClick(id) {
alert('on catClick ' + id);
}
Если сейчас всё у нас без ошибок, то по клику на кнопку должен появляться алерт:
В первую очередь нужно написать стандартную bootstrap формочку, которую нужно положить, опять ж, в стандартное bootstrap всплывающее окошко:
<div class="modal fade" id="ownerform" tabindex="-1" role="dialog" aria-labelledby="exampleModalCenterTitle" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLongTitle">Cat owner</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<div class="row">
<div class="col-sm-12">
<div class="form-group row">
<label class="col-sm-2 col-form-label">Phone:</label>
<div class="col-sm-10">
<input type="text" readonly class="form-control-plaintext" id="ownerform_phone" value="">
</div>
</div>
<div class="form-group row">
<label class="col-sm-2 col-form-label">Name:</label>
<div class="col-sm-10">
<input type="text" readonly class="form-control-plaintext" id="ownerform_name" value="">
</div>
</div>
<div class="form-group row">
<label class="col-sm-2 col-form-label">City:</label>
<div class="col-sm-10">
<input type="text" readonly class="form-control-plaintext" id="ownerform_city" value="">
</div>
</div>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
Да, чтобы всё работало красиво, приходится копипастить могабукаф. Но благо, для этого есть типовые примеры на сайте с документацией.
Ок, теперь у нас есть форма, есть кнопка с обработчиком события, давайте навесим на неё активацию bootstrap modal:
function catClick(id) {
//alert('on catClick ' + id);
$("#ownerform").modal()
}
И cделаем api запрос за расширенной информацией по котику:
function catClick(id) {
$.get(`./api/cats/${id}.json`)
.then( data => {
console.log('* DATA:', data);
$("#ownerform").modal()
});
}
Теперь мы сперва получаем данные с помощью $.get(./api/cats/${id}.json
), и уже только после этого, показываем формочку. Остался последний этап, закинуть данные полученные по апи, в саму форму:
function catClick(id) {
$.get(`./api/cats/${id}.json`)
.then( data => {
$('#ownerform_phone').val(data.owner.phone);
$('#ownerform_name').val(data.owner.name);
$('#ownerform_city').val(data.owner.city);
$("#ownerform").modal();
});
}
На этом этапе, если у нас всё ок, то увидим следующее:
Ну и для закрепления, давайте создадим форму редактирования и добавления данных. Это будет ода форма, сразу на 2 действия, чтобы не создавать 2 отдельные формы. Как обычно, копипастим шаблон модального окошка с getbootstrap и в нём фигачим форму:
<div class="modal fade" id="editform" tabindex="-1" role="dialog" aria-labelledby="exampleModalCenterTitle" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered modal-lg" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLongTitle">Cat</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<div class="row">
<div class="col-sm-6">
<h3>Cat</h3>
<div class="form-group row">
<label class="col-sm-2 col-form-label">Name:</label>
<div class="col-sm-10">
<input type="text" class="form-control" id="editform_name" value="">
</div>
</div>
<div class="form-group row">
<label class="col-sm-2 col-form-label">Breed:</label>
<div class="col-sm-10">
<input type="text" class="form-control" id="editform_breed" value="">
</div>
</div>
<div class="form-group row">
<label class="col-sm-2 col-form-label">Age:</label>
<div class="col-sm-10">
<input type="number" class="form-control" id="editform_age" value="">
</div>
</div>
<div class="form-group row">
<label class="col-sm-2 col-form-label">Sex:</label>
<div class="col-sm-10">
<select class="form-control" id="editform_sex">
<option value="male">male</option>
<option value="female">female</option>
<option value="no">not sure</option>
</select>
</div>
</div>
</div>
<div class="col-sm-6">
<h3>Owner</h3>
<div class="form-group row">
<label class="col-sm-2 col-form-label">Phone:</label>
<div class="col-sm-10">
<input type="text" class="form-control" id="editform_phone" value="">
</div>
</div>
<div class="form-group row">
<label class="col-sm-2 col-form-label">Name:</label>
<div class="col-sm-10">
<input type="text" class="form-control" id="editform_owner_name" value="">
</div>
</div>
<div class="form-group row">
<label class="col-sm-2 col-form-label">City:</label>
<div class="col-sm-10">
<input type="text" class="form-control" id="editform_city" value="">
</div>
</div>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
<button type="button" class="btn btn-primary" data-dismiss="modal">Save</button>
</div>
</div>
</div>
</div>
Подправляем табличку, в неё нужно добавить кнопку активации редактирования:
result += `<td><a class="btn btn-primary" href="javascript:catClick(${item.id})">Owner info</a> `;
result += `<a class="btn btn-primary" href="javascript:catEdit(${item.id})">Edit</a></td>`;
И фигачим функцию catEdit, которая далет почти то же самое что и catClick, только будет активировать другую форму, и данные будет запихивать в другие эдиты:
function catEdit(id) {
$.get(`./api/cats/${id}.json`)
.then( data => {
addEditAction = 'edit';
$('#editform_phone').val(data.owner.phone);
$('#editform_owner_name').val(data.owner.name);
$('#editform_city').val(data.owner.city);
$('#editform_name').val(data.name);
$('#editform_breed').val(data.breed);
$('#editform_age').val(data.age);
$('#editform_sex').val(data.sex);
$("#editform").modal();
});
}
И ещё нужно добавить глобальную переменную, в которой будет лежать инструкция, что нам делать с данными в форме:
var addEditAction = 'none';
Сейчас, если кликнуть по кнопке "edit", то будет следующая картина:
Осталось повесить обработчик нажатия кнопки "save":
function saveEditClick() {
let formData = {
owner: {}
};
formData.owner.phone = $('#editform_phone').val();
formData.owner.name = $('#editform_owner_name').val();
formData.owner.city = $('#editform_city').val();
formData.name = $('#editform_name').val();
formData.breed = $('#editform_breed').val();
formData.age = $('#editform_age').val();
formData.sex = $('#editform_sex').val();
if( addEditAction == 'edit' )
$.ajax({
type: "PUT",
url: './api/cats',
data: formData
});
if( addEditAction == 'add' )
$.ajax({
type: "POST",
url: './api/cats',
data: formData
});
addEditAction = 'none';
$('#editform').modal('hide');
}
А ещё нам не хватает кнопки "создать котика". Давайте зафигачим её в пустой хедер таблички:
<button class="btn btn-primary" onClick="addCat();">Add cat</button>
И функция addCat:
function addCat() {
addEditAction = 'add';
$('#editform_phone').val('');
$('#editform_owner_name').val('');
$('#editform_city').val('');
$('#editform_name').val('');
$('#editform_breed').val('');
$('#editform_age').val(0);
$('#editform_sex').val('no');
$("#editform").modal();
}
Готовый пример можно посмотреть тут, архив в исходниками на гите. Единственно, так как я ленивая жопа, я не сделал на сервере обработчики пост и пут запросов, по этому, они будут отваливаться. Но посмотреть, работают они или нет, можно в дев консоли браузера:
Если вся наша приложунька- это действительно только одна табличка и пара не сложных формочек, то минусов тут и нет. Весь код понятен, лишних зависимостей нет. Но, если приложунька выростает до хотя бы нескольких страниц, со сложной логикой, кучей элементов, то мы сталкиваемся с проблемами:
Вот чтобы решить все эти проблемы, ну и дать, ещё немного плюшек сверху, напридумывали всякие vue/react/angular итд. Но про них как-нибудь попозже.