Skip to content

Add Comments to to-do list #871

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
node_modules
.env
.env
.vscode
44 changes: 42 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,42 @@
npm install
add DB_STRING to .env file
# todo-list - Express App

This is a simplified CRUD app for Todo List.
It provides basic functionalities to manage to-do list items including add, delete, and update the status as complete or incomplete. The to-do list items are on a database on MongoDB Atlas for easy access.

## How It's Made:

**Tech used:** HTML, CSS, JavaScript, Node, Express, MongoDB Atlas

CRUD app created using Node, Express, MongoDB Atlas. The app functions include -- Create, Read, Update and Delete data that are on a MongoDB Atlas database using Node and Express.

## How to Use:

### Set up & Requirements to Play with it on your local machine

🔑 Clone this repo:

```clone git
git clone https://github.com/100devs/todo-list-express.git
```

🔑 To install requirements:

```
mkdir todo-list
cd todo-list
npm init
npm install express mongodb ejs dotenv --save
```

🔑 Create `.env` file and add `DB_STRING` that is a Connection String to your MongoDB.
For example,

```
echo "DB_STRING = mongodb+srv://<your-user-id>:<your-password>@cluster0.example.mongodb.net/?retryWrites=true&w=majority" > .env
```

**For more information, check out MongoDB Docs on [Connection Strings](https://www.mongodb.com/docs/manual/reference/connection-string/)**

## Lessons Learned:

Learned how to create a simple API that allows users to handle data on MongoDB via creating requests to Post, Get, Put, Delete via server and render results as HTML using EJS engine.
6 changes: 3 additions & 3 deletions public/css/style.css
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
h1{
color: red;
color: red; /* set h1 text color to red */
}
.completed{
color: gray;
text-decoration: line-through;
color: gray; /* turn the text color gray for the completed item*/
text-decoration: line-through; /* strike through the completed item */
}
51 changes: 27 additions & 24 deletions public/js/main.js
Original file line number Diff line number Diff line change
@@ -1,72 +1,75 @@
const deleteBtn = document.querySelectorAll('.fa-trash')
const item = document.querySelectorAll('.item span')
const itemCompleted = document.querySelectorAll('.item span.completed')

const deleteBtn = document.querySelectorAll('.fa-trash') // trash can icons
const item = document.querySelectorAll('.item span') // select todo items
const itemCompleted = document.querySelectorAll('.item span.completed') // select completed todo items
// add event listener to trash can icons next to todo items. Click to Delete the todo item
Array.from(deleteBtn).forEach((element)=>{
element.addEventListener('click', deleteItem)
})

// add event listener todo items. Click to toggle complete/incomplete
Array.from(item).forEach((element)=>{
element.addEventListener('click', markComplete)
})

// add event listener to completed todo items next to todo items. Click to toggle complete/incomplete
Array.from(itemCompleted).forEach((element)=>{
element.addEventListener('click', markUnComplete)
})

// Asynchronously delete an item
async function deleteItem(){
const itemText = this.parentNode.childNodes[1].innerText
const itemText = this.parentNode.childNodes[1].innerText // Get the text content of the item to be deleted, that is the second child of the parent node
try{
// Send a delete request to the server with the itemText in its body
const response = await fetch('deleteItem', {
method: 'delete',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
'itemFromJS': itemText
})
})
const data = await response.json()
console.log(data)
location.reload()
const data = await response.json() // Parse the JSON response
console.log(data) // Log the data
location.reload() // reload page to render deletion

}catch(err){
console.log(err)
console.log(err) // Log any errors
}
}

// Asynchronously mark an item as completed
async function markComplete(){
const itemText = this.parentNode.childNodes[1].innerText
const itemText = this.parentNode.childNodes[1].innerText // Get the text content of the todo item to mark complete
try{
// Send a put/update request to the server to mark the item complete with its itemText in its body
const response = await fetch('markComplete', {
method: 'put',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
'itemFromJS': itemText
})
})
const data = await response.json()
console.log(data)
location.reload()
const data = await response.json() // Parse the JSON response
console.log(data) // Log the data
location.reload() // reload page to render deletion

}catch(err){
console.log(err)
console.log(err) // Log any errors
}
}

// Asynchronously mark an item as incomplete
async function markUnComplete(){
const itemText = this.parentNode.childNodes[1].innerText
const itemText = this.parentNode.childNodes[1].innerText // Get the text content of the todo item to unmark complete
try{
// Send a put/update request to the server to unmark the item complete with its itemText in its body
const response = await fetch('markUnComplete', {
method: 'put',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
'itemFromJS': itemText
})
})
const data = await response.json()
console.log(data)
location.reload()
const data = await response.json() // Parse the JSON response
console.log(data) // Log the data
location.reload() // reload page to render deletion

}catch(err){
console.log(err)
console.log(err) // Log any errors
}
}
83 changes: 46 additions & 37 deletions server.js
Original file line number Diff line number Diff line change
@@ -1,93 +1,102 @@
// Load modules
const express = require('express')
const app = express()
const MongoClient = require('mongodb').MongoClient
const PORT = 2121
require('dotenv').config()


// initialize db related variables
let db,
dbConnectionStr = process.env.DB_STRING,
dbName = 'todo'

dbName = 'todos'
// MongoDB client setup
MongoClient.connect(dbConnectionStr, { useUnifiedTopology: true })
.then(client => {
console.log(`Connected to ${dbName} Database`)
db = client.db(dbName)
})

app.set('view engine', 'ejs')
app.use(express.static('public'))
app.use(express.urlencoded({ extended: true }))
app.use(express.json())
app.set('view engine', 'ejs') // set EJS as template engine
app.use(express.static('public')) // server static from public folder
app.use(express.urlencoded({ extended: true })) // Parse incoming requests with URL-encoded payloads
app.use(express.json()) // Parse incoming requests with JSON payloads


// CRUD - Read
// 1. GET data from MongoDB
// 2. Render the data in HTML with a template engine (ejs)
app.get('/',async (request, response)=>{
const todoItems = await db.collection('todos').find().toArray()
const itemsLeft = await db.collection('todos').countDocuments({completed: false})
response.render('index.ejs', { items: todoItems, left: itemsLeft })
// db.collection('todos').find().toArray()
// find all documents under the collection and return promise with the data
const todoItems = await db.collection('todos').find().toArray() // find all docs and put them in an array `data`
const itemsLeft = await db.collection('todos').countDocuments({completed: false}) // count documents with completed:false
response.render('index.ejs', { items: todoItems, left: itemsLeft }) // data array passed to template as key-value pair(named 'items')
// db.collection('todos').find().toArray()
// .then(data => {
// db.collection('todos').countDocuments({completed: false})
// .then(itemsLeft => {
// response.render('index.ejs', { items: data, left: itemsLeft })
// response.render('index.ejs', { items: data, left: itemsLeft })
// })
// })
// .catch(error => console.error(error))
})

// CRUD - Create
// 1. form submission triggers POST request to server
// 2. refresh/get('/') with updated data from MongoDB
app.post('/addTodo', (request, response) => {
db.collection('todos').insertOne({thing: request.body.todoItem, completed: false})
db.collection('todos').insertOne({thing: request.body.todoItem, completed: false}) // find 'todos' collection and insert the object to database. toDoItem is the body of input from the forms.
.then(result => {
console.log('Todo Added')
response.redirect('/')
console.log('Todo Added') // console log so we know this happened
response.redirect('/') // response to refresh the page -> trigger get('/') req
})
.catch(error => console.error(error))
.catch(error => console.error(error)) // catch and log error
})

// CRUD - Update
// 1. Update `completed` to true.
app.put('/markComplete', (request, response) => {
db.collection('todos').updateOne({thing: request.body.itemFromJS},{
$set: {
completed: true
completed: true // notice set `true`
}
},{
sort: {_id: -1},
upsert: false
sort: {_id: -1}, // descending order
upsert: false // upsert: insert a document if no documents can be found
})
.then(result => {
console.log('Marked Complete')
response.json('Marked Complete')
console.log('Marked Complete') // console log so we know this happened
response.json('Marked Complete') // response to json
})
.catch(error => console.error(error))
.catch(error => console.error(error)) // catch and log error

})

// CRUD - Update
// 1. Update `completed` to false
app.put('/markUnComplete', (request, response) => {
db.collection('todos').updateOne({thing: request.body.itemFromJS},{
$set: {
completed: false
completed: false // notice set `false`
}
},{
sort: {_id: -1},
upsert: false
sort: {_id: -1}, // descending order
upsert: false // upsert: insert a document if no documents can be found
})
.then(result => {
console.log('Marked Complete')
response.json('Marked Complete')
console.log('Marked Complete') // console log so we know this happened
response.json('Marked Complete') // response to json
})
.catch(error => console.error(error))
.catch(error => console.error(error)) // catch and log error

})

// CRUD - Delete
// 1. delete an item `deleteOne`
app.delete('/deleteItem', (request, response) => {
db.collection('todos').deleteOne({thing: request.body.itemFromJS})
.then(result => {
console.log('Todo Deleted')
response.json('Todo Deleted')
console.log('Todo Deleted') // console log so we know this happened
response.json('Todo Deleted') // response to json
})
.catch(error => console.error(error))
.catch(error => console.error(error)) // catch and log error

})

app.listen(process.env.PORT || PORT, ()=>{
console.log(`Server running on port ${PORT}`)
console.log(`Server running on port ${PORT}`) // so we know which server is running
})
11 changes: 8 additions & 3 deletions views/index.ejs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,11 @@
<body>
<h1>Todo List: </h1>
<ul class="todoItems">
<% for(let i=0; i < items.length; i++) {%>
<!-- items hold array of objects from database passed from server.js-->
<!-- for each item of items add to li and if completed is marked true, add `completed` class -->
<!-- and add trash can icon as well -->

<% for(let i = 0; i < items.length; i++) {%>
<li class="item">
<% if(items[i].completed === true) {%>
<span class='completed'><%= items[i].thing %></span>
Expand All @@ -34,8 +38,9 @@

<h2>Left to do: <%= left %></h2>

<h2>Add A Todo:</h2>

<h2>Add A Todo:</h2>

<!-- form to add todo item to database via Post req to server -->
<form action="/addTodo" method="POST">
<input type="text" placeholder="Thing To Do" name="todoItem">
<input type="submit">
Expand Down