Добавление постов - админ панель
Начать следует с добавления постов в блог. Для этого нам потребуется некая форма, которая будет принимать информацию от пользователя, обрабатывать ее и передавать на хранение в БД.
Все материалы по 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()}
Метод приводит даты к локальному формату.
В итоге мы получили мини блог с адаптивным дизайном, возможностью сохранения в БД, удалением и созданием постов.








Комментариев нет:
Отправить комментарий