I am trying to show the hourly weather on my website from this API: https://opendata-download-metfcst.smhi.se/api/category/pmp3g/version/2/geotype/point/lon/18.1489/lat/57.3081/data.json
I have a hard time understanding the functions needed to do so. I think I need some sort of loop, but can't figure out what to put in it. This is my code so far:
export async function getWeather() {
var res = await fetch("https://opendata-download-metfcst.smhi.se/api/category/pmp3g/version/2/geotype/point/lon/18.1489/lat/57.3081/data.json");
if (res.ok)
return await res.json();
else
alert('HTTP Error: ' + res.status);
}
--My other file--
import {getWeather} from './fetch.js'
async function generate_weather() {
var jsonRes = await getWeather();
var i = 0;
if(jsonRes.timeSeries[i].validTime == timeNow())
{
document.createTextNode(jsonRes.timeSeries[i].parameters[i].values[0] + " grader");
}
}
function timeNow() {
var date = new Date();
var hours = date.getHours();
return hours;
}
generate_weather();
I tried the example below, but it didn't work. Am I missing a step?
async function generate_weather() {
var jsonRes = await getWeather();
for (let times in jsonRes.timeSeries) {
let currentTime = times.validTime;
let date = new Date(currentTime);
let currentHour = date.getHours();
for (let param in times.parameters) {
let value = param.values[0];
let unit = param.unit;
}
for (var i = 0; i == currentHour;) {
var newElement = document.createElement('h2');
var newElementText = document.createTextNode(jsonRes.timeSeries[i].times[i].parameters[0]);
newElement.appendChild(newElementText);
document.getElementById("body").appendChild(newElement);
}
}
}
generate_weather();
In response, you have two arrays one for timeseries and other for parameters inside the timeseries-
So, you will have to use loop inside the loop like this in generate_weather function -
for (let times in jsonRes.timeSeries) {
let currentTime = times.validTime;
let date = new Date(currentTime);
let currentHour = date.getHours();
for (let param in times.parameters) {
let name = param.name;
let value = param.values[0];
let unit = param.unit;
Console.log('Tempreture of ' + name + ' is ' + value + ' ' + unit); // Tempreture of t is 14.8 Cel
}
}
Above code will show the tempreture of every name per every hour, if you need to show the tempreture for any specific name then you can use if/else statement.
Related
I'm creating a todo list in javascript and I'm in the adding phase
tasks for the user. He wants to do so that if the user enters a task, he cannot enter it again
const taskInput = document.getElementById('enter-task');
const addTaskBtn = document.querySelector('[data-submit-task]');
const toDoContainer = document.querySelector('[data-todo]');
addTaskBtn.addEventListener('click', addTask);
function addTask() {
if (taskInput.value == '') {
alert("Pole nie może być puste");
return;
}
const taskContainer = document.createElement('div');
taskContainer.classList.add('task-container');
taskContainer.innerHTML = `<input type="checkbox" data-complete><p class="task-date">Data utworzenia: <span data-task-date>${getDate()}</span></p><div class="content-edit"><span data-task-title>${taskInput.value}</span><button type="button" data-edit>Edycja</button></div><button type="button" data-delete>X</button>`;
const completeTaskBtn = taskContainer.querySelector('[data-complete]');
const deleteTaskBtn = taskContainer.querySelector('[data-delete]');
const editTaskBtn = taskContainer.querySelector('[data-edit]');
completeTaskBtn.addEventListener('click', markAsDone);
deleteTaskBtn.addEventListener('click', deleteTask);
editTaskBtn.addEventListener('click', editTask);
toDoContainer.appendChild(taskContainer);
}
function markAsDone() {
const taskTitle = this.parentNode.querySelector('[data-task-title]');
if (this.checked) {
taskTitle.style.textDecoration = 'line-through';
return;
}
taskTitle.style.textDecoration = 'none';
}
function deleteTask() {
toDoContainer.removeChild(this.parentNode);
}
function editTask() {
const taskContainer = this.parentNode.parentNode;
if (taskContainer.contains(taskContainer.querySelector('.task-edit-container'))) {
alert("Nie możesz dodać więcej niż jednego pola do edycji");
return;
}
const currentTaskTitle = this.previousElementSibling;
const editTaskContainer = document.createElement('div');
editTaskContainer.classList.add('task-edit-container');
editTaskContainer.innerHTML = `<label>Wprowadź treść zadania</label><input type="text"><button type="button">OK</button>`;
const submitTitleBtn = editTaskContainer.querySelector('button');
submitTitleBtn.addEventListener('click', function() {
const newTitle = editTaskContainer.querySelector('input').value;
if(newTitle == '') {
alert("Pole do edycji nie może być puste");
return;
}
const currentDate = document.querySelector('[data-task-date]');
currentDate.innerHTML = `${getDate()}`;
currentTaskTitle.innerHTML = newTitle;
taskContainer.removeChild(editTaskContainer);
});
taskContainer.appendChild(editTaskContainer);
}
function getDate() {
let date = new Date();
let day = date.getDate();
let monthIndex = date.getMonth();
let year = date.getFullYear();
let hours = date.getHours();
let minutes = date.getMinutes();
let seconds = date.getSeconds();
if(minutes < 10) minutes = "0" + minutes;
if(seconds < 10) seconds = "0" + seconds;
const months = ["Styczeń", "Luty", "Marzec", "Kwiecień", "Maj", "Czerwiec", "Lipiec", "Sierpień", "Wrzesień", "Październik", "Listopad", "Grudzień"];
return `${day} ${months[monthIndex]} ${year} ${hours} : ${minutes} : ${seconds}`;
}
Here is the code responsible for adding editing as well as removing tasks from the todo list and generating the date when adding a task as well as editing it. Now he wants to do so that if the user creates a task for himself, he will normally be added to the list, while if he wants to add the same task to the list of tasks again, he will return a message like "You have already added this task"
What you can do is create array or something where you store all the added tasks and when you add new task just check whether same task is available inside array or not, if it is available you can show some message to user saying already available.
Mediocre javascript developer here and need some help..
I want to make a GET call to a url several times in a for loop.
I am trying to use fetch, but because of the promise/timing situation, I can't figure out how to make it work.
Below is the code and it's entirely possible that the fetch approach doesn't make sense for what I am trying to do. Would appreciate your help either helping me with code or telling me I am idiot and advising an alternative :)
var fromAmt = 100;
var fromOOP = 50;
var fromGM = 50;
var fromCur = "USD"
var toCur = ["USD","EUR","INR","GBP","SGD"];
var adjAmt = [];
async function getConversionAsync(fcur,tcur,amt)
{
let response = await fetch('https://data.fixer.io/api/convert?access_key=xyxyxyxyxyxyxy&from=' + fcur + '&to=' + tcur + '&amount=' + amt);
let data = await response.json()
return data;
}
for (i = 0; i < toCur.length; i++) {
getConversionAsync(fromCur,toCur[0].toString(),fromAmt)
.then(data => display(data));
}
function display(thing){
adjAmt.push(thing.result);
}
document.getElementById("something").innerHTML = adjAmt[0].toString();
In your example, document.getElementById("something").innerHTML = adjAmt[0].toString(); is executed before anything is pushed to adjAmt. You need to wait for the loop calls to finish before displaying a result, and for this you could wrap everything inside an async function.
const fromAmt = 100;
const fromOOP = 50;
const fromGM = 50;
const fromCur = 'USD';
const toCur = ['USD', 'EUR', 'INR', 'GBP', 'SGD'];
const adjAmt = [];
const getConversionAsync = async (fcur, tcur, amt) => {
const response = await fetch(`https://data.fixer.io/api/convert?access_key=xyxyxyxyxyxyxy&from=${fcur}&to=${tcur}&amount=${amt}`);
return response.json();
}
function display(thing) {
adjAmt.push(thing.result);
}
(async () => {
for (i = 0; i < toCur.length; i += 1) {
const data = await getConversionAsync(fromCur, toCur[0], fromAmt);
display(data);
}
document.getElementById('something').innerHTML = adjAmt[0].toString();
})();
Some small changes to make it work without the API call, but you'll want to access the correct index in your loop. I don't know what the exact output you're wanting here but in this case I just joined all the values in the array.
Additionally, the setting of innerHTML needs to be done once all the values are retrieved from the API, so I would even suggest doing that when the loop terminates, or some other "done" type event.
Additionally, you can use Promise.all instead of a loop, which is what I would go with personally.
var fromAmt = 100;
var fromOOP = 50;
var fromGM = 50;
var fromCur = "USD"
var toCur = ["USD", "EUR", "INR", "GBP", "SGD"];
var adjAmt = [];
async function getConversionAsync(fcur, tcur, amt) {
let response = await sampleRequest()
/* let data = await response.json() */
return response;
}
for (i = 0; i < toCur.length; i++) {
const data = getConversionAsync(fromCur, toCur[i].toString(), fromAmt).then(data => {
display(data)
})
}
function display(thing) {
adjAmt.push(thing);
document.getElementById("something").innerHTML = adjAmt.join(', ')
}
function sampleRequest() {
return new Promise((resolve, reject) => {
resolve(Math.round(Math.random() * 1000))
})
}
<div id="something"></div>
I am trying to create a stack area graph from the values I get from api's.
My first api gives me a range of dates. Eg: June 1 - June 7.
My second api gives me values need for the graph. The data looks like this
My idea is to call the api and push the count values to function every time to a function. But somehow I could not make that possible so I am calling api once for every 100 ms and grabbing all the data and triggering the graph. Now when I want to call the second api after one iteration is complete the call interrupts the graph. Please help me fix this.
d3.json('/service/dates', function(error, dates) {
var dran = dates;
if (dates != null) {
sDt = new Date(dates.st);
eDt = new Date(dates.et);
var i = 0;
var start = function() {
if (sDt > eDt) {
clearInterval(interval);
$('.wrapper').trigger('newPoint');
return;
}
var sDate = sDt.toISOString();
var eDate = new Date(sDt.setMinutes(sDt.getMinutes() + 30)).toISOString();
//Calling the api for graph values
d3.json("/service/dat?s=" + sDate + "&e=" + eDate, function(error, results) {
if (results != null) {
numbers = numbers.push(results.numbers);
values = values.push(results.values);
}
});
i++;
}
var interval = setInterval(start, 100);
}
});
}
Put the entire API call inside a named function. Then call that from the start() function to restart everything.
function doAPILoop() {
d3.json('/service/dates', function(error, dates) {
var dran = dates;
if (dates != null) {
sDt = new Date(dates.st);
eDt = new Date(dates.et);
var i = 0;
var start = function() {
if (sDt > eDt) {
clearInterval(interval);
$('.wrapper').trigger('newPoint');
doAPILoop();
return;
}
var sDate = sDt.toISOString();
var eDate = new Date(sDt.setMinutes(sDt.getMinutes() + 30)).toISOString();
//Calling the api for graph values
d3.json("/service/dat?s=" + sDate + "&e=" + eDate, function(error, results) {
if (results != null) {
numbers = numbers.concat(results.numbers);
values[values.length] = results.values;
}
});
i++;
}
var interval = setInterval(start, 10);
}
});
}
doAPILoop();
I am using Moment.js to track the duration of an action in my app. Currently, I have the following code:
var startMoment = null;
var actionClock = null;
function startClock() {
actionClock = setInterval(function(){
if (startMoment === null) {
startMoment = moment();
$('#duration').text('00:00:00');
} else {
var now = moment();
var span = moment.duration(now - startMoment);
$('#duration').text(span.minutes() + ':' + span.seconds() + '.' + span.milliseconds());
}
}, 1000);
}
function stopClock() {
clearInterval(actionClock);
var now = moment();
var span = moment.duration(now - startMoment);
$('#duration').text(span.minutes() + ':' + span.seconds() + '.' + span.milliseconds());
startMoment = null;
actionClock = null;
}
My problem is, the format of duration is awkward. I want to display the duration as mm:ss.lll. In other words, always show two digits for the minutes, two digits for the seconds, and three digits for the milliseconds. Currently, I see durations printed like 1:2.45. How do I format a duration created with Moment.js? If this is not possible, is there another library I should be using to track a duration and display it?
Thank you!
One way of doing this might be to convert your duration back into a moment (perhaps using milliseconds), and then using the moment's formatting.
moment.utc(span.asMilliseconds()).format('mm:ss.SSS');
var startMoment = null,
$durForm = $('#durationFormatted');
var actionClock = null;
function startClock() {
actionClock = setInterval(function() {
if (startMoment === null) {
startMoment = moment();
$('#duration').text('00:00:00');
$durForm.text('00:00.000');
} else {
var now = moment();
var span = moment.duration(now - startMoment);
$('#duration').text(span.minutes() + ':' + span.seconds() + '.' + span.milliseconds());
$durForm.text(moment(span.asMilliseconds()).format('mm:ss.SSS'));
}
}, 1000);
}
function stopClock() {
clearInterval(actionClock);
var now = moment();
var span = moment.duration(now - startMoment);
$('#duration').text(span.minutes() + ':' + span.seconds() + '.' + span.milliseconds());
$durForm.text(moment(span.asMilliseconds()).format('mm:ss.SSS'));
startMoment = null;
actionClock = null;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.10.3/moment.min.js"></script>
<div>Duration (original): <span id='duration'></span>
</div>
<div>Formatted moment: <span id='durationFormatted'></span>
</div>
<button onClick='startClock()'>Start Clock</button>
<button onClick='stopClock()'>Stop Clock</button>
I had a similar issue in my project and thankfully, Lodash was already a dependency. I was able to use the _.padStart method to get there.
let yourHours = _.padStart(span.hours().toString(),2,'0');
let yourMinute = _.padStart(span.minutes().toString(),2,'0');
let yourSeconds = _.padStart(span.seconds().toString(),2,'0');
I want to create a array containing objects, and I'm using Parse to query all the data.
However, the for loop which loops over the results doesn't does that in the correct order but randomly loops over the data. If I log i each iteration, the logs show different results every time.
Here is my code:
for (var i = 0; i < results.length; i++)
{
Parse.Cloud.useMasterKey();
// retrieve params
var objectid = results[i];
var self = request.params.userid;
// start query
var Payment = Parse.Object.extend("Payments");
var query = new Parse.Query(Payment);
query.get(objectid, {
success: function (payment) {
// get all the correct variables
var from_user_id = payment.get("from_user_id");
var to_user_id = payment.get("to_user_id");
var amount = payment.get("amount");
var createdAt = payment.updatedAt;
var note = payment.get("note");
var img = payment.get("photo");
var location = payment.get("location");
var status = payment.get("status");
var fromquery = new Parse.Query(Parse.User);
fromquery.get(from_user_id, {
success: function(userObject) {
var fromusername = userObject.get("name");
var currency = userObject.get("currency");
var toquery = new Parse.Query(Parse.User);
toquery.get(to_user_id, {
success: function(touser)
{
var tousername = touser.get("name");
if(tousername !== null || tousername !== "")
{
sendArray(tousername);
}
},
error: function(touser, error)
{
var tousername = to_user_id;
if(tousername !== null || tousername !== "")
{
sendArray(tousername);
}
}
});
function sendArray(tousername) {
var array = new Array();
// create the time and date
var day = createdAt.getDate();
var year = createdAt.getFullYear();
var month = createdAt.getMonth();
var hour = createdAt.getHours();
var minutes = createdAt.getMinutes();
// create the timestamp
var time = "" + hour + ":" + minutes;
var date = "" + day + " " + month + " " + year;
var associativeArray = {};
if(self == from_user_id)
{
fromusername = "self";
}
if(self == to_user_id)
{
tousername = "self";
}
associativeArray["from"] = fromusername;
associativeArray["to"] = tousername;
associativeArray["amount"] = amount;
associativeArray["currency"] = currency;
associativeArray["date"] = date;
associativeArray["time"] = time;
associativeArray["status"] = status;
if(note == "" || note == null)
{
associativeArray["note"] = null;
}
else
{
associativeArray["note"] = note;
}
if(img == "" || img == null)
{
associativeArray["img"] = null;
}
else
{
associativeArray["img"] = img;
}
if(location == "" || location == null)
{
associativeArray["location"] = null;
}
else
{
associativeArray["location"] = location;
}
array[i] = associativeArray;
if((i + 1) == results.length)
{
response.success(array);
}
},
error: function(userObject, error)
{
response.error(106);
}
});
},
error: function(payment, error) {
response.error(125);
}
});
}
But the i var is always set to seven, so the associative arrays are appended at array[7] instead of the correct i (like 1,2,3,4,5)
The reason that this is so important is because I want to order the payment chronologically (which I have done in the query providing the results).
What can I do to solve this issue?
Success is a callback that happens at a later point in time. So what happens is, the for loop runs 7 times and calls parse 7 times. Then after it has run each of parse success calls will be executed, they look at i which is now at 7.
A simple way to fix this is to wrap the whole thing in an immediate function and create a new closure for i. Something like this
for(var i = 0; i < results.length; i++){
function(iClosure) {
//rest of code goes here, replace i's with iClosure
}(i);
}
Now what will happen is that each success function will have access to it's own iClosure variable and they will be set to the value of i at the point they were created in the loop.