{
"name": "Alexandru PΔvΔloi",
"nickname": "PΔvΔ",
"age": 25,
"blog": "https://iampava.com",
"e-mail": "pava@iampava.com"
}
_iampava
{ about you }
{ about you }
π₯ will have the skills to build not trivial Web Apps projects with a strong focus on JavaScript
π€ will have worked on a personal project which showcases your skills
π¨βπ could be ready to venture into the real world and get into a summer job/internship
π
will understand why JavaScript is the best language in the world
( probably )
β modern JavaScript knowledge
β browser API's
β server side via Node.js
{
Pass this object
Solve the exercises/problems weekly
* mandatory
{
β frameworksΒ allowed on Node.js
β no frameworks on the client
β 3 unannounced tests during the labs
β 1 optional test at the end of the course
π Get at least 5 for all project components and 5 for the lab exercises in order to pass!
Every Friday I'll post in-advance exercises for the next lab!
Tests will assume you've understood all exercises from there.
Student presentations
email at: pava@iampava.com
VS Code
Git
Firefox & Chrome
OK, let's do this!
<p
class="bold"
data-author="David Smith"
>
Content
</p>
<p
class="bold"
data-author="David Smith"
>
Content
</p>
tag
attribute
custom attribute
Build this using only HTML
HTML in modern times
<div class="card">
<h1> {{ user.username }} </h1>
<p class="about"> {{ user.about }} </p>
<span *ngFor="job in user.jobs" class="chip"> {{ job }} </span>
<button type="button" *ngIf="user.isAdmin">
Edit
</button>
</div>
<div class="card">
<h1> {{ user.username }} </h1>
<p class="about"> {{ user.about }} </p>
<span *ngFor="job in user.jobs" class="chip"> {{ job }} </span>
<button type="button" *ngIf="user.isAdmin">
Edit
</button>
</div>
<div class="card">
<h1> {{ user.username }} </h1>
<p class="about"> {{ user.about }} </p>
<span v-for="job in user.jobs" class="chip"> {{ job }} </span>
<button type="button" v-if="user.isAdmin">
Edit
</button>
</div>
div class="card">
<h1> {{ user.username }} </h1>
<p class="about"> {{ user.about }} </p>
<span v-for="job in user.jobs" class="chip"> {{ job }} </span>
<button type="button" v-if="user.isAdmin">
Edit
</button>
</div>
function cardComponent({ user }) {
return ( <div className="card">
<h1> { user.username } </h1>
<p class="about"> { user.about } </p>
{user.jobs.map(job => (
<span class="chip"> {{ job }} </span>
))}
{user.isAdmin && <button type="button">
Edit
</button>
}
</div>
)
}
function cardComponent({ user }) {
return ( <div className="card">
<h1> { user.username } </h1>
<p class="about"> { user.about } </p>
{user.jobs.map(job => (
<span class="chip"> {{ job }} </span>
))}
{user.isAdmin && <button type="button">
Edit
</button>}
}
Websites in modern life
Multi Page Apps
Single Page App
Hybrid
V
V
(ex: Wikipedia)
(ex: DevDrive)
(ex: Twitter)
Client only
Server & client
Server Side Rendered
Client Side Rendered
Hybrid
V
V
<h2>
<div class="btn btn-lg btn-primary btn-rounded px-4 mr-3" @click="acceptTeamInvitations(team_invitation)">
Accept Invitations
</div>
<div class="btn btn-lg btn-secondary btn-rounded px-4 mr-3" @click="rejectTeamInvitations(team_invitation)">
Reject Invitations
</div>
</h2>
<h2>
<div class="btn btn-lg btn-primary btn-rounded px-4 mr-3" @click="acceptTeamInvitations(team_invitation)">
Accept Invitations
</div>
<div class="btn btn-lg btn-secondary btn-rounded px-4 mr-3" @click="rejectTeamInvitations(team_invitation)">
Reject Invitations
</div>
</h2>
<div id="userCardContainer">
<div id="centeredCardContainer">
<div id="centeredCard">
<div id="userDetailsHolder">
<span id="userName">{{ (userDetails$ | async).FirstName }}</span>
<img [src]="(userDetails$ | async).PictureProfile" id="menuUserIcon" />
</div>
</div>
</div>
</div>
<div id="userCardContainer">
<div id="centeredCardContainer">
<div id="centeredCard">
<div id="userDetailsHolder">
<span id="userName">{{ (userDetails$ | async).FirstName }}</span>
<img [src]="(userDetails$ | async).PictureProfile" id="menuUserIcon" />
</div>
</div>
</div>
</div>
Custom HTML elements
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Simple word count web component</title>
</head>
<body>
<h1>Word count rating widget</h1>
<article contenteditable="">
<h2>Sample heading</h2>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc pulvinar sed justo sed viverra. Aliquam ac scelerisque tellus. Vivamus porttitor nunc vel nibh rutrum hendrerit. Donec viverra vestibulum pretium. Mauris at eros vitae ante pellentesque bibendum. Etiam et blandit purus, nec aliquam libero. Etiam leo felis, pulvinar et diam id, sagittis pulvinar diam. Nunc pellentesque rutrum sapien, sed faucibus urna sodales in. Sed tortor nisl, egestas nec egestas luctus, faucibus vitae purus. Ut elit nunc, pretium eget fermentum id, accumsan et velit. Sed mattis velit diam, a elementum nunc facilisis sit amet.</p>
<p>Pellentesque ornare tellus sit amet massa tincidunt congue. Morbi cursus, tellus vitae pulvinar dictum, dui turpis faucibus ipsum, nec hendrerit augue nisi et enim. Curabitur felis metus, euismod et augue et, luctus dignissim metus. Mauris placerat tellus id efficitur ornare. Cras enim urna, vestibulum vel molestie vitae, mollis vitae eros. Sed lacinia scelerisque diam, a varius urna iaculis ut. Nam lacinia, velit consequat venenatis pellentesque, leo tortor porttitor est, sit amet accumsan ex lectus eget ipsum. Quisque luctus, ex ac fringilla tincidunt, risus mauris sagittis mauris, at iaculis mauris purus eget neque. Donec viverra in ex sed ullamcorper. In ac nisi vel enim accumsan feugiat et sed augue. Donec nisl metus, sollicitudin eu tempus a, scelerisque sed diam.</p>
<p is="word-count"></p>
</article>
<script>
class WordCount extends HTMLParagraphElement {
constructor() {
super();
const wcParent = this.parentNode; // count words in element's parent element
function countWords(node){
const text = node.innerText || node.textContent;
return text.split(/\s+/g).length;
}
const count = `Words: ${countWords(wcParent)}`;
const shadow = this.attachShadow({mode: 'open'});
const text = document.createElement('span');
text.textContent = count;
shadow.appendChild(text);
setInterval(function() {
const count = `Words: ${countWords(wcParent)}`;
text.textContent = count;
}, 200);
}
}
customElements.define('word-count', WordCount, { extends: 'p' });
</script>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Simple word count web component</title>
</head>
<body>
<h1>Word count rating widget</h1>
<article contenteditable="">
<h2>Sample heading</h2>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc pulvinar sed justo sed viverra. Aliquam ac scelerisque tellus. Vivamus porttitor nunc vel nibh rutrum hendrerit. Donec viverra vestibulum pretium. Mauris at eros vitae ante pellentesque bibendum. Etiam et blandit purus, nec aliquam libero. Etiam leo felis, pulvinar et diam id, sagittis pulvinar diam. Nunc pellentesque rutrum sapien, sed faucibus urna sodales in. Sed tortor nisl, egestas nec egestas luctus, faucibus vitae purus. Ut elit nunc, pretium eget fermentum id, accumsan et velit. Sed mattis velit diam, a elementum nunc facilisis sit amet.</p>
<p is="word-count"></p>
</article>
<script>
class WordCount extends HTMLParagraphElement {
constructor() {
super();
const wcParent = this.parentNode;
function countWords(node){
const text = node.innerText || node.textContent;
return text.split(/\s+/g).length;
}
const count = `Words: ${countWords(wcParent)}`;
const shadow = this.attachShadow({mode: 'open'});
const text = document.createElement('span');
text.textContent = count;
shadow.appendChild(text);
setInterval(function() {
const count = `Words: ${countWords(wcParent)}`;
text.textContent = count;
}, 200);
}
}
customElements.define('word-count', WordCount, { extends: 'p' });
</script>
</body>
</html>
a {
border-bottom: 2px solid blue;
}
a {
border-bottom: 2px solid blue;
}
selector
property
value
@media (prefers-color-scheme: dark) {
body { background: black; }
h1, h2, h3 {
color: #fff;
}
}
@media (prefers-color-scheme: dark) {
body { background: black; }
h1, h2, h3 {
color: #fff;
}
}
media queries
<p style="font-weight: bold">
A simple styled paragraph...
</p>
<p style="font-weight: bold">
A simple styled paragraph...
</p>
<style>
h1 {
text-align: center;
}
</style>
<style>
h1 {
text-align: center;
}
</style>
<!-- index.html -->
<link rel="stylesheet" href="style.css" />
<!-- style.css -->
footer {
color: #000;
background-color: #fff;
}
<!-- index.html -->
<link rel="stylesheet" href="style.css" />
<!-- style.css -->
footer {
color: #000;
background-color: #fff;
}
<img class="image round-image" id="profile"
src="user.jpg" />
<style>
img {
border: 1px solid black;
}
.round-image {
border: 5px dotted yellow;
}
.image {
border: 10px dotted red;
}
#profile {
border: 2px solid blue;
}
</style>
<img class="image round-image" id="profile"
src="user.jpg" />
<style>
img {
border: 1px solid black;
}
.round-image {
border: 5px dotted yellow;
}
.image {
border: 10px dotted red;
}
#profile {
border: 2px solid blue;
}
</style>
.valentines.in-love {
// both "valentines" & "in-love"
// classes
text-decoration: make-up;
}
.valentines.in-love {
// both "valentines" & "in-love"
// classes
text-decoration: make-up;
}
.raining .human {
/* "human" class elements inside a
"raining"-class element
*/
display: hidden;
position: under-blanket;
}
.raining .human {
/* "human" class elements inside a
"raining"-class element
*/
display: hidden;
position: under-blanket;
}
.raining.valentines .in-love {
color: red;
}
.raining.valentines .in-love {
color: red;
}
faculty.in-iasi.state-owned .b4 #gicu {
display: none;
}
faculty.in-iasi.state-owned .b4 #gicu {
display: none;
}
π In-depth reading
PS: check DevDrive over the weekend for practice exercises
Week #2
validate your HTML
meta tag
buttonsssss
modal: no fixed widths for the modal
before & after pseudo elements
no bonus points so why bother copying the solution?
<style>
.red {
color: red;
}
.blue {
color: blue;
}
</style>
<p class="red blue"> Bob </p>
<p class="blue red"> Alice </p>
<style>
.red {
color: red;
}
.blue {
color: blue;
}
</style>
<p class="red blue"> Bob </p>
<p class="blue red"> Alice </p>
I'm using a mix of tag-based styling (whenever possible) plus BEM but without the modifiers part. For those I just go for the `is--{modifier}` syntax.
β Alwasy give the page and `block` element a unique class.
.not-found-page {
text-align: center;
h1 {
margin-top: 0;
font-size: calc(30px + 2vw);
}
h2 {
font-weight: normal;
font-size: calc(18px + 1.5vw);
line-height: 2em;
}
button {
margin-top: 1em;
font-size: 20px;
}
}
Elements that bring meaning on their own!
Elements that have meaning only as part of a block.
.block { /** ... */ }
.block__element { /** ... */ }
A particular state of a block or element
.block__element--modifier { /** ... */ }
.topic-item {
overflow: hidden;
position: relative;
border-radius: 0.5em;
background: darken(color(black), 5%);
border: 1px solid darken(color(grey), 10%);
&.is--editing {
padding: 0.5em 0.75em;
}
&.is--menu-open {
.menu {
display: block;
}
.menu-btn {
text-align: center;
}
}
&__name {
display: inline-block;
color: color(white);
margin: 0.25em 0;
font-size: 1.2em;
}
&__number {
display: block;
text-align: right;
color: color(white);
}
}
@media screen and (max-width: 450px) {
/* ... */
}
@media screen and (orientation: portrait) {
/* ... */
}
@media (prefers-color-scheme: dark) {
/** ... */
}
1 - Server-side solution
give me `cover.jpg`
seems like you're on mobile
sending `cover_small.jpg`
<picture>
<source
srcset="image_high.jpg"
media="(min-width: 800px)">
<source
srcset="image_low.jpg"
media="(min-width: 450px) and (max-width: 800px)">
<img src="image_very_low.jpg" alt="image">
</picture>
2 - Client side solution
@media screen and (min-width: 800px) {
.cover {
background: url('cover_high.jpg');
}
}
@media screen and (max-width: 800px) {
.cover {
background: url('cover_low.jpg');
}
}
From my experience, Flex is in many cases enough. However, I believe it soon won't be...
.5 sec
#logo {
transition: transform .5s ease-in;
}
#logo:hover {
transform: scale(1.1);
}
property
timing function
duration
or
A
A
A
A
#logo {
animation: rotate 1s linear infinite;
}
@keyframes rotate {
0% {
transform: rotate(0deg);
}
50% {
transform: rotate(180deg);
}
100% {
transform: rotate(360deg)
}
}
duration
name
timing function
iteration count
Bonus
@media (prefers-color-scheme: dark) {
/** ... */
}
PS: you might need to use this π
CSS in modern times
.topic-item {
overflow: hidden;
position: relative;
/* ... */
&__name {
display: inline-block;
margin: 0.25em 0;
font-size: 1.2em;
}
&__number {
/* ... */
}
}
@for $i from 1 through 3 {
.slider.displaying-#{$i} .content {
@media (max-width: 640px) {
transform: translateX(-$i * 100 + %);
}
@media (min-width: 641px) {
transform: translateY(-$i * 100 + %);
}
}
}
.topic-item {
overflow: hidden;
position: relative;
/* ... */
&__name {
display: inline-block;
margin: 0.25em 0;
font-size: 1.2em;
}
&__number {
/* ... */
}
}
@for $i from 1 through 3 {
.slider.displaying-#{$i} .content {
@media (max-width: 640px) {
transform: translateX(-$i * 100 + %);
}
@media (min-width: 641px) {
transform: translateY(-$i * 100 + %);
}
}
}
import React from 'react';
import './Topic.style.scss';
function TopicComponent() {
return (<div> /* ... */ </div>);
}
export default TopicComponent;
import React from 'react';
import './Topic.style.scss';
function TopicComponent() {
return (<div> /* ... */ </div>);
}
export default TopicComponent;
Bonus
CSS
Modules
import React from 'react';
import styles from './Topic.style.scss';
function TopicComponent() {
return (<div className={style.topic}> /* ... */ </div>);
}
export default TopicComponent;
import React from 'react';
import styles from './Topic.style.scss';
function TopicComponent() {
return (<div className={style.topic}> /* ... */ </div>);
}
export default TopicComponent;
/* Topic.style.scss */
.topic {
/* ... */
}
/* Topic.style.scss */
.topic {
/* ... */
}
<div class="topic_x21as">
<!-- -->
</div>
<div class="topic_x21as">
<!-- -->
</div>
CSS in JS
import React from 'react';
import styled from 'styled-components';
const Link = styled.a`
display: inline-block;
border-radius: 3px;
border: 2px solid white;
${props => props.primary && css`
background: white;
color: palevioletred;
`}
`
function LinkComponent() {
return ( <div>
<Link
href="https://github.com/styled-components/styled-components"
target="_blank"
rel="noopener"
primary
>
GitHub
</Link>
</div>)
}
import React from 'react';
import styled from 'styled-components';
const Link = styled.a`
display: inline-block;
border-radius: 3px;
border: 2px solid white;
${props => props.primary && css`
background: white;
color: palevioletred;
`}
`
function LinkComponent() {
return ( <div>
<Link
href="https://github.com/styled-components/styled-components"
target="_blank"
rel="noopener"
primary
>
GitHub
</Link>
</div>)
}
[discussions]
PS: check DevDrive over the weekend for practice exercises
Week #3
β
it's single threaded
β
it's a dynamic language
β
it's an open language, actively developed by TC39
β
everything in the same global scope
β
naming convention is usually camelCase
Brendan Eich
ES5
ES6
ES10
...
π compat table
let myBake = {
mark: "unknown",
year: 2015,
boughtFrom: {
name: "Decathlon",
address: "Era Shopping Park"
}
}
// Get properties
console.log(myBike.boughtAt.name);
console.log(myBike["boughtAt"]["name"]);
// Add properties
myBike.suspension = "full";
// Object creation
let veryRandomList = [
2, null, undefined, "bob",
[1, 2, 3]
];
console.log(veryRandomList[3]);
// Arrays are Objects too
veryRandomList.name = "My List";
console.log(veryRandomList);
function sum(x,y) {
return x + y;
}
let anotherVariable = function (x,y) {
return x - y;
}
let letsBeFancy = (x,y) => x * y;
β can be named or anonymous
β doesn't brake if missing params. It's a dynamic lang π
β if no explicit return, it implicitly returns undefined
β are first class citizens - whatever you can do with the other variables you can do with functions
returned from functions
passed to functions as arguments
stored in variables
Where can we access the declared variables β
var name = "Bob";
printName();
function printName() {
console.log(name);
}
β
The JS engine doesn't find that variable in the printName function so starts searching for it upstairs.
Β
Think of an elevator. If you can't find what you're looking for on that floor, you go up and search there. And so on...
createPerson();
printName();
function createPerson() {
let name = "Bob";
}
function printName() {
console.log(name);
}
β every function creates a new scope [function scope] β you can't search in sibling scopes
function scope vs block scope
since forever in JavaScript
available from ES6
goOut();
function goOut() {
if (weather === "sunny") {
let outfit = "light & casual";
} else {
let outfit = "light & casual + umbrella";
}
console.log(outfit);
}
β a block scoped variable is only available inside it's closest curly brackets { }
can be reassigned
cannot be reassigned
function goOut() {
const outfit;
if (weather === "sunny") {
outfit = "light & casual";
} else {
outfit = "light & casual + umbrella";
}
console.log(outfit);
}
β
β
const person = {
name: 'Bob',
occupation: 'JavaScriptER',
umbrella: true
};
if (weather === 'sunny') {
person.umbrella = false;
}
β
πΉ F12 or Cmd + F12
startGame();
function startGame() {
let players = [{
name: "Alice",
gold: 120.123,
life: .75
}, {
name: "Bob",
life: 1
}];
for(let i = 0; i< players.length; i++) {
console.log(getChangeToWin(players[i]));
}
}
function getChangeToWin(player) {
return Number(player.gold.toFixed(1)) ** 2 * player.life;
}
Find the bug and fix the code
β The ability of a function to access lexical scope variables even when it is executed in a different context.
function simpleF(x) {
return function() {
return x;
}
}
let get11 = simpleF(11);
get11();
Closures are the only way of having true private data in JavaScript.*
function counter() {
let value = 0;
return increase() {
value++;
}
}
The 'value' variable cannot be accessed directly, only through the 'increase' function.
Create a standalone module with a Public API and a Private one.
let notificationModule = (function() {
let notifications = {};
return {
subscribe(username, topic) {
notifications[topic] = notifications[topic] || [];
notifications[topic].push(username);
},
unsubscribe(username, topic) {
let indexOf;
if (
notifications[topic] &&
(indexOf = notifications[topic].indexOf(username)) >= 0
) {
notifications[topic].splice(indexOf, 1);
}
}
};
})();
β But, how do I access the public API from inside the module?
let module = (function() {
let notifications = {};
let publicApi = {
/** ... */
}
return publicApi;
function somePrivateFunction() {
/** can access publicApi */
}
})();
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Internationalization module pattern</title>
</head>
<body data-lang="en">
<h1 data-text="hello_there"></h1>
<p data-text="intro_paragraph"></p>
<p data-text="later_text"></p>
</body>
<script>
let i18n = (function() {
/** */
})();
i18n.registerLanguage('en', {
hello_there: 'Hey there',
intro_paragraph: 'Welcome to this exercise'
});
try {
i18n.applyLanguage(document.querySelector('body').dataset.lang);
} catch (err) {
alert('unsupported language');
}
i18n.registerSentences('en', {
later_text: 'Some later text'
});
i18n.refresh();
</script>
</html>
Create an internationalization module and make it work for the code below.
ModulePattern seems great but I feels there's a "but" somewhere...
β don't use it for instances because all those functions will be declared again and again.
PS: check DevDrive over the weekend for practice exercises
Week #4
1) No need to declare another variable. We can use directly the param. Plus, don't use the same name for the inner function.
function addF(x) {
let value = x;
return function addF(y){
return value + y;
}
}
function addF(x) {
return function innerAddF(y){
return x + y;
}
}
2) What if we skip var, let and const?
double(12);
function double(x) {
xDouble = x * 2;
}
β The variable is declared globally! Big NO NO!
3) Difference between == and ===/ != and !==
{
}
The prototype represents the collection of properties and methods that all instances of a certain type/class inherit.
All Array's have a sort function already defined by the language. We say that the sort function is on the Array prototype.
β We can add properties/methods
The prototype is an object meaning that...
β And even delete them...
{
}
Everything* inherits from Object.prototype so technically everything is an object π€
Object.prototype
Array.prototype
Function.prototype
String.prototype
Number.prototype
...
}
let myBike = new Bike("Buburuza");
myBike.ride();
function Bike() {
this.name = name;
}
Bike.prototype.ride = function() {
console.log(`I'm riding ${this.name} bike`);
}
let myBike = new Bike("Buburuza");
myBike.ride();
class Bike {
constructor(name) {
this.name = name;
}
ride() {
console.log(`I'm riding ${this.name} bike`);
}
}
function Animal(name) {
this.name = name;
}
Animal.prototype.sayHello = function() {
console.log(`Hello! I am ${this.name}, your pet!`);
}
function Dog(name, favoriteFood) {
Animal.call(this, name);
this.favoriteFood = favoriteFood;
}
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.bark = function() {
console.log(`${this.name}: woof woff`);
}
class Animal {
constructor() {
this.name = name;
}
sayHello() {
console.log(`Hello, I am ${this.name}, your pet!`);
}
}
class Dog extends Animal {
constructor(name, favoriteFood) {
super(name);
this.favoriteFood = favoriteFood;
}
bark() {
console.log(`${this.name}: woof woof!`);
}
}
import Http from './Http.service';
import AppConstants from '~/App.constants';
class ExerciseService {
static getAll() {
return Http.get(`${AppConstants.ENDPOINT}/exercises`)
.then(resp => resp.json());
}
static getTopics(username, exerciseId) {
return Http.get(`${AppConstants.ENDPOINT}/exercises/${username}/${exerciseId}/topics`)
.then(resp => resp.json());
}
/** ... */
}
export default ExerciseService;
I use them mostly as API Services
{
}
Probably the most misunderstood thing in JavaScript πΏ
But actually, there are just 4 simple rules...
1) Implicit binding
obj.sayHello();
let foo = {
name: 'Foo',
sayHello() {
console.log(`Hi! My name is ${this.name}`);
}
}
let bar = { name: 'Bar' }
bar.sayHello = foo.sayHello;
foo.sayHello();
bar.sayHello();
2) Explicit binding
obj.sayHello.call(newThis);
obj.sayHello.apply(newThis);
let foo = {
name: 'Foo',
sayHello() {
console.log(`Hi! My name is ${this.name}`);
}
}
let bar = { name: 'Bar' }
foo.sayHello();
foo.sayHello.apply(bar)
obj.sayHello.bind(newThis);
let foo = { name: 'Foo' }
let bar = { name: 'Bar' }
function sayHello() {
console.log(`Hello, my name is ${this.name}`);
}
foo.sayHello = sayHello.bind(bar);
foo.sayHello();
3) New keyword
function JavaScripter(name) {
console.log(this);
this.name = name;
this.motto = 'JS Rulz!';
}
new JavaScripter('Bob');
4) Default rule
function sayHello() {
console.log(`Hello, my name is ${this.name}`);
}
sayHello();
If none of the above applies, this = window. *
Or undefined in strict mode
window.name = 'Window';
let foo = {
name: 'Foo',
sayHello() {
console.log(`Hello, my name is ${this.name}`);
}
}
let bar = { name: 'Bar' }
let baz = { name: 'Baz' }
foo.sayHello.bind(bar).bind(baz).call(foo);
β Arrow functions
Borrows this from their enclosing scope.
class Dog {
constructor(name) {
this.name = name;
}
bark() {
console.log(`${this.name}: woof woof!`);
}
delayBark(delay) {
setTimeout(this.bark, delay);
}
}
let dog = new Dog("Bob");
dog.delayBark(1000);
class Dog {
constructor(name) {
this.name = name;
}
bark() {
console.log(`${this.name}: woof woof!`);
}
delayBark(delay) {
setTimeout(() => {
this.bark();
}, delay);
}
}
let dog = new Dog("Bob");
dog.delayBark(1000);
PS: check DevDrive over the weekend for practice exercises
Week #5
1) The constructor property is not a good way of checking an objects type.
class MySpecialArray extends Array { }
let x = new MySpecialArray(1,2,3);
if(x.constructor === Array) {
console.log("As expected...");
} else {
console.log("...or not");
}
if(x.prototype.isPrototypeOf(Array.prototype)) {
console.log("Better...");
}
if(Array.isArray(x)) {
console.log("Much much better...");
}
function PiggyBank(){
let money = [];
return {
store: function(index, value){
money[index] = value;
},
push: function(value){
money.push(value);
}
}
}
let sharedPiggyBank = PiggyBank();
// Mom gives the sharedPiggyBank to Bob & Alice
// But, there's a weakness in this code which allows direct access to the money Array.
// Can you spot it?
Classes exercise from DevDrive.io
OR
class Tree {
/** ... */
}
class TreeNode {
/* ... */
}
const myTree = new Tree();
const rootNode = new TreeNode(22);
rootNode.putLeft(new TreeNode(11));
rootNode.putRight(new TreeNode(12));
rootNode.getLeft().putLeft(new TreeNode(10));
rootNode.getRight().putRight(13);
myTree.setRoot(rootNode);
myTree.print(); // 22 11 10 12 13
setTimeout(function() {
console.log('500ms have passed!');
}, 500)
[1,2,3].forEach((el) => {
console.log(el);
})
setTimeout(function() {
console.log('0ms have passed?');
}, 0)
console.log('before or after?');
document
.getElementById('logo')
.addEventListener('click', textBob);
function textBob() {
console.log("On my way!");
}
A callback is a function invoked from another function which received it as a parameter.
setTimeout(textBob, 500);
document
.getElementById("logo")
.addEventEventListener("click", textBob);
callback
callback
Game engine
Hero Class
life decreases every second
interested
class Hero(name, onUpdate) {
constructor(onUpdate) {
this.life = 100;
setInterval(() => {
this.life-= 10;
onUpdate(this.life);
}, 1000);
}
}
let GameEngine = (function (){
let newHero = new Hero(
"Bob",
onHeroUpdate
);
function onHeroUpdate(data) {
// do stuff
}
}());
PS: check DevDrive over the weekend for practice exercises
Week #6
Describes the project and all it's dependencies! Used both on the server (Node.js) and in modern client apps.
{
"name": "imagemin-webp-webpack-plugin",
"version": "3.2.2",
"description": "Webpack plugin which converts images to the WebP format while also keeping the original files.",
"author": {
"name": "Alexandru Pavaloi",
"email": "pava@iampava.com",
"url": "https://iampava.com"
},
"license": "MIT",
"main": "plugin.js",
"keywords": [
"webpack",
"plugin",
"webp",
"imagemin",
"images"
],
"repository": "https://github.com/iampava/imagemin-webp-webpack-plugin.git",
"bugs": "https://github.com/iampava/imagemin-webp-webpack-plugin/issues",
"homepage": "https://github.com/iampava/imagemin-webp-webpack-plugin",
"dependencies": {
"imagemin": "^6.1.0",
"imagemin-webp": "^5.1.0"
}
}
#1 About
{
"name": "imagemin-webp-webpack-plugin",
"version": "3.2.2",
"description": "Webpack plugin which converts images to the WebP format while also keeping the original files.",
"author": {
"name": "Alexandru Pavaloi",
"email": "pava@iampava.com",
"url": "https://iampava.com"
},
/* and much more */
}
Name, description, author, etc... Stuff you'd want to appear in NPM Registry. Here's an example package.
#2 Scripts
A set of command line instructions with a name so you can easily execute them.
{
"name": "devdrive",
"scripts": {
"clear": "rm -rf dist/",
"webpack": "webpack",
"webpack-dev-server": "webpack-dev-server",
"dev": "npm run webpack-dev-server -- --env.mode development --hot",
"prod": "npm run clear && npm run webpack -- --env.mode production && node build-utils/add-template.js",
"serve": "nodemon server.js --watch server --watch server.js"
/* ... */
}
}
#3 dependencies
3rd part packages that this projects needs in order to work in production.
{
"name": "imagemin-webp-webpack-plugin",
"version": "3.2.2",
"dependencies": {
"imagemin": "^6.1.0",
"imagemin-webp": "^5.1.0"
}
}
#4 devDependencies
3rd party packages that you need while developing and building this project for production. They are not actually used in the finished product.
{
"name": "devdrive",
"devDependencies": {
"browserslist": "^4.7.0",
"copy-webpack-plugin": "^5.0.4",
"css-loader": "^3.2.0",
"html-webpack-plugin": "^3.2.0",
"imagemin-webp-webpack-plugin": "^3.2.1",
/* ... */
}
}
$ npm init
To initialize a new package.json
$ npm install
Installs all dependencies in the package.json present in that folder.
$ npm install lodash
Install the latest version of a package and add it to dependencies or devDependencies.
$ npm install lodash --save-dev
$ npm install -g heroku
Install a package globally. Now you can access it from from anywhere in the PC.
$ npm update
Update all packages. Beware of breaking changes π±
β Add theΒ node_modules folder to .gitignore. No point in pushing dozens of MB's of dependencies on GIT
Before ES Modules the only way of adding scripts into your Web App was... well actually inserting them in HTML.
<script src="app.js"></script>
<script src="lodash.js"></script>
<script src="react.js"></script>
Well... Node.js has no HTML so it comes with a module system
// index.js
let ImageminWebpPlugin = require('imagemin-web-webpack-plugin');
// β‘ importing from a node_modules package
let routing = require('./routing.js');
// β‘ importing from a relative file, maybe one we defined
let { add, mul } = require('./math-operations.js');
// β‘ importing just 2 functions
// routing.js
class RoutingClass { /*...*/ }
let router = new RoutingClass(/**/);
module.exports = router;
// math-operations.js
module.exports = {
PI: 3.14,
add: (x, y) => x + y,
diff: (x, y) => x - y,
mul: (x,y) => x * y
}
Implement a simple HTTP server which serves files from that directory.
$ npm run serve
Bonus
Implement a routing system to make developing the app easier:
class HTTPServer {
/* ... */
}
let server = new HTTPServer(/** ... */);
server.get('/', (req, res) => {
// only get requests on root arrive here
})
server.get('/user/:id', (req, res) => {
let { id } = req.params;
// β‘ automatically extract params from the URL
// respond with some user data
})
server.get('*', (req, res) => {
// all get requests not intercepted arrive here.
// great place for showing a generic 404 message
});
PS: check DevDrive over the weekend for practice exercises
Week #7
Let's build the same app in 3 different ways:
Node.js
only
Client
only
Client,
Server &
REST API
Node.js only
Keep all the logic and state on the server.
Reload the page after each action!
http://localhost:8080
π HTML markup
π First Button click
is actually a Form POST submit
π a new page πͺ stores data in cookie
π Button click
is actually a Form POST submit
reads cookie data
π & πͺ
Client only
Keep all the logic and state on the client.
Modify DOM directly upon user actions
Client, server & REST API
Keep all the logic and state on the server.
Modify DOM directly based on API responses.
http://localhost:8080
π HTML markup
Button click
triggers a GET/POST request
π JSON data
PS: good luck working on the projects!
Week #10
Imagine a blank Canvas on which you can programmatically paint stuff!
β geometrical shapes
β images
β pixel by pixel!
Highly interactive and dynamic apps.
β games
β photo/video editors
β custom visualizations
<!DOCTYPE html>
<html lang="en">
<head>
<title>Simple canvas</title>
</head>
<body>
<canvas width="1280" height="720"></canvas>
<script>
let canvas = document.querySelector("canvas");
let ctx = canvas.getContext("2d");
ctx.beginPath();
ctx.rect(10, 100, 50, 50);
ctx.fill();
</script>
</body>
</html>
Draw some mountains, a sky and a sun!
Drawing a polygonal shape
Drawing a round shape
Just like movies and their fps, we paint the canvas many times a second which gives the illusion of movement.
window.requestAnimationFrame(function onFrame() {
/* ... */
});
<!DOCTYPE html>
<html lang="en">
<head>
<title>Animated canvas</title>
</head>
<body>
<canvas style="border: 2px solid green" width="1280" height="720"></canvas>
<script>
let size = 2;
let sign = 1;
let canvas = document.querySelector('canvas');
let ctx = canvas.getContext('2d');
window.requestAnimationFrame(function onFrame() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.beginPath();
ctx.rect(0, 0, size, size);
ctx.fill();
if(size < 0 || size > 50) {
sign *= -1;
}
size+= sign;
window.requestAnimationFrame(onFrame);
});
</script>
</body>
</html>
Let's animate the birds across the sky! π
Bonus: move them across the sky using keyboard inputs!
Week #11
Adrian Ursaciuc
Cristian Gatu
β Web workers
β Shared workers
β Service workers
Week #11
George BΔdiΘΔ
Run JavaScript code separately from the main thread.
Avoid huge computations on the main thread to keep the app responsive. Ideally at 60fps β€
<!DOCTYPE html>
<html lang="en">
<head>
<title>Web Workers demo</title>
</head>
<body>
<p></p>
<script>
const randomValues = [Math.random() * 1000, Math.random() * 1000];
if (window.Worker) {
let myWorker = new Worker("worker.js");
myWorker.postMessage(randomValues);
myWorker.addEventListener('message', (e) => {
document.querySelector('p').innerHTML = e.data;
})
} else {
console.log("Oups, seems like this is a legacy browser!");
}
</script>
</body>
</html>
index.html
worker.js
self.addEventListener('message', (e) => {
let { data } = e;
postMessage(`The result is ${data[0] + data[1]}`);
})
{
type: string,
payload: any
}
A widely used structure for sending Worker events so that it's easy to understand!
Server
Client
Service Worker
if ('serviceWorker' in navigator) {
navigator.serviceWorker
.register('service-worker.js')
.then(reg => {
console.log("Successfully registered!");
})
.catch(err => {
console.error("Oups", err);
});
}
main.js
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<title>Service Workers demo</title>
</head>
<body>
<script src="main.js"></script>
</body>
</html>
// Empty for now
service-worker.js
example.com
π index.html
π style.css
π ...
Server
π index.html
π style.css
π ...
service-worker.js
const filesToCache = ["/", "style.css"];
self.addEventListener("install", event => {
event.waitUntil(
new Promise((res, rej) => {
caches
.open("static-files")
.then(cache => {
return Promise.all(
filesToCache.map(url =>
fetch(url).then(resp => {
cache.put(url, resp);
})
)
).then(res);
})
.catch(rej);
})
);
});
self.addEventListener("fetch", event => {
event.respondWith(
new Promise((res, rej) => {
const clonedRequest = event.request.clone();
return caches
.match(event.request, {
cacheName: "static-files"
})
.then(resp => resp || fetch(clonedRequest));
})
);
});
<link rel="manifest" href="manifest.json" />
{
"name": "Geek Alert",
"short_name": "Geek Alert",
"theme_color": "#4141ac",
"background_color": "#b1fcf8",
"display": "standalone",
"orientation": "portrait",
"scope": ".",
"start_url": ".",
"icons": [
{
"src": "icons/icon-72x72.png",
"sizes": "72x72",
"type": "image/png"
},
/* ... */
]
}
// All the code from before
// Plus β¬
firebase.messaging().useServiceWorker(swRegistration);
Notification.requestPermission().then(permission => {
if (permission === "granted") {
messaging.getToken().then(token => {
console.log(token);
});
}
});
main.js
service-worker.js
// All the code from before
// Plus β¬
self.addEventListener("push", event => {
const payload = event.data.json().data;
event.waitUntil(
self.registration.showNotification(payload.title, {
body: payload.body
})
);
});
Week #12
Giving many exs in a lab that most of us are unable to finish
Talking/teaching so fast
Student presentations shouldnβt last the whole class
[...] static module bundler for modern JavaScript applications. - Official Docs
β optimize your code and 3rd party one for release
β use non-native Web technologies in your projects
β seamlessly use more advanced techniques
minification
dead-code elimination
Typescript
SASS
lazy-loading
Final Week
π Timetable (soon)
&
Project presentations