Skip to content

Instantly share code, notes, and snippets.

@qodesmith
Created September 9, 2018 22:29
Show Gist options
  • Select an option

  • Save qodesmith/c0ae06a71c6f366779d6506ba31f8593 to your computer and use it in GitHub Desktop.

Select an option

Save qodesmith/c0ae06a71c6f366779d6506ba31f8593 to your computer and use it in GitHub Desktop.

Revisions

  1. qodesmith created this gist Sep 9, 2018.
    30 changes: 30 additions & 0 deletions index.html
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,30 @@
    <!doctype html>
    <html class="no-js" lang="">

    <head>
    <meta charset="utf-8">
    <meta http-equiv="x-ua-compatible" content="ie=edge">
    <title></title>
    <meta name="description" content="">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <link rel="apple-touch-icon" href="apple-touch-icon.png">
    <!-- Place favicon.ico in the root directory -->

    <link rel="stylesheet" href="resources/css/normalize.css">
    <link rel="stylesheet" href="resources/css/style.css">
    <link href="https://fonts.googleapis.com/css?family=Quicksand:300,400,500,700" rel="stylesheet">
    </head>

    <body>
    <h1>Weather Now</h1>
    <form class="weather-search">
    <input type="text" name="city" placeholder="Enter a U.S. city"></input>
    <button id="go" type="submit">GO</button>
    </form>

    <main id="weather-results"></main>
    <script src="resources/js/main2.js"></script>
    </body>

    </html>
    146 changes: 146 additions & 0 deletions main.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,146 @@
    const locationUrl = 'https://dataservice.accuweather.com/locations/v1/cities/search';
    const currentUrl = 'https://dataservice.accuweather.com/currentconditions/v1/';
    const forecastUrl = 'https://dataservice.accuweather.com/forecasts/v1/daily/5day/';
    const api = '?apikey=dWD7IznmsGfuKaKFufT5l9vOGUgNiiQG';

    // The only DOM reference we need :)
    const weatherResults = document.querySelector('#weather-results');

    /*
    Adding the event listener to the form instead.
    Doing so gives us access to some features the form comes with out of the box.
    If there are any named fields in the form, we have access to those on the event object.
    */
    document.querySelector('.weather-search').addEventListener('submit', e => {
    /*
    Prevent the form from doing it's ancient default HTML behavior
    which is to try to submit data somewhere and reload the page.
    No thanks. We know what we're doing. We'll take it from here.
    */
    e.preventDefault();

    /*
    The input field is named "city" so we can access its value
    like so *because* our listener is on the form.
    `e.target` is the form, and HTML5 forms will put any named fields
    inside the form on the `e.target` object like below.
    */
    const location = e.target.city.value;

    // Dynamically construct the url for the weather api.
    const newLocationUrl = `${locationUrl}${api}&q=${location}`;

    // Begin a series of AJAX requests to the weather API via fetch.
    fetch(newLocationUrl)
    .then(res => res.json())
    .then(data => {
    const locationKey = data[0].Key;
    const newCurrentUrl = `${currentUrl}${locationKey}${api}`;
    const newForecastUrl = `${forecastUrl}${locationKey}${api}`;

    /*
    This object will serve to collect all the data we want to put on the screen.
    As you'll see below, we use the spread (...) operator to update this objects
    contents with each fetch's response.
    */
    let templateData = {
    city: data[0].LocalizedName,
    state: data[0].AdministrativeArea.LocalizedName
    };

    /*
    Since we have 2 more calls to the weather API that don't depend on
    eachothers results, we can use `Promise.all` to resolve them.
    */
    const promise1 = fetch(newCurrentUrl)
    .then(res => res.json())
    .then(data => {
    const iconNum = data[0].WeatherIcon

    templateData = {
    ...templateData,
    temp: data[0].Temperature.Imperial.Value,
    desc: data[0].WeatherText,
    iconClass: `wi wi-${iconClass(iconNum)}`
    };
    });

    const promise2 = fetch(newForecastUrl)
    .then(res => res.json())
    .then(data => {
    const days = [0,1,2,3,4].map(num => ({
    high: data.DailyForecasts[num].Temperature.Maximum.Value,
    low: data.DailyForecasts[num].Temperature.Minimum.Value
    }));

    templateData = { ...templateData, days };
    });

    /*
    Remember, `fetch` and `.then` are nothing more than promises themselves.
    So by storing them in variables above, we're storing the promises themselves,
    not their return values. That's the reason we need to chain another `.then`
    to `Promise.all` - which is also a promise!
    */
    Promise.all([promise1, promise2]).then(() => {
    weatherResults.innerHTML = htmlTemplate(templateData);
    })
    });
    });

    /*
    There's no way around it - there's a lot of conditions we have to consider.
    We can reduce the amount of code needed by simply declaring `if` statements
    that each return a value. Remember, the `return` statement will both
    spit out a value and prevent any code below it from executing.
    */
    function iconClass(num) {
    if (num <= 5) return 'day-sunny';
    if (num <= 8) return 'day-cloudy';
    if (num === 11) return 'fog';
    if (num > 11 && num <= 14) return 'day-showers';
    if (num <= 17) return 'thunderstorm';
    if (num === 18) return 'rain';
    if (num <= 21) return 'snowflake-cold';
    if (num <= 29) return 'snow';
    if (num === 30) return 'hot';
    if (num === 31) return 'cold';
    if (num === 32) return 'windy';
    if (num <= 34) return 'night-clear';
    if (num <= 38) return 'cloudy';
    if (num <= 42) return 'showers';

    return 'snow'
    }

    /*
    One of the best features to hit JavaScript in ES6 was template literals!
    As you know, we can use backticks to declare strings and interpolate
    expressions inside the ${}. Using this functionality, we can create a
    template that will be used to set the innerHTML of something in the DOM.
    Looks pretty similar to the JSX in a React components render method, right?
    */
    function htmlTemplate(data) {
    return `
    <h2>${data.city}, ${data.state}</h2>
    <div>
    <div id="current-temp">
    <i class="${data.iconClass}"></i>
    <div class="temp">${data.temp}&deg;F ${data.desc}</div>
    </div>
    <section class="five-day">
    ${
    data.days
    .map(day => (
    `
    <div class="days">
    <span class="high">${day.high}</span>
    <span class="low">${day.low}</span>
    </div>
    `
    )).join('')
    }
    </section>
    </div>
    `
    }