Простое приложение со списком задач с базой данных Postgres.

Пройдя курс Андрея с нуля до совершенства в веб-разработке, я решил попрактиковать свои навыки Node.js, создав приложение со списком задач, которое имеет следующие функции:

  • Добавить и выполнить задачу на одной странице
  • Сохраните вновь добавленную задачу в базе данных Postgres
  • Использует экспресс-фреймворк
  • Использует knex, конструктор SQL-запросов для Postgres
  • Бутстрап для укладки

Базовая настройка:

  • Создайте пустой каталог с именем nodejs-todoList
  • Откройте консоль, перейдите в каталог и запустите npm init
  • Заполните необходимую информацию для инициализации проекта.
  • В каталоге nodejs-todoList создайте новый файл с именем server.js - сюда мы поместим код для маршрутизации и других связанных с сервером вещей.
  • Создайте папку представления, в которой будут отображаться наши представления, и общую папку для style.css, поскольку мы используем шаблоны представления (ejs).

Структура папок: вам нужно будет структурировать свой проект, как описано в блокноте, создать файл с именем style.css в общей папке для стилей CSS, а затем для представления приложения. index.ejs будет сохранен в папке просмотра. а код нашего узла будет записан в server.js

Настройка сервера: Для запуска сервера нам необходимо установить Экспресс. Express - это веб-фреймворк для Node.js, который упрощает запуск веб-сервера в Node.js. Чтобы использовать экспресс, нам нужно установить его в нашем проекте

npm install express — save

после установки мы требуем, чтобы он проверил, работает ли сервер, добавив код в server.js

const express = require(“express”); 
const app = express();
//route to the root 
app.get(‘/’, (req, res)=> {
res.send(‘Hello Chisom’);
}
// server port connection
app.listen(8080, ()=> console.log(“app is running on port 8080”));

как только файл был добавлен для тестирования нашего сервера

node server.js
// app is running on port 8080

Откройте свой браузер и зайдите на localhost: 8080, и вы увидите Hello Chisom.

Примечание. Для автоматического перезапуска сервера и отслеживания изменений файлов можно использовать Nodemon. запустив эту команду в своем терминалеnpm install nodemon и добавив "start": "nodemon server.js” в тег скрипта package.json. так что, выполнив npm start, немедленно запускает nodemon

Настройка представления: На наш взгляд, не рекомендуется отвечать простым текстом Html Hello Chisom, когда кто-либо посещает наш корневой маршрут, мы хотим отобразить файл Html, который будет отображать нашу задачу из база данных. поэтому для этого мы будем использовать EJS (Встроенный JavaScript). язык шаблонов.

npm install ejs --save

затем настройте экспресс для использования механизма шаблонов в нашем файле server.js

app.set(‘view engine’, ‘ejs’);

Доступ к EJS по умолчанию осуществляется в каталоге. Создайте файл в папке просмотра с именем index. ejs, который мы будем рассматривать как наш файл index.html

<!DOCTYPE html>
<html lang=”en”>
<head>
<meta charset=”UTF-8" />
<meta name=”viewport” content=”width=device-width, initial-scale=1.0" />
<title>Todo List</title>
<link rel=”stylesheet” href=”https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css" />
<link href=”/style.css” rel=”stylesheet”/>
</head>
<body>
<div class=”container”>
<div class=”row”>
<form action=”/addTask” method=”POST”>
<div class=”col-md-offset-2 col-md-6 col-sm-offset-1 col-sm-7">
<input type=”text” class=”form-control” name=”textTodo”    placeholder=”create new task”/>
</div>
<div class=”col-md-4 col-sm-4">
      <button type=”submit” class=”btn btn-md btn-default” >ADD
      </button>
</div>
</form>
</div>
<hr />
<div class=”row”>
<div class=”col-md-6 col-sm-6">
<h4>Todo Items</h4>
<ul id=”todo”> 
</ul>
</div>
<div class=”col-md-6 col-sm-6">
<h4>Done Items</h4>
<ul id=”done”>
</ul>
</div>
</div>
</div>
<script src=”https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script src=”https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/js/bootstrap.min.js"></script>
</body>
</html>

Настройка базы данных: в нашем приложении мы собираемся использовать базу данных PostgreSQL. Postgres - это система объектно-реляционной базы данных с открытым исходным кодом, которая использует и расширяет язык SQL в сочетании со многими функциями, которые безопасно хранят и масштабируют самые сложные рабочие нагрузки с данными.

Во-первых, мы должны установить Postgres на наш компьютер поверх здесь. Postgres предоставляет нам инструмент управления под названием pgAdmin и терминал SQL shell.
Как только это будет сделано, вы можете открыть оболочку SQL, имя пользователя Postgres по умолчанию - Postgres, а пароль пользователя - это то, что вы задали в процессе установки.
Создание базы данных: как только у вас появится доступ к Postgres из терминала, для создания базы данных

CREATE DATABASE dbname;

Создание таблицы: вам нужно создать таблицу для хранения наших записей.

CREATE TABLE todo(
id SERIAL PRIMARY KEY NOT NULL,
task text UNIQUE,
status INTEGER DEFAULT 0
);

SERIAL в идентификаторе помогает установить для него АВТОМАТИЧЕСКОЕ УВЕЛИЧЕНИЕ, в то время как УНИКАЛЬНЫЙ означает, что мы не хотим дублировать записи о задачах, а статус помогает мне отмечать вновь созданную задачу и выполненную задачу.

Подключение к базе данных: при использовании Knex для подключения к нашей базе данных мы должны сообщить нашему приложению, что мы используем Postgres в качестве клиента, поэтому мы устанавливаем knex и от pg до npm в нашем приложении. затем подключитесь к нашей созданной базе данных в файле server.js

const kenx = require(“knex”);
const db = kenx({
client: “pg”,  // postgres
connection: {
host: “127.0.0.1”,
user: “postgres”,
password: DATABASE PASSWORD HERE,
database: DATABASE NAME HERE
}
});

Маршрутизация API: если вы находитесь на этой странице и все в порядке, то все готово. Следующее, что нужно сделать, это обновить наш корневой маршрут, чтобы отобразить наш индекс. ejs вместе с нашими данными из нашей базы данных, если таковые имеются.

app.get(“/”, (req, res) => { 
 db.select(“*”).from(“task”).then(data => {
   res.render(“index”, { todos: data });
 }).catch(err => res.status(400).json(err));
});

Здесь мы запрашиваем нашу базу данных для отображения всех данных из таблицы, используя наш конструктор запросов knex. таким образом, если в нашей базе данных есть какой-либо элемент, к нему можно будет получить доступ в переменной, называемой todos в шаблоне. а в файле index.ejs для рендеринга нашего массива у нас есть

<% for(var i = 0; i <todos.length; i++){%>
<% if(todos[i].status == 0){ %>
<li>
  <input type=”checkbox” name=”todo” value=”<%= todos[i].id%>”/> <%= todos[i].task%>
</li>
<%} %>
<%} %>

В наших тегах ‹ul› мы помещаем приведенный выше код в раздел задач, а это - в раздел готовых элементов.

<% for(var i = 0; i <todos.length; i++){%>
<% if(todos[i].status == 1){ %>
<li>
  <input type=”checkbox” name=”todo” value=”<%= todos[i].id%>” /> <%= todos[i].task%>
</li>
<%} %>
<%} %>

Добавить функцию задачи: из нашего макета дизайна нам нужно иметь возможность отправить форму для создания элемента задачи. и если вы посмотрите файл index.ejs, вы заметите, что наша форма имеет действие, которое она отправляет, и атрибут метода. это означает, что теперь мы знаем, куда наша форма отправляет запрос.

<form action=”/addTask” method=”POST”>

Итак, теперь мы можем настроить маршрут почтового запроса, нам нужно иметь возможность получать данные, введенные пользователем, и для этого нам нужно промежуточное ПО промежуточного уровня (body-parser), которое имеет доступ к телам req и res для выполнения более сложных задач.

Чтобы использовать body-parser, мы должны сначала установить его:

npm install body-parser --save

После установки мы можем потребовать его, а затем использовать наше промежуточное программное обеспечение со следующей строкой кода в нашем server.js

const bodyParser = require("body-parser");
app.use(bodyParser.urlencoded({ extended: true }));

Теперь наш файл server.js добавит в него следующий код для создания задачи.

// create new task
app.post(“/addTask”, (req, res) => {
const { textTodo } = req.body;
db(“task”).insert({ task: textTodo }).returning(“*”)
.then(_=> {
   res.redirect(“/”);
}).catch(err => {
    res.status(400).json({ message: “unable to create a new task”
   });
});
});

Тестирование приложения: запустите node server.jsили, если у вас установлен nodemon, вы можете просто запустить npm start, затем открыть браузер и посетить: localhost:8080, ввести новую задачу в текстовое поле и нажать Enter или нажать кнопку «Добавить». , вы увидите, что ваша задача отображается под Todo Items

Перемещение функции задачи: после добавления задачи мы хотим иметь возможность перемещать ее из списка задач в выполненные, и наоборот, мы можем добиться этого, установив метод onclick для флажка. рендеринга вида. нам нужно обновить входной тег индекса. ejs файл, чтобы иметь метод для действия.

onclick = ’moveTask (событие,” ‹% = todos [i] .id%› ”)’

<input type=”checkbox” name=”todo” value=”<%= todos[i].id%>” onclick=’moveTask(event,”<%= todos[i].id %>”)’ /> <%= todos[i].task%>

Затем мы создаем метод put в server.js для обработки маршрута для перемещения задач.

app.put(“/moveTaskDone”, (req, res) => {
const { name, id } = req.body;
if (name === “todo”) {
  db(“task”).where(“id”, “=”, id).update(“status”,   1).returning(“status”)
.then(task => {res.json(task[0])});
} 
else {
db(“task”).where(“id”, “=”, id).update(“status”, 0)
.returning(“status”)
.then(task => {res.json(task[0])});
}
});

В представлении рядом с закрывающим тегом тела добавьте этот фрагмент кода, который запускает API moveTaskDone.

function moveTask (event, id){
const{name} = event.target;
fetch(“http://localhost:8080/moveTaskDone", 
{method: “PUT”,
headers: {
“Content-Type”: “application/json”
},
body: JSON.stringify({name, id})
}).then(response => response.json()).then(status=> {
//console.log(status);
window.location.reload()
}).catch(err => console.log(err))
}

Вывод. Выполнив все шаги, упомянутые выше, ваш серверный код (server.js) будет выглядеть следующим образом

const express = require("express");
const bodyParser = require("body-parser");
const kenx = require("knex");
const db = kenx({
client: "pg",
connection: {
host: "127.0.0.1",
user: "postgres",
password: "DATABASE PASSWORD",
database: "DATABASE NAME"
}
});
const app = express();
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
app.set("view engine", "ejs");
app.use(express.static("public"));
// res.render
app.get("/", (req, res) => {
db.select("*").from("task").then(data => {
res.render("index", { todos: data });
}).catch(err => res.status(400).json(err));
});
// create new task
app.post("/addTask", (req, res) => {
const { textTodo } = req.body;
db("task").insert({ task: textTodo }).returning("*")
.then(todo => {res.redirect("/")}).catch(err => {
res.status(400).json({ message: "unable to create a new task" });
});
});
app.put("/moveTaskDone", (req, res) => {
const { name, id } = req.body;
if (name === "todo") {
db("task")
.where("id", "=", id).update("status", 1)
.returning("status").then(task => {res.json(task[0])})}
 else {
db("task").where("id", "=", id).update("status", 0)
.returning("status")
.then(task => {res.json(task[0])});
}
});
app.listen(8080, () => console.log("app is running on port 8080"));

в то время как ваше представление (index.ejs) будет

<!DOCTYPE html>
<html lang=”en”>
<head>
<meta charset=”UTF-8" />
<meta name=”viewport” content=”width=device-width, initial-scale=1.0" />
<title>Todo List</title>
<link rel=”stylesheet” href=”https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css" />
<link href=”/style.css” rel=”stylesheet”/>
</head>
<body>
<div class=”container”>
<div class=”row”>
<form action=”/addTask” method=”POST”>
<div class=”col-md-offset-2 col-md-6 col-sm-offset-1 col-sm-7">
<input type=”text” class=”form-control” name=”textTodo” placeholder=”create new task”/>
</div>
<div class=”col-md-4 col-sm-4">
<button type=”submit” class=”btn btn-md btn-default” >ADD</button>
</div>
</form>
</div>
<hr />
<div class=”row”>
<div class=”col-md-6 col-sm-6">
<h4>Todo Items</h4>
<ul id=”todo”>
<% for(var i = 0; i <todos.length; i++){%>
<% if(todos[i].status == 0){ %>
<li>
<input type=”checkbox” name=”todo” value=”<%= todos[i].id%>” onclick=’moveTask(event,”<%= todos[i].id %>”)’ /> <%= todos[i].task%>
</li>
<%} %>
<%} %>
</ul>
</div>
<div class=”col-md-6 col-sm-6">
<h4>Done Items</h4>
<ul id=”done”>
<% for(var i = 0; i <todos.length; i++){%>
<% if(todos[i].status == 1){ %>
<li><input type=”checkbox” name=”done” value=”<%= todos[i].id%>” onclick=’moveTask(event,”<%= todos[i].id %>”)’/> <%= todos[i].task%> </li>
<%} %>
<%} %>
</ul>
</div>
</div>
</div>
<script src=”https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script src=”https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/js/bootstrap.min.js"></script>
<script>
function moveTask (event, id){
const{name} = event.target;
fetch(“http://localhost:8080/moveTaskDone", {method: “PUT”,
headers: {
“Content-Type”: “application/json”
},
body: JSON.stringify({
name,
id
})
}).then(response => response.json()).then(status=> {
console.log(status);
window.location.reload()
}).catch(err => console.log(err))
}
</script>
</body>
</html>

Надеюсь, этот пост был так или иначе полезен, вы можете просмотреть полный код проекта на Github.

Спасибо за чтение.