Добавление постов - админ панель
Начать следует с добавления постов в блог. Для этого нам потребуется некая форма, которая будет принимать информацию от пользователя, обрабатывать ее и передавать на хранение в БД.
Все материалы по ES6
Добавление формы поста
Кнопка
На сайте Materialize возьмем кнопку.Копируем код кнопки и добавляем в самый конец файла index.html.
Все что внутри тега
ul
удалим вместе с тегом. Это нам не нужно. Нам достаточно одной кнопки.
По ней сложно кликнуть, ничего не происходит. Поэтому надо поставит модальное окно.
Идем на сайт Materialize и там нам нужно посмотреть как работают модальные окна.
Берем
modal-trigger
и добавляем к ссылке нашей кнопки и далее нам нужно указать название модального окна, которое нам нужно открыть. Мы это будем делать через data-target="название"
Модальное окно
Так же берем с сайта Materialize готовый код окна и вставляем после кнопки.Исправляем id - оно должно совпадать с значением
data-target="createForm"
.
Название поставим - "Создать новый пост", а вместо контента поставим поля ввода.
Файл index.html полностью
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"> <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"> <!-- Compiled and minified CSS --> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/css/materialize.min.css"> <!--Import Google Icon Font--> <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet"> <title>FS-Blog</title> <!--[if IE]> <script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script> <![endif]--> </head> <body> <div class="container center" style="padding-top: 50px;"> <div class="row"> <div class="col s12 m8 offset-m2 lg6 offset-3" id="posts"> <div class="preloader-wrapper small active"> <div class="spinner-layer spinner-green-only"> <div class="circle-clipper left"> <div class="circle"></div> </div><div class="gap-patch"> <div class="circle"></div> </div><div class="circle-clipper right"> <div class="circle"></div> </div> </div> </div> </div> </div> </div> <!-- button --> <div class="fixed-action-btn"> <a class="btn-floating btn-large red modal-trigger" data-target="createForm"> <i class="large material-icons">add</i> </a> </div> <!-- Modal Structure --> <div id="createForm" class="modal"> <div class="modal-content"> <h4>Создать новый пост</h4> <div class="input-field"> <input id="title" type="text" class="validate" required> <label for="title">Название.</label> </div> <div class="input-field"> <textarea id="text" class="materialize-textarea"></textarea> <label for="text">Название.</label> </div> </div> <div class="modal-footer"> <a class="waves-effect waves-light btn" id="createPost">Создать пост.</a> </div> </div> <script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/js/materialize.min.js"></script> <script src="index.js"></script> </body> </html>
Теперь нужно проинициализировать модальное окно
На нужно передать в глобальный объект Материалайза элемент, который нам нужно проинициализировать и после этого все должно работать.
M.Modal.init(elems, options);
Поэтому идем в скрипт - index.js
Уберем за одно и таймаут. Он нам уже не нужен.
Инициализируем модальное окно.
M.Modal.init(document.querySelector('.modal'))
внутри события загрузки всего документа.
Создаем глобальную переменную modal вверху файла и занесем в нее инстанс модального окна внизу, чтобы в последствии им можно было управлять.
Файл index.js
const card = post => { return ` <div class="card z-depth-4"> <div class="card-content"> <span class="card-title">${post.title}</span> <p>${post.text}</p> <small>${post.date}</small> </div> <div class="card-action"> <button class="btn btn-small red"> <i class="material-icons">Delete</i> </button> </div> </div> ` } const BASE_URL = '/api/post' let posts =[] let modal; class PostApi { static fetch(){ return fetch(BASE_URL, {method: 'get'}).then(res=>res.json()) } } document.addEventListener('DOMContentLoaded', () => { PostApi.fetch().then(backendPosts => { posts = backendPosts.concat() renderPosts(posts) }) modal = M.Modal.init(document.querySelector('.modal')) }) function renderPosts(_posts=[]) { const $posts = document.querySelector('#posts') if(_posts.length > 0) { $posts.innerHTML = _posts.map(post=> card(post)).join() } else { $posts.innerHTML = `<div class="center">Постов пока нет.</div>` } }
Проверяем.
Придаем функциональность
Первое, что нам нужно сделать, это поставить прослушку события на кнопку в модальном окне. Когда мы кликаем по кнопке "Создать пост" мы будем вызывать функцию.Внутри основного события загрузки документа в файле index.js возьмем кнопку -
document.querySelector('#createPost').addEventListener('click', onCreatePost)
Функцию мы вызываем без круглых скобок - просто передаем название.
Создаем функцию
onCreatePost
В ней мы должны получит контент, который написан в инпутах. Посему, создадим переменные
$title = document.querySelector('#title')
$text = document.querySelector('#text')
Проверим, что эти поля не пустые:
if ($title.value && $text.value)
Если они не пустые, то нам нужно отправить POST-запрос, который мы реализовывали на сервере.
Для этого создадим
date
, которую мы хотим отправить на сервер. На сервере мы ожидаем увидеть два поля - title и text с запросом.
Поэтому мы здесь создадим новую переменную -
newPost
const newPost = { title: $title.value text: $text.value }
И теперь когда у нас есть данные которые мы принимаем на сервере (объект) нам нужно отправить запрос.
Для этого у нас есть вспомогательный класс
PostApi
и давайте здесь же реализуем метод, который нам позволит создавать посты.
static create(post) { }Будет принимать объект поста.
Далее мы будем возвращать результат работы метода
fetch(BASE_URL)
по BASE_URL и далее нам необходимо передать объект конфигурации {}
Во первых мы ожидаем, что будем отправлять метод POST, поэтому
static create(post) { return fetch(BASE_URL, { method: 'post', }) }
Далее нам необходимо указать
body
(посмотрите на сервер - title: req.body.title, text: req.body.text, (14 и 15 строка файла routes/post.js)
Поэтому в методе fetch() мы так и пишем
body
и далее нам необходимо сделать строкой - сериализовать объект post
Сделаем так:
JSON.stringify(post)
Так же нам нужно здесь реализовать хедеры, чтобы сервер понимал, что работа идет с JSON-ом.
hraders: { 'Accept': 'application/json', 'Content-Type':'application/json' }
Конфигурация запроса на этом закончена и после того, как пройдет метод
fetch
мф хотим получить некоторый body. Для этого в методу .then(res => res.json())
делаем то, что обычно с запросом. Отдаем некоторый json.
Метод
create
готов.
class PostApi {
static fetch() {
return fetch(BASE_URL, {method: 'get'}).then(res=>res.json())
}
static create(post) {
return fetch(BASE_URL, {
method: 'post',
body: JSON.stringify(post),
hraders: {
'Accept': 'application/json',
'Content-Type':'application/json'
}
}).then(res => res.json())
}
}
Теперь у нас метод
create
готов и мы можем продолжить работу.
В функции
onCreatePost
мы сформировали новый пост и далее можем обратиться к классу PostAPI
и с помощью метода create() передать ему данные - newPost и далее, когда обработается запрос и сервер нам ответит, в метод .then() мы получим новый объект post.
Нам нужно отобразить его в списке всех постов. Для этого у нас есть локальная переменная
posts
, гд хранятся все посты. Мы просто добавим его туда методом push()
И далее. чтобы отобразить новые посты, мы вызываем метод
renderPosts(posts)
c массивом posts
.
Помимо того. что мы создали пост на сервере нам нужно сразу закрыть модальное окно -
modal.close()
И очистим значения инпутов -
$title.value = ''
$text.value = ''
И обновим текстовые инпуты - Обратиться к библиотеке материолайз и вызвать метод
M.updateTextFields()
- по новому отобразит текстовые инпуты.
Сейчас при попытке создания посты мы получим следующее сообщение:
Для правильной работы нашего сервера нам нужно добавить плагин, который будет за нас парсить входящие данные. Мы с клиента их сериализовали - превратили в строку и теперь нужно сделать так, чтобы сервер их принимал как обычный JS-объект.
Для этого нам потребуется пакет body-parser
Остановим выполнение сервера.
Установим пакет, который автоматически будет парсить за нас входящие данные.
npm install body-parser
Подключаем его в папке app.js где-нибудь вверху.
const bodyParser = require('body-parser')
Теперь нужно применить пакет к нашему серверу.
Например. пред тем, как мы определяем роуты мы можем написать такую конструкцию:
app.use(bodyParser.json())
Будет преобразовывать входящие параметры в json-объект.
app.js
const express = require('express') const mongoose = require('mongoose') const bodyParser = require('body-parser') const path = require('path') const postRouter = require('./routes/post') const keys = require('./keys') const port = process.env.PORT || 5000 const clientPath = path.join(__dirname, 'client') mongoose.connect(keys.mongoURI) .then(() => console.log('MongoDB connected')) .catch(err => console.error(err)) const app = express() app.use(bodyParser.json()) app.use('/api/post', postRouter) app.use(express.static(clientPath)) app.listen(port,()=>{ console.log(`Server run on ${port} port`) })
Запускаем сервер
Добавляем пост:
Если обновить страницу, то мы все равно увиим данный пост, потому что он берется уже из БД. В консоли можно посмотреть данные поста
Если посмотреть в mLab в коллекциях то мы увидим данные нашего поста.
Теперь можно добавлять посты на страницу.
Мы реализовали функционал по фетчингу и добавлению постов. осталось добавить удаление поста и правильный вывод даты, а так же сохранить форматирование строк поста.
Реализация удаления поста.
Нас интересует клиентский скрипт - index.jsЗдесь есть функция, которая вызывается когда полностью загрузится контент DOM. В ней у нас уже есть прослушка событий на кнопке "Добавить пост". Создадим еще одну, которая позволит нам удалять определенные посты. В данном случае мы будем делегировать события и прослушку повесим на весь cписок постов (id="posts"), потому что на кнопку удаления повесить ее мы не можем, так как они все время меняются. Поэтому лучше делегировать события.
document.querySelector('#posts').addEventListener('click', onDeletePost)
и создаем данную функцию внизу файла.
В нее принимаем некоторый
event
Теперь нам нужно определить, что нам нужно что-то удалить. Так как сейчас событие
event
будет срабатывать при клике по любому месту поста.
Нам нужно понять, что клик был именно по кнопке удаления поста.
Для этого мы можем в функции, которая генерирует нам html-карточки добавить на кнопку удаления поста специальный класс, напрмер
.js-remove
Теперь нужно в функции определить, есть ли у элемента по которому мы кликнули этот класс.
В конструкцию
if
запишем: если у event.target в объекте classList есть класс .js-remove
, то мы что-то делаем.
if (event.target.classList.contains('js-remove'))
далее спросим у пользователя через функцию
confirm
желает ли он удалить этот пост.
const decision = confirm('Вы уверены, что хотите удалить пост?')
А далее если пользователь нажал (согласился), то нужно понять какой именно пост нужно удалить.
Нам для этого нужно получить id поста. id хранится в объекте
post._id
. И зная что мы будем кликать именно по кнопке удаления, мы можем на кнопку добавить атрибут - data-id="${post._id}"
и будем забирать именно этот атрибут.
const id = event.target.getAttribute('data-id')
Далее, зная нужный id мы можем отправить его на сервер.
Для этого нам нужно реализовать функцию удаления в классе
PostApi
.
Там мы создадим новую функцию
static remove(id)
в нее мы будем получать id и все что мы будем делать - возвращать функцию fetch()
по BASE_URL но к нему надо еще добавить id, поэтому В параметрах метод delete, потому что мы будем слушать именно его. и после этого получим результат.
static remove(id) { return fetch(`${BASE_URL}/${id}`, { method: 'delete' }).then(res => res.json()) }
Теперь мы можем обратиться к классе PostApi взывать у него метод remove передать ему нужный id и как только сервер ответи успехом, мы в функции .then() удалим пост из списков (визуально)
function onDeletePost(event) { if (event.target.classList.contains('js-remove')) { const decision = confirm('Вы уверены, что хотите удалить пост?') if (decision) { const id = event.target.getAttribute('data-id') PostApi.remove(id).then(() => { const postIndex = post.findIndex(post => post._id === id) posts.splice(postIndex, 1) renderPosts(posts) }) } } }
Правильно указать дату поста и мультимтрочный текст
Текст.
Параграфу просто добавить инлайн стиль white-space:pre-line
Для нормального отображения даты нужно поле
${post.date}
обернуть в конструктор ${new Date(post.date).toLocaleDateString()}
Метод приводит даты к локальному формату.
В итоге мы получили мини блог с адаптивным дизайном, возможностью сохранения в БД, удалением и созданием постов.
Комментариев нет:
Отправить комментарий