Your Web News in One Place

Help Webnuz

Referal links:

Sign up for GreenGeeks web hosting
June 18, 2021 10:54 am GMT

Steps to Create A To-Do App With MERN stack

Tools You Will Need

Make sure Node and NPM are installed on your computer. You can download both at nodejs.org (NPM is included in your Node installation)

Tech Stack

Node.js
Express.js
MongoDB
React.js

Dependencies for Node.js

body-parser
mongoose
mongoose-auto-increment

Create your Node (Express) Backend

First create a folder for your project, called to-do-node (for example).
Then, open that folder in your code editor.
To create our Node project, run the following command in your terminal:

npm init -y

This will create a package.json file that will allow us to keep track of all our app scripts and manage any dependencies our Node app needs.

We'll use Express to create a simple web server for us that runs on port 3000.

So let's create an index file where our app starts to run with the name of index.js.

const express = require('express')const app = express()const bodyParser = require('body-parser');//import routerconst router = require('./app/index.js');// body parserapp.use(bodyParser.urlencoded({ limit: '100mb', extended: true }))app.use(bodyParser.json({ limit: '100mb', extended: true }))app.use(function(req, res, next) {  res.header("Access-Control-Allow-Origin", "*");  res.header("Access-Control-Allow-Methods", "GET, PUT, POST, DELETE");  res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, Authorization");  if ('OPTIONS' === req.method) {    //respond with 200    res.send(200);  }  else {    //move on    next();  }});app.get('/', (req, res) => {  res.send("incorrect route");})//add routesconst base = '/api/v1/';app.use(base, router);app.listen(process.env.PORT || 3000, () => console.log('Running on port 3000!'));

Then in our terminal, we will install dependencies which we need:

npm I express mongoose body-parser nodemon mongoose-auto-increment

Create a folder called app inside the project folder and add index.js file to defines routes.

const express = require('express');// Routes Importconst toDo = require("./toDo/index.js");const router = express.Router();// Adding Routesrouter.use('/to-do', toDo);module.exports = router;

Create folder name called config and add db.js file to that.

db.js file will have the following configurations:

const mongoose = require('mongoose');mongoose.connect('mongodb+srv://<mongodb_username>:<cluster_password>@cluster0.nxcni.mongodb.net/toDo?retryWrites=true&w=majority', { useNewUrlParser: true });module.exports = mongoose.connection;

Let's write APIs now.

Create a folder called toDo and model.js, index.js, and router.js files in that.

index.js

module.exports = require("./router");

model.js

const mongoose = require('mongoose'); const autoIncrement = require('mongoose-auto-increment');const db = require('../config/db.js');autoIncrement.initialize(db);const schema = new config.mongoose.Schema({  toDos: [    {      toDo: String,      tag: String,      tagColor: String,      done: Boolean    }  ],  createdAt: {    type: Date,    default: Date.now  },  updatedAt: {    type: Date,    default: Date.now  },  status: {    type: Boolean,    default: true  }}, {  strict: true});var toDo = mongoose.model('toDos', schema);module.exports = toDo;

router.js

const config = require('../config/routes.js');const router = config.express.Router();const collection = require('./model.js');// @route GET api/v1/to-do/list// @desc get users list with pagination// @access Publicrouter.get('/list', function (req, res) {    if(!req.query.id) {          res.status(200).send({data: []});        return false;    }    getToDosList(req.query.tag ? req.query.tag : '', req.query.id).then(resp => {          res.status(200).send(resp[0]);    }, err => {          res.status(500).send({message: "Something went wrong, please try after sometime"});    })});// @route CREATE api/v1/to-do/add// @desc add to-do// @access Publicrouter.post('/add', function(req, res) {    if(!req.query.id) {        collection.create({toDos: [{toDo: req.body.text, done: false, tag: req.body.tag, tagColor: req.body.tagColor}]}, function (err, toDo) {            if (!err) {                return res.status(200).json({error: false, data: toDo, message: 'success'})            } else {                  return res.status(500).send({error: true, message: 'Error adding to-do'})            }        });    } else {            let updateData = {            $push: {                "toDos": {toDo: req.body.text, done: false, tag: req.body.tag, tagColor: req.body.tagColor}            }        };            updateToDo({_id: req.query.id}, updateData).then(toDo => {              return res.status(200).json({error: false, data: toDo, message: 'success'})            }, err => {            return res.status(500).send({error: true, message: 'Error adding to-do'})            })    }});// @route UPDATE api/v1/to-do/done// @desc update toDo status// @access Publicrouter.put('/done/:userId/:toDoId', function(req, res) {    let updateData = {        $set: {            "toDos.$.done": req.body.done        }    };    updateToDo({_id: req.params.userId, "toDos._id": req.params.toDoId}, updateData).then((toDo) => {        return res.status(200).json({error: false, message: 'Updated successfully'})    }, err => {        return res.status(500).send({error: true, message: err})    })});// @route UPDATE api/v1/to-do/delete// @desc delete toDo// @access Publicrouter.put('/delete/:userId/:toDoId', function(req, res) {    let updateData = { "$pull": { "toDos": { "_id": req.params.toDoId } } }    updateToDo({_id: req.params.userId, "toDos._id": req.params.toDoId}, updateData).then((toDo) => {        return res.status(200).json({error: false, message: 'Updated successfully'})    }, err => {        return res.status(500).send({error: true, message: err})    })});// function to get to-dos list with tag filterfunction getToDosList(tag, id) {    return new Promise(function(resolve, reject) {        let agg = [            {                "$unwind": "$toDos"            }, {                "$match": {                    $or: [{"_id": id}, {"toDos.tag": {$regex: `${tag}.*`, $options: "i" }}]                }            }, {                "$group": {                    _id: null,                    data: {$push: "$toDos"}                }            }        ]        collection.aggregate(agg, function(err, response) {          if(err) return reject({message: "Something went wrong"})          if(!response) return reject({message: "Error while getting remitters data"})          return resolve(response)        })    })}//function to update to-dofunction updateToDo(query, updateData) {    return new Promise(function(resolve, reject) {        collection.findOneAndUpdate(query, updateData, {new: true},            function (err, resp) {                    if (err) return reject({error: 1, message: "There was a problem while updating data"});                    return resolve(resp);            }        );    })}function getToDos(query) {    return new Promise(function(resolve, reject) {        collection.find(query,            function (err, resp) {                    if (err) return reject({error: 1, message: "There was a problem while updating data"});                    return resolve(resp);            }        );    })}module.exports = router

Finally, we can run our app by running nodemon index.js in our terminal and we should see that app is running on port 3000.

Dependencies for React.js

bootstrap
react-bootstrap
react-icons

Create your React Frontend

After creating our backend, let's move to the frontend.

Open another terminal tab and use create-react-app to create a new React project with the name to-do-react (for example):

npx create-react-app to-do-react

After that, we will have a React app with all of its dependencies installed.

Now, go to the folder

cd to-do-react

Change directory to src and run the below commands:

cd src

rm *

Now create index.js file by running below command:

touch index.js

This file will render our app to an HTML file which is in the public folder. Also, create a folder name components with the file name app.js.

mkdir components && cd components && touch app.js

app.js will contain our To-Do app.

Edit index.js file in src:

import React from 'react';import ReactDOM from 'react-dom';import App from './components/app';import 'bootstrap/dist/css/bootstrap.min.css';ReactDOM.render(<App/>, document.getElementById('root'));

Create a folder with the name of api and add the file with name of to-do.js and write API calls in that file as below:

import axios from 'axios';let base = 'http://localhost:3000/api/v1/';export default function api(url, method='GET', data={}) {    return new Promise(function(resolve, reject) {        const requestOptions = {            url: base + url,            method: method,            headers: {                'Content-Type': 'application/json'            },            data        };        axios(requestOptions)        .then(function (response) {            resolve(response.data);        })        .catch(function (error) {            reject(error);        });    });}export function AddToDoAPI(data) {    return new Promise(function(resolve, reject) {        api(`to-do/add?id=${localStorage.userId ? localStorage.userId : ''}`, 'POST', data)        .then((resp) => {            return resolve(resp);        }, (error) => {            return reject(error.response.data.message);        })    })}export function GetToDoListAPI(tag='') {    return new Promise(function(resolve, reject) {        api(`to-do/list?id=${localStorage.userId ? localStorage.userId : ''}&tag=${tag}`)        .then((resp) => {            return resolve(resp);        }, (error) => {            console.log(error)            debugger            return reject(error.response.data.message);        })    })}export function UpdateToDoAPI(data, toDoId) {    return new Promise(function(resolve, reject) {        api(`to-do/done/${localStorage.userId}/${toDoId}`, 'PUT', data)        .then((resp) => {            return resolve(resp);        }, (error) => {            return reject(error.response.data.message);        })    })}export function DeleteToDoAPI(toDoId) {    return new Promise(function(resolve, reject) {        api(`to-do/delete/${localStorage.userId}/${toDoId}`, 'PUT', {})        .then((resp) => {            return resolve(resp);        }, (error) => {            return reject(error.response.data.message);        })    })}

Edit app.js in components:

import React, {Component} from 'react';// Bootstrap for reactimport Container from 'react-bootstrap/Container';import Row from 'react-bootstrap/Row';import Col from 'react-bootstrap/Col';import Button from 'react-bootstrap/Button';import InputGroup from 'react-bootstrap/InputGroup';import FormControl from 'react-bootstrap/FormControl';import ListGroup from 'react-bootstrap/ListGroup';import Form from 'react-bootstrap/Form'import Dropdown from 'react-bootstrap/Dropdown'import DropdownButton from 'react-bootstrap/DropdownButton'import {AddToDoAPI, GetToDoListAPI, UpdateToDoAPI, DeleteToDoAPI} from '../api/to-do'import { BsStop, BsX } from 'react-icons/bs';import {Badge} from "react-bootstrap";class AppComponent extends Component {    constructor(props) {        super(props);        // Setting up state        this.state = {            userInput : "",            list:[],            selectedTag: "Other",            selectedTagColor: "grey",            tags: [                {tagName: 'Other', color: 'grey'},                {tagName: 'Work', color: 'red'},                {tagName: 'Personal', color: 'green'}            ]        }    }    componentDidMount() {        this.getItems()    }    // Set a user input value    updateInput(value){        this.setState({            userInput: value,        });    }    // Set a selected tag value    updateTag(value){        this.setState({            selectedTag: value.split(" ")[0],            selectedTagColor: value.split(" ")[1]        });    }    // Add item if user input in not empty    addItem(event){        if(event.code === 'Enter') {            AddToDoAPI({text: this.state.userInput, tag: this.state.selectedTag, tagColor: this.state.selectedTagColor}).then(resp => {                if(!localStorage.userId) {                    localStorage.setItem('userId', resp.data._id);                }                this.getItems()            })        }    }    //Get to-do list    getItems(tag='') {        GetToDoListAPI(tag).then(resp => {            // Update list            const list = [...resp ? resp.data : []];            // reset state            this.setState({                list,                userInput: ""            });        })    }    UpdateToDo(val, id) {        UpdateToDoAPI({done: val}, id).then(resp => {            this.getItems()        })    }    // Function to delete item from list use id to delete    deleteItem(id) {        DeleteToDoAPI(id).then(resp => {            this.getItems()        })    }    render(){        return(        <Container>            <Row style={{                display: "flex",                justifyContent: "center",                alignItems: "center",                fontSize: '3rem',                fontWeight: 'bolder',                fontFamily: 'DejaVu Sans Mono, monospace',                paddingTop: 2                }}                >TODO LIST            </Row>            <hr style={{marginTop: 0}}/>            <Row>                <Col md={{ span: 5, offset: 4 }}>                    <InputGroup className="mb-3">                        <DropdownButton                            variant="outline-secondary"                            id="input-group-dropdown-2"                            title={this.state.selectedTag}                            align="end"                            size="lg"                            style={{backgroundColor: 'white'}}                            onSelect = {e => this.updateTag(e)}                            >                                {this.state.tags.map(tag => (                                    <span style={{display: 'flex'}}><BsStop style={{fontSize: 30, marginTop: 1, color: tag.color}}/><Dropdown.Item key={tag.tagName} eventKey={tag.tagName + ' ' + tag.color}>{tag.tagName}</Dropdown.Item></span>                                ))}                        </DropdownButton>                        <FormControl                            placeholder="add item . . . "                            size="lg"                            value = {this.state.userInput}                            onChange = {item => this.updateInput(item.target.value)}                            onKeyPress = {e => this.addItem(e)}                            aria-label="add something"                            aria-describedby="basic-addon2"                        />                    </InputGroup>                </Col>            </Row>            {this.state.list.length ?                 <Row>                    <Col md={{ span: 5, offset: 4 }} style={{paddingBottom: 18}}>                        <Button variant="primary" style={{paddingTop: 0, paddingBottom: 0}} onClick={e => this.getItems('')} size="sm">All</Button>{' '}                        <Button variant="secondary" style={{paddingTop: 0, paddingBottom: 0}} onClick={e => this.getItems('Other')} size="sm">Other</Button>{' '}                        <Button variant="danger" style={{paddingTop: 0, paddingBottom: 0}} onClick={e => this.getItems('Work')} size="sm">Work</Button>{' '}                        <Button variant="success" style={{paddingTop: 0, paddingBottom: 0}} onClick={e => this.getItems('Personal')} size="sm">Personal</Button>{' '}                    </Col>                </Row> : null            }            <Row>                <Col md={{ span: 5, offset: 4 }}>                    <ListGroup>                    {/* map over and print items */}                    {this.state.list.map(item => {return(                        <ListGroup.Item variant="white" action                             key={item._id}>                            <Form.Group id="formGridCheckbox" style={{display: 'flex'}}>                            <Form.Check type="checkbox" style={{width: 10}} className="my-1 mr-sm-2" onChange={e => this.UpdateToDo(!item.done, item._id)} checked={item.done}/>                            {item.done ? <span style={{textDecoration: 'line-through', marginTop: 5, width: 380}}>{item.toDo}</span> : <span style={{marginTop: 5, width: 380}}>{item.toDo}</span>}                            <BsStop style={{fontSize: 25, marginTop: 1, color: item.tagColor, float: 'right', width: 30}}/>                            <BsX                                onClick = { () => this.deleteItem(item._id) }                                style={{float: 'right', fontSize: 25, marginLeft: 'auto', width: 30}}                            />                            </Form.Group>                        </ListGroup.Item>                    )})}                    </ListGroup>                </Col>            </Row>        </Container>        );    }}export default AppComponent;

Start the server by typing the following command in the terminal:

npm start

Output: Open http://localhost:3000 in browser:

To-Do app

Also, Apply filters and see the personalized to-dos:
To-Do app


Original Link: https://dev.to/chytrakr/steps-to-create-a-to-do-app-with-mern-stack-4912

Share this article:    Share on Facebook
View Full Article

Dev To

An online community for sharing and discovering great ideas, having debates, and making friends

More About this Source Visit Dev To