Membuat Single Page Aplikasi Tanpa Framework – Ide di balik aplikasi satu halaman (SPA) adalah untuk menciptakan pengalaman browsing yang lancar, seperti yang ditemukan di aplikasi desktop asli. Semua kode yang diperlukan untuk halaman ini dimuat hanya sekali, dan isinya diubah secara dinamis melalui JavaScript. Jika semuanya sudah benar, jangan pernah memuat ulang halaman kecuali pengguna menyegarkan halaman secara manual.

Membuat Single Page Aplikasi Tanpa Framework
Source : appkey.id

Ada banyak kerangka kerja untuk aplikasi satu halaman. Pertama, Backbone, lalu Angular, dan sekarang React. Dibutuhkan banyak pekerjaan untuk terus belajar dan mempelajari kembali hal-hal (belum lagi harus mendukung kode lama Anda yang ditulis dalam kerangka kerja yang sudah lama terlupakan). Dalam beberapa kasus, seperti ketika ide aplikasi Anda tidak terlalu rumit, sebenarnya tidak sulit untuk membuat aplikasi satu halaman tanpa menggunakan framework eksternal apa pun. Ini adalah cara untuk melakukannya.

Kami tidak akan menggunakan kerangka kerja, tetapi akan menggunakan dua perpustakaan-jQuery untuk manipulasi DOM dan penanganan acara, dan Handlebars untuk templat. Jika Anda ingin menguranginya seminimal mungkin, Anda dapat dengan mudah mengabaikannya, tetapi kami akan menggunakannya untuk meningkatkan produktivitas. Setelah melupakan kerangka kerja klien yang modis saat ini, mereka akan berada di sini untuk waktu yang lama.

Aplikasi yang akan kita buat akan memperoleh data produk dari file JSON dan menampilkan datanya dengan merender grid produk menggunakan Handlebars. Setelah pemuatan awal, aplikasi kita akan tetap berada di URL yang sama dan memantau perubahan pada bagian hash melalui event hashchange. Untuk menelusuri aplikasi, kita hanya perlu mengubah nilai hash. Ini memiliki keunggulan lain, yaitu history browser akan dapat bekerja tanpa usaha ekstra dari kita.

Mempersiapkan

Source : tutorialzine

Seperti yang Anda lihat, tidak banyak konten di folder proyek kami. javascriptoo memiliki pengaturan aplikasi web reguler-file HTML, JavaScript dan CSS, serta products.json (berisi data tentang produk di toko kami) dan folder yang berisi gambar produk.

Products JSON

File .json digunakan untuk menyimpan data tentang setiap produk SPA. File ini dapat dengan mudah diganti dengan skrip sisi server untuk mendapatkan data dari database yang sebenarnya.

Baca Juga : Membuat Sistem Pendaftaran Dengan PHP Dan Mysql

products.json

[
  {
    "id": 1,
    "name": "Sony Xperia Z3",
    "price": 899,
    "specs": {
      "manufacturer": "Sony",
      "storage": 16,
      "os": "Android",
      "camera": 15
    },
    "description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam tristique ipsum in efficitur pharetra. Maecenas luctus ante in neque maximus, sed viverra sem posuere. Vestibulum lectus nisi, laoreet vel suscipit nec, feugiat at odio. Etiam eget tellus arcu.",
    "rating": 4,
    "image": {
      "small": "/images/sony-xperia-z3.jpg",
      "large": "/images/sony-xperia-z3-large.jpg"
    }
  },
  {
    "id": 2,
    "name": "Iphone 6",
    "price": 899,
    "specs": {
      "manufacturer": "Apple",
      "storage": 16,
      "os": "iOS",
      "camera": 8
    },
    "description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam tristique ipsum in efficitur pharetra. Maecenas luctus ante in neque maximus, sed viverra sem posuere. Vestibulum lectus nisi, laoreet vel suscipit nec, feugiat at odio. Etiam eget tellus arcu.",
    "rating": 4,
    "image": {
      "small": "/images/iphone6.jpg",
      "large": "/images/iphone6-large.jpg"
    }
  }
]

HTML

Di file html kami, kami memiliki beberapa div yang berbagi “halaman” kelas yang sama. Ini adalah halaman berbeda (atau status di SPA) yang dapat ditampilkan aplikasi kita. Namun, saat halaman dimuat, semua ini disembunyikan melalui CSS, dan JavaScript diperlukan untuk menampilkannya. Idenya adalah bahwa hanya satu halaman yang dapat dilihat dalam satu waktu, dan skrip kami adalah skrip yang menentukan halaman mana.

index.html

<div class="main-content">

    <div class="all-products page">

        <h3>Our products</h3>

        <div class="filters">
            <form>
                Checkboxes here
            </form>
        </div>

    <ul class="products-list">
      <script id="products-template" type="x-handlebars-template">​
        {{#each this}}
          <li data-index="{{id}}">
            <a href="#" class="product-photo"><img src="{{image.small}}" height="130" alt="{{name}}"/></a>
            <h2><a href="#"> {{name}} </a></h2>
            <ul class="product-description">
              <li><span>Manufacturer: </span>{{specs.manufacturer}}</li>
              <li><span>Storage: </span>{{specs.storage}} GB</li>
              <li><span>OS: </span>{{specs.os}}</li>
              <li><span>Camera: </span>{{specs.camera}} Mpx</li>
            </ul>
            <button>Buy Now!</button>
            <p class="product-price">{{price}}$</p>
            <div class="highlight"></div>
          </li>
        {{/each}}
      </script></ul>

    </div>

    <div class="single-product page">

        <div class="overlay"></div>

        <div class="preview-large">
            <h3>Single product view</h3>
            <img src=""/>
            <p></p>

            <span class="close">&times;</span>
        </div>

    </div>

    <div class="error page">
        <h3>Sorry, something went wrong :(</h3>
    </div>

</div>

Kami memiliki tiga halaman: semua produk (daftar produk), produk tunggal (halaman produk tunggal) dan kesalahan.

Semua halaman produk menyertakan judul, formulir dengan kotak centang untuk pemfilteran, dan tagdengan kategori “daftar-produk”. Daftar dibuat oleh stang menggunakan data yang disimpan di products.json, membuat untuk setiap entri di json. Hasilnya adalah sebagai berikut:

Source : tutorialzine

Produk tunggal hanya digunakan untuk menampilkan informasi tentang satu produk. Itu kosong dan tersembunyi saat halaman dimuat. Setelah mencapai alamat hash yang sesuai, alamat hash akan diisi dan ditampilkan dengan data produk.

Baca Juga : 20 PHP Libraries Terbaik Di Awal 2021

Halaman kesalahan hanya berisi pesan kesalahan untuk memberi tahu Anda ketika Anda tiba di alamat yang salah.

Kode JavaScript

Pertama, mari kita lihat dulu fungsi dan perannya.

script.js

$(function () {

    checkboxes.click(function () {
        // The checkboxes in our app serve the purpose of filters.
        // Here on every click we add or remove filtering criteria from a filters object.

        // Then we call this function which writes the filtering criteria in the url hash.
        createQueryHash(filters);
    });

    $.getJSON( "products.json", function( data ) {
        // Get data about our products from products.json.

        // Call a function that will turn that data into HTML.
        generateAllProductsHTML(data);

        // Manually trigger a hashchange to start the app.
        $(window).trigger('hashchange');
    });

    $(window).on('hashchange', function(){
        // On every hash change the render function is called with the new hash.
        // This is how the navigation of our app happens.
        render(decodeURI(window.location.hash));
    });

    function render(url) {
        // This function decides what type of page to show 
        // depending on the current url hash value.
    }

    function generateAllProductsHTML(data){
        // Uses Handlebars to create a list of products using the provided data.
        // This function is called only once on page load.
    }

    function renderProductsPage(data){
        // Hides and shows products in the All Products Page depending on the data it recieves.
    }

    function renderSingleProductPage(index, data){
        // Shows the Single Product Page with appropriate data.
    }

    function renderFilterResults(filters, products){
        // Crates an object with filtered products and passes it to renderProductsPage.
        renderProductsPage(results);
    }

    function renderErrorPage(){
        // Shows the error page.
    }

    function createQueryHash(filters){
        // Get the filters object, turn it into a string and write it into the hash.
    }

});

Ingat, konsep SPA adalah tidak mengambil beban apa pun saat aplikasi sedang berjalan. Inilah sebabnya mengapa setelah pemuatan halaman awal, kami ingin tetap berada di halaman yang sama, dan server telah mengambil semua konten yang kami butuhkan di halaman itu.

Baca Juga : Belajar JavaScript Async/Await Dalam 10 Menit

Namun, kami tetap ingin dapat memasukkan lokasi tertentu ke dalam aplikasi, misalnya menyalin URL dan mengirimkannya ke teman. Jika kami tidak pernah mengubah alamat aplikasi, mereka hanya akan mendapatkan aplikasi seperti sebelumnya, bukan konten yang ingin Anda bagikan dengan mereka. Untuk mengatasi masalah ini, kami menulis informasi tentang status lamaran di URL sebagai #hash. Hash tidak menyebabkan halaman dimuat ulang dan mudah diakses dan dimanipulasi.

Di setiap perubahan hash, kami menyebutnya:

function render(url) {

        // Get the keyword from the url.
        var temp = url.split('/')[0];

        // Hide whatever page is currently shown.
        $('.main-content .page').removeClass('visible');

        var map = {

            // The Homepage.
            '': function() {

                // Clear the filters object, uncheck all checkboxes, show all the products
                filters = {};
                checkboxes.prop('checked',false);

                renderProductsPage(products);
            },

            // Single Products page.
            '#product': function() {

                // Get the index of which product we want to show and call the appropriate function.
                var index = url.split('#product/')[1].trim();

                renderSingleProductPage(index, products);
            },

            // Page with filtered products
            '#filter': function() {

                // Grab the string after the '#filter/' keyword. Call the filtering function.
                url = url.split('#filter/')[1].trim();

                // Try and parse the filters object from the query string.
                try {
                    filters = JSON.parse(url);
                }
                // If it isn't a valid json, go back to homepage ( the rest of the code won't be executed ).
                catch(err) {
                    window.location.hash = '#';
                }

                renderFilterResults(filters, products);
            }

        };

        // Execute the needed function depending on the url keyword (stored in temp).
        if(map[temp]){
            map[temp]();
        }
        // If the keyword isn't listed in the above - render the error page.
        else {
            renderErrorPage();
        }

    }

Fungsi ini mempertimbangkan string awal dari hash, menentukan halaman yang akan ditampilkan dan memanggil fungsi yang sesuai.

Misalnya, jika hashnya adalah ‘# filter / {“storage”: [“16”], “camera”: [“5”]}’, kata kodenya adalah ‘# filter’. Sekarang, fungsi rendering tahu bahwa kita ingin melihat halaman yang berisi daftar produk yang difilter dan akan menavigasi ke halaman itu. Sisa hash akan mengurai menjadi objek dan menampilkan halaman dengan produk yang difilter, sehingga mengubah status aplikasi.

Ini hanya dipanggil sekali saat startup dan mengubah JSON menjadi konten HTML5 yang sebenarnya melalui pegangan.

Baca Juga : Cara Membuat Kode Aplikasi Menggunakan Template

function generateAllProductsHTML(data){

    var list = $('.all-products .products-list');

    var theTemplateScript = $("#products-template").html();
    //Compile the template​
    var theTemplate = Handlebars.compile (theTemplateScript);
    list.append (theTemplate(data));

    // Each products has a data-index attribute.
    // On click change the url hash to open up a preview for this product only.
    // Remember: every hashchange triggers the render function.
    list.find('li').on('click', function (e) {
      e.preventDefault();

      var productIndex = $(this).data('index');

      window.location.hash = 'product/' + productIndex;
    })
  }

Fungsi ini menerima objek yang hanya berisi produk yang ingin kita tampilkan dan tampilkan.

function renderProductsPage(data){

    var page = $('.all-products'),
      allProducts = $('.all-products .products-list > li');

    // Hide all the products in the products list.
    allProducts.addClass('hidden');

    // Iterate over all of the products.
    // If their ID is somewhere in the data object remove the hidden class to reveal them.
    allProducts.each(function () {

      var that = $(this);

      data.forEach(function (item) {
        if(that.data('index') == item.id){
          that.removeClass('hidden');
        }
      });
    });

    // Show the page itself.
    // (the render function hides all pages so we need to show the one we want).
    page.addClass('visible');

  }

Tampilkan satu halaman pratinjau produk:

function renderSingleProductPage(index, data){

    var page = $('.single-product'),
      container = $('.preview-large');

    // Find the wanted product by iterating the data object and searching for the chosen index.
    if(data.length){
      data.forEach(function (item) {
        if(item.id == index){
          // Populate '.preview-large' with the chosen product's data.
          container.find('h3').text(item.name);
          container.find('img').attr('src', item.image.large);
          container.find('p').text(item.description);
        }
      });
    }

    // Show the page.
    page.addClass('visible');

  }

Dapatkan semua produk, filter sesuai dengan kueri kami, dan kembalikan objek dengan hasil.

function renderFilterResults(filters, products){

      // This array contains all the possible filter criteria.
    var criteria = ['manufacturer','storage','os','camera'],
      results = [],
      isFiltered = false;

    // Uncheck all the checkboxes.
    // We will be checking them again one by one.
    checkboxes.prop('checked', false);

    criteria.forEach(function (c) {

      // Check if each of the possible filter criteria is actually in the filters object.
      if(filters[c] && filters[c].length){

        // After we've filtered the products once, we want to keep filtering them.
        // That's why we make the object we search in (products) to equal the one with the results.
        // Then the results array is cleared, so it can be filled with the newly filtered data.
        if(isFiltered){
          products = results;
          results = [];
        }

        // In these nested 'for loops' we will iterate over the filters and the products
        // and check if they contain the same values (the ones we are filtering by).

        // Iterate over the entries inside filters.criteria (remember each criteria contains an array).
        filters[c].forEach(function (filter) {

          // Iterate over the products.
          products.forEach(function (item){

            // If the product has the same specification value as the one in the filter
            // push it inside the results array and mark the isFiltered flag true.

            if(typeof item.specs[c] == 'number'){
              if(item.specs[c] == filter){
                results.push(item);
                isFiltered = true;
              }
            }

            if(typeof item.specs[c] == 'string'){
              if(item.specs[c].toLowerCase().indexOf(filter) != -1){
                results.push(item);
                isFiltered = true;
              }
            }

          });

          // Here we can make the checkboxes representing the filters true,
          // keeping the app up to date.
          if(c && filter){
            $('input[name='+c+'][value='+filter+']').prop('checked',true);
          }
        });
      }

    });

    // Call the renderProductsPage.
    // As it's argument give the object with filtered products.
    renderProductsPage(results);
  }

Menampilkan status kesalahan:

function renderErrorPage(){
    var page = $('.error');
    page.addClass('visible');
  }

Objek filter string dan tuliskan ke hash.

function createQueryHash(filters){

    // Here we check if filters isn't empty.
    if(!$.isEmptyObject(filters)){
      // Stringify the object via JSON.stringify and write it after the '#filter' keyword.
      window.location.hash = '#filter/' + JSON.stringify(filters);
    }
    else{
      // If it's empty change the hash to '#' (the homepage).
      window.location.hash = '#';
    }

  }

Kesimpulannya

Aplikasi satu halaman sangat cocok untuk aplikasi yang ingin menghadirkan lebih dinamis dan kelancaran proyek, dan dengan beberapa pilihan desain yang bijaksana, Anda dapat memberikan pengalaman yang indah dan menyenangkan kepada pengunjung.