Отображение данных в шаблоне ejs - PullRequest
0 голосов
/ 30 марта 2019

Я пытаюсь создать простую веб-страницу, которая предлагает пользователю ввести время и нажать Подтвердить. При отправке я хочу, чтобы данные, которые соответствуют этой коллекции, были показаны на веб-странице.

Когда я нажимаю кнопку отправки. Это экономит время, которое я вставил, в соответствующую коллекцию Mongo. На самом деле, у меня даже есть console.logging, чтобы показать всю коллекцию. Я просто не могу заставить его показываться на веб-странице.

Я новичок в NodeJS и MongoDB, так что терпите меня.

Вот файл index.ejs. Clients - это имя коллекции, которая содержит времена.

<div>

    <ul class="clients">
        <% for(var i=0; i< clients.length; i++) {%>
            <li class="client">
                <span><%= clients[i].time %></span>

            </li>
        <% } %>
    </ul>
</div>
</head>

<body>

<form action="/clients" method="POST">
    <input type="text" placeholder="time" name="time">
    <button type="submit">Submit</button>
</form>

У меня есть это в моем app.js, который успешно помещает вставленное время в коллекцию.

app.post('/clients', (req, res) => {
var url = "mongodb://localhost:27017/lesson-data";
mongoose.connect(url, function (err, db) {
    if (err) throw err;
    db.collection('clients').save(req.body, (err, result) => {
        if (err) return console.log(err)

        console.log('saved to database')
        res.redirect('/')
    });
  });

});

И это в моих маршрутах> index.js - который успешно регистрирует время в консоли, но не будет отображаться на веб-странице.

router.get('/', function(req, res, next) {

var url = "mongodb://localhost:27017/lesson-data";
mongoose.connect(url, function (err, db) {
    if (err) throw err;

    db.collection('clients').find().toArray((err, result) => {
        if (err) return console.log(err);
        console.log(result );
        console.log("chea");
        // renders index.ejs
        res.render('index', {clients: result});
    });
  });
});

Что я делаю не так? Я чувствую, что я близко и потратил несколько часов, пытаясь решить эту проблему.

1 Ответ

2 голосов
/ 31 марта 2019

Таким образом, здесь есть не только несколько неправильных вещей, и, вероятно, лучше всего написать это в виде небольшого приложения с нуля, чтобы объяснить вещи.

Создание и установка зависимостей

Первое, что вы хотите сделать, это выбрать папку и создать пространство для проекта. Вам понадобятся несколько подпапок в проекте, чтобы вы могли сделать что-то подобное через bash, если у вас есть это:

 mkdir -p ejsdemo/{models,routes,views/pages}

Если вы делаете это в Windows, тогда делайте все, что хотите, чтобы создать похожую структуру, но вы в основном хотите что-то подобное в этой папке верхнего уровня ejs-demo:

.
├── models
├── routes
└── views
    └── pages

Затем вы хотите инициализировать проект nodejs и установить зависимости. Вы можете сделать это снова с помощью следующего:

cd ejs-demo
npm init -y && npm i -S express ejs mongoose morgan body-parser

Опять же, это может варьироваться в зависимости от того, какую операционную систему вы используете, но вам нужен установленный node_modules в папке ejs-demo и файл package.json, который в основном читается как:

{
  "name": "ejsdemo",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "body-parser": "^1.18.3",
    "ejs": "^2.6.1",
    "express": "^4.16.4",
    "mongoose": "^5.4.20",
    "morgan": "^1.9.1"
  }
}

При желании вы можете просто создать package.json на основе этого содержимого в папке и запустить npm i, что, в основном, мало для npm install, и это установит все.

Добавление моделей

В подпапке models, которая уже должна быть создана, вы теперь можете добавить базовый список. Mongoose ODM (Object Document Mapper) на самом деле имеет концепцию регистрации «моделей» для ваших коллекций, которые определяют «схему», а также могут применять другие ограничения проверки или даже специальные методы экземпляров или «статические» методы класса для специальных целей.

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

Модели / client.js

const { Schema } = mongoose = require('mongoose');

const clientSchema = new Schema({
  name: String,
  time: String
});

module.exports = mongoose.model('Client', clientSchema);

Это очень простой способ, и он просто импортирует вспомогательную функцию Schema для определения «схемы», которая используется с функцией mongoose.model(), которая фактически регистрирует модель.

Это все, что нужно добавить в этот «модуль», и мы будем require() этот же файл в других модулях, где мы хотим использовать эту модель. Обратите внимание, что нам не нужно знать о соединении здесь.

Добавить маршруты

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

маршруты / root.js * ** тысяча пятьдесят-дв * тысяча пятьдесят три const express = require('express'); const router = express.Router(); const Client = require('../models/client'); router.get('/', async (req, res, next) => { try { let clients = await Client.find(); console.log(clients); res.render('pages/index', { clients }); } catch (e) { next(e); } }); module.exports = router; маршруты / clients.js

const express = require('express');
const router = express.Router();

const Client = require('../models/client');

router.post('/', async (req, res, next) => {

  try {
    console.log(req.body);
    await Client.create(req.body);
    res.redirect('/');
  } catch (e) {
    next(e);
  }

});

module.exports = router;

Оба эти примера очень простые. Обратите внимание, как они оба импортируют Client из модели, созданной ранее. Оба также имеют один метод - GET и POST, соответственно, и пытаются получить «корневой» путь. Это будет относительный маршрут к конечной конечной точке, которая будет зарегистрирована позже. Но такая структура позволяет добавлять «под-маршруты» и другие действия «глагола» Http.

Я демонстрирую все это, используя async/await из NodeJS 8.x и выше. Если вы изучаете , то это должна быть минимальная версия, на которой вы работаете. При желании вы можете использовать обратные вызовы или простые обещания, если это соответствует вашему стилю, но современный синтаксис async/await обычно приводит к более чистому и легкому чтению кода, за который ваши коллеги будут благодарить вас.

Очень простые вызовы либо .find(), либо .create() от модели в любом случае, которые просто «ожидаются» с использованием await, так как каждый из них возвращает Обещание, и вы можете это сделать. Обратите внимание на async перед определением каждого обработчика функции. Это необходимо, чтобы пометить блок как async, прежде чем вы сможете await в результате.

Конечно, .find() просто возвращает все данные в коллекции, и, поскольку это метод мангусты в модели, он возвращается уже как Array для удобства. Кроме того, .create() - это, по сути, оболочка insertOne(), которая может по выбору перебирать массив документов для создания и которая по существу «сохраняет» коллекцию. Это просто берет req.body, который к моменту фактического вызова этого маршрута будет содержать объект JavaScript с некоторым «опубликованным» содержимым формы.

Добавить просмотров

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

просмотры / страницы / index.ejs

<div>

  <ul class="clients">
    <% for ( let client of clients ) { %>
      <li class="client">
        <span><%= client.name %>
        <span><%= client.time %>
      </li>
    <% } %>
  </ul>
</div>

<form action="/clients" method="POST">
  <input type="text" placeholder="name" name="name">
  <input type="text" placeholder="time" name="time">
  <div>
    <button type="submit">Submit</button>
  </div>
</form>

Я даже не беспокоюсь о стилях или любой другой обёрточной структуре HTML. Простой список и форма достаточно хороши для демонстрации. Также обратите внимание на современный цикл for..of, который намного чище, чем обращение к элементам массива по индексу. EJS в основном поддерживает JavaScript в шаблонах. Так что, если это допустимый JavaScript, то он действителен для использования шаблона. В пределах разумного:

Основное применение

Все, что в основном осталось, - это основной index.js файл, который нужно поместить в корневую папку проекта. На самом деле все, что мы собираемся здесь сделать, это загрузить некоторые из модулей, которые мы создали ранее, зарегистрировать конечные точки, установить соединение с базой данных и запустить прослушиватель http. Это в основном последовательное, но мы можем выполнить некоторые вещи:

index.js

const mongoose = require('mongoose');
const express = require('express');
const morgan = require('morgan');
const bodyParser = require('body-parser');

const Client = require('./models/client');
const rootRoutes = require('./routes/root');
const clientRoutes = require('./routes/clients');

const uri = 'mongodb://localhost:27017/lesson-test';
const opts = { useNewUrlParser: true };

mongoose.set('useFindAndModify', false);
mongoose.set('useCreateIndex', true);
mongoose.set('debug', true);

const app = express();

app.set('view engine', 'ejs');
app.use(morgan('combined'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));

app.use('/', rootRoutes);
app.use('/clients', clientRoutes);

(async function() {

  try {

    const conn = await mongoose.connect(uri, opts);

    // Clean data for demo
    await Promise.all(
      Object.entries(conn.models).map(([k, m]) => m.deleteMany())
    );

    // Insert some starter sample

    await Client.insertMany([
      { name: 'One', time: '2:00' },
      { name: 'Two', time: '3:00' }
    ]);

    app.listen(3000);


  } catch (e) {
    console.error(e)
  }


})()

Справа вверху списка находится просто блок, который требуется для основных модулей, которые мы установили ранее при инициализации проекта. Это, конечно, включает mongoose, поскольку мы хотим connect() для MongoDB и express, поскольку нам нужно настроить основные обработчики приложения. Другие вещи, такие как morgan, предназначены только для того, чтобы показать некоторую «регистрацию» в консоли, подтверждающую запросы, и bodyParser, что очень важно, поскольку нам необходимо декодировать POST-запрос из формы позже.

Следующая часть:

const Client = require('./models/client');
const rootRoutes = require('./routes/root');
const clientRoutes = require('./routes/clients');

Это просто импорт "модулей", которые мы создали ранее. Обычно вы не хотите, чтобы Client или другие модели были включены в этот список index.js, но для этой демонстрации мы собираемся настроить некоторые данные, готовые для первого запроса. Другие импортируют обработчики маршрутов, которые мы установили ранее.

Следующая часть списка на самом деле просто настроена для мангуста и в основном необязательна. Здесь важны только реальные параметры uri и opts, которые предназначены для фактического подключения. Это только в верхней части примера списка на случай, если uri потребуются изменения для вашего подключения MongoDB. Обратите внимание, что демонстрация является «автономной», поэтому НЕ указывайте это ни на одну из существующих баз данных, так как она ожидает имя, которое в противном случае не используется.

Тогда есть экспресс-установка:

app.set('view engine', 'ejs');
app.use(morgan('combined'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));

app.use('/', rootRoutes);
app.use('/clients', clientRoutes);

Первая строка регистрирует ejs для шаблонов без других настроек, поэтому местоположения по умолчанию используются в местах, которые мы уже определили. Строка morgan устанавливает промежуточное программное обеспечение для регистрации запросов, как и два вызова bodyParser, также регистрирующие соответствующее промежуточное программное обеспечение для анализа JSON и содержимого UrlEndcoded, где последний является значением по умолчанию для сообщений HTML-формы.

Последние две строки берут эти импорты для обработчиков маршрута и присваивают их конечным конечным точкам. Вот почему в самих определениях оба обработчика запросов использовали /, поскольку это относительно конечных точек, определенных здесь в app.use(). Это довольно распространенная практика.

Далее идет основной кодовый блок, который снова довольно прост:

(async function() {

  try {

    const conn = await mongoose.connect(uri, opts);

    // Clean data for demo
    await Promise.all(
      Object.entries(conn.models).map(([k, m]) => m.deleteMany())
    );

    // Insert some starter sample

    await Client.insertMany([
      { name: 'One', time: '2:00' },
      { name: 'Two', time: '3:00' }
    ]);

    app.listen(3000);


  } catch (e) {
    console.error(e)
  }


})()

Обратите внимание, что блок помечен async, поэтому мы можем использовать ключевое слово await внутри него.Также есть тот же стиль блока try..catch для обработки ошибок.Простой первый вызов внутри этого на самом деле соединяется с MongoDB.Это первый фактический асинхронный вызов метода в запущенном приложении.Так что вы await, прежде чем мы пойдем дальше в выполнении кода.Он просто использует аргументы uri и opts, определенные ранее.

Поскольку это «самодостаточная» демонстрация, я просто очищаю целевые коллекции от всех зарегистрированных моделей, прежде чем делать что-либо еще.Не то, что вы обычно делаете, но вещь Promise.all( Object.entries(..).map(..) ) - это способ обработки чего-либо для каждой зарегистрированной модели с помощью mongoose.Эта «регистрация» происходит в начальном require() для любой модели, как показано в верхней части списка.

Следующая вещь должна быть довольно очевидной, поскольку мы просто используем Client.insertMany() для вставки некоторых образцов данных вначать с.Опять же, это асинхронная функция, поэтому вы await получаете результат перед продолжением выполнения.

Наконец, мы должны быть счастливы, что мы подключены к MongoDB и вставили некоторые образцы данных для начала, так что можно начинать прослушиваниедля запросов на порт 3000 из localhost по умолчанию.

Запуск приложения

Если у вас есть все это на месте, то структура каталогов теперь должна выглядеть примерно так (опускаявсе подробности в node_modules, конечно):

.
├── index.js
├── models
│   └── client.js
|── node_modules
├── package.json
├── package-lock.json
├── routes
│   ├── clients.js
│   └── root.js
└── views
    └── pages
        └── index.ejs

Если это так и с сохранением точно такого же кода, как показано выше, то он готов для запуска с:

node index.js

Вы должнызатем появятся следующие строки:

Mongoose: clients.deleteMany({}, {})
Mongoose: clients.insertMany([ { _id: 5ca06fbc38a9b536315d732c, name: 'One', time: '2:00', __v: 0 }, { _id: 5ca06fbc38a9b536315d732d, name: 'Two', time: '3:00', __v: 0 } ], {})

Теперь вы должны быть готовы открыть свой браузер для http://localhost:3000/ и просмотреть отрендеренный шаблон, назначенный этому маршруту.Консоль, на которой вы запустили приложение, должна указывать, что был пройден маршрут:

Mongoose: clients.find({}, { projection: {} })
[ { _id: 5ca06fbc38a9b536315d732c,
    name: 'One',
    time: '2:00',
    __v: 0 },
  { _id: 5ca06fbc38a9b536315d732d,
    name: 'Two',
    time: '3:00',
    __v: 0 } ]
::ffff:10.0.2.2 - - [31/Mar/2019:07:45:26 +0000] "GET / HTTP/1.1" 200 423 "-" "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36"

И, конечно, это также показывает запрос от Mongoose к серверу MongoDB.Те же самые данные теперь должны отображаться в элементах <li> на странице.

Вы также можете заполнить поля формы и отправить, что должно показать в консоли ответ, подобный:

{ name: 'Four', time: '4:00' }
Mongoose: clients.insertOne({ _id: ObjectId("5ca0710038a9b536315d732e"), name: 'Four', time: '4:00', __v: 0 })
::ffff:10.0.2.2 - - [31/Mar/2019:07:49:20 +0000] "POST /clients HTTP/1.1" 302 46 "http://localhost:3000/" "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36"

Показывает проанализированное req.body содержимое и полученный insertOne() из create() метода модели и, конечно, запись запроса POST.Затем действие перенаправления приведет к маршруту /:

Mongoose: clients.find({}, { projection: {} })
[ { _id: 5ca06fbc38a9b536315d732c,
    name: 'One',
    time: '2:00',
    __v: 0 },
  { _id: 5ca06fbc38a9b536315d732d,
    name: 'Two',
    time: '3:00',
    __v: 0 },
  { _id: 5ca0710038a9b536315d732e,
    name: 'Four',
    time: '4:00',
    __v: 0 } ]
::ffff:10.0.2.2 - - [31/Mar/2019:07:49:20 +0000] "GET / HTTP/1.1" 200 504 "http://localhost:3000/" "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36"

Заключение

И это основные понятия, которые вам нужно повторить в своих собственных приложениях.Здесь мы рассмотрели следующие основные вещи:

  • Создание моделей - где вы определяете модель для каждой коллекции, позволяя вам устанавливать правила для схемы.Mongoose может быть дополнительно установлен на { strict: false } и вообще не вызывать проверки схемы или преобразования типов.Как правило, это немного более дружелюбно, чем работа с методами основного драйвера.

  • Отдельные маршруты - Действия и обработчики могут быть настроены в логические группы там, где они могут понадобитьсябыть без привязки к строгой конечной точке.Настройка возможных конечных точек может быть выполнена позже, и этот интерфейс «контроллера» на самом деле является просто «уровнем рукопожатия» между представлением презентации и моделями.

  • Подключение кБаза данных ОДНАЖДЫ - Это важное правило, которое принудительно соблюдается общим шаблоном использования Mongoose.Ваше приложение, основанное на запросе, не имеет делового подключения и отключения в каждом запросе (как вы делали).Вы только когда-либо подключаетесь ONCE и оставляете это открытым.Драйвер фактически управляет такими вещами, как пул соединений, и помогает распределять, чтобы несколько одновременных запросов не блокировались.

    Также любые дополнительные соединения в пуле будут управляться драйвером и отключаться, когда они не нужны.Хотя обычно размер пула по умолчанию остается открытым, он всегда готов к следующему запросу.Как правило, вам не следует беспокоиться об этом на данном этапе, так как это деталь, которую вам нужно узнать только тогда, когда вы действительно столкнетесь с необходимостью знать.И это ненадолго.

По сути, если вы будете здесь все следовать, то у вас будет рабочий пример того, что вы в основном пытались сделать, и что-то, на что вы можете «опираться»сделать больше и лучше.

Веселись!

...