Презентация

Creating Single-Page App with jQuery, AJAX and Kinvey (without framework) - JS Applications - ноември 2016

 

Материали от темата

Съдържание на документа

SPA with AJAX and Kinvey

Creating Single Page Apps (SPA) with jQuery AJAX, REST and Kinvey

SoftUni Team

Technical Trainers

Software University

http://softuni.bg

SPA with AJAX
and Kinvey

AJAX

REST

Kinvey


Table of Contents

The Book Library App

Kinvey Back-End

App Skeleton: HTML + CSS

Session / Local Storage

App Code Structure

Login / Register / Logout

CRUD Operations

2


3



sli.do
#spa

Have a Question?


The Book Library Project

Book Library: CRUD + Login / Logout


5

Design and implement a “Book Library” front-end single-page application (SPA) in HTML5 with REST back-end in Kinvey

Books have title, author and description

Implement the following functionality:

Login, register, logout, list all books, create a new book, edit existing book, delete existing book

Books have ownership:

Anyone can view all the books

Only the book creator can edit / delete his own books

The "Book Library" App


6

Home Screen






7

Login Screen






8

Login Screen: Invalid Login




9

Register Screen






10

List Books Screen




11

Create Book Screen






12

Edit Book Screen






13

Delete Book Screen






14

Logout Screen


The Kinvey-Based Back-End

Users and Books Collections


16

Create Kinvey App


17

Create a collection "books"











Add a few books

Columns: title + author + description

Create Books Collection


18

Test the Kinvey Back-End: Register User

POST

https://baas.kinvey.com/user/{app_id}/

Authorization: Basic base64(app_id:app_secret)

{"username":"todor", "password":"pass123"}


19

Test the Kinvey Back-End: Login User

POST

https://baas.kinvey.com/user/{app_id}/login

Authorization: Basic base64(app_id:app_secret)

{"username":"todor", "password":"pass123"}


20

Test the Kinvey Back-End: List All Books

GET

https://baas.kinvey.com/appdata/{app_id}/books

Authorization: Kinvey authtoken


21

Test the Kinvey Back-End: Create New Book

POST

https://baas.kinvey.com/appdata/{app_id}/books 

Authorization: Kinvey authtoken

{ "title":"ttt", "author":"aaa", "description":"ddd" }


22

Test the Kinvey Back-End: Edit Book

PUT

https://baas.kinvey.com/appdata/{app_id}/books/{id}

Authorization: Kinvey authtoken

{ "title":"t2", "author":"a2", "description":"d2" }


23

Test the Kinvey Back-End: Delete Book

DELETE

https://baas.kinvey.com/appdata/{app_id}/books/{id}

Authorization: Kinvey authtoken


Create the Application Skeleton

HTML, CSS, Views, Forms, Info Boxes


25

Create the Project Structure


26

Start with the HTML Page: books.html

<!DOCTYPE html>

<html>

  <head>

    <title>Book Library</title>

    <script src="scripts/jquery-3.1.1.min.js"></script>

    <script src="scripts/book-library.js"></script>

    <link rel="stylesheet" type="text/css"

      href="styles/book-library.css" />

  </head>

  <body>…</body>

</html>


27

HTML Body Structure

<body onload="startApp()">

  <header id="menu">

    <a href="#" id="link1">Link1</a>  

    <a href="#" id="link2">Link2</a>  

    …

  </header>

  <main>

    <section id="view1">Section #1</section>

    <section id="view2">Section #2</section>

    …

  </main>

  <footer>Book Library - Simple SPA Application</footer>

</body>


28

Main Navigation (Menu)

<header id="menu">

  <a href="#" id="linkHome">Home</a>

  <a href="#" id="linkLogin">Login</a>

  <a href="#" id="linkRegister">Register</a>

  <a href="#" id="linkListBooks">List Books</a>

  <a href="#" id="linkCreateBook">Create Book</a>

  <a href="#" id="linkLogout">Logout</a>

  <span id="loggedInUser"></span>

</header>


29

App Sections

<section id="loadingBox">Loading ...</section>

<section id="infoBox">Info</section>

<section id="errorBox">Error</section>

<section id="viewHome">

  <h1>Welcome</h1>

  Welcome to our book library.

</section>


30

Login View

<section id="viewLogin">

  <h1>Please login</h1>

  <form id="formLogin">

    <div>Username:</div>

    <div><input type="text" name=

      "username" required /></div>

    <div>Password:</div>

    <div><input type="password"

      name="passwd" required /></div>

    <div><input type="submit" value="Login" /></div>

  </form>

</section>


31

Register View

<section id="viewRegister">

  <h1>Please register here</h1>

  <form id="formRegister">

    <div>Username:</div>

    <div><input type="text" name=

      "username" required /></div>

    <div>Password:</div>

    <div><input type="password"

      name="passwd" required /></div>

    <div><input type="submit" value="Register" /></div>

  </form>

</section>


32

Books View

<section id="viewBooks">

  <h1>Books</h1>

  <div id="books">

    <table>

      <tr>

        <th>Title</th>

        <th>Author</th>

        <th>Description</th>

        <th>Actions</th>

      </tr>

      <tr>

        <td>Book title</td>

        <td>Book author</td>

        <td>Book description</td>

        <td>

          <a href="#">[Delete]</a>

          <a href="#">[Edit]</a>

        </td>

      </tr>

      …

    </table>

  </div>

</section>


33

Create Book View

<section id="viewCreateBook">

  <h1>Create new book</h1>

  <form id="formCreateBook">

    <div>Title:</div>

    <div><input type="text" name="title" required /></div>

    <div>Author:</div>

    <div><input type="text" name="author" required /></div>

    <div>Description:</div>

    <div><textarea name="descr"

      rows="10" required></textarea></div>

    <div><input type="submit" value="Create" /></div>

  </form>

</section>


34

Edit Book View

<section id="viewEditBook">

  <h1>Edit existing book</h1>

  <form id="formEditBook">

    <div><input type="hidden" name="id" required /></div>

    <div>Title:</div>

    <div><input type="text" name="title" required /></div>

    <div>Author:</div>

    <div><input type="text" name="author" required /></div>

    <div>Description:</div>

    <div><textarea name="descr" rows="10"

      required></textarea></div>

    <div><input type="submit value="Edit" /></div>

  </form>

</section>


35

CSS: Style the Navigation Bar (Menus)

#menu {

  background: #DDD;

  text-align: center;

  padding: 5px;

  line-height: 1.5;

  border-radius: 3px;

  overflow: auto;

}

#menu>#loggedInUser {

  float: right;

  margin-right: 10px;

}

#menu a {

  text-decoration: none;

  padding: 5px 10px;

  border-radius: 5px;

}



#menu a:hover {

  background: #BBB;

}


36

Style the Sections and Tables

table th {

  background: #DDD;

  padding: 10px;

}



table td {

  padding: 5px 10px;

  background: #EEE;

}

main > section {

  display: none;

  padding: 20px 5px;

}



section h1 {

  margin: 10px 0px;

  font-size: 1.2em;

}


37

Style the Loading / Info / Error Boxes

#loadingBox {

  background: #7CB3E9;

}

#infoBox {

  background: #393;

}

#errorBox {

  background: #F50;

}

#infoBox, #errorBox, #loadingBox {

  width: 80%;

  margin: 10px auto;

  color: white;

  text-align: center;

  padding: 5px;

  border-radius: 3px;

}


38

Style the App Footer

footer {

  background: #DDD;

  padding: 5px 10px;

  font-size: 0.8em;

  text-align: center;

  border-radius: 3px;

}


39

Test the App Skeleton


Session / Local Storage


41

Session storage holds key / value pairs in the browser session

All data is lost when the browser is closed, survives page reloads

Session / Local Storage – Overview

// Save data to sessionStorage

sessionStorage.setItem('username', 'maria');

// Get saved data from sessionStorage

let currentUser = sessionStorage.getItem('username');

// Remove all saved data from sessionStorage

sessionStorage.clear();


42

Local storage holds key / value pairs in the browser

Data survives for long time, until manually deleted









Each origin (site location) has its own storage

https://softuni.bg holds different data than https://google.com

Local Storage

// Save data to localStorage

localStorage.setItem('language', 'en');

// Get saved data from localStorage

let lang = localStorage.getItem('languuage');


43

Play with the Session Storage


App Code Structure


45

App Structure

book-library.js

function startApp() {

  sessionStorage.clear(); // Clear user auth data

  showHideMenuLinks();

  showView('viewHome');

  // Bind the navigation menu links

  $("#linkHome").click(showHomeView);

  …

  // Bind the form submit actions

  $("#formLogin").submit(loginUser);

  …

  $("form").submit(function(e) { e.preventDefault() });

}

Handle form.submit() not button.click(). Otherwise validation fill be bypassed.

Disable default submit for all forms


46

Bind the Navigation Links

// Bind the navigation menu links

$("#linkHome").click(showHomeView);

$("#linkLogin").click(showLoginView);

$("#linkRegister").click(showRegisterView);

$("#linkListBooks").click(listBooks);

$("#linkCreateBook").click(showCreateBookView);

$("#linkLogout").click(logoutUser);


47

Bind the Form Submit Actions

// Bind the form submit buttons

$("#buttonLoginUser").click(loginUser);

$("#buttonRegisterUser").click(registerUser);

$("#buttonCreateBook").click(createBook);

$("#buttonEditBook").click(editBook);


48

Bind Info Boxes

// Bind the info / error boxes: hide on click

$("#infoBox, #errorBox").click(function() {

  $(this).fadeOut();

});

// Attach AJAX "loading" event listener

$(document).on({

  ajaxStart: function() { $("#loadingBox").show() },

  ajaxStop: function() { $("#loadingBox").hide() }

});


49

Implement a Simple Navigation System

function showHideMenuLinks() {

  $("#linkHome").show();

  if (sessionStorage.getItem('authToken')) {

    // We have logged in user

    $("#linkLogin").hide();

    $("#linkRegister").hide();

    $("#linkListBooks").show();

    $("#linkCreateBook").show();

    $("#linkLogout").show();


50

Implement a Simple Navigation System (2)

  } else {

    // No logged in user

    $("#linkLogin").show();

    $("#linkRegister").show();

    $("#linkListBooks").hide();

    $("#linkCreateBook").hide();

    $("#linkLogout").hide();

  }

}


51

Implement a Simple Navigation System (3)

function showView(viewName) {

  // Hide all views and show the selected view only

  $('main > section').hide();

  $('#' + viewName).show();

}

function showHomeView() {

  showView('viewHome');

}


52

Implement a Simple Navigation System (4)

function showLoginView() {

  showView('viewLogin');

  $('#formLogin').trigger('reset');

}

function showRegisterView() {

  $('#formRegister').trigger('reset');

  showView('viewRegister');

}


53

Implement a Simple Navigation System (5)

function showCreateBookView() {

  $('#formCreateBook').trigger('reset');

  showView('viewCreateBook');

}

function loginUser() { // TODO }

function registerUser() { // TODO }

function logoutUser() { // TODO }


54

Implement a Simple Navigation System (6)

function createBook() { // TODO }

function editBook() { // TODO }

function listBooks() {

  // TODO: to be implemented later

}

function deleteBook() { // TODO }


55

Test the App Navigation


Login / Register / Logout

User Management with Kinvey


57

App Constants

const kinveyBaseUrl = "https://baas.kinvey.com/";

const kinveyAppKey = "kid_rkcLxcUr";

const kinveyAppSecret = 

  "e234a245b3864b2eb7ee41e19b8ca4e5";

const kinveyAppAuthHeaders = {

  'Authorization': "Basic " + 

      btoa(kinveyAppKey + ":" + kinveyAppSecret),

};


58

User Registration: AJAX Request

function registerUser() {

  let userData = {

    username: $('#formRegister input[name=username]').val(),

    password: $('#formRegister input[name=passwd]').val()

  };

  $.ajax({

    method: "POST",

    url: kinveyBaseUrl + "user/" + kinveyAppKey + "/",

    headers: kinveyAppAuthHeaders,

    data: userData,

    success: registerSuccess,

    error: handleAjaxError

  });


59

User Registration: After AJAX Request

function registerUser() {

  …



  function registerSuccess(userInfo) {

    saveAuthInSession(userInfo);

    showHideMenuLinks();

    listBooks();

    showInfo('User registration successful.');

  }

}


60

Remember User Authentication Data

function saveAuthInSession(userInfo) {

  let userAuth = userInfo._kmd.authtoken;

  sessionStorage.setItem('authToken', userAuth);

  let userId = userInfo._id;

  sessionStorage.setItem('userId', userId);

  let username = userInfo.username;

  $('#loggedInUser').text(

    "Welcome, " + username + "!");

}


61

Handle AJAX Errors: Show the Error Box

function handleAjaxError(response) {

  let errorMsg = JSON.stringify(response);

  if (response.readyState === 0)

    errorMsg = "Cannot connect due to network error.";

  if (response.responseJSON &&

      response.responseJSON.description)

    errorMsg = response.responseJSON.description;

  showError(errorMsg);

}


62

Show Info / Error Message

function showInfo(message) {

  $('#infoBox').text(message);

  $('#infoBox').show();

  setTimeout(function() {

    $('#infoBox').fadeOut();

  }, 3000);

}

function showError(errorMsg) {

  $('#errorBox').text("Error: " + errorMsg);

  $('#errorBox').show();

}


63

Test the User Registration: Success


64

Test the User Registration: Error


65

User Login: AJAX Request

function loginUser() {

  let userData = {

    username: $('#formLogin input[name=username]').val(),

    password: $('#formLogin input[name=passwd]').val()

  };

  $.ajax({

    method: "POST",

    url: kinveyBaseUrl + "user/" + kinveyAppKey + "/login",

    headers: kinveyAppAuthHeaders,

    data: userData,

    success: loginSuccess,

    error: handleAjaxError

  });


66

User Login: After AJAX Request

function loginUser() {

  …



  function loginSuccess(userInfo) {

    saveAuthInSession(userInfo);

    showHideMenuLinks();

    listBooks();

    showInfo('Login successful.');

  }

}


67

User Logout

function logoutUser() {

  sessionStorage.clear();

  $('#loggedInUser').text("");

  showHideMenuLinks();

  showView('viewHome');

  showInfo('Logout successful.');

}


Implementing CRUD Operations

List / Create / Delete / Edit


69

List Books: AJAX Request

function listBooks() {

  $('#books').empty();

  showView('viewBooks');

  $.ajax({

    method: "GET",

    url: kinveyBaseUrl + "appdata/" + kinveyAppKey + "/books",

    headers: getKinveyUserAuthHeaders(),

    success: loadBooksSuccess,

    error: handleAjaxError

  });

  function loadBooksSuccess(books) { … }

}


70

Kinvey Authorization Headers

function getKinveyUserAuthHeaders() {

  return {

    'Authorization': "Kinvey " +

      sessionStorage.getItem('authToken'),

  };

}


71

List Books: After AJAX Request

function loadBooksSuccess(books) {

  showInfo('Books loaded.');

  if (books.length == 0) {

    $('#books').text('No books in the library.');

  } else {

    let booksTable = $('<table>')

      .append($('<tr>').append(

        '<th>Title</th><th>Author</th>',

        '<th>Description</th><th>Actions</th>'));

    for (let book of books)

      appendBookRow(book, booksTable);

    $('#books').append(booksTable);

  }

}


72

Display Single Book Line

function appendBookRow(book, booksTable) {

  let links = [];

  // TODO: action links will come later

  booksTable.append($('<tr>').append(

    $('<td>').text(book.title),

    $('<td>').text(book.author),

    $('<td>').text(book.description),

    $('<td>').append(links)

  ));

}


73

Test: List Books


74

Create New Book: AJAX Request

function createBook() {

  let bookData = {

    title: $('#formCreateBook input[name=title]').val(),

    author: $('#formCreateBook input[name=author]').val(),

    description: $('#formCreateBook textarea[name=descr]').val()

  };

  $.ajax({

    method: "POST",

    url: kinveyBaseUrl + "appdata/" + kinveyAppKey + "/books",

    headers: getKinveyUserAuthHeaders(),

    data: bookData,

    success: createBookSuccess,

    error: handleAjaxError

  });


75

Create New Book: After AJAX Request

  function createBookSuccess(response) {

    listBooks();

    showInfo('Book created.');

  }

}


76

Test: Create New Book


77

Display Edit / Delete Links

function appendBookRow(book, booksTable) {

  let links = [];

  if (book._acl.creator == sessionStorage['userId']) {

    let deleteLink = $('<a href="#">[Delete]</a>')

      .click(deleteBook.bind(this, book));

    let editLink = $('<a href="#">[Edit]</a>')

      .click(loadBookForEdit.bind(this, book));

    links = [deleteLink, ' ', editLink];

  }

  booksTable.append($('<tr>')

    .append( … cells & links ));

}

Bind the event handler with the current book


78

Test: Display Edit / Delete Links


79

Delete Book: AJAX Request

function deleteBook(book) {

  $.ajax({

    method: "DELETE",

    url: kinveyBookUrl = kinveyBaseUrl + "appdata/" +

      kinveyAppKey + "/books/" + book._id,

    headers: getKinveyUserAuthHeaders(),

    success: deleteBookSuccess,

    error: handleAjaxError

  });

  function deleteBookSuccess(response) {

    listBooks();

    showInfo('Book deleted.');

  }

}


80

Test: Delete Book


81

Load Book for Edit: AJAX Request

function loadBookForEdit(book) {

  $.ajax({

    method: "GET",

    url: kinveyBookUrl = kinveyBaseUrl + "appdata/" +

      kinveyAppKey + "/books/" + book._id,

    headers: getKinveyUserAuthHeaders(),

    success: loadBookForEditSuccess,

    error: handleAjaxError

  });

  …


82

Load Book for Edit: After AJAX Request

  function loadBookForEditSuccess(book) {

    $('#formEditBook input[name=id]').val(book._id);

    $('#formEditBook input[name=title]').val(book.title);

    $('#formEditBook input[name=author]')

      .val(book.author);

    $('#formEditBook textarea[name=descr]')

      .val(book.description);

    showView('viewEditBook');

  }

}


83

Edit Book: AJAX Request

function editBook() {

  let bookData = {

    title: $('#formEditBook input[name=title]').val(),

    author: $('#formEditBook input[name=author]').val(),

    description: 

      $('#formEditBook textarea[name=descr]').val()

  };

  $.ajax({

    method: "PUT",

    url: kinveyBaseUrl + "appdata/" + kinveyAppKey +

      "/books/" + $('#formEditBook input[name=id]').val(),


84

Edit Book: AJAX Request (2)

    headers: getKinveyUserAuthHeaders(),

    data: bookData,

    success: editBookSuccess,

    error: handleAjaxError

  });



  function editBookSuccess(response) {

    listBooks();

    showInfo('Book edited.');

  }

}


85

Test: Edit Book




Practice: Create "Book Library" App

Live Exercises in Class (Lab)


87

Single Page Apps (SPA) are built with HTML5, AJAX and REST + some back-end

App navigation may consist of DOM elements, which are shown / hidden

Login / register / logout is typically implemented with sessionStorage

CRUD operations is typically send AJAX request and render the results after that

Edit / delete may require to load the item first, then edit it / confirm delete, then post changes



Summary


https://softuni.bg/courses/javascript-applications

SPA with AJAX and Kinvey


License

This course (slides, examples, demos, videos, homework, etc.)
is licensed under the "Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International" license

89


Free Trainings @ Software University

Software University Foundation – softuni.org

Software University – High-Quality Education, Profession and Job for Software Developers

softuni.bg 

	Software University @ Facebook

	facebook.com/SoftwareUniversity

	Software University @ YouTube

	youtube.com/SoftwareUniversity

	Software University Forums – forum.softuni.bg