Research and functionality - ReiniervanLimpt/project-tech-21 GitHub Wiki
To create my functionality (login and registering) ive looked up some packages and terms that help me create this part of the application.
package Session allows for the users state to be saved server side, this session is assigned to users in the login part of my application. I also use the session to store states of the user or example, The user enters a registered state when being redirected to the home page through the registration process
req.session.registered = true
This state is used to render conditional data which in this case is the login screen being shown.
{{#if registered}} {{else}} hidden {{/if}}">
package I am using Handlebars as my templating engine, this is used to display dynamic data as well as render some blocks conditionally or as a partial. Partials of my application are:
- Header
- Login Form
- Footer
these are loaded into my HTML(HBS) files using
{{> header}}
{{> loginform}}
{{> footer}}
Ive also found out that i can use handlebars helpers to improve the conditional rendering of my data based on which data is present in the object, for example: I can pass a ifEquals helper as a parameter in the res.render function which allows me to check for states passed from the server/session.
helpers: {
ifEquals: function(arg1, arg2, options) {
return (arg1 == arg2) ? options.fn(this) : options.inverse(this)
}
},
activePage: req.session.currentPage,
I can use this helper to pass a state such as
session.currentpage
to add a highlight to the page navigation:<a {{#ifEquals currentpage "mymatches"}} class=active {{/ifEquals}} href="/matches">
This adds a class (active) to the navigation item if whats stored in currentpage equals to "mymatches" i can also use it to render a select dropdown to set the select value upon rendering like:
<select>
<option {{#ifEquals cuisine "chinese"}} selected {{/ifEquals}} value="chinese">Chinese</option>
<option {{#ifEquals cuisine "italian"}} selected {{/ifEquals}} value="italian">Italian</option>
</select>
package I use argon2 to hash passwords so they are stored in a hashed state in the database.
Logging in after hash is the same as submitted password argon2.verify(user.hash, password).then(verified)
function verified(match) {
if (match) {
req.session.user = match
res.send("logged in")
} else {
res.send("wrong password")
}
I found that refreshing/redirecting on each post request from a form does not provide the user experience i want, to do this i need some client side function that waits for a response from my server in case something went wrong, thats where AJAX (Asynchronous JavaScript And XML) comes in. AJAX is a XMLHttpRequest object which can send requests to the server and wait for a response, i a using this technology to show the suer an error which originates from a dataManager module on the server.
I wanted to include some functionality which checks the users login credentials on the server before logging the user in, this required me to check the data on the server without the form submitting and refreshing the page.
To do this ive included some client side js which sends a request through http which i then intercept on the server and send a response back manually.
async function login(form) {
xhr = new XMLHttpRequest()
event.preventDefault()
------
bodyData object here
------
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") {
window.location.replace("/")
} else {
errorDisplay.innerHTML = this.responseText
}
} else {
console.log(this.responseText)
}
}
} catch (e) {
console.log(e)
}
}
xhr.send(bodyData)
This function is asynchronous because it has to wait for the request to be handled by the server, on the server i intercept this request with .post('/login', dataManager.logIn)
which calls the module (dataManages.js) i wrote to check if the users submitted email is present in the "users" collection which is a static json file which i have yet te replace with my mongoDB cluster.
If the server responds with a responseText "logged in" the user is redirected to the home page but with a session assigned (see the function below).
fs.readFile(jsonFile, (err, content) => {
if (err) return console.log(err)
const contentJSON = JSON.parse(content)
dataManager.checkIfExists(req, res, contentJSON).then(function(result) {
const user = contentJSON.users.find(x => x.email === email)
if (result) {
if (user.password === password) {
req.session.user = user
res.send("logged in")
} else {
res.send("wrong password")
}
} else {
res.send("user doesnt exist")
}
})
})
const correspondingData = await User.findOne({
email: email
}).exec((error, data) => {
if (error) {
console.log(error)
} else if (data) {
argon2.verify(data.password, password).then(verified, data).then(function(result) {
req.session.userData = data
res.send(result)
})
} else {
res.send("user doesnt exist")
}
})
In this function the data from the users.json is checked with another function dataManager.checkIfExists
which checks if the email is present in the json file, (i also use this function i the registration progress to check if an email address is already in use) and then compares the submitted password with the password present in the database to login the user.
Ive used AJAX on every field of the account form to update its key, i find and update the data based on the email that is stored within the seesion thats assigned when a user logged in.
async function updateAccount(element, toUpdate, value) {
xhr = new XMLHttpRequest()
const url = `/updateAccount`
let data = {
[toUpdate]: value
}
let bodyData = []
for (property in data) {
const encodedKey = encodeURIComponent(property)
const encodedValue = encodeURIComponent(data[property])
bodyData.push(encodedKey + "=" + encodedValue)
}
bodyData = bodyData.join("&")
try {
xhr.open('POST', url)
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')
updateDisplay.innerHTML = ""
element.classList.remove('updated')
xhr.onload = function() {
if (this.responseText === "data has been updated") {
element.classList.add('updated')
} else {
updateDisplay.innerHTML = "something went wrong with your update..."
}
}
xhr.send(bodyData)
} catch (e) {
console.log(e)
}
}
updateAccount: async function(req, res) {
const filter = {
email: req.session.userData.email
}
const updateData = await User.findOneAndUpdate(filter, req.body, {
new: true
}).exec((error, data) => {
req.session.userData = data
res.send("data has been updated")
})
},
I wanted to create a smooth registratoin progress which provides the user with feedback, looking at other appplications i found that getting feedback on what is going wrong (like an incorrect email/password submission) really helps lift the usability of an application.