Translate

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

пятница, 29 марта 2019 г.

SVG line chart in React

Create SVG line chart in React

                                                                                                                                                             

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

четверг, 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


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