JS: Instantiated variable won't recognise input value - javascript

I am instantiating a new variable from a class. The class has one constructor, city and then fetches jazz clubs through the foursquare API.
When I hard-code the city name into the instantiated class, it works fine. But when I want to feed it a dynamic value (a query from the search bar which I grab through the DOM), it won't recognise the city. Here is the code:
The Class:
class Venues {
constructor(city) {
this.id = '...';
this.secret = '...';
this.city = city;
}
async getVenues() {
const response = await fetch(`https://api.foursquare.com/v2/venues/search?near=${this.city}&categoryId=4bf58dd8d48988d1e7931735&client_id=${this.id}&client_secret=${this.secret}&v=20190309`);
const venues = await response.json();
return venues;
}
}
const input = document.getElementById('search-input').value;
const button = document.getElementById('button');
const jazzClubs = new Venues(input);
button.addEventListener('click', (e) => {
e.preventDefault();
getJazzVenues();
})
function getJazzVenues() {
jazzClubs.getVenues()
.then(venues => {
console.log(venues);
})
.catch(err => {
console.log(err);
});
}
Anyone knows why the the input variable's value is not recognised by the newly instantiated jazzClubs variable?
Also, if you have tips on how to structure this code better or neater, I'd welcome any suggestions (the class definition is in a separate file already).
Many thanks guys!
Adam

You need to make sure, the following statements are triggered after the button click.
const input = document.getElementById('search-input').value;
const jazzClubs = new Venues(input);
Also your code looks too complex. Use simpler code using jquery.
Try something like this:
$(document).ready(function() {
$("#search-button").click(function() {
var searchval = $("#search-input").val();
var id = "xxx";
var secret = "yyy";
alert(searchval);
var url = "https://api.foursquare.com/v2/venues/search?near=" + searchval + "&categoryId=4bf58dd8d48988d1e7931735&client_id=" + id + "&client_secret=" + secret + "&v=20190309";
alert(url);
$.ajax({
url: url,
dataType: 'json',
success: function(data) {
var venues = data.response.venues;
alert(venues);
$.each(venues, function(i, venue) {
$('#venue-result').append(venue.name + '<br />');
});
}
});
});
});
<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
</head>
<body>
<label>City:</label>
<input type="text" id="search-input" />
<button type="button" id="search-button">Search</button>
<div id="venue-result"></div>
</body>
</html>

Related

jQuery Append only works in debug in Mozilla Firefox

I am using SignalR for Chat in my app. When a user connects, I use .append to add the users name to a div with a list of users. The code works fine in Chrome and Edge, but when I run it in FireFox the append does not work unless I hit F12 and run the code in debug mode.
Here is my javascript:
$(document).ready(function () {
initLoad = true;
loadRequests();
#Html.Raw(tabstr)
// Declare a proxy to reference the hub.
var chatHub = $.connection.chatHub;
$.connection.hub.start().done(function () {
registerEvents(chatHub)
});
registerClientMethods(chatHub);
});
function registerClientMethods(chatHub) {
chatHub.client.onNewUserConnected = function (connectionId, name, jobtitle) {
AddUser(chatHub, connectionId, name, jobtitle);
}
}
function AddUser(chatHub, connectionId, name, jobtitle) {
var userId = $('#hdId').val();
const connectionID = connectionId;
const userName = name;
const jobTitle = jobtitle;
const connectedUser = $("#divusers").append('<div id="' + connectionID + '" class="kt-widget__item" style="cursor:pointer"><div class="kt-widget__info"><div class="kt-widget__section"><a class="kt-widget__username">' + userName + '</a><span class="kt-badge kt-badge--success kt-badge--dot"></span></div>' + jobTitle + '</div></div>');
connectedUser.on("click", function () {
var groupWindows = [];
$('#groupWindowList').empty();
$('div[id^="ctr"]').each(function () { groupWindows.push(this.id); })
$.each(groupWindows, function (index, value) {
var controlname = value;
const groupName = controlname.replace("ctr", "")
const listItem = $(`<li>${groupName}</li>`);
listItem.on("click", function () {
$('#addToGroupModal').modal('toggle');
chatHub.server.addUserToGroup(groupName, connectionID);
});
$('#groupWindowList').append(listItem);
})
$('#addToGroupModal').modal({});
});
}
const connectedUser + $("#divusers").append is the problem. In debug, it is fine, but if I just run the code, the append does not take place and user's name does not display in the list.
Here is my html:
<div class="kt-portlet__body">
<div class="kt-widget kt-widget--users kt-mt-20">
<div class="kt-scroll kt-scroll--pull">
<div id="divusers" class="kt-widget__items">
</div>
</div>
</div>
</div>
UPDATE
So I added:
var myElement = $('#divusers')[0];
var observer = new MutationObserver(function(mutations) {
if (document.contains(myElement)) {
alert('hi');
});
observer.observe(document, {attributes: false, childList: true, characterData: false, subtree:true});
to check if the element exists in the DOM. I replaced the alert('hi') with the code to start the ChatHub and the append still does not work.
Another weird thing is if I make almost ANY change in the html and run it. It works the first time, but if I stop it and run it again. It doesn't work.
Any assistance is greatly appreciated.

add items to map, collectively, one at a time, per attribute id

I have successfully been able to filter, for instance shakemaps, by attribute id - I have successfully achieved this multiple ways - but all ending with the same problem.
1.) with the where filter 2.) definition expression 3.) iterating through all attribute ids and bringing them back.
The problem: All exists for only allowing/displaying one per attribute id at a time.. my goal is to feed the attribute ids into a checkbox list (which I have done), but allowing for items via attribute id to be added to the map as they are checked, collectively, one at a time - currently I can not seem to get this to work with the aspect of having multiple or more then one appear at a time on the map.
1.) i.e. the below filter (attempted logic 1) & CodePen:
.....
function filterByID(event) {
const selectedID = event.target.getAttribute("data-id");
eqLayerView.filter = {
where: "id = '" + selectedID + "'"
};
}
view.whenLayerView(fl)
.then(function(layerView) {
eqLayerView = layerView;
eqLayerView.filter = {
where: "id = ''"
};
.............
2.) i.e. another attempted logic (adding multiple at a time here, line by line, or via array):
layer.definitionExpression = "id = 'us70008jr5'",
layer.definitionExpression = "id = 'cgi988jr52'",
3.) i.e. 3rd attempt with a suggestion here on GIS exchange: https://gis.stackexchange.com/questions/364578/loop-through-attribute-ids-of-featurelayer
layer
.load()
.then(() => {
// create a query from the layer
const query = layer.createQuery();
query.returnDistinctValues = true;
query.where = "grid_value > 2"; // or 1=1 if you need them all
// the field you want distinct values for
query.outFields = ["id"];
return layer.queryFeatures(query);
})
.then(({ features }) => {
// extract the ids to a list
const ids = features.map(({ attributes }) => attributes.id);
return ids;
})
.then((ids) => {
// You can store them how you want, in this case,
// I put them in a dropdown select menu
const fragment = document.createDocumentFragment();
ids.forEach((id) => {
const option = document.createElement('option');
option.value = id;
option.innerText = id;
fragment.appendChild(option);
});
list.appendChild(fragment);
map.add(layer);
});
All attempted logic above result in the toggling of a shakemap by attribute id to be displayed only one at a time — by toggling a new on, the previous turns off, I need the ability to have multiple being able to exist on the map at once.
ArcGIS - javascript - 4.15
I am pretty sure I answer this question a couple of weeks ago, but I can't find my answer so maybe I just thought I did, in that case sorry about that.
Anyway, with a couple of fixes your code should work. The main issue here I think is that your query is not correct if you are going to use checkboxs. You need to use a set query like IN. I would be fine if you use radio buttons.
Here you have your example with the fixes I mention,
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8' />
<meta name='viewport' content='initial-scale=1,maximum-scale=1,user-scalable=no' />
<title>Filter features by attribute - 4.15</title>
<link rel='stylesheet' href='https://js.arcgis.com/4.15/esri/themes/light/main.css' />
<script src='https://js.arcgis.com/4.15/'></script>
<style>
html,
body,
#viewDiv {
padding: 0;
margin: 0;
height: 500px;
width: 100%;
}
</style>
<script>
require([
'esri/views/MapView',
'esri/Map',
'esri/layers/FeatureLayer',
'esri/widgets/Expand'
], function (MapView, Map, FeatureLayer, Expand) {
const layer = new FeatureLayer({
url: 'https://services9.arcgis.com/RHVPKKiFTONKtxq3/arcgis/rest/services/USGS_Seismic_Data_v1/FeatureServer/1/',
outFields: ['*']
, definitionExpression: 'eventTime >= CURRENT_TIMESTAMP - 30 AND grid_value > 2'
});
const map = new Map({
basemap: 'gray-vector'
, layers: [layer]
});
const view = new MapView({
map: map,
container: 'viewDiv',
center: [-98, 40],
zoom: 4
});
let eqLayerView;
let selectedIDs = {};
function updatedFilter() {
const ids = [];
for(const [key, value] of Object.entries(selectedIDs)) {
if (value) {
ids.push(`'${key}'`);
}
}
eqLayerView.filter = {
where: `id IN (${ids.join(',')})`
};
console.log(`[updateFilter] ${eqLayerView.filter.where}`);
}
const idElement = document.getElementById('id-filter');
idElement.addEventListener('click', filterByID);
function filterByID(event) {
const chk = event.target;
console.log(`[filterByID] ${chk.getAttribute('data-id')} ${chk.checked}`);
selectedIDs[chk.getAttribute('data-id')] = chk.checked;
updatedFilter();
}
view.whenLayerView(layer).then(function (layerView) {
eqLayerView = layerView;
updatedFilter();
var query = layer.createQuery();
query.outFields = ['id'];
query.returnDistinctValues = true;
query.returnGeometry = false;
layer.queryFeatures(query).then(function (results) {
results.features.map(function (feat) {
let id = feat.attributes['id'];
let opt = document.createElement('input');
opt.type = 'checkbox';
let label = document.createElement('label')
label.innerHTML = id;
opt.className = 'id-item visible-id';
opt.setAttribute('data-id', id);
idElement.appendChild(opt);
idElement.appendChild(label);
selectedIDs[id] = false;
});
});
});
});
</script>
</head>
<body>
<div id='id-filter' class='esri-widget'>
</div>
<div id='viewDiv'></div>
</body>
</html>
BTW, I added some logs for you to see what I consider import info of the events.
Last thing, I use an auxiliary dictionary to keep track of the visible features to filter. Another way to do it would simply to query all the checkbox and add the id to the filter if it is checked.

Error message from console stating that res is not defined

I am currently trying to build a fullstack application using javascript(node.js). My goal is to create a weather API with dummy data and everything was going well until I reached the end portion where I am trying to get the users search results to show up below the search box. I've tried using a fetch API from my main js file to retrieve data from my index.js file (which contains the server, imported data, etc.) but I continue to get an error message from the console stating that 'res' is not defined. 'res.data.city' is what I am using to acquire the input information from the user and render it to the screen but I keep getting told that it is not defined; I believe that it should be coming from the index.js file. Here is my code. (PLEASE be nice guys. I am a new developer and am just trying to better myself at programming.)
script.js file (main javascript file that is linked to html document)
const form = document.querySelector('form');
const textInput = document.getElementById('city');
let city = textInput.value;
textInput.addEventListener('input', (e) => {
city = e.target.value;
})
const getData = () => {
fetch('http://localhost:3000/weather/?city=' + city)
.then(res => {
return res.json();
})
.then(resData => {
console.log(resData)
}).catch((error) => {
console.log(error)
});
};
form.addEventListener('submit', (e) => {
e.preventDefault();
getData()
let location = document.querySelector('.cityName');
let celsius = document.querySelector('.celsius')
let fahrenheit = document.querySelector('.fahrenheit')
let error = document.querySelector('.error')
if(res.data.city) {
location.innerHTML = 'City: ' + res.data.city;
celsius.innerHTML = 'Temperature (C): ' + res.data['temperature (C)']
fahrenheit.innerHTML = 'Temperature (F): ' + res.data['temperature (F)'];
} else {
error.Message.innerHTML = 'SORRY! This city is not currently in our
database :{'
}
textInput.value = '';
})
index.js(file with the server running)
const express = require('express')
const app = express();
const cors = require('cors')
const fakeWeatherData = require('./data.js')
app.use(cors());
app.get('/weather', (req, res) => {
let cityName = req.query.city.toLowerCase();
for(let i = 0; i < fakeWeatherData.length; i++)
if(!cityName) {
res.send({"status": "error", "message": "Please enter a city name"})
} else if (cityName === fakeWeatherData[i].city.toLowerCase()) {
return res.send(fakeWeatherData[i])
}
res.send({"status": "error", "message": "Sorry! This city isn't in the database :{"})
})
index.html(main file that will be rendered to broswer)
<body>
<div class='main'>
<form>
<label for='city'>City:</label>
<div class='city-box'>
<input type='text' placeholder='Please enter a city' id='city' name='city' require>
</div>
<button type='submit'>CLICK</button>
</form>
</div>
<div class='error'></div>
<div class='weather'>
<h1 class='cityName'></h1>
<h4 class='celsius'></h4>
<h4 class='fahrenheit'></h4>
</div>
<script src="./js/script.js"></script>
</body>
</html>
#GaloisGirl is correct, res is not defined because you are not defining res within the scope of the anonymous function where you set the submit event listener.
Given that you're not already using async/await, seems best to stick with promise notation so it would look something like this:
form.addEventListener('submit', (e) => {
e.preventDefault();
let location = document.querySelector('.cityName');
let celsius = document.querySelector('.celsius')
let fahrenheit = document.querySelector('.fahrenheit')
let error = document.querySelector('.error')
getData()
.then((res) => {
if(res.data.city) {
location.innerHTML = 'City: ' + res.data.city
celsius.innerHTML = 'Temperature (C): ' + res.data['temperature (C)']
fahrenheit.innerHTML = 'Temperature (F): ' + res.data['temperature (F)']
} else {
error.Message.innerHTML = 'SORRY! This city is not currently in our database'
}
textInput.value = ''
})
})
And you'll also want to return the promise from getData
const getData = () => {
return fetch('http://localhost:3000/weather/?city=' + city)
...OTHER STUFF

window.location result is null

I want to build a website which returns products from a database and when click on See More, should return product details from server in another html page. The problem is that when I click See More, productID%20=%20null :(.
productDetails = second html page.
productDetails = div - in index.html where products are returned from server
<script>
var productsUrlList = 'https://finalonlineshop.firebaseio.com/.json';
async function getProductsFromServer() {
var productsResponse = await fetch(productsUrlList)
var products = await productsResponse.json();
return products;
}
async function showProducts(productsPromise) {
var products = await productsPromise;
var generatedHTML = '';
var productsIDs = Object.keys(products);
productsIDs.forEach(productID => {
var product = products[productID];
generatedHTML += getGeneratedHTMLForProduct(productID, product);
});
document.getElementById('categories').innerHTML = generatedHTML;
}
function getGeneratedHTMLForProduct(productID, product) {
var generatedHTML = `
<div id = categoriesDiv>
<img class = "categoryImage" src = ${product.Image} />
<div class = "categoryName">${product.Name}</div>
<div class = "categoryPrice">$ ${product.Price}</div>
<br>
<button id = "seeMore" onclick = "seeMore('${productID}')">See
more</button>
</div>
`;
return generatedHTML;
}
function seeMore (productID) {
window.location = `./productDetails.html?productID = ${productID}`;//issue
}
function getProductIDFromUrl () {
var params = new URLSearchParams(window.location.search);
var productID = params.get('productID');
return productID;
}
async function getDetailsFromServer(productID) {
var detailsResponse = await fetch(`https://finalonlineshop.firebaseio.com/Products/details/${productID}.json`);
var details = await detailsResponse.json();
return details;
}
async function seeDetails(detailsPromise) {
var details = await detailsPromise;
var generatedHTML = `
<div id = "detailsAboutProduct">
<img src = "${details.Image}" /> //Cannot read property "Image" of null
<div>${details.Name}</div>
<div>${details.Details}</div>
<div>$ ${details.Price}</div>
<div>${details.Qty}</div>
<button id = "addToCart" onclick = "addToCart();">Add to
cart</button>
</div>
`;
document.getElementById('details').innerHTML = generatedHTML;
}
</script>
get rid of the spaces around the = in the URL, that gets encoded as %20. You should also use encodeURIComponent() to escape any special characters in the product ID.
function seeMore (productID) {
window.location = `./productDetails.html?productID=${encodeURIComponent(productID)}`;
}
Your query to your firebase app seems to be wrong.
you are fetching : https://finalonlineshop.firebaseio.com/Products/details/${productID}.json
which return null
but you have to fetch: https://finalonlineshop.firebaseio.com/${productID}.json
instead and it will return the right object

JSON List ASP.net MVC

I have /Views/Movies/Index.cshtml with
<input type="button" id="getmoviex" value="Get moviex" />
<ul id="moviex_list"/>
<p>
Title: #Html.TextBox("SearchTitle") <br />
</p>
I have /Controllers/MoviesController.cs with
[AcceptVerbs(HttpVerbs.Get)]
public ActionResult moviex(string SearchGenre, string SearchTitle, string SearchActor)
{
var db = new CinemaContext();
db.Configuration.ProxyCreationEnabled = false;
var Movie = from m in db.Movie
select m;
if (!String.IsNullOrEmpty(SearchTitle))
{
Movie = Movie.Where(s => s.Title.Contains(SearchTitle));
}
return Json(db.Movie.ToList(), JsonRequestBehavior.AllowGet);
}
I have Javascript.js with
$(document).ready(function () {
$('#getmoviex').click(function () {
$.getJSON("/Movies", null, getmoviex);
});
});
Have I correctly written /Movies? Or this should be /Views/Movies?
function getmoviex(moviex) {
$("#moviex_list").text("");
$.each(moviex, function (i) {
$("#moviex_list").append("<li>" + this + "</li>");
});
}
How can I display info or list info from my query? Or view some output with error?
First make sure you button click does not trigger a request to server. Preventing default behavior is a standard way of doing it:
$('#getmoviex').click(function (event) {
$.getJSON("/Movies", null, getmoviex);
event.preventDefault();
});
As for the URL, it should not be to view, but to action instead. Your action is moviex and controller is Movies, so
$.getJSON("/Movies/moviex", null, getmoviex);
The rest looks fine, so that should do it.
you need to pass your arguments as well in url (GET).
Something like this could work:
$('#getmoviex').click(function(event) {
event.preventDefault();
$.getJSON("/Movies/moviex?SearchGenre=yuorgenre&SearchTitle=Cal&SearchActor=youractor", function(moviex) {
var lis;
//please check the console
console.log(moviex);
$.each(moviex, function(b) {
lis += "<li id='" + b.Id + "'>" + b.Title + "</li>");
}); document.getElementById("moviex_list").innerHTML += lis;
});
});
To avoid circular reference in Serializing you may use:
if (String.IsNullOrEmpty(SearchTitle)) {
return View("Error");
}
var db = new CinemaContext();
var Movie = (from m in db.Movie
Where m.Title.Contains(SearchTitle)
select new {
Id = m.MovieID,
Title = m.Title // can add more properties
}).ToList();
return Json(Movie, JsonRequestBehavior.AllowGet);

Categories