Setelah kita melakukan implementasi HTML pada PWA (Progressive Web App) maka kita akan meneruskan dengan implementasi Javascript. Kita akan belajar dengan melihat contoh apa yang dikerjakan di dalam Javascript .

Pada implementasi HTML pada PWA kita melihat ada pemanggilan Javascript dengan source code:

Selanjutnya source Javascript saya capture dengan pembagian-pembagian seperti berikut ini:

  1. Bagian awal:
    Source code selengkapnya bagian ini adalah

    (function() {
      'use strict';
    
      var app = {
        isLoading: true,
        visibleCards: {},
        selectedCities: [],
        spinner: document.querySelector('.loader'),
        cardTemplate: document.querySelector('.cardTemplate'),
        container: document.querySelector('.main'),
        addDialog: document.querySelector('.dialog-container'),
        daysOfWeek: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
      };
    

    Statement (function() menunjukkan bahwa function ini akan segera dijalankan atau  IIFE (Immediately Invoked Function Expression). Statemen ‘use strict’ berarti bahwa setiap variabel dalam Javascript ini harus dideklarasikan.

  2. Event Listeners for UI elements:
    Pada source code di atas terlihat apa yang akan dikerjakan oleh masing-masing button jika diklik.Source code selengkapnya bagian ini:

      /*****************************************************************************
       *
       * Event listeners for UI elements
       *
       ****************************************************************************/
    
      document.getElementById('butRefresh').addEventListener('click', function() {
        // Refresh all of the forecasts
        app.updateForecasts();
      });
    
      document.getElementById('butAdd').addEventListener('click', function() {
        // Open/show the add new city dialog
        app.toggleAddDialog(true);
      });
    
      document.getElementById('butAddCity').addEventListener('click', function() {
        // Add the newly selected city
        var select = document.getElementById('selectCityToAdd');
        var selected = select.options[select.selectedIndex];
        var key = selected.value;
        var label = selected.textContent;
        if (!app.selectedCities) {
          app.selectedCities = [];
        }
        app.getForecast(key, label);
        app.selectedCities.push({key: key, label: label});
        app.saveSelectedCities();
        app.toggleAddDialog(false);
      });
    
      document.getElementById('butAddCancel').addEventListener('click', function() {
        // Close the add new city dialog
        app.toggleAddDialog(false);
      });
    
  3. Methods to update/refresh the UI:

    Penjelasan source ini sebenarnya sudah jelas seperti gambar di atas yaitu
    a. Fungsi untuk show dan hide dialog penambahan kota
    b. Update weather card, jika belum ada akan menyalin dari template
    Source code lengkapnya adalah:

      /*****************************************************************************
       *
       * Methods to update/refresh the UI
       *
       ****************************************************************************/
    
      // Toggles the visibility of the add new city dialog.
      app.toggleAddDialog = function(visible) {
        if (visible) {
          app.addDialog.classList.add('dialog-container--visible');
        } else {
          app.addDialog.classList.remove('dialog-container--visible');
        }
      };
    
      // Updates a weather card with the latest weather forecast. If the card
      // doesn't already exist, it's cloned from the template.
      app.updateForecastCard = function(data) {
        var dataLastUpdated = new Date(data.created);
        var sunrise = data.channel.astronomy.sunrise;
        var sunset = data.channel.astronomy.sunset;
        var current = data.channel.item.condition;
        var humidity = data.channel.atmosphere.humidity;
        var wind = data.channel.wind;
    
        var card = app.visibleCards[data.key];
        if (!card) {
          card = app.cardTemplate.cloneNode(true);
          card.classList.remove('cardTemplate');
          card.querySelector('.location').textContent = data.label;
          card.removeAttribute('hidden');
          app.container.appendChild(card);
          app.visibleCards[data.key] = card;
        }
    
        // Verifies the data provide is newer than what's already visible
        // on the card, if it's not bail, if it is, continue and update the
        // time saved in the card
        var cardLastUpdatedElem = card.querySelector('.card-last-updated');
        var cardLastUpdated = cardLastUpdatedElem.textContent;
        if (cardLastUpdated) {
          cardLastUpdated = new Date(cardLastUpdated);
          // Bail if the card has more recent data then the data
          if (dataLastUpdated.getTime() < cardLastUpdated.getTime()) {
            return;
          }
        }
        cardLastUpdatedElem.textContent = data.created;
    
        card.querySelector('.description').textContent = current.text;
        card.querySelector('.date').textContent = current.date;
        card.querySelector('.current .icon').classList.add(app.getIconClass(current.code));
        card.querySelector('.current .temperature .value').textContent =
          Math.round(current.temp);
        card.querySelector('.current .sunrise').textContent = sunrise;
        card.querySelector('.current .sunset').textContent = sunset;
        card.querySelector('.current .humidity').textContent =
          Math.round(humidity) + '%';
        card.querySelector('.current .wind .value').textContent =
          Math.round(wind.speed);
        card.querySelector('.current .wind .direction').textContent = wind.direction;
        var nextDays = card.querySelectorAll('.future .oneday');
        var today = new Date();
        today = today.getDay();
        for (var i = 0; i < 7; i++) {
          var nextDay = nextDays[i];
          var daily = data.channel.item.forecast[i];
          if (daily && nextDay) {
            nextDay.querySelector('.date').textContent =
              app.daysOfWeek[(i + today) % 7];
            nextDay.querySelector('.icon').classList.add(app.getIconClass(daily.code));
            nextDay.querySelector('.temp-high .value').textContent =
              Math.round(daily.high);
            nextDay.querySelector('.temp-low .value').textContent =
              Math.round(daily.low);
          }
        }
        if (app.isLoading) {
          app.spinner.setAttribute('hidden', true);
          app.container.removeAttribute('hidden');
          app.isLoading = false;
        }
      };
    
  4. Methods for dealing with model:

    Source code bagian ini adalah

      /*****************************************************************************
       *
       * Methods for dealing with the model
       *
       ****************************************************************************/
    
      /*
       * Gets a forecast for a specific city and updates the card with the data.
       * getForecast() first checks if the weather data is in the cache. If so,
       * then it gets that data and populates the card with the cached data.
       * Then, getForecast() goes to the network for fresh data. If the network
       * request goes through, then the card gets updated a second time with the
       * freshest data.
       */
      app.getForecast = function(key, label) {
        var statement = 'select * from weather.forecast where woeid=' + key;
        var url = 'https://query.yahooapis.com/v1/public/yql?format=json&q=' +
            statement;
        // TODO add cache logic here
        if ('caches' in window) {
          /*
           * Check if the service worker has already cached this city's weather
           * data. If the service worker has the data, then display the cached
           * data while the app fetches the latest data.
           */
          caches.match(url).then(function(response) {
            if (response) {
              response.json().then(function updateFromCache(json) {
                var results = json.query.results;
                results.key = key;
                results.label = label;
                results.created = json.query.created;
                app.updateForecastCard(results);
              });
            }
          });
        }
        // Fetch the latest data.
        var request = new XMLHttpRequest();
        request.onreadystatechange = function() {
          if (request.readyState === XMLHttpRequest.DONE) {
            if (request.status === 200) {
              var response = JSON.parse(request.response);
              var results = response.query.results;
              results.key = key;
              results.label = label;
              results.created = response.query.created;
              app.updateForecastCard(results);
            }
          } else {
            // Return the initial weather forecast since no data is available.
            app.updateForecastCard(initialWeatherForecast);
          }
        };
        request.open('GET', url);
        request.send();
      };
    
      // Iterate all of the cards and attempt to get the latest forecast data
      app.updateForecasts = function() {
        var keys = Object.keys(app.visibleCards);
        keys.forEach(function(key) {
          app.getForecast(key);
        });
      };
    
      // TODO add saveSelectedCities function here
      // Save list of cities to localStorage.
      app.saveSelectedCities = function() {
        var selectedCities = JSON.stringify(app.selectedCities);
        localStorage.selectedCities = selectedCities;
      };
    
      app.getIconClass = function(weatherCode) {
        // Weather codes: https://developer.yahoo.com/weather/documentation.html#codes
        weatherCode = parseInt(weatherCode);
        switch (weatherCode) {
          case 25: // cold
          case 32: // sunny
          case 33: // fair (night)
          case 34: // fair (day)
          case 36: // hot
          case 3200: // not available
            return 'clear-day';
          case 0: // tornado
          case 1: // tropical storm
          case 2: // hurricane
          case 6: // mixed rain and sleet
          case 8: // freezing drizzle
          case 9: // drizzle
          case 10: // freezing rain
          case 11: // showers
          case 12: // showers
          case 17: // hail
          case 35: // mixed rain and hail
          case 40: // scattered showers
            return 'rain';
          case 3: // severe thunderstorms
          case 4: // thunderstorms
          case 37: // isolated thunderstorms
          case 38: // scattered thunderstorms
          case 39: // scattered thunderstorms (not a typo)
          case 45: // thundershowers
          case 47: // isolated thundershowers
            return 'thunderstorms';
          case 5: // mixed rain and snow
          case 7: // mixed snow and sleet
          case 13: // snow flurries
          case 14: // light snow showers
          case 16: // snow
          case 18: // sleet
          case 41: // heavy snow
          case 42: // scattered snow showers
          case 43: // heavy snow
          case 46: // snow showers
            return 'snow';
          case 15: // blowing snow
          case 19: // dust
          case 20: // foggy
          case 21: // haze
          case 22: // smoky
            return 'fog';
          case 24: // windy
          case 23: // blustery
            return 'windy';
          case 26: // cloudy
          case 27: // mostly cloudy (night)
          case 28: // mostly cloudy (day)
          case 31: // clear (night)
            return 'cloudy';
          case 29: // partly cloudy (night)
          case 30: // partly cloudy (day)
          case 44: // partly cloudy
            return 'partly-cloudy-day';
        }
      };
    
      /*
       * Fake weather data that is presented when the user first uses the app,
       * or when the user has not saved any cities. See startup code for more
       * discussion.
       */
      var initialWeatherForecast = {
        key: '2459115',
        label: 'New York, NY',
        created: '2016-07-22T01:00:00Z',
        channel: {
          astronomy: {
            sunrise: "5:43 am",
            sunset: "8:21 pm"
          },
          item: {
            condition: {
              text: "Windy",
              date: "Thu, 21 Jul 2016 09:00 PM EDT",
              temp: 56,
              code: 24
            },
            forecast: [
              {code: 44, high: 86, low: 70},
              {code: 44, high: 94, low: 73},
              {code: 4, high: 95, low: 78},
              {code: 24, high: 75, low: 89},
              {code: 24, high: 89, low: 77},
              {code: 44, high: 92, low: 79},
              {code: 44, high: 89, low: 77}
            ]
          },
          atmosphere: {
            humidity: 56
          },
          wind: {
            speed: 25,
            direction: 195
          }
        }
      };
      // TODO uncomment line below to test app with fake data
      // app.updateForecastCard(initialWeatherForecast);
    

    Pada pemrograman tersebut pertama-tama akan mengambil cache, kemudian dilanjutkan dengan mencoba mengambil data terakhir melalui jaringan.

  5. Code required to start up

    Source code lengkapnya adalah:

      /************************************************************************
       *
       * Code required to start the app
       *
       * NOTE: To simplify this codelab, we've used localStorage.
       *   localStorage is a synchronous API and has serious performance
       *   implications. It should not be used in production applications!
       *   Instead, check out IDB (https://www.npmjs.com/package/idb) or
       *   SimpleDB (https://gist.github.com/inexorabletash/c8069c042b734519680c)
       ************************************************************************/
    
      // TODO add startup code here
      app.selectedCities = localStorage.selectedCities;
      if (app.selectedCities) {
        app.selectedCities = JSON.parse(app.selectedCities);
        app.selectedCities.forEach(function(city) {
          app.getForecast(city.key, city.label);
        });
      } else {
        /* The user is using the app for the first time, or the user has not
         * saved any cities, so show the user some fake data. A real app in this
         * scenario could guess the user's location via IP lookup and then inject
         * that data into the page.
         */
        app.updateForecastCard(initialWeatherForecast);
        app.selectedCities = [
          {key: initialWeatherForecast.key, label: initialWeatherForecast.label}
        ];
        app.saveSelectedCities();
      }
    
      // TODO add service worker code here
      if ('serviceWorker' in navigator) {
        navigator.serviceWorker
                 .register('./service-worker.js')
                 .then(function() { console.log('Service Worker Registered'); });
      }
    })();
    

Informasi lebih lanjut silahkan mengunjungi
1. https://developers.google.com/web/fundamentals/codelabs/your-first-pwapp/
2. https://developers.google.com/web/progressive-web-apps/ .
3. Progressive Web Apps Training di https://developers.google.com/web/ilt/pwa/ .
4. IIFE (Immediately Invoked Function Expression) pada JavaScript https://developer.mozilla.org/en-US/docs/Glossary/IIFE .
5. Use Strict pada JavaScript https://www.w3schools.com/js/js_strict.asp .
6. JavaScript Versions https://www.w3schools.com/js/js_versions.asp .

Kunjungi www.proweb.co.id untuk menambah wawasan anda.

Implementasi Javascript pada PWA