Team project: Matcher app - ReiniervanLimpt/project-tech-21 GitHub Wiki
Implementing ajax functionality
Ive worked with Deanna Bosschert and Daniel van de Velde on the Matcher App. i will describe how weve worked together on git and the points ive made improvements or additions on in the development of the final product.
My pull requests
- Refactor/controllers ISSUE: Split large modules into smaller ones
- Feat/predev scripts ISSUE: need scripts to build (or watch) css
- Accessibility/ajax
- Added ajax functionality in registering
Ive added ajax to improve on the accessibility of our app, i did this on the registration,login and edit profile page
Login page
On the login page ive added some error handling so users no longer get redirected to a error page with a single line of text on what went wrong.
Heres a snippet of the login page ive changed, ive added a new client side js file that serves as an additional layer between the server and client side HTTP requests, it intercepts the usual form behaviour and sends a request from the input values.
const login = (form) => {
event.preventDefault()
const url = `/login`
let data = {
username: form.elements.username.value,
password: form.elements.password.value
}
sendData(url, data);
}
in this snippet you can see ive disabled the default form event behaviour upon submitting, this is done in combination with adding an onsubmit event on the form in the hbs file
<form action="/login" method="POST" onsubmit="login(this, event)">
. It calls for the sendData function which ive refactored in a later because i have multiple functions which require this functionality.
:disappointed_relieved: difficulties of the login page :disappointed_relieved:
Daniel had implementen Passport to authenticate users, however the info received from a failed authenitcation gave limited info so ive had to write a custom callback which ment i had to set the users session manually as documented here
userController.doLogin = (req, res) => {
passport.authenticate("local")(req, res, () => {
res.redirect("/");
});
old code
userController.doLogin = (req, res, next) => {
passport.authenticate('local', function(err, user, info) {
if (!user && info) { res.send(info.message) }
req.logIn(user, function(err) {
if(user) {
//session needs to be set manually in a custom callback -http://www.passportjs.org/docs/authenticate/
req.session.user = user
res.send("logged in")
}
});
})(req, res, next);
};
new code
Registration page
When submitting your registration you now get information based on what went wrong
Ive had to use a bit of the leaflet map code Daniel wrote in as a handlebars helper to extract the location of the marker a user had placed as you can see in this big commit
client side js
const registerUser = (form) => {
event.preventDefault()
const url = `/register`
let latitude
let longitude
if (marker) {
latitude = marker._latlng.lat;
longitude = marker._latlng.lng;
}
let data = {
username: form.elements.username.value,
password: form.elements.password.value,
email: form.elements.email.value,
name: form.elements.name.value,
gender: form.elements.gender.value,
age: form.elements.age.value,
looking: form.elements.looking.value,
lat: latitude,
lng: longitude
}
sendData(url, data);
}
server side changes
err => {
if (err) {
console.log(err)
return res.render("register", { err: err })
}
old code
err => {
if (err) {
res.send(err.message)
}
new code
As you can see the only change ive had to make was res.send the error object sent by mongoose, the error data is very precise because of the User model we defined because of these strict rules the error object can tell you exactly what went wrong, this data is sent to the user.
Edit profile
The edit profile page now updates data upon leaving input fields or clicking on the map.
Once again ive had to rewrite some of the code daniel wrote in the handlebars helpers file (we know this is just client side js and wanted to place these functions in another script file but we forget ;^)), you can check out the pull request here The code ive written for automatically changing and updating vaues of the latitude and longitude inputs of the map location selector is kind of split between two files (because of the unadressed refactoring issue ive mentioned).
mapclick
const onMapClick = (e) => {
if (marker) {
mymap.removeLayer(marker);
marker = L.marker(e.latlng, {icon: redIcon}).addTo(mymap).on("click", onMarkerClick);
if (document.getElementById("form")) {
updateValues(marker)
}
} else {
marker = L.marker(e.latlng, {icon: redIcon}).addTo(mymap).on("click", onMarkerClick);
if (document.getElementById("form")) {
updateValues(marker)
}
}
};
update values
const updateValues = (marker) => {
let lat = document.getElementById("lat");
let lng = document.getElementById("lng");
let form = document.getElementById("form");
lat.setAttribute("value", marker._latlng.lat);
lng.setAttribute("value", marker._latlng.lng);
updateProfile(form);
}
the sendData function
refactoring happened in [this commit] (https://github.com/deannabosschert/Matcher/pull/43/commits/17f0ed9444abc6a7bbdb8b1eee2ea67057cd01bd)
let bodyData = []
for (property in data) {
const encodedKey = encodeURIComponent(property)
const encodedValue = encodeURIComponent(data[property])
bodyData.push(encodedKey + "=" + encodedValue)
}
bodyData = bodyData.join("&")
if (editForm) {
editForm.parentNode.classList.remove('updated')
} else if (registerForm) {
registerForm.parentNode.classList.remove('error')
}
try {
xhr.open('POST', url)
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')
xhr.onload = function() {
if (this.status == 200) {
if (this.responseText === "logged in") {
// handle what happens when passport authenticates
window.location.replace("/")
} else if (this.responseText === "updated profile") {
editForm.parentNode.classList.add('updated')
} else {
if (registerForm) {
registerForm.parentNode.classList.add('error')
}
errorDisplay.innerHTML = this.responseText
}
} else {
errorDisplay.innerHTML = this.responseText
}
}
xhr.send(bodyData)
This is a small section of the sendData function you can see it uses the url and data parameter it received to send an XMLHttpRequest() with the data generated bodyData. As you can see ive added some elements which serve as an error display to show what the response from the server is.