Translate

Поиск по этому блогу

Показаны сообщения с ярлыком MLab. Показать все сообщения
Показаны сообщения с ярлыком MLab. Показать все сообщения

четверг, 28 марта 2019 г.

Raect приложение с MLabDB ( II).





На этом этапе у нас есть приложение, которое не сохраняет внесенные изменения в базу данных MLab.

Давайте научим его делать это.

Внесение изменений в базу данных (checked)

Первое, что мы сделаем, это зададим в файле App.css класс, который будет добавляться к каждому таску, если у него checked: true и будет делать текст зачеркнутым.


 .completed {
    text-decoration: line-through;
}



Теперь применим этот класс к компоненту. Для того, чтобы это сработало мы создадим функцию, которая будет срабатывать на событие onChange, привяжем ее к this и конкретному таску, чтобы была возможность отследить изменения.

TaskItem.js

import React, { Component } from "react";
import { Checkbox } from "muicss/react";

class TaskItem extends Component {
  constructor(props) {
    super(props)
    this.state = {
      task: props.task
    }
  }
  onChange(task, e) {
    this.props.onEditState(task, e.target.checked);
  }
  render() {
    return <div className="mui--divider-bottom">
      <Checkbox
        className={(this.state.task.completed) ? "completed" : ""}
        onChange={this.onChange.bind(this, this.state.task)}
        name={this.state.task._id.$oid}
        label={this.state.task.text}
        defaultChecked={this.state.task.completed}
      />
    </div>;
  }
}

export default TaskItem;



Так как у нас все взаимодействия с базой идут из файла App.js, то нам нужно оттуда и передавать данные в базу, поэтому поднимаемся на уровень выше - к родителю Tasks.js.

Здесь мы создадим функцию handleEditState(task, checked), которая принимает два параметра, а именно таск и отмеченный и передадим ее в компонент TaskItem.js, где мы ее написали внутри функции onChange.


import React, { Component } from 'react';
import { Panel } from 'muicss/react';
import TaskItem from './TaskItem';


class Tasks extends Component {
  handleEditState(task, checked) {
    this.props.onEditState(task, checked)
  }
  render() {
    let taskItems;
    if (this.props.tasks) {
      taskItems = this.props.tasks.map(task => {
        return (
          <TaskItem
            onEditState={this.handleEditState.bind(this)}
            key={task._id.$oid}
            task={task}
          />
        )
      })
    }
    return (
      <Panel>
        {taskItems}
      </Panel>
    );
  }
}

export default Tasks;




И теперь, наконец-то создадим самое основное в родителе - App.js

Вначале мы создадим саму функцию


editState(task,checked){
    console.log(task);
  }


теперь передадим ее компоненту:

<Tasks 
            onEditState={this.editState.bind(this)} 
            ... 



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



Если в консоль вывести параметр чекед, то мы увидим следующее:



Таким образом, мы убедились, что в функцию приходят именно те данные, которые нам нужны.

Теперь наша задача внести изменения в базу.

Для этого мы воспользуемся опять axios, почти как при получении данных, но с некоторым отличием в URL, туда нам нужно передать идентификатор отмеченного бокса, чтобы внести изменения именно в нужный таск.

Обратите внимание, что в строку мы вставляем JS-код обычным сложением строк - конкатенацией.


"https://api.mlab.com/api/1/databases/react-tasks/collections/tasks/" + task._id.$oid + "?apiKey=yourAPIkey"



Метод запроса будет уже put. Передаем данные data. Создаем временную переменную let tasks , проходим циклом по всем таскам и если есть соответсвие по идентификатору, то меняем значение нужного таска completed на checked

И на основании этого изменяем состояние компонента: this.setState({ tasks: tasks });

Приведу код полностью


import React, { Component } from 'react';
import { Appbar, Container } from 'muicss/react';
import axios from 'axios';
import Tasks from './Components/Tasks';
import './App.css';

class App extends Component {
  constructor() {
    super()
    this.state = {
      tasks: []
    }
  }

  componentWillMount() {
    this.getTasks();
  }

  getTasks() {
    axios
      .request({
        method: "get",
        url:
          "https://api.mlab.com/api/1/databases/react-tasks/collections/tasks?apiKey=yourAPIkey"
      })
      .then(response => {
        this.setState(
          {
            tasks: response.data
          },
          () => {
            console.log(this.state);
          }
        );
      })
      .catch(error => {
        console.log(error);
      });
  }
  editState(task, checked) {
    axios
      .request({
        method: "put",
        url:
          "https://api.mlab.com/api/1/databases/react-tasks/collections/tasks/" + task._id.$oid + "?apiKey=yourAPIkey",
        data: {
          text: task.text,
          completed: checked
        }
      }).then((response) => {
        let tasks = this.state.tasks;
        for (let i = 0; i < tasks.length; i++) {
          if (tasks[i]._id.$oid === response.data._id.$oid) {
            tasks[i].completed = checked;
          }
        }
        this.setState({ tasks: tasks });
      }).catch(error => {
        console.log(error);
      });
  }
  render() {
    return (
      <div className="App">
        <Appbar>
          <Container>
            <table width="100%">
              <tbody>
                <tr>
                  <td className="mui--appbar-height">
                    <h3>React Tasks</h3>
                  </td>
                </tr>
              </tbody>
            </table>
          </Container>
        </Appbar>
        <br />
        <Container>
          <Tasks
            onEditState={this.editState.bind(this)}
            tasks={this.state.tasks}
          />
        </Container>
      </div>
    );
  }
}

export default App;



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

Ниже на фото вы можете увидеть, что все изменения чекбокса успешно попадают в базу.



Слева - браузер, справа - база данных MLab.

Таким образом мы получили сохранение изменений при отметке чекбокса в базу данных MLab (mongodb).

Да и еще, я уже писал про сохранение ключей в безопасном месте и поэтому я вам рекомендую хранить их в отдельном файле.

Например keys/apiKeys.js в переменной:

export const apiKey = 'ВАШ_КЛЮЧ_API';

и получать их в App.js импортом -

import {apiKey} from './keys/apikeys';

Это позволит легко добавить ключи в .gitignore и они не попадут, даже случайно, в "нечистые руки".

Код на данном этапе доступен по ссылке: react-mui-mlab-abc
ci -m "Transfer of changes to the database and their preservation."


Создание формы добавления задачи.

В папке компонентов для этого создадим отдельный файл AddTask.js Все содержимое приведу полностью, чтобы вы могли сразу понять, что в нем нет ничего необычного или достойного отдельного объяснения.


import React, { Component } from "react";
import { Form, Input } from "muicss/react";

class AddTask extends Component {
  constructor(props) {
    super(props)
    this.state = {
      task: ''
    }
  }
  onSubmit(e) {
    this.props.onAddTask(this.state.task);
    e.preventDefault();
  }

  onChange(e) {
    this.setState({ task: e.target.value })
  }
  render() {
    return <div className="mui--divider-bottom">
      <Form onSubmit={this.onSubmit.bind(this)}>
        <Input hint="Add Task" onChange={this.onChange.bind(this)} />
      </Form>
    </div>;
  }
}

export default AddTask;



Поля Form и Input из библиотеки MUI.

На поле ввода повесили событие на изменение его содержимого. В функции onChange сразу сохраняем изменения в стейт.

Кнопкой запустим функцию добавления задачи - onAddTask, куда и передадим значение стейта.

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

Теперь идем в файл App.js и импортируем в него вновь созданный компонент:

import AddTask from './Components/AddTask';

Выведем его на страницу сразу после Контейнера. Передадим ему как свойство функцию addTask и создадим ее сразу над render(). Пока что выведем в нее значение переменной text:


import React, { Component } from 'react';
import { Appbar, Container, Button } from 'muicss/react';
import axios from 'axios';
import Tasks from './Components/Tasks';
import AddTask from './Components/AddTask';
import './App.css';
import { apiKey } from './keys/apikeys';

class App extends Component {
  constructor() {
    super()
    this.state = {
      tasks: []
    }
  }

  componentWillMount() {
    this.getTasks();
  }

  getTasks() {
    axios
      .request({
        method: "get",
        url:
          "https://api.mlab.com/api/1/databases/react-tasks/collections/tasks?apiKey=" + apiKey
      })
      .then(response => {
        this.setState(
          {
            tasks: response.data
          },
          () => {
            console.log(this.state);
          }
        );
      })
      .catch(error => {
        console.log(error);
      });
  }
  editState(task, checked) {
    axios
      .request({
        method: "put",
        url:
          "https://api.mlab.com/api/1/databases/react-tasks/collections/tasks/" + task._id.$oid + "?apiKey=" + apiKey,
        data: {
          text: task.text,
          completed: checked
        }
      }).then((response) => {
        let tasks = this.state.tasks;
        for (let i = 0; i < tasks.length; i++) {
          if (tasks[i]._id.$oid === response.data._id.$oid) {
            tasks[i].completed = checked;
          }
        }
        this.setState({ tasks: tasks });
      }).catch(error => {
        console.log(error);
      });
  }

  addTask(text) {
       console.log(text);
  }

  render() {
    return (
      <div className="App">
        <Appbar>
          <Container>
            <table width="100%">
              <tbody>
                <tr>
                  <td className="mui--appbar-height">
                    <h3>React Tasks</h3>
                  </td>
                </tr>
              </tbody>
            </table>
          </Container>
        </Appbar>
        <br />
        <Container>
          <AddTask onAddTask={this.addTask.bind(this)} />
          <Tasks
            onEditState={this.editState.bind(this)}
            tasks={this.state.tasks}
          />
        </Container>
      </div>
    );
  }
}

export default App;



Если посмотреть на то, что получилось в браузере, то мы можем ввести какой-либо текст, нажать Enter и в консоли увидим это же значение:



Запрос на добавление данных в базу.

Все что нам нужно это написать функцию, которая будет взаимодействовать с базой при помощи axios

Пишем простой запрос. Указываем метод post, немного изменяем URL (это можно посмотреть в данных базы. Об этом рассказывал в первой части.).

Далее пишем данные, которые будем передавать.

Во временную переменную сохранили то что было в стейте до этого. Добавили методом push новые данные.

В данных completed: false, потому что мы предполагаем, что изначально задание не сделано.

При успешном выполнении вносим изменения в стейт.

В случае ошибки - поймали ее и вывели в консоль. Это если коротко...


  addTask(text) {
    axios
      .request({
        method: "post",
        url:
          "https://api.mlab.com/api/1/databases/react-tasks/collections/tasks?apiKey=" + apiKey,
        data: {
          text: text,
          completed: false
        }
      }).then((response) => {
        let tasks = this.state.tasks;
        tasks.push({
          _id: response.data._id,
          text: text,
          completed: false
        })
        this.setState({ tasks: tasks });
      }).catch(error => {
        console.log(error);
      });
  }



Посмотрим в браузер:



Вводим какой-либо текст. Нажимаем Enter и видим обновление данных на странице без перезагрузки и, соответственно данных в базе.

Для того, чтобы в этом убедиться, можно просто перезагрузить страницу и мы получим те же "новые" данные.

Код на данном этапе доступен по ссылке: react-mui-mlab-abc
ci -m "Added task to DataBase"


Добавление кнопки удаления выполненных заданий.

Начнем с тобой, что добавим саму кнопку в компонент.

В импорт из MUI добавим импорт кнопки:

import { Appbar, Container, Button } from 'muicss/react';

И прямо над закрывающим тегом контейнера, выведем саму кнопку удаления. Добавим ей красный цвет.

На клик повесим функцию, которую сейчас же и создадим.

        <Container>
          <AddTask onAddTask={this.addTask.bind(this)} />
          <Tasks
            onEditState={this.editState.bind(this)}
            tasks={this.state.tasks}
          />
          <Button color="danger" onClick={this.clearTasks.bind(this)} >
            Clear Tasks
          </Button>
        </Container>


Посмотрим в браузере:



Сделаем кнопку работающей.

Функция clearTasks

Во временную переменную tasks, получим все задания из состояния.

Для удобства, кол-во заданий вынесем в отдельную переменную - i

Пройдем по всем заданиям циклом while от большего к меньшему и если оно выполнено (completed), то методом splice уберем его из массива.

Здесь же внесем изменения в базу методом delete

Для работы с базой данных MongoDB нам нужен доступ не просто по идентификатору, а по _id.$oid

Ответ - стандартное подтверждение удаления и ошибку ловим в консоль.


  clearTasks() {
    let tasks = this.state.tasks;
    let i = tasks.length;

    while (i--) {
      if (tasks[i].completed === true) {
        let id = tasks[i]._id.$oid;
        tasks.splice(i, 1);
        axios.request({
          method: 'delete',
          url: "https://api.mlab.com/api/1/databases/react-tasks/collections/tasks/" + id + "?apiKey=" + apiKey,
        }).then(response => {

        }).catch((error) => {
          console.log(error)
        })
      }
    }
    this.setState({ tasks: tasks })
  }



Теперь вы сможете не только добавить новые задачи, но и отметить выполненные, и при необходимости их удалить.

Проверяем:



Код на данном этапе доступен по ссылке: react-mui-mlab-abc
ci -m "Delete completed tasks from DataBase"


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

К примеру, для очистки поля ввода можно сделать так.
  1. К самой форме ввода добавить id - компонент <Form id="form"/ >
  2. В функцию отправки onSubmit, добавить строку очистки всей формы: document.getElementById("form").reset();
  3.     onSubmit(e){
            this.props.onAddTask(this.state.task);
            e.preventDefault(); 
            document.getElementById("form").reset();  
        }
    
    


Данный проект в постах, можно рассматривать, скорее как учебный. Здесь все понятно. Я специально не пытался сокращать код, именно для его понимания и простоты.

Спасибо тем. кто дочитал до конца и

...Happy Coding!



                                                                                                                                                             

Телеграм канал - Full Stack JavaScript Developer

вторник, 26 марта 2019 г.

Raect приложение с MLabDB ( I ).

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



Приложение будет простое. Оно будет состоять из списка задач. Главное чем оно будет отличаться от обычного ToDo React-приложения, это возможностью сохранения данных после перезагрузки (открытия заново) нашего приложения.

Готовое приложение вы можете посмотреть на хостинге здесь.

Приступим.

Создаем базу данных на сервисе MLab

О регистрации и первых шагах по созданию базы данных, я многократно рассказывал в своих постах. Более подробно, вы можете посмотреть в посте - Блог на NodeJS Express MongoDB ( I ).

Если вы уже работали с этим сервисом, то тогда нажимаем кнопку "Create New" и создаем новую базу (документ).



Назовем ее (его), предположим, так - react-tasks.

Добавляем пользователя для БД, вводим пароль. Галочку - "Только чтение" не ставим!

Все данные сохраняем себе в блокнот.

Теперь создаем коллекцию - Add Collection



Далее нажимаем - Add Document



Данные в коллекцию добавим такие:


   {
    "text":"My Task One",
    "completed": false
   }



Обратите внимание, что значение boolean без кавычек. Фото ниже:



Вы можете добавить свои данные, в поля text и completed.

Нажимаем кнопку "Create and go back"



Таким же образом мы введем и вторую задачу.

Теперь мы имеем базу с двумя заданиями.



Создание приложения

Открываем КС (командую строку, если вы на винде, как и я) или другим способом в нужной директории создаем само Реакт-приложение. Для этого будем использовать готовое решение - create-react-app.

create-react-app reacttasks


reacttasks - название самого приложения.

Сразу после его установки, нам нужно перейти в него в КС и установить дополнительные модули:

cd reacttasks
npm install --save muicss axios


  1. muicss - для стилизации нашего приложения
  2. axios - HTTP-клиент для запросов.
После завершения утстановки, мы можем запустить наше приложение:

npm start


Обычное готовое приложение реакт запустится на порту 3000.

Удаляем лишнее

Удалим logo.svg, в файле App.css - всё содержимое, а в App.js все что внутри <div className="App">. Там мы что-нибудь напишем. например - My App. Это нужно только для того, чтобы убедиться сразу, что мы все сделали правильно. В браузере мы увидим эту надпись.

Добавляем Appbar

В самом верху, после импорта реакт, импортируем MUI :

import { Appbar, Container } from 'muicss/react';

Не забываем подключить и линк к public/index.html

<link href="//cdn.muicss.com/mui-0.9.41/css/mui.min.css" rel="stylesheet" type="text/css" media="screen" />

Его я взял с сайта MUI и от туда же возьмем готовый appbar . Уберем у него все лишнее и получим:



И следуя подсказки из консоли завернем все в контейнер:

import React, { Component } from 'react';
 import { Appbar, Container } from 'muicss/react';
import './App.css';

class App extends Component {

    render() {
        return (
            <div className="App">
                <Appbar>
                    <Container>
                        <table width="100%">
                            <tbody>
                                <tr>
                                    <td className="mui--appbar-height">
                                        <h3>React Tasks</h3>
                                    </td>
                                </tr>
                            </tbody>
                        </table>
                    </Container>
                </Appbar>
            </div>
        );
    }
}

export default App;





Ниже, внутри div, сделаем отступ и в контейнере напишем вывод нового компонента <Tasks /> и сразу его импортируем в наш файл App.js

import Tasks from './Components/Tasks';

App.js

import React, { Component } from 'react';
// Access all components from `muicss/react` module
import { Appbar, Container } from 'muicss/react';
import Tasks from './Components/Tasks';
import './App.css';

class App extends Component {

    render() {
        return (
            <div className="App">
                <Appbar>
                    <Container>
                        <table width="100%">
                            <tbody>
                                <tr>
                                    <td className="mui--appbar-height">
                                        <h3>React Tasks</h3>
                                    </td>
                                </tr>
                            </tbody>
                        </table>
                    </Container>
                </Appbar>
                <br />
                <Container>
                    <Tasks /&gr;
                </Container>
            </div>
        );
    }
}

export default App;



В папке src создадим папку Components, а в ней файл компонента - Tasks.js. В нем мы импортируем Panel из MUI, и выведем просто слово TASKS.

Tasks.js

import React, { Component } from 'react';
import { Panel } from 'muicss/react';

class Tasks extends Component {
    render() {
        return (
            
                TASKS
            
        );
    }
}

export default Tasks;




Посмотрим в браузере:





Код на данном этапе доступен по ссылке: react-mui-mlab-abc
ci -m "Create Navbar and Tasks Component"
(ci -m - коммит. В данном случае смотрите коммит - Retrieving data and outputting it in a component).

API MLab

В дальнейшем нам для работы потребуется получить доступ к API MLab по ключу.

Для этого, вам нужно выполнить следующие действия:

В правом верхнем углу аккаунта вы увидите нечто похожее:

{ user: "Имя Пользователя", account: "Псевдоним" }



нажимаем на поле user { user: "Имя Пользователя", account: "Псевдоним" }

попадем на страницу, где есть ваш ключ :



Обязательно обратите внимание, чтобы доступ по ключу был разрешен - на фото эта кнопка, ниже вашего ключа!


Ключ копируем и можно сохранить.

Более подробно можно прочитать на официальном сайте - API Authentication.

В целях безопасности храните ключ в тайне. Не давайте злоумышленникам получить доступ к вашим БД!


Получение данных от MLab по API

Сейчас наша задача состоит в том. чтобы получить данные, которые мы сохранили в базу на предварительном этапе. Это лучше всего сделать в компоненте жизненного цикла - componentWillMount()

Более подробно о жизненных циклах React-компонентов я писал в посте - React.js (6) Жизненный цикл компонентов.

Идем в файл App.js и создадим там функцию - getTasks(), которой мы будем обращаться к API MLab и получать данные.

Не забываем подключить axios.

Вызовем функцию поучения наших заданий в жизненном цикле компонента и сохраняем полученные данные в стейт.

Добавили вывод в консоль стейта.

App.js

import React, { Component } from 'react';
// Access all components from `muicss/react` module
import { Appbar, Container } from 'muicss/react';
import axios from 'axios';
import Tasks from './Components/Tasks';
import './App.css';

class App extends Component {

    constructor() {
        super()
        this.state = {
            tasks: []
        }
    }

    componentWillMount() {
        this.getTasks();
    }

    getTasks() {
        axios
            .request({
                method: "get",
                url:
                    "https://api.mlab.com/api/1/databases/react-tasks/collections/tasks?apiKey=yourAPIkey"
            })
            .then(response => {
                this.setState(
                    {
                        tasks: response.data
                    },
                    () => {
                        console.log(this.state);
                    }
                );
            })
            .catch(error => {
                console.log(error);
            });
    }
    render() {
        return (
            <div className="App">
                <Appbar>
                    <Container>
                        <table width="100%">
                            <tbody>
                                <tr>
                                    <td className="mui--appbar-height">
                                        <h3>React Tasks</h3>
                                    </td>
                                </tr>
                            </tbody>
                        </table>
                    </Container>
                </Appbar>
                <br />
                <Container>
                    <Tasks />
                </Container>
            </div>
        );
    }
}

export default App;


Более подробно о работе с API MLab

Смотрим в браузере. В консоли мы увидим, все наши таски:



Вывод данных на страницу.

Теперь мы можем передать полученные данные, как обычное состояние, дочернему компоненту. В данном случае - <Tasks tasks={this.state.tasks}/>



Для вывод каждого отдельного компонента (таска) мы создадим отдельный компонент - TaskItem .

import React, { Component } from "react";
import { Checkbox } from "muicss/react";

class TaskItem extends Component {
    constructor(props) {
        super(props)
        this.state = {
            task: props.task
        }
    }
    render() {
        return <div className="mui--divider-bottom">
                   <Checkbox name={this.state.task._id.$oid}
                        label={this.state.task.text} 
                        defaultChecked={this.state.task.completed}
                  />
               </div>;
    }
}

export default TaskItem;



Здесь ничего сложного. Мы передадим в этот компонент уже одну конкретную задачу из его родителя и выведем ее на экран в виде чекбокса с именем ( для этого используем идентификатор для каждого компонента - _id.$oid)



Родительскому div дадим класс с нижним подчеркиванием для красоты из MUI. В значение label выведем текстовое значение, а сделано\не сделано - как значение true \ false поля - completed.

Теперь нам нужно в теле самого компонента Tasks воспользоваться методом map() и передать в TaskItem одну задачу.

Для этого в методеrender компонента создадим пустую переменную - let taskItems;. Ниже проверим - Если пропсы пришли, то в эту переменную вернем наш компонент для каждого таска с данными. Эти данные для каждого отдельного компонента мы получаем методом map().

Tasks.js

import React, { Component } from "react";
import { Panel } from "muicss/react";
import TaskItem from "./TaskItem";

class Tasks extends Component {
  render() {
    let taskItems;
    if (this.props.tasks) {
      taskItems = this.props.tasks.map(task => {
        return <TaskItem key={task._id.$oid} task={task} />;
      });
    }
    return <Panel>{taskItems}</Panel>;
  }
}

export default Tasks;



Если посмотреть в браузере. то можно увидеть:



Теперь у нас есть те компоненты, которые мы создавали в БД на сервере MLab.

Давайте проделаем уже знакомые нам действия и добавим в базу третий таск с состоянием "completed": true



Добавляем.

И теперь у нас в базе три таска:



Перезагружаем браузер и...



Таким образом мы с вами получили доступ к БД MLab по API и вывели данные в React - приложение, стилизованное MUI.

Код на данном этапе доступен по ссылке: react-mui-mlab-abc
ci -m "Retrieving data and outputting it in a component"


В следующем посте я расскажу вам, как сохранять данные в базе и много другого не менее интересного.

                                                                                                                                                             

Телеграм канал - Full Stack JavaScript Developer

среда, 3 октября 2018 г.

Блог на NodeJS Express MongoDB ( I ).

С этого поста начинаем разработку блога с использованием fullstack - технологий.



Все материалы по ES6




Самое простое объяснение, что такое fullstack - разработка клиентской и серверной частей. Разработка идет двух этих сущностей независимо друг от друга, а потом мы их связываем в единый блок.

В этих трех постах мы создадим блог, где мы сможем создавать посты, выводить их и удалять.

Сервер мы напишем на NodeJS с использованием фреймворка Express. Плюс, мы будем связываться с фронтэндом, который будет написан на нативном JavaScript через REST.API

  1. Создадим папку нашего проекта. Я создал папку - fs-blog.


  2. Нам потребуется установленная NodeJS.

    Проверить очень просто. В КС (здесь и далее - командная строка) нужно ввести:

    node -v


    Enter

    Если NodeJS установлена, то уведите ее версию, а если нет, то ее стоит установить с официального сайта. Это очень просто и я останавливаться на этом не буду.


  3. Инициализация проекта.

    Эту необходимо сделать для автоматической генерации файла package.json, где будут храниться все зависимости нашего проекта.

    Переходим в КС в папку проекта. Для этого можно использовать команду

    cd NameFolder


    Можно просто перетащить папку в КС и получить путь, можно начать вводить имя папки и нажать Tab.

    После того, как вы оказались в папке проекта, в КС вводите:

    npm init


    Здесь мы можем ввести необходимые данные в файл package.json - зависимости и настройки нашего проекта.

    Можно добавить описание - description: simple blog

    entry point - это основной файл (точка входа в проект). Я укажу app.js

    keywords: blog, node.js, fullstack

    Avtor: Your Name

    y

    Enter

    Теперь в корне проекта мы получим файл package.json где хранятся все данные, которые мы добавляли через КС.


  4. Создание точки входа app.js

    В этом файле мы будем вести разработку нашего сервера.


  5. Устанавливаем фреймворк Express.


npm install express


В современных npm версиях указывать флаг --save не нужно. Зависимости и так пропишутся автоматически в файле package.json.

Если посмотреть, то в файле package.json появится ваша версия "express" в разделе "dependencies"

В папке node_modules хранятся файлы библиотеки, и появится файл pachage-lock.json, который служит для разруливания различных зависимостей и версий пакетов. Его трогать не нужно. Он генерируется автоматически.

Создание и запуск сервера.

Давайте разберемся - с чего начинается разработка сервера на Express.

  1. - Нам нужно подключить сам Express к нашему проекту.

    const express = require('express')

    Создали одноименную фреймворку переменную, куда с помощью глобальной для Node.js функции require() положили фреймворк, а точнее содержимое папки express, которая находится в node_modules.Указываем не путь, а название пакета и NodeJSпонимает, что данный пакет нужно брать из папки node_modules.


  2. -создаем сервер в переменной, которая будет отвечать за наше приложение - app.

    const app = express()

    Таким образом мы получили объект app, который будет ключевым для нашего сервера.


  3. - для запуска сервера нужно указать Экспресу о том, что мы будем что-то слушать :-)


Создадим переменную const port = 5000

Для запуска сервера нам нужно обратиться к объекту app, его методу listen()-с помощью этого метода мы можем запускать прослушку всего сервера. Первым параметром в этот метод мы передадим номер порта где мы хотим все это слушать ( наша переменная port). Вторым параметром - callback-функцию, которая будет вызвана, когда сервер заработает.

Для порта допишем функциональность. Обычно, мы можем передавать различные переменные, информацию через консоль в NodeJS и обычно порт передается через консоль, например, если мы будем куда-то выкладывать наш проект. Поэтому нам нужно проверить - если у нас указана системная переменная, которая содержит номер порта, тогда мы будем использовать её, а если нет, то порт 5000. Для того, чтобы в NodeJS обратиться к каким-то системным переменным, мы обращаемся к глобальному объекту process. У него есть объект environment (окружение) и название параметра, если он существует. А если его нет, то порт 5000:

const port = process.env.PORT || 5000

Таким образом мы получили файл app.js


const express = require('express')

const port = process.env.PORT || 5000

const app = express()

app.listen(port,()=>{
	console.log(`Server run on ${port} port`)
})



Запустим сервер:

node app.js


В консоли мы увидим Server run on 5000 port



Перейдем по адресу localhost:5000

Если мы увидим вот такое сообщение - Cannot GET /, значит сервер запущен и все работает правильно.



Давайте еще подготовим наше окружение для работы.

Переходим в файл package.json и напишем два скрипта в поле "scripts". Сейчас здесь есть один тестовый скрипт. Он нам не нужен и мы его удалим. Вместо него запишем скрипт, который будет запускать весь наш сервер:


  "scripts": {
    "start": "node app.js"
  },



Теперь в консоли (КС) мы можем для запуска приложения (сервера) написать команду:

npm run start


И нажать Enter.



Отдаем html-страницу.

В корне нашего проекта создадим папку, которая будет отвечать за клиентскую часть - client.

Внутри нее создадим файл - 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">
	<title>FS-Blog</title>
   <!--[if IE]>
   <script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script>
 <![endif]-->
</head>
<body>
	<h1>FS-BLOG</h1>
</body>
</html>



Теперь нам нужно указать серверу, что при запросе на корневой роут, нам нужно запускать именно index.html

По сути - это сделать папку client публичной, статической. И когда мы будем делать запрос на сервер, то будем оказываться в папке client и по умолчанию отдавать файл index.html.

Для этого мы в файле арр подключим новый модуль path. Данный модуль входит в состав NodeJS и потому не требует дополнительной установки.

const path = require('path')

С помощью него можно очень удобно работать с разными путями.

Создадим новую переменную const clientPath, куда мы положим абсолютный путь до папки client. Воспользуемся методом join() модуля path.

В NodeJS есть глобальная переменная - __dirname, которая отвечает за абсолютный путь к текущей папке. Так, как мы сейчас в корневой папке нашего блока. то в __dirname мы получим абсолютный путь в папке fs-blog.

И чтобы теперь указать путь к папке client, нам нужно просто указать папку вторым параметром в методе join(). Вот так:

const clientPath = path.join(__dirname, 'client')

Теперь, когда мы получили путь до нужной нам папки, нам нужно сделать её статической.

Статической она делается довольно просто. После того, как мы с вами определим объект app, мы можем "оповестить" Экспресс о том. что хотим с помощью метода use(), добавить некий функционал серверу. Внутри метода указываем статическую, публичную папку. Для этого мы обращаемся к статическому методу экспреса и указываем путь до этой папки - clientPath:

app.use(express.static(clientPath))

Теперь, если мы будем делать запрос на сервер, он должен отдавать нам файл index.html.



Запуск рабочего процесса сервера - настройка окружения

Для остановки сервера нам приходится нажать Ctrl + C, потом ввест y и нажать Enter. Запуск его требует введения в КС:

npm run start


и опять Enter

Дело в том, что при внесении изменений в файлы, нам нужно постоянно перезапускать сервер, чтобы изменения вступили в силу (пересобрался сервер). делать это постоянно не очень удобно. Для того, чтобы сервер перезапускался автоматически после внесенных изменений мы установим пакет nodemon.

Чтобы его установить нам нужно ввести команду:

npm install nodemon -D


Enter

Здесь -D флаг, который указывает, что данная зависимость нужна нам только для разработки (development).

Проверим файл package.json. Там нас интересует поле


  "devDependencies": {
    "nodemon": "^1.18.4"
  }



Значит все прошло правильно. Остается только добавить еще один пользовательский скрипт в это файл:


  "scripts": {
    "start": "node app.js",
    "dev": "nodemon app.js"
  },



Он будет делать тоже самое, что start но через пакет nodemon.

Теперь запуск сервера будет происходить командой:

npm run dev




База данных

Подключение MongoDB

Для этих целей нам потребуется локальный пакет mongoose, с помощью которого мы сможем делать очень удобные запросы к MongoDB. С помощью него мы сможем создавать модели, которые в последствии будут сущьностями в БД (база данных - здесь и далее.)

Установка пакета:

npm install mongoose


Есть разные возможности работы с БД - MongoDB. Можно её установить на компьютер локально. Плюсом такого подхода - это очень быстрое взаимодействие между сервером и БД. Минус - сложности установки MongoDB на комп. У всех разные системы, версии и пр. Для того, чтобы не отвлекаться на локальную установку, мы можем воспользоваться сервисом, который предоставляет полный API для MongoDB. Это сервис mLab.

Как можно предположить, это сервис который является удаленной версией MongoDB. Плюсы: простота использования, удобсвто настроек, бесплатно. Минусы - немного потеряем в скорости.

Нужно зарегистрироваться (создать аккаунт). Регистрация обычная. С подтверждением на почту.

Посте того, как залогинетесь появится окно с базами. Нам нужна кнопка - "Создать"



Выбираем Amazon и Бесплатную версию и нажимаем "Продолжить".



Выбрать точку поближе к себе на карте ( из предложеного и нажать "Продолжить"



Выбираем и вводим название:



Готовый чек - проверяем. Все действительно бесплатно и соглашаемся.



Происходит создание базы. Это может занять некоторое время и далее вы увидете:



База создана и готова к работе.

Переходим в нее (просто нажать на нее). и ...



Копируем эту строчку и возвращаемся в проект (редактор).

Давайте создадим некторую конфигурацию для наешего проекта, путем создания нового файла в корне приложения и назовем его keys.js

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


module.exports = {
	mongoURI: 'mongodb://<dbuser>:<dbpassword>@ds121413.mlab.com:21413/fs-blog'
}



Значением данного свойства мы вставили скопированную ссылку. В ней нам интересуют две вещи (отмечено красным).

Именно эти теги user и password нас интересуют.

Их нужно получить. В данном сервисе (mLab) нам нужно создать пользователя, который получит права доступа к БД. На записаь, на чтение и на удаление записей.

Выбираем "Users" "Add database user"



В сплывающем окне вводим имя и пароль. Галочку "read only" - НЕ СТАВИМ !

потому что мы хотим изменять базу данных

Данными имения и пароля мы заменяем теги user и password вместе с угловыми скобками.

После того, как мы получили ключи от БД мы идем в app.js и здесь мы подключаем mongoose


const express = require('express')
const mongoose = require('mongoose')
const path = require('path')
const port = process.env.PORT || 5000
const clientPath = path.join(__dirname, 'client')
const app = express()
app.use(express.static(clientPath))
app.listen(port,()=>{
	console.log(`Server run on ${port} port`)
})



теперь в этом файле нам необходимо подключиться к MongoDB

Подключение к MongoDB.

Сделаем это после подключения констант. Для этого мы обращаемся к пакету mongoose и вызываем у него метод - connect(). Первым параметром нам нужно передать адрес БД к которой мы хотим подключиться. Для этого нам нужно подключить файл констант, подключить переменную keys из файла который лежит на одном уровне с файлом app.js, потому пишем './keys.js':

const keys = require('./keys')

.js можно не указывать, потому что NodeJS понимает, что по умолчанию это .js- файл.

Таким образом мы передаем первым параметром переменную keys.mongoURI

Метод connect() возвращает нам промис.

Мы можем использовать два метода .then() и .catch().

В случае успеха передадим в консоль 'MongoDB connected', а в случае неудачи выведем в консоль ошибку.


const express = require('express')
const mongoose = require('mongoose')
const path = require('path')
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(express.static(clientPath))
app.listen(port,()=>{
	console.log(`Server run on ${port} port`)
})



Запустим сервер

npm run dev


и в консоли увидим:



Теперь нам предстоит создать модель, для того, чтобы определить некоторую сущность в БД.

Наше приложение это блог, и по сути, мы будем работать с постами.

В корне проекта создадим новую папку, которая будет отвечать только за модели models.

Файлы этой папки (модели) в NodeJS принято называть с заглавной буквы.

Для работы с постом. создадим файл, в этой папке - Post.js

Теперь нам нужно описать модель, то есть какие сущьности будут у определенной модели в БД для поста.

первое что мы будем делать - это подключим mongoose, потому что с помощью него мы и будем описывать это все.

И далее. как это принято в мангусе. Мы создаем определенную схему, описываем поля,типы, описываем дефолтные значения, и после этого мы регистрируем нужную нам модель.

Первый шаг - это создание схемы!

Создание схемы

. Создаем переменную Schema она определяется из переменной mongoose и у нее есть класс Schema.

const Schema = mongoose.Schema

Теперь мы создаем схему нашего поста то есть - описание модели. Назовем её postSchema, например.

Она является экземпляром класса Schema, где в конструктор мы передаем объект конфигурации.

Давайте решим. что у нашего поста может быть?


 const mongoose = require('mongoose')

 const Schema = mongoose.Schema

 const postSchema = new Schema({
 	
 })



Во-первых - уникальный id, но его будет выдавать сама БД (MongoDB), поэтому нам его описывать необязательно.

У поста будет название, текст и дата создания.

Начнем с описания названия поста. Допустим, что это будет title и это объект. Так как мы описываем схему.

Какого типа будет у нас заголовок? Мы описываем его как строку - String и говорим, что заголовок для каждого поста нам нужен обязательно :


 const postSchema = new Schema({
 	title: {
 		type: String,
 		required: true
 	}

 })



Далее описываем контент поста - точно так же.

Еще одно поле date - дата когда был создан пост. По сути, было бы логично, если бы сервер при создании поста сделал это за нас.

Тип переменной будет Date, потому что мы будем работать с датой. И так как сервер у нас будет сам выдавать дату создания, то вместо флага required - default

default: Date.now

Важно без скобок, чтобы не вызывать эту функцию.

С помощью метода мы экспортируем наружу данную модель и экспортируем мы результат регистрации данной модели в базе. То есть говорим mongoose о том, что с помощью метода model() мы хотим зарегистрировать в БД новую коллекцию - posts и модель каждого поста будет являться схемой которую мы создали.

module.exports = mongoose.model('posts', postSchema)

Файл Post.js


 const mongoose = require('mongoose')

 const Schema = mongoose.Schema

 const postSchema = new Schema({
 	title: {
 		type: String,
 		required: true
 	},
 	text: {
 		type: String,
 		required: true
 	},
 	date: {
 		type: Date,
 		default: Date.now
 	}

 })

 module.exports = mongoose.model('posts', postSchema)



База подключена и осталось разработать на сервере роуты, которые будут работать с разными постами.





                                                                                                                                                             


Хотите освоить самые современные методы написания React приложений? Надоели простые проекты? Нужны курсы, книги, руководства, индивидуальные занятия по React и не только? Хотите стать разработчиком полного цикла, освоить стек MERN, или вы только начинаете свой путь в программировании, и не знаете с чего начать, то пишите через форму связи, подписывайтесь на мой канал в Телеге, вступайте в группу на Facebook.Пишите мне - kolesnikovy70 почта gmail.com