last update: Fall 2021
During the semester this repo will be updated on Thursdays before the TA office hours, the goal is to further explore concepts learned in the lectures and work closely with their related code.
JavaScript (JS) is the standard language for web browsers and becoming popular among web servers. It has come a long way, isn't it? In a nutshell:
Like Python, Perl, or PHP, so no need for String x= "";
, instead let x="";
does the trick.
Yet you can be explicit when manipulating data types:
- String casting:
let x = '1', y =1; let z= x+y; // string concat
- Integer casting:
let x = '1', y =1; let z= +x+y; // int sum
To manipulate data, you can count on the following syntax:
- Primitives:
let x = '1', y =1, z = true, w = null;
- Objects and Arrays:
let z= {x: '1', y:1}, w = ['1', 1];
ECMAScript (ES) 2021 is the current general reference specification to build the Javascript engines we use in our web apps. For instance, Google built the V8 JS engine and updated recently to satisfy ES2021.
JS is often used with other frameworks and libraries making it hard to differentiate which features are native and which are from third-parties, so when refering to JS directly, the community refers to it as Vanilla JS. To further differentiate its original flavor from all the features that have been added to it through the years, I call its current version, French Vanilla JS.
Since developers and content online may not be up to date with JS evolution, this is a brief summary of features that has evolved recently, achieving the same functionality with a more succint syntax:
// Functions
function z (x, y) { return x+y; }
z(1, 2);
// Arrow Functions
const z = (x, y) => x+y;
z(1, 2);
// Object construction
let z= {x: '1', y:1}, a = true;
let w = {x: z.x, y: z.y, a: a};
w;
// Object spreading
let z= {x: '1', y:1}, a = true;
let w = {...z, a};
w;
// Object property extraction
let z= {x: '1', y:1};
z.x + z.y;
// Object destructuring
let z= {x: '1', y:1}; let {x, y} = z;
x + y;
// Explicit data iterators
let z= ['1', 1];
for(let i = 0; i < z.length; i++)
{ let item = z[i];}
// Implicit data iterators
let z= ['1', 1];
for(let i in z){let item = z[i];}
for(let item of z){}
// Conditional Expressions
let x= ()=>true;
x ? x(): null;
// Conditional Chaining
let x= ()=>true;
x?.();
// Null Checks
let x = ’’;
let y = x!== null && x!== undefined? x:'';
// Nullish coalescing
let x = ''; let y = x ??'value’;
- Install NPM, it comes bundled with Node.js.
- Locate the folder containing the
package.json
file and run these commands:
npm install
npm run start
In the case of npm install
, you only have to run it when you add packages to your package.json
.
If you get a cryptic error like throw er; // Unhandled 'error' event
during npm install
, try removing the package-lock.json file and the node_modules folder, then try re-running the command.
Alternatively, open a terminal, locate your project's root folder, and run these commands:
rm package-lock.json
rm yarn.lock
rm -rf ./node_modules
npm install
rm package-lock.json
rm yarn.lock
rd -r .\node_modules
npm install
Asynchronous behavior was not as simple in earlier versions of JavaScript, often it was done via XML Http Requests (XHR):
const URL = 'https://swe432tomcat.herokuapp.com/zipLookup?zip=22030';
xhr = new XMLHttpRequest();
xhr.onreadystatechange = ()=>{
if(xhr.readyState===4){
if(xhr.status===200)
console.log( JSON.parse(xhr.responseText));
else
console.log(xhr.statusText);
}
};
xhr.open('GET', URL); xhr.send();
Now is often done via the fetch API:
const URL = 'https://swe432tomcat.herokuapp.com/zipLookup?zip=22030';
fetch(URL).then(
response=>{
response.json()
.then(
responseObj=>{
console.log(
responseObj
); }
); }
).catch(console.log);
This API relies on promises, which are the building abstraction to implement asynchronous behavior.
A promise is asynchronous in nature, it is an abstraction that encapsulates asynchronous behavior, it offers an API where an object can indicate events such as resolve or reject via callback functions defined by users. Promises can be used:
let x= ()=>new Promise(
(onComplete, onReject)=>{
onComplete(true);
});
Or,
let x= async ()=>{
return await true;
};
x().then(console.log).catch(console.log);
// or
let y= async ()=>{
const value = await x();
console.log(value);
};
y(); // finally?
Try using this API to make the same server request with 4 different libraries. You can run it live.
This is an API usage example:
// function getPlaceAjax
// parameter: zip
// parameter: success callback
// parameter: error callback (optional)
// parameter: url (optional)
// library: string, one of: "promise" (default), "ajax", "axios", "fetch" (optional)
// action: create the XMLHttpRequest object, register the
// handler for onreadystatechange, prepare to send
// the request (with open), and send the request,
// along with the zip code, to the server
// includes: the anonymous handler for onreadystatechange
getPlace('22033', (data)=>{console.log(data)}, error=>console.warn(error), "https://swe432tomcat.herokuapp.com/zipLookup", "fetch");
And this is the API implementation:
import axios from "axios";
// popcornA.js
// Ajax JavaScript code for the popcornA.html document
// Thanks to Sebesta, Programming the World Wide Web
/********************************************************/
export function getPlaceAjax(
zip,
thenCallback,
catchCallback,
url = "https://swe432tomcat.herokuapp.com/zipLookup"
) {
let xhr = null;
if (window.XMLHttpRequest) {
// IE7+, Firefox, Chrome, Opera, Safari
xhr = new XMLHttpRequest();
} else {
// IE5, IE6
// eslint-disable-next-line
xhr = new ActiveXObject("Microsoft.XMLHTTP");
}
// Register the embedded handler function
// This function will be called when the server returns
// (the "callback" function)
xhr.onreadystatechange = () => {
// 4 means finished,
if (xhr.readyState === 4) {
// and 200 means okay.
if (xhr.status === 200) {
// Data should look like "Fairfax, Virginia"
thenCallback(xhr.responseText);
} else {
catchCallback(xhr.statusText, xhr);
}
}
};
// Call the response software component
const query = url + "?zip=" + zip;
xhr.open("GET", query);
xhr.send(null);
}
export function getPlaceAjaxPromise(
zip,
thenCallback,
catchCallback,
url = "https://swe432tomcat.herokuapp.com/zipLookup"
) {
const zipPromise = new Promise((resolutionFunc, rejectionFunc) => {
let xhr = null;
if (window.XMLHttpRequest) {
// IE7+, Firefox, Chrome, Opera, Safari
xhr = new XMLHttpRequest();
} else {
// IE5, IE6
// eslint-disable-next-line
xhr = new ActiveXObject("Microsoft.XMLHTTP");
}
// Register the embedded handler function
// This function will be called when the server returns
// (the "callback" function)
xhr.onreadystatechange = () => {
// 4 means finished,
if (xhr.readyState === 4) {
// and 200 means okay.
if (xhr.status === 200) {
// Data should look like "Fairfax, Virginia"
resolutionFunc(xhr.responseText);
} else {
rejectionFunc(xhr.statusText, xhr);
}
}
};
// Call the response software component
const query = url + "?zip=" + zip;
xhr.open("GET", query);
xhr.send(null);
});
zipPromise.then(thenCallback).catch(catchCallback);
}
export function getPlaceFetch(
zip,
thenCallback,
catchCallback,
url = "https://swe432tomcat.herokuapp.com/zipLookup"
) {
// Call the response software component
const query = url + "?zip=" + zip;
fetch(query)
.then(
// Register the embedded handler function
// This function will be called when the server returns
// (the "callback" function)
// 4 means finished, and 200 means okay.
(response) => {
// Data should look like "Fairfax, Virginia"
response.text().then(thenCallback);
}
)
.catch(catchCallback);
}
export function getPlaceAxios(
zip,
thenCallback,
catchCallback,
url = "https://swe432tomcat.herokuapp.com/zipLookup"
) {
// Call the response software component
const query = "zip=" + zip;
axios
.post(url, query)
.then(
// Register the embedded handler function
// This function will be called when the server returns
// (the "callback" function)
// 4 means finished, and 200 means okay.
(response) => {
// Data should look like "Fairfax, Virginia"
thenCallback(response.data);
}
)
.catch(catchCallback);
}
function getPlace(
zip,
thenCallback,
catchCallback,
url = "https://swe432tomcat.herokuapp.com/zipLookup",
library = "promise"
) {
let targetLibrary = null;
switch (library) {
case "axios":
targetLibrary = getPlaceAxios;
break;
case "fetch":
targetLibrary = getPlaceFetch;
break;
case "promise":
targetLibrary = getPlaceAjaxPromise;
break;
case "ajax":
default:
targetLibrary = getPlaceAjax;
}
targetLibrary(zip, thenCallback, catchCallback, url);
}
export default getPlace;
Note: The zip microservice is implemented in Java, this does not affect your frontend, however, if feeling curious, check out session 6 to know more on how this was done.
We will dive into a real application that uses Firebase to implement a chat: https://github.com/devuxd/SeeCodeRun/blob/master/scr-app/src/containers/Chat.js
Wondering how you can write microservices in Java, here is a Heroku project using Tomcat to provide microservices: https://github.com/luminaxster/swe432tomcat
Note: Can you rest assure the service is using REST principles?
We will revisit session 4's example, this time we will focus on the React code.
Looking for a React UI framework? Try MUI: https://codesandbox.io/s/swe-432-react-two-buttons-example-mui-yohyi