I have a function that adds an item to a list and a function that deletes an item. When I add the item it displays correctly but the item cannot be deleted until I refresh the page. I'm using python 3, flask and javascript but I don't know why the delete function cannot be called on the newly created item. This is the relevant code:
index.html:
const checkboxes = document.querySelectorAll('.check-completed');
for (let i = 0; i < checkboxes.length; i++) {
const checkbox = checkboxes[i];
checkbox.onchange = function(e) {
console.log('click')
const newCompleted = e.target.checked;
const todoId = e.target.dataset['id'];
fetch('/todos/' + todoId + '/set-completed', {
method: 'POST',
body: JSON.stringify({
'completed': newCompleted
}),
headers: {
'Content-Type': 'application/json'
}
})
.then(function() {
document.getElementById('error').className = 'hidden';
})
.catch(function() {
document.getElementById('error').className = '';
})
}
}
const descInput = document.getElementById('description');
document.getElementById('form').onsubmit = function(e) {
e.preventDefault();
const desc = descInput.value;
descInput.value = '';
fetch('/todos/create', {
method: 'POST',
body: JSON.stringify({
'description': desc,
}),
headers: {
'Content-Type': 'application/json',
}
})
//.then(response => {
// return response.json()
// data = response.json()
//})
.then(response => response.json())
.then(jsonResponse => {
const li = document.createElement('li');
const checkbox = document.createElement('input');
checkbox.className = 'check-completed';
checkbox.type = 'checkbox';
checkbox.setAttribute('data-id', jsonResponse.id);
li.appendChild(checkbox);
const text = document.createTextNode(' ' + jsonResponse.description);
li.appendChild(text);
const deleteBtn = document.createElement('button');
deleteBtn.className = 'xbox';
deleteBtn.setAttribute('data-id', jsonResponse.id);
deleteBtn.innerHTML = '✗';
li.appendChild(deleteBtn);
document.getElementById('todos').appendChild(li);
document.getElementById('error').className = 'hidden';
})
.catch(function() {
console.error('Error occurred');
document.getElementById('error').className = '';
})
}
Delete code
const deletes = document.querySelectorAll('.xbox');
for (let i = 0; i < deletes.length; i++) {
const btn = deletes[i];
btn.onclick = function(e) {
console.log('click')
const todoId = e.target.dataset['id'];
fetch('/todos/' + todoId, {
method: 'DELETE'
})
.then(function() {
const item = e.target.parentElement;
item.remove();
})
}
}
app.py
#app.route('/todos/create', methods=['POST'])
def create_todo():
error = False
body = {}
# description = request.form.get('description', '')
# return render_template('index.html')
try:
description = request.get_json()['description']
todo = Todo(description=description)
#body['description'] = todo.description
db.session.add(todo)
db.session.commit()
body['id'] = todo.id
body['completed'] = todo.completed
body['description'] = todo.description
except:
error=True
db.session.rollback()
print(sys.exc_info())
finally:
db.session.close()
if error:
abort (400)
else:
return jsonify(body)
I got it. I had to name the update and delete functions and call them inside the add item function to attach the functions to the new css elements.
Related
I can't figure out where to put the curly brackets or parenthesis to make it work correctly. At first I thought the server was down or something but then I managed to console log the data. I kept uninstalling and reinstalling this visual studio code extension called "Bracket Colorizer" to try and solve the but I am all out of juice.
document.addEventListener('DOMContentLoaded', () => {
const title = document.createElement('h1');
title.innerText = 'Online Chatroom';
document.querySelector('body').appendChild(title);
// make AJAX call here....
fetch('https://curriculum-api.codesmith.io/messages')
.then(data => data.json())
.then(data => {
const main = document.querySelector('main')
for (let i = 0; i < data.length; i++) {
writeHTML(data[i], main)
// console.log(data);
});
};
};
Here is the rest of the code me and my tutor from wyzant worked on together if it helps.
document.querySelector('form').addEventListener('submit', sendMessage)
function writeHTML(message, htmlNode) {
let messageContainer = document.createElement('div')
let messageText = document.createElement('p')
messageText.innerHTML = message.message
messageContainer.appendChild(messageText)
let messageTime = document.createElement('span')
messageTime.classList.add('time')
messageTime.innerText = message.created_at
messageContainer.appendChild(messageTime)
linebreak = document.createElement("br");
messageContainer.appendChild(linebreak);
// // document.innerHTML(<br>)
let createdBy = document.createElement('span')
createdBy.classList.add('message_sender')
createdBy.innerText = message.created_by
messageContainer.appendChild(createdBy)
htmlNode.appendChild(messageContainer)
}
function sendMessage(event) {
event.preventDefault()
let newMessage = document.querySelector('textarea').value
let data = {
message: newMessage,
//figure out how to add another text box and insert that data here
created_by: "Matthew",
created_at: Date.now()
}
fetch('https://curriculum-api.codesmith.io/messages', {
method: 'POST',
body: JSON.stringify(data),
headers: {
'Content-Type': 'application/json'
}
}).then(function(response){
return response.json()
})
.then(function(response){
const main = document.querySelector('main')
writeHTML(response[0], main)
})
.catch(function(error){
console.error(error)
})
};
This parenthesis is misplaced:
// console.log(data);
}); <-- this one
};
// console.log(data);
}
}); <-- should be here
You had two parenthesis misplaced, you can check with the code below:
document.addEventListener('DOMContentLoaded', () => {
const title = document.createElement('h1');
title.innerText = 'Online Chatroom';
document.querySelector('body').appendChild(title);
// make AJAX call here....
fetch('https://curriculum-api.codesmith.io/messages')
.then(data => data.json())
.then(data => {
const main = document.querySelector('main')
for (let i = 0; i < data.length; i++) {
writeHTML(data[i], main)
// console.log(data);
}
})
})
CS50 WEB's project3 mail
When I use single funciton it works properly which is
function compose_email() {
// Show compose view and hide other views
document.querySelector('#read-view').style.display = 'none';
document.querySelector('#emails-view').style.display = 'none';
document.querySelector('#compose-view').style.display = 'block';
// Clear out composition fields
document.querySelector('#compose-recipients').value = '';
document.querySelector('#compose-subject').value = '';
document.querySelector('#compose-body').value = '';
document.querySelector('#compose-form').onsubmit = function() {
let recipient = document.querySelector('#compose-recipients');
let subject = document.querySelector('#compose-subject');
let body = document.querySelector('#compose-body');
fetch('/emails', {
method: 'POST',
body: JSON.stringify({
recipients: recipient.value,
subject: subject.value,
body: body.value,
})
})
.then(response => response.json())
.then(result => {
console.log(result);
});
load_mailbox('sent')
return false;
};
};
But when I split it into two function it doesn't load load_mailbox('sent')
function compose_email() {
// Show compose view and hide other views
document.querySelector('#read-view').style.display = 'none';
document.querySelector('#emails-view').style.display = 'none';
document.querySelector('#compose-view').style.display = 'block';
// Clear out composition fields
document.querySelector('#compose-recipients').value = '';
document.querySelector('#compose-subject').value = '';
document.querySelector('#compose-body').value = '';
document.querySelector('#compose-form').onsubmit = function() {
send_email();
};
};
function send_email() {
let recipient = document.querySelector('#compose-recipients');
let subject = document.querySelector('#compose-subject');
let body = document.querySelector('#compose-body');
fetch('/emails', {
method: 'POST',
body: JSON.stringify({
recipients: recipient.value,
subject: subject.value,
body: body.value,
})
})
.then(response => response.json())
.then(result => {
console.log(result);
});
load_mailbox('sent')
return false;
};
Your return false in send_email is being ignored by the caller.
Have the caller return that result, so that it will prevent the default behavior.
document.querySelector('#compose-form').onsubmit = function() {
return send_email();
// ^^^
};
Or just assign send_email as the handler. Be sure you do not include the () call operator.
document.querySelector('#compose-form').onsubmit = send_email;
I created a frontend javascript for my ruby on rails backend and I am receiving the following error when I try to update a book title:
Error: app.js:65 Uncaught TypeError: Cannot read property 'renderUpdateForm' of undefined
at App.handleEditClick (app.js:65)
I am receiving books titles and their authors from a backend api.
Here is the code:
document.addEventListener('DOMContentLoaded', () => {
const app = new App();
app.attachEventListeners();
app.adapter.fetchAuthors().then(app.createAuthors);
app.adapter.fetchBooks().then(app.createBooks);
});
class Book {
constructor(data) {
this.id = data.id;
this.title = data.title;
const author = Author.findById(data.author_id);
this.author = author.name;
Book.all.push(this);
}
update({ title }) {
this.title = title;
}
renderListItem() {
return `
<li>
<h3>${this.title} - ${this.author}
<button data-id=${this.id}>update</button>
<button data-id=${this.id}>delete</button>
</h3>
</li>`;
}
renderUpdateForm() {
return `
<form data-id='${this.id}'>
<label>Title</label>
<p>
<input type="text" value="${this.title}" />
</p>
<button type='submit'>Save Book</button>
</form>
`;
}
static findById(id) {
return this.all.find(book => book.id === id);
}
}
Book.all = [];
class App {
constructor() {
this.adapter = new Adapter();
this.handleEditClick = this.handleEditClick.bind(this);
this.handleEditClick = this.handleDeleteClick.bind(this);
this.handleFormSubmit = this.handleFormSubmit.bind(this);
this.createBooks = this.createBooks.bind(this);
this.createAuthors = this.createAuthors.bind(this);
this.addBooks = this.addBooks.bind(this);
}
attachEventListeners() {
$('#books-list').on('click', 'button', this.handleEditClick);
$('#books-list').on('click', 'button', this.handleDeleteClick);
$('#update').on('submit', 'form', this.handleFormSubmit);
}
createBooks(books) {
books.forEach(book => {
new Book(book);
});
console.log(this);
this.addBooks();
}
createAuthors(authors) {
authors.forEach(author => {
new Author(author);
});
console.log(this);
this.addAuthors();
}
addBooks() {
$('#books-list').empty();
Book.all.forEach(book => $('#books-list').append(book.renderListItem()));
}
addAuthors() {
$('#authors-list').empty();
Author.all.forEach(author => $('#authors-list').append(author.renderListItem()));
}
handleFormSubmit(e) {
e.preventDefault();
const id = e.target.dataset.id;
const book = Book.findById(id);
const title = $(e.target)
.find('input')
.val();
const bodyJSON = { title };
this.adapter.updateBook(book.id, bodyJSON).then(updatedBook => {
const book = Book.findById(updatedBook.id);
book.update(updatedBook);
this.addBooks();
});
}
handleEditClick(e) {
const id = e.target.dataset.id;
const book = Book.findById(id);
$('#update').html(book.renderUpdateForm());
}
handleDeleteClick(e) {
const id = e.target.dataset.id;
const book = Book.findById(id);
const title = $(e.target)
.find('input')
.val();
const bodyJSON = { title };
this.adapter.deleteBook(book.id, bodyJSON);
}
}
class Adapter {
constructor() {
this.baseUrl = 'http://localhost:3000/api/v1';
this.headers = {
'Content-Type': 'application/json',
Accept: 'application/json'
};
}
fetchBooks() {
return this.get(`${this.baseUrl}/books`);
}
fetchAuthors() {
return this.get(`${this.baseUrl}/authors`);
}
updateBook(id, body) {
return this.patch(`${this.baseUrl}/books/${id}`, body);
}
deleteBook(id, body) {
return this.delete(`${this.baseUrl}/books/${id}`, body);
}
get(url) {
return fetch(url).then(res => res.json());
}
patch(url, body) {
return fetch(url, {
method: 'PATCH',
headers: this.headers,
body: JSON.stringify(body)
}).then(res => res.json());
}
delete(url, body) {
return fetch(url, {
method: 'DELETE',
headers: this.headers,
body: JSON.stringify(body)
}).then(res => res.json());
}
}
const book = Book.findById(id);
returns undefined.
Which means there is no entry with that id. e.target.dataset.id probably has a different value than expected (console.log is your friend).
To guard against this type of error (so your app doesn't break when you're using an invalid/non-existent id), you could just wrap next line in a condition:
handleEditClick(e) {
const id = e.target.dataset.id;
const book = Book.findById(id);
if (book) {
$('#update').html(book.renderUpdateForm());
}
}
Better checks are whether the result has a function named renderUpdateForm:
if (book && typeof book.renderUpdateForm === 'function') {
$('#update').html(book.renderUpdateForm())
}
or if it is a Book instance:
if (book instanceof Book) {
$('#update').html(book.renderUpdateForm())
}
I have this code, but now I don't know how to delete from there, if I click on the X. I would like to find the id of the row, and then put it after the url. Now I don't know how to find the id.
const tBody = document.getElementById("tbody");
var url = "http://localhost:3000/users";
//
fetch(url, {method: "get"})
.then(result => {return result.json()})
.then(data => {document.addEventListener('DOMContentLoaded', (event) => {
tBody.innerHTML="";
for(let i = 0; i < data.length; i++){
let newRow = document.createElement("tr");
newRow.innerHTML = `
<td>${data[i].id}</td>
<td>${data[i].title}</td>
<td>${data[i].author}</td>
<td> <button class="delete btn btn-primary">X</button> </td>`
tBody.appendChild(newRow);
}
});
});
//
const submitButton = document.getElementById("submit-button");
const titleInput = document.getElementById("inputTitle");
const authorInput = document.getElementById("inputAuthor");
submitButton.addEventListener("click", function(e){
e.preventDefault();
var newUser = {
title: titleInput.value,
author: authorInput.value,
};
var van = true;
fetch(url, {method: "get"})
.then(result => {return result.json()})
.then(data => {
for(let i = 0; i < data.length; i++){
if (data[i].title===titleInput.value || data[i].author===authorInput.value) {
var z = i + 1;
var x = "/" + z;
var postUrl = url + x;
fetch(postUrl, {
method: 'PUT',
body: JSON.stringify(
{
title: titleInput.value,
author: authorInput.value,
id: data[i].id
}
),
headers: {
"Content-type": "application/json; charset=UTF-8"
}
});
van = false;
}
}
if(van===true) {
fetch(url, {
method: 'POST',
headers: {
"Content-type": "application/json; charset=UTF-8"
},
body: JSON.stringify(newUser)
}).then(response => response.json)
.then(json => console.log(json));
}
});
});
I tried this:
var tomb = [];
const removeTomb=(id)=>{
fetch(url, {method: "get"})
.then(result => {return result.json()})
.then(data => {
for(let i = 0; i < data.length; i++){
var b = {
id: data[i].id,
title: data[i].title,
author: data[i].author
}
tomb.push(b);
console.log(tomb);
}
let index=tomb.findIndex(tomb => tomb.id==id);
tomb.splice(index,1);
console.log(tomb);
});
};
and I put this onclick="removeTomb(${tomb[i].id})" before the X, but it does not work, because removeTomb is undefined.
Can you please explain me how it works? I want to learn from it! Thanks a lot!
Using const, function literal way wont work with HTML onclick="removeTomb(${tomb[i].id})". Try function removeTomb
Or assign the function to the window.
window.removeTomb = removeTomb
OR
var tomb = [];
function removeTomb(id) {
fetch(url, { method: "get" })
.then((result) => {
return result.json();
})
.then((data) => {
for (let i = 0; i < data.length; i++) {
var b = {
id: data[i].id,
title: data[i].title,
author: data[i].author,
};
tomb.push(b);
console.log(tomb);
}
let index = tomb.findIndex((tomb) => tomb.id == id);
tomb.splice(index, 1);
console.log(tomb);
});
};
I'm working on a project for school, and i'm trying to use 2 parameters in a function, but they are from 2 different functions. I have no clue how i can use them both.
What i'm trying to do is to actually use the code in showCityInfo in the function handleClickImg, but in order to do that i need the data 'city'
which is passed onto showCityInfo from the function getCityInfo, where the data is collected from my php.
So in short, i want to use the data 'city' and 'marker(=e.currentTarget)' in a function to do the following code i put in bold
{
const init = () => {
let radiobutton = document.querySelectorAll('.radio_button');
radiobutton.forEach(r => {
r.addEventListener("change", changeOpacity);
let $heart = document.querySelectorAll('.sidebar > svg');
$heart.forEach(h => {
h.addEventListener('click', changeColor);
})
document.documentElement.classList.add('has-js');
const $input = document.querySelector(`.world_form`);
console.log($input);
if ($input) {
$input.addEventListener(`change`, handleChangeFilter);
}
});
$markers = document.querySelectorAll('.world_map > img');
console.log($markers);
$markers.forEach(marker => {
marker.addEventListener('click', handleClickImg);
})
}
const handleChangeFilter = e => {
const alignment = e.target.value;
console.log(alignment);
const path = window.location.href.split(`?`)[0];
const qs = `?alignment=${alignment}`;
getCityInfo(`${path}${qs}`);
};
const getCityInfo = async url => {
console.log(url);
const response = await fetch(url, {
headers: new Headers({
Accept: 'application/json'
})
});
const city = await response.json();
console.log(city);
window.history.pushState({}, ``, url);
showCityInfo(city);
};
const showCityInfo = city => { **
const $parent = document.querySelector(`.contact_wrapper`);
$parent.innerHTML = ``;
$parent.innerHTML += `<p class="contact_info"><span>email:</span> Carvee${city.name}#hotmail.com</p>
<p class="contact_info"><span>tel:</span> ${city.tel} 476 03 51 07</p>` **
};
const handleClickImg = e => {
marker = e.currentTarget;
console.log(marker);
if (marker.id == city.city_code) {
}
}
In case you only have to compare a city and a marker at a time, I think what you should do is declare two global variables outside of the functions to store the city and marker data and be able to access and compare them anywhere.
//...
let city, marker;
const getCityInfo = async url => {
console.log(url);
const response = await fetch(url, {
headers: new Headers({
Accept: 'application/json'
})
});
city = await response.json();
console.log(city);
window.history.pushState({}, ``, url);
showCityInfo();
};
const showCityInfo = () => {
const $parent = document.querySelector(`.contact_wrapper`);
$parent.innerHTML = ``;
$parent.innerHTML += `<p class="contact_info"><span>email:</span> Carvee${city.name}#hotmail.com</p><p class="contact_info"><span>tel:</span> ${city.tel} 476 03 51 07</p>`
};
const handleClickImg = e => {
marker = e.currentTarget;
console.log(marker);
if (marker.id == city.city_code) {
}
}