So I'm trying to get a list of events from my database using a filter. You type in what you want to filter by and hit submit, then the browser makes an XMLHttpRequest to a get route, which sends back the list of events, and the browser renders them on the page without reloading. Supposedly, you should be able to then click on one of these events and it will bring you to that event's page with more detail - except when it tries to query the database for that specific event, the node mysql module doesn't even replace the question mark like it's intended to. If I go straight to the specific event page without running the filter, it makes the query just fine, but if I do the filter first, it leaves the query as a literal string, without adding the insert, even though I know it's there. That's the mystery - it's just not escaping on the ? after the filter query.
The maddening part is that it was working just fine, then I changed some stuff I would have thought was unrelated (mostly how to insert new events), then it broke. But I don't know how long ago that was.
I'm using node js with express, the mysql library from npm, and jade for my template engine. I would love to include a small code snippet, but I'm so clueless about what the problem is, I can't figure out which parts are expendable. So here's a big code snippet.
The jade file that shows all the events:
h1 All Events go here
div(id = 'filter')
span Distance:
select(id = 'distance')
option(value = '10') 10mi
option(value = '25') 25mi
option(value = '50') 50mi
br
span Tags:
div(id = 'tag-control')
br
input(type = 'hidden', id = 'lat')
input(type = 'hidden', id = 'lon')
button(type = 'button', onClick = 'getEvents()', style = 'display: none;' id = 'filter-button') Submit
div(id = 'filter-message')
ul(id = 'event-list')
if events
for event in events
li: a(href = 'events/#{event.eventID}') #{event.name}
a(href = 'events/new') New event
script(src = 'js/tags.js')
script(src = 'js/events.js') // I'll show this next
script.
(function getLocation(){
if(navigator.geolocation){
navigator.geolocation.getCurrentPosition((position) => {
var lat = document.getElementById('lat');
var lon = document.getElementById('lon');
lat.value = position.coords.latitude;
lon.value = position.coords.longitude;
var button = document.getElementById('filter-button');
button.removeAttribute('style'); // this ensures the filter button is invisible until your location is found
});
} else {
alert('Could not get your location. Try manually typing in your address');
}
})();
The js files:
function eventFilterQuery() {
var distance = document.getElementById('distance');
distance = distance.options[distance.selectedIndex].value;
var query = '?';
distance = 'distance=' + distance;
query += distance;
var lat = document.getElementById('lat').value;
var lon = document.getElementById('lon').value;
lat = 'lat=' + lat;
lon = 'lon=' + lon;
query += '&' + lat + '&' + lon;
var tagblock = document.getElementById('tags');
var tags = [];
tagblock.childNodes.forEach((tagspan) => {
tags.push(tagspan.innerText);
});
tags.forEach((tag, i, tags) => {
tags[i] = 'tags[]=' + tags[i];
});
query += '&' + tags.join('&');
return query;
}
function getEvents(){
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = () => {
if(xhr.readyState == 4){
if(xhr.status == 200){
// clear the current list
var eventlist = document.getElementById('event-list');
while(eventlist.firstChild){
eventlist.removeChild(eventlist.firstChild);
}
// clear the message if there is one
var message = document.getElementById('filter-message');
if(message.firstChild){
message.removeChild(message.firstChild);
}
// parse the server response
var events = JSON.parse(xhr.responseText);
// if there are any events, add them
if(events.length > 0){
events.forEach((event) => {
var li = document.createElement('li');
var a = document.createElement('a');
a.setAttribute('href', 'events/' + event.eventID);
var text = document.createTextNode(event.name);
a.appendChild(text);
li.appendChild(a);
eventlist.appendChild(li);
});
} else { // otherwise, let us know on the message
var text = document.createTextNode('No events like that yet');
message.appendChild(text);
}
}
}
};
xhr.open('get', '/events/filter' + eventFilterQuery());
xhr.send();
}
the code from the backend
router.get('/filter', (req, res) => {
pool.getConnection((err, con) => {
con.config.queryFormat = queryconfig; // I took this straight from the node mysql docs, a function I stored on another file so I could insert objects into the
var query = 'SELECT DISTINCT events.eventID, events.name, (3959 * acos(cos(radians(:lat)) * cos(radians(lat)) * cos(radians(lon) - radians(:lon)) + sin(radians(:lat)) * sin(radians(lat)))) AS distance FROM events ';
var inserts = {
lat: req.query.lat,
lon: req.query.lon,
distance: req.query.distance,
tags: [req.query.tags]
};
if(req.query.tags){
query += 'JOIN events_tags ON events.eventID = events_tags.eventID JOIN tags ON events_tags.tagID = tags.tagID WHERE tags.name IN :tags HAVING distance < :distance';
} else {
query += 'HAVING distance < :distance';
delete inserts.tags;
}
con.query(query, inserts, (err, result) => {
res.send(result);
});
con.release(); // I tried moving this around because I don't really know where you're supposed to put it in relation to your query, but it didn't seem to matter
});
});
router.get('/:id', (req, res) => {
pool.getConnection((err, con) => {
console.log(req.params.id);
var q = con.query('SELECT * FROM events WHERE eventID = ?', req.params.id, (err, result) => {
console.log(q.sql); // THIS RIGHT HERE IS THE PROBLEM. If I've used the filter route at all before, this prints 'SELECT * FROM events WHERE eventID = ?' exactly, question mark and all. If I haven't used the filter route, it substitutes the id number in no problem.
var event = result[0];
con.query('SELECT name FROM tags JOIN events_tags ON tags.tagID = events_tags.tagID WHERE eventID = ?', event.eventID, (err, tags) => {
event.tags = tags;
res.render('events/show', {event: event});
});
});
con.release();
});
});
PS this is that queryFormat thing I mentioned: https://github.com/mysqljs/mysql#custom-format
Thanks in advance
via Cordello
No comments:
Post a Comment