diff --git a/SUMMARY.md b/SUMMARY.md index 27b9a01c..fe12b483 100644 --- a/SUMMARY.md +++ b/SUMMARY.md @@ -99,10 +99,12 @@ - [Week 3](courses/frontend/advanced-javascript/week3/README.md) - [Preparation](courses/frontend/advanced-javascript/week3/preparation.md) - [Session Plan](courses/frontend/advanced-javascript/week3/session-plan.md) + - [Exercises](courses/frontend/advanced-javascript/week3/session-materials/exercises.md) - [Assignment](courses/frontend/advanced-javascript/week3/assignment.md) - [Week 4](courses/frontend/advanced-javascript/week4/README.md) - [Preparation](courses/frontend/advanced-javascript/week4/preparation.md) - [Session Plan](courses/frontend/advanced-javascript/week4/session-plan.md) + - [Exercises](courses/frontend/advanced-javascript/week4/session-materials/exercises.md) - [Assignment](courses/frontend/advanced-javascript/week4/assignment.md) - [React](courses/frontend/react/README.md) - [Week 1](courses/frontend/react/week1/README.md) diff --git a/courses/frontend/advanced-javascript/README.md b/courses/frontend/advanced-javascript/README.md index 95241b80..d3847af8 100644 --- a/courses/frontend/advanced-javascript/README.md +++ b/courses/frontend/advanced-javascript/README.md @@ -11,7 +11,7 @@ In this module, you will advance your JavaScript expertise to build interactive | 1. | [Array functions & Arrow functions](./week1/README.md) | [Preparation](./week1/preparation.md) | [Session Plan](./week1/session-plan.md) (for mentors) | [Assignment](./week1/assignment.md) | | 2. | [Callback functions & Asynchronous code](./week2/README.md) | [Preparation](./week2/preparation.md) | [Session Plan](./week2/session-plan.md) (for mentors) | [Assignment](./week2/assignment.md) | | 3. | [Promises & `async`/`await`](./week3/README.md) | [Preparation](./week3/preparation.md) | [Session Plan](./week3/session-plan.md) (for mentors) | [Assignment](./week3/assignment.md) | -| 4. | [Classes & Advanced Promises](./week4/README.md) | [Preparation](./week4/preparation.md) | [Session Plan](./week4/session-plan.md) (for mentors) | [Assignment](./week4/assignment.md) | +| 4. | [Classes & Object-Oriented Programming](./week4/README.md) | [Preparation](./week4/preparation.md) | [Session Plan](./week4/session-plan.md) (for mentors) | [Assignment](./week4/assignment.md) | ## Module Learning Goals diff --git a/courses/frontend/advanced-javascript/week2/assignment.md b/courses/frontend/advanced-javascript/week2/assignment.md index 0b0eed8e..836c04ca 100644 --- a/courses/frontend/advanced-javascript/week2/assignment.md +++ b/courses/frontend/advanced-javascript/week2/assignment.md @@ -11,25 +11,25 @@ The warmup is a **little abstract**, it will get more concrete later on! 1. Display the text `Called after 2.5 seconds` on the page 2.5 seconds after the script is loaded. 2. Create a function that takes 2 parameters: `delay` and `stringToLog`. Calling this function should display the `stringToLog` on the page after `delay` seconds. Call the function you have created with some different arguments. - ![second task](session-materials/carbon.png) + ![second task](./session-materials/carbon.png) 3. Create a button in html. When clicking this button, use the function you created in the previous task to display the text `Called after 5 seconds` on the page 5 seconds after the button is clicked. -![second task](session-materials/button-delay.gif) +![second task](./session-materials/button-delay.gif) 4. Create two functions and assign them to two different variables. One function displays `Earth` on the page, the other displays `Saturn`. Now create a new third function that has one parameter: `planetLogFunction`. The only thing the third function should do is call the provided parameter function. Try calling the third function once with the `Earth` function and once with the `Saturn` function. -![second task](session-materials/planet-log.png) +![second task](./session-materials/planet-log.png) 5. Create a button with the text "Log location". When this button is clicked, display the user's location (latitude, longitude) on the page using this [browser API](https://developer.mozilla.org/en-US/docs/Web/API/Geolocation_API). -![second task](session-materials/log-location.gif) +![second task](./session-materials/log-location.gif) 6. _Optional_ Now show that location on a map using e.g. the [Google maps api](https://developers.google.com/maps/documentation/javascript/tutorial) 7. Create a function called `runAfterDelay`. It has two parameters: `delay` and `callback`. When called the function should wait `delay` seconds and then call the provided callback function. Add an input in the HTML for the delay (in seconds) and a button; when the button is clicked, read the delay from the input and call `runAfterDelay` with that delay and a callback that displays something on the page. -![second task](session-materials/run-after-delay.png) +![second task](./session-materials/run-after-delay.png) 8. Check if the user has double-clicked on the page. A double click is two clicks within 0.5 seconds. If a double click is detected, display the text "double click!" on the page. @@ -57,7 +57,7 @@ A user specifies how long time the game should be, and presses **"start game!"** Here is a gif of how the site should work: -![session material](session-materials/fastest-clicker.gif) +![Fastest presser game demo](./session-materials/fastest-clicker.gif) You can implement it exactly like you want to, but here is my recommended order: diff --git a/courses/frontend/advanced-javascript/week3/README.md b/courses/frontend/advanced-javascript/week3/README.md index 430dbb24..3ffd5ab9 100644 --- a/courses/frontend/advanced-javascript/week3/README.md +++ b/courses/frontend/advanced-javascript/week3/README.md @@ -6,6 +6,7 @@ In this session, you'll learn how to write asynchronous code that is both effici - [Preparation](./preparation.md) - [Session Plan](./session-plan.md) (for mentors) +- [Exercises](./session-materials/exercises.md) - [Assignment](./assignment.md) ## Session Learning Goals diff --git a/courses/frontend/advanced-javascript/week3/assignment.md b/courses/frontend/advanced-javascript/week3/assignment.md index 84430345..d8948746 100644 --- a/courses/frontend/advanced-javascript/week3/assignment.md +++ b/courses/frontend/advanced-javascript/week3/assignment.md @@ -1,11 +1,13 @@ # Assignment -The assignment for this week is to build a currency calculator using [this API](https://open.er-api.com/v6/latest/USD) +The assignment for this week is to build a currency calculator using [this API](https://open.er-api.com/v6/latest/USD). + +Deliverable: a small browser application, so the user can interact with it and see the converted amount on the page. ## Technical specifications -1. Make a request to the API and store the Exchange rates as well as a list of currencies for the dropdowns. -2. User can enter an amount -3. User can choose a currency to convert from(default should be EUR) -4. User can choose a currency to convert to(Default should be DKK) -5. Whenever amount, currency from or currency to changes we show what the amount translates to in the to currency +1. Make a request to the API and use the response to obtain exchange rates and to populate the currency dropdowns. +2. The user can enter an amount. +3. The user can choose a currency to convert from (default: EUR). +4. The user can choose a currency to convert to (default: DKK). +5. When the amount, the "from" currency, or the "to" currency changes, show the equivalent amount in the "to" currency. diff --git a/courses/frontend/advanced-javascript/week3/session-materials/Promises.pdf b/courses/frontend/advanced-javascript/week3/session-materials/Promises.pdf new file mode 100644 index 00000000..852575db Binary files /dev/null and b/courses/frontend/advanced-javascript/week3/session-materials/Promises.pdf differ diff --git a/courses/frontend/advanced-javascript/week3/session-materials/console-order.md b/courses/frontend/advanced-javascript/week3/session-materials/console-order.md new file mode 100644 index 00000000..9344678f --- /dev/null +++ b/courses/frontend/advanced-javascript/week3/session-materials/console-order.md @@ -0,0 +1,139 @@ +# Promise chaining – what is logged? + +Use these in class: show the code, ask “What will appear in the console, and in what order?”, then run it and compare. + +--- + +## Task 1 — basic: sync vs `.then` + +```js +console.log("A"); + +Promise.resolve().then(() => { + console.log("B"); +}); + +console.log("C"); +``` + +
+Answer + +Order: A, C, B + +Synchronous code runs first (A, then C). Callbacks passed to `.then` are scheduled as microtasks and run after the current script finishes, so B appears last. + +
+ +--- + +## Task 2 — values through the chain + +```js +Promise.resolve(1) + .then((x) => { + console.log(x); + return x + 1; + }) + .then((y) => { + console.log(y); + }); +``` + +
+Answer + +Logs: `1` then `2` + +Each `.then` receives the value returned by the previous handler. Returning a plain value wraps it in a resolved promise for the next step. + +
+ +--- + +## Task 3 — returning a Promise (flattening) + +```js +Promise.resolve("go") + .then((s) => { + console.log("a:", s); + return Promise.resolve("step"); + }) + .then((t) => { + console.log("b:", t); + }); +``` + +
+Answer + +Logs: `a: go` then `b: step` + +When a handler returns a Promise, the chain waits for it and passes its settled value to the next `.then` (the inner Promise is “flattened”). + +
+ +--- + +## Task 4 — rejection, skipped handlers, `.catch`, recovery + +```js +Promise.resolve() + .then(() => { + console.log("1"); + throw new Error("oops"); + }) + .then(() => { + console.log("2"); + }) + .catch(() => { + console.log("3"); + }) + .then(() => { + console.log("4"); + }); +``` + +
+Answer + +Logs: `1`, `3`, `4` + +The error skips the next `.then` (so `2` never runs). `.catch` handles the rejection; a successful `catch` returns a fulfilled promise, so the following `.then` still runs (`4`). + +
+ +--- + +## Task 5 — multiple `.catch` and `.then` in one chain + +```js +Promise.resolve() + .then(() => { + console.log("1"); + throw "first-error"; + }) + .catch((err) => { + console.log("catch-A", err); + return "recovered"; + }) + .then((value) => { + console.log("2", value); + throw "second-error"; + }) + .catch((err) => { + console.log("catch-B", err); + }) + .then(() => { + console.log("3"); + }); +``` + +
+Answer + +Logs: `1`, `catch-A first-error`, `2 recovered`, `catch-B second-error`, `3` + +The first `throw` is handled by `catch-A`, which returns `"recovered"`, so the chain continues fulfilled and `2` runs with that value. The next `throw` is handled by `catch-B`; a successful `catch` still yields a fulfilled promise, so the final `.then` runs (`3`). The second `.catch` never sees `first-error` because `catch-A` already handled it. + +
diff --git a/courses/frontend/advanced-javascript/week3/session-materials/demo/README.md b/courses/frontend/advanced-javascript/week3/session-materials/demo/README.md new file mode 100644 index 00000000..57b27aec --- /dev/null +++ b/courses/frontend/advanced-javascript/week3/session-materials/demo/README.md @@ -0,0 +1,45 @@ +# Mentors demo – Promises & `async`/`await` + +In-session live coding for **Week 3** (Advanced JavaScript). The demo walks through `fetch` with **JSONPlaceholder**, `async`/`await`, consuming promises with `.then()` / `.catch()`, creating promises with `new Promise`, `try` / `catch` with async code, `Promise.all`, and an optional promise microtask loop. You implement the worksheet during class; the solution file is the finished version. + +--- + +## Files in this folder + +| File | Purpose | +| --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| **index.js** | Worksheet: section banners, `// Task:` lines, and `// Next:` hints. Only `getUser` and `promiseLoop` are declared; you add the rest while teaching. Use this file when leading the session. | +| **index-solution.js** | Full implementation: `showOutput`, `getUser`, promise consumption, timed and pizza promises, `try` / `catch` fetch, `Promise.all`, and `promiseLoop`. | +| **index.html** | Minimal page that loads `index.js`. Add markup (e.g. `
`) when you want on-page output; the solution’s `showOutput` writes to `#out`.                                          |
+| **style.css**         | Basic layout and styles for `#out` (and `main` if you use it).                                                                                                                              |
+
+---
+
+## Where to find tasks and how they are marked
+
+Everything lives in **index.js**. Search for `// ==========` for section breaks, `// Task:` for what to build, and `// Next:` for suggested links to the trainee exercises.
+
+---
+
+## How the code works
+
+### URLs (JSONPlaceholder)
+
+- **`USER_URL`** – `https://jsonplaceholder.typicode.com/users/1`
+- **`POST_URL`** – `https://jsonplaceholder.typicode.com/posts/1`
+- **`TODO_URL`** – `https://jsonplaceholder.typicode.com/todos/1` (optional extra)
+
+### Solution-only helpers and functions
+
+- **`showOutput(text)`** – Sets `textContent` on `#out` when that element exists.
+- **`getUser()`** – `async` `fetch` of **`USER_URL`**, then `.json()`, then `showOutput` with stringified user data.
+- **`loadOneResourceWithThen()`** – Same resource with `.then` / `.catch` only (no `async`/`await`).
+- **`oneSecondMessage()`** – Promise that resolves after one second, then shows `"It worked"`.
+- **`demoOrderPizza()`** – Delayed resolve or reject, then shows pizza or error text.
+- **`getUserWithTryCatch()`** – Same fetch pattern as `getUser` with `try` / `catch` and errors on the page.
+- **`demoPromiseAll()`** – Fetches **`USER_URL`** and **`POST_URL`** in parallel, then shows a short two-line summary.
+- **`promiseLoop()`** – Schedules endless microtasks (illustration only; can freeze the tab if called).
+
+### Callback hell
+
+Not implemented in these files; use the session plan (e.g. npm `q` or your own example on the board).
diff --git a/courses/frontend/advanced-javascript/week3/session-materials/demo/index-solution.js b/courses/frontend/advanced-javascript/week3/session-materials/demo/index-solution.js
new file mode 100644
index 00000000..cd5cedab
--- /dev/null
+++ b/courses/frontend/advanced-javascript/week3/session-materials/demo/index-solution.js
@@ -0,0 +1,151 @@
+// Week 3 demo – Promises & async/await (solution)
+
+// JSONPlaceholder only (reliable in the browser). Session plan may mention Open Notify —
+// same async ideas, different URL.
+const USER_URL = "https://jsonplaceholder.typicode.com/users/1";
+const POST_URL = "https://jsonplaceholder.typicode.com/posts/1";
+const TODO_URL = "https://jsonplaceholder.typicode.com/todos/1";
+
+function showOutput(text) {
+  const el = document.getElementById("out");
+  if (el) {
+    el.textContent = text;
+  }
+}
+
+// =============================================================================
+// Async/await – simple usage
+// =============================================================================
+// Task: Load USER_URL with async/await
+
+async function getUser() {
+  const response = await fetch(USER_URL);
+  const user = await response.json();
+  showOutput(JSON.stringify(user, null, 2));
+}
+
+// Next: Exercise 1
+
+// =============================================================================
+// Why use Promises? :: Callback Hell
+// =============================================================================
+// Show Callback Hell example in https://www.npmjs.com/package/q
+
+// =============================================================================
+// Promise consumption
+// =============================================================================
+// Task: Load one of the resources (e.g. USER_URL); show success or error on the page using .then / .catch only.
+
+function loadOneResourceWithThen() {
+  showOutput("Loading…");
+  fetch(USER_URL)
+    .then((response) => response.json())
+    .then((data) => {
+      showOutput(JSON.stringify(data, null, 2));
+    })
+    .catch((error) => {
+      showOutput(String(error));
+    });
+}
+
+// Next: Chaining examples
+// Next: Exercise 2
+
+// =============================================================================
+// Promise creation
+// =============================================================================
+// Task: Create a Promise that resolves after 1 second and shows "It worked" on the page.
+// Task: Create demoOrderPizza: a pizza-order Promise — after a 'baking' delay it either resolves with a pizza you can eat (show that on the page) or rejects if baking failed (show the failure on the page).
+
+function oneSecondMessage() {
+  showOutput("…");
+  const oneSecondTimeoutPromise = new Promise((resolve) => {
+    setTimeout(() => {
+      resolve();
+    }, 1000);
+  });
+
+  oneSecondTimeoutPromise.then(() => {
+    showOutput("It worked");
+  });
+}
+
+function demoOrderPizza() {
+  showOutput("Baking… (3s for demo)");
+  const pizzaMakingTime = 3000;
+  const didPizzaBakingSucceed = true;
+  const pizza = "Macaroni pizza";
+
+  const orderPizzaPromise = new Promise((resolve, reject) => {
+    setTimeout(() => {
+      if (didPizzaBakingSucceed) {
+        resolve(pizza);
+      } else {
+        reject("The pizza was a mess");
+      }
+    }, pizzaMakingTime);
+  });
+
+  orderPizzaPromise
+    .then((p) => {
+      showOutput(`Let's eat the ${p}`);
+    })
+    .catch((error) => {
+      showOutput(`Let's eat nothing: ${error}`);
+    });
+}
+
+// Next: Exercise 3
+// Next: Exercise 4
+
+// =============================================================================
+// Back to async/await (try / catch)
+// =============================================================================
+// Task: improve getUser to use try/catch to handle errors and show the error on the page.
+
+async function getUserWithTryCatch() {
+  try {
+    const response = await fetch(USER_URL);
+    const user = await response.json();
+    showOutput(JSON.stringify(user, null, 2));
+  } catch (err) {
+    showOutput(String(err));
+  }
+}
+
+// Next: Exercise 5
+
+// =============================================================================
+// Promise.all
+// =============================================================================
+
+async function demoPromiseAll() {
+  showOutput("Loading both…");
+  try {
+    const [userRes, postRes] = await Promise.all([
+      fetch(USER_URL),
+      fetch(POST_URL),
+    ]);
+    const [user, post] = await Promise.all([userRes.json(), postRes.json()]);
+    const summary = [
+      "User: " + user.name + " (" + user.email + ")",
+      "Post: " + post.title,
+    ].join("\n");
+    showOutput(summary);
+  } catch (e) {
+    showOutput(String(e));
+  }
+}
+
+// Next: Exercise 6
+
+// =============================================================================
+// (Optional) Infinite loop via Promises
+// =============================================================================
+
+function promiseLoop() {
+  return Promise.resolve().then(() => {
+    console.log("tick");
+    return promiseLoop();
+  });
+}
diff --git a/courses/frontend/advanced-javascript/week3/session-materials/demo/index.html b/courses/frontend/advanced-javascript/week3/session-materials/demo/index.html
new file mode 100644
index 00000000..cae179b8
--- /dev/null
+++ b/courses/frontend/advanced-javascript/week3/session-materials/demo/index.html
@@ -0,0 +1,12 @@
+
+
+  
+    
+    
+    Demo – Week 3
+    
+  
+  
+    
+  
+
diff --git a/courses/frontend/advanced-javascript/week3/session-materials/demo/index.js b/courses/frontend/advanced-javascript/week3/session-materials/demo/index.js
new file mode 100644
index 00000000..be2447e4
--- /dev/null
+++ b/courses/frontend/advanced-javascript/week3/session-materials/demo/index.js
@@ -0,0 +1,62 @@
+// Week 3 demo – Promises & async/await (worksheet for class)
+
+// JSONPlaceholder only (reliable in the browser). Session plan may mention Open Notify —
+// same async ideas, different URL.
+const USER_URL = "https://jsonplaceholder.typicode.com/users/1";
+const POST_URL = "https://jsonplaceholder.typicode.com/posts/1";
+const TODO_URL = "https://jsonplaceholder.typicode.com/todos/1";
+
+// =============================================================================
+// Async/await – simple usage
+// =============================================================================
+// Task: Load USER_URL with async/await
+
+async function getUser() {}
+
+// Next: Exercise 1
+
+// =============================================================================
+// Why use Promises? :: Callback Hell
+// =============================================================================
+// Show Callback Hell example in https://www.npmjs.com/package/q
+
+// =============================================================================
+// Promise consumption
+// =============================================================================
+// Task: Load one of the resources (e.g. USER_URL); show success or error on the page using .then / .catch only.
+
+// Next: Chaining examples
+// Next: Exercise 2
+
+// =============================================================================
+// Promise creation
+// =============================================================================
+// Task: Create a Promise that resolves after 1 second and shows "It worked" on the page.
+// Task: Create demoOrderPizza: a pizza-order Promise — after a 'baking' delay it either resolves with a pizza you can eat (show that on the page) or rejects if baking failed (show the failure on the page).
+
+// Next: Exercise 3
+// Next: Exercise 4
+
+// =============================================================================
+// Back to async/await (try / catch)
+// =============================================================================
+// Task: improve getUser to use try/catch to handle errors and show the error on the page.
+
+// Next: Exercise 5
+
+// =============================================================================
+// Promise.all
+// =============================================================================
+
+// Next: Exercise 6
+
+// =============================================================================
+// (Optional) Infinite loop via Promises
+// =============================================================================
+
+function promiseLoop() {
+  return Promise.resolve().then(() => {
+    console.log("tick");
+    return promiseLoop();
+  });
+}
diff --git a/courses/frontend/advanced-javascript/week3/session-materials/demo/style.css b/courses/frontend/advanced-javascript/week3/session-materials/demo/style.css
new file mode 100644
index 00000000..ab055fd9
--- /dev/null
+++ b/courses/frontend/advanced-javascript/week3/session-materials/demo/style.css
@@ -0,0 +1,34 @@
+/* Week 3 demo – minimal */
+
+*,
+*::before,
+*::after {
+  box-sizing: border-box;
+}
+
+body {
+  margin: 0;
+  font-family: system-ui, sans-serif;
+  line-height: 1.5;
+}
+
+main {
+  max-width: 40rem;
+  margin: 0 auto;
+  padding: 1rem;
+}
+
+#out {
+  margin: 0.25rem 0 1rem;
+  padding: 0.5rem;
+  border: 1px solid #ccc;
+  font-family: ui-monospace, monospace;
+  font-size: 0.8rem;
+  white-space: pre-wrap;
+  word-break: break-word;
+  min-height: 1.25rem;
+}
+
+#out:empty {
+  display: none;
+}
diff --git a/courses/frontend/advanced-javascript/week3/session-materials/exercises.md b/courses/frontend/advanced-javascript/week3/session-materials/exercises.md
new file mode 100644
index 00000000..056292b2
--- /dev/null
+++ b/courses/frontend/advanced-javascript/week3/session-materials/exercises.md
@@ -0,0 +1,54 @@
+# Exercises
+
+Work through these in order.
+
+## Exercise 1
+
+Using async await
+
+1. `fetch` yes or no from this api: `https://yesno.wtf/api`. Show the answer on the page.
+
+## Exercise 2
+
+Using promises
+
+1. `fetch` yes or no from this api: `https://yesno.wtf/api`. Show the answer on the page.
+2. Try fetching a url that rejects e.g. `https://knajskdskj.jasdk`. Show the error message on the page.
+
+## Exercise 3
+
+1. Create a promise that resolves after 4 seconds. Use this promise to show the text `hello` on the page after 4 seconds.
+2. Now make the promise fail by rejecting it with an error message instead of resolving it, and show the error message on the page.
+
+## Exercise 4
+
+Create a function that returns a promise, that you can use like this:
+
+```js
+// YesNoFail4Seconds should wait 4 seconds before it does one of the following 3 things:
+// resolves with a yes
+// resolves with a no
+// or rejects
+// Look into Math.random()
+YesNoFail4Seconds()
+  .then((data) => {
+    // Show on the page: The answer is ${data}
+  })
+  .catch((error) => {
+    // Show on the page: the error
+  });
+```
+
+The above example show how to consume the promise using promises. Now try consume the `YesNoFail4Seconds` using async/await
+
+## Exercise 5
+
+Using async await
+
+1. Fetch a user from JSONPlaceholder (for example `https://jsonplaceholder.typicode.com/users/1`)
+2. After that succeeds, fetch movies using [this api](https://gist.githubusercontent.com/pankaj28843/08f397fcea7c760a99206bcb0ae8d0a4/raw/02d8bc9ec9a73e463b13c44df77a87255def5ab9/movies.json)
+3. Show the movies on the page
+
+## Exercise 6
+
+Get the JSONPlaceholder user and the movies at the same time. Show the movies and the battery status on the page when the related promises have resolved.
diff --git a/courses/frontend/advanced-javascript/week3/session-plan.md b/courses/frontend/advanced-javascript/week3/session-plan.md
index 8af8c832..50b95edf 100644
--- a/courses/frontend/advanced-javascript/week3/session-plan.md
+++ b/courses/frontend/advanced-javascript/week3/session-plan.md
@@ -7,6 +7,8 @@
 These are some examples of previously created materials by mentors that you can use yourself, or for inspiration.
 
 - [Notion Page Handout](https://dandy-birth-1b2.notion.site/HYF-Aarhus-JS-3-Week-2-0287dd1293df4a0a92171e62ce12f5c8?pvs=4) (by [Thomas](https://github.com/te-online))
+- [Demo](./session-materials/demo/) – In-session live coding from **Code inspiration** below (not the trainee exercises). **index.js** = worksheet stubs; **index-solution.js** = reference. [README](./session-materials/demo/README.md).
+- [Promises chaining diagram (PDF)](./session-materials/Promises.pdf) – Hand-drawn sketch you can project or redraw on the board when explaining how `.then()` chains.
 
 ## Session Outline
 
@@ -20,78 +22,30 @@ First when they fully understand one part of promises, I move on! Don't over-com
   - Quickly recap asynchronicity
     - Ask the trainees what it means that some code is asynchronous
   - Practical example of async/await
-  - [Exercises 1](#exercise-1)
+  - [Exercises 1](./session-materials/exercises.md#exercise-1)
 - Promise
   - Why do we use promises?
     - So important to explain this, the trainees always ask this! [Is there specific functionality that can only be done with promises in JS?](https://stackoverflow.com/questions/39004567/why-do-we-need-promise-in-js)
   - Consumption
     - [Code inspiration](#promise-consumption)
     - Example, call some function that returns a promise (like fetch)
-    - [Exercises 2](#exercise-2)
+    - [Exercises 2](./session-materials/exercises.md#exercise-2)
   - Creation
     - [Code inspiration](#promise-creation)
-    - [Exercises 3](#exercise-3) and then [Exercises 4](#exercise-4)
+    - [Exercises 3](./session-materials/exercises.md#exercise-3) and then [Exercises 4](./session-materials/exercises.md#exercise-4)
   - Async await
-    - [Exercises 5](#exercise-5)
+    - [Exercises 5](./session-materials/exercises.md#exercise-5)
   - `Promise.all` - Let trainees investigate
   - Optional - Chaining. Calling `.then` returns a promise. Only get to here when they understand async/await and promise consumption and creation.
+    - I found that drawing/demoing how it works under the hood useful when explaining how promises feed into the next `.then()`. You can find example in Session matherials.
     - [Reason for promise](https://mobile.twitter.com/addyosmani/status/1097035418657144832?s=19)
-  - [Exercises 5](#exercise-5) and [Exercises 6](#exercise-6)
+  - [Exercises 5](./session-materials/exercises.md#exercise-5) and [Exercises 6](./session-materials/exercises.md#exercise-6)
 
 ## Exercises
 
-
+See [Exercises](./session-materials/exercises.md). Trainees show results on the page (update the DOM), not in the console.
 
-### Exercise 1
-
-Using async await
-
-1. `fetch` yes or no from this api: `https://yesno.wtf/api`. log out the answer
-
-### Exercise 2
-
-Using promises
-
-1. `fetch` yes or no from this api: `https://yesno.wtf/api`. log out the answer
-2. Try fetching a url that rejects e.g. `https://knajskdskj.jasdk`. Log out the error message
-
-### Exercise 3
-
-1. Create a promise that resolves after 4 seconds. Use this promise to log out the text 'hello' after 4 seconds.
-2. Now make the promise fail by rejecting it with an error message instead of resolving it, and log the error message to the console.
-
-### Exercise 4
-
-Create a function that returns a promise, that you can use like this:
-
-```js
-// YesNoFail4Seconds should wait 4 seconds before it does one of the following 3 things:
-// resolves with a yes
-// resolves with a no
-// or rejects
-// Look into Math.random()
-YesNoFail4Seconds()
-  .then((data) => {
-    console.log(`The answer is ${data}`);
-  })
-  .catch((error) => {
-    console.log(error);
-  });
-```
-
-The above example show how to consume the promise using promises. Now try consume the `YesNoFail4Seconds` using async/await
-
-### Exercise 5
-
-Using async await
-
-1. Fetch the astronauts
-2. After the astronauts has been fetched, fetch movies using [this api](https://gist.githubusercontent.com/pankaj28843/08f397fcea7c760a99206bcb0ae8d0a4/raw/02d8bc9ec9a73e463b13c44df77a87255def5ab9/movies.json)
-3. Log out the movies
-
-### Exercise 6
-
-Get the astronauts and the movies at the same time. Log out the movies and the battery status when both promises has been resolved.
+[Console order](./session-materials/console-order.md) – Optional “what is logged?” promise chaining.
 
 ## Code inspiration
 
@@ -108,22 +62,21 @@ Get the astronauts and the movies at the same time. Log out the movies and the b
 // await waits until we have fetched the data from the api. Or said in another way await waits until fetch has resolved with the data from the api
 
 // write async before a function for await to work. What does it mean that something is asynchronous?
-async function getAstronauts() {
+// JSONPlaceholder works reliably in the browser (same idea as Open Notify / astronauts, different URL).
+async function getJsonPlaceholderUser() {
   // await waits until we have data from fetch before it runs the next line. No need for callbacks 🤯
   console.log("Before we fetch data");
-  const astronautsResponse = await fetch(
-    "http://api.open-notify.org/astros.json",
-  );
+  const response = await fetch("https://jsonplaceholder.typicode.com/users/1");
   console.log(
     "This is logged out after some time, even though the code looks synchronous! 🤯",
   );
-  const astronauts = await astronautsResponse.json();
+  const user = await response.json();
   console.log("This is logged out after some time! 🤯");
-  console.log(astronauts);
-  return astronauts;
+  console.log(user);
+  return user;
 }
 
-getAstronauts();
+getJsonPlaceholderUser();
 ```
 
 ### Promise consumption
@@ -138,10 +91,10 @@ The trainees should be able to answer these questions:
 // How would you explain your mom what resolved and rejected means?
 
 ```js
-fetch("http://api.open-notify.org/astros.json")
-  .then((astronautsResponse) => astronautsResponse.json())
-  .then((astronauts) => {
-    console.log(astronauts);
+fetch("https://jsonplaceholder.typicode.com/users/1")
+  .then((response) => response.json())
+  .then((user) => {
+    console.log(user);
   })
   .catch((error) => console.log(error));
 
@@ -215,19 +168,19 @@ console.log(test());
 So writing `async` in front of a function makes it return a promise! The keyword `await` makes JavaScript wait until that promise resolved and returns its result.
 
 ```js
-async function getAstronauts() {
+async function getJsonPlaceholderUserSafe() {
   try {
-    const astronautsResponse = await fetch(
-      "http://api.open-notify.org/astros.json",
+    const response = await fetch(
+      "https://jsonplaceholder.typicode.com/users/1",
     );
-    const astronauts = await astronautsResponse.json();
-    return astronauts;
+    const user = await response.json();
+    return user;
   } catch (err) {
-    throw "Fetching the astronauts went wrong";
+    throw "Fetching the user went wrong";
   }
 }
 
-const astronauts = getAstronauts();
+const userPromise = getJsonPlaceholderUserSafe();
 ```
 
 ### Function that returns a promise
diff --git a/courses/frontend/advanced-javascript/week4/README.md b/courses/frontend/advanced-javascript/week4/README.md
index 67b810bd..75d2dd93 100644
--- a/courses/frontend/advanced-javascript/week4/README.md
+++ b/courses/frontend/advanced-javascript/week4/README.md
@@ -1,4 +1,4 @@
-# Classes & Advanced Promises (Week 4)
+# Classes & Object-Oriented Programming (Week 4)
 
 In this session, you'll learn how to use JavaScript classes to create reusable templates for objects that share common properties and behaviors. By mastering classes and inheritance, you'll be able to organize your code more efficiently and implement object-oriented programming principles. These skills will help you write cleaner, more maintainable code and understand the differences between classes and objects in JavaScript.
 
@@ -6,13 +6,14 @@ In this session, you'll learn how to use JavaScript classes to create reusable t
 
 - [Preparation](./preparation.md)
 - [Session Plan](./session-plan.md) (for mentors)
+- [Exercises](./session-materials/exercises.md)
 - [Assignment](./assignment.md)
 
 ## Session Learning Goals
 
 By the end of this session, you will be able to:
 
-- [ ] Use **classes** to easily create similar objects.
+- [ ] Use **classes** to create objects with consistent structure and built-in behavior
   - [ ] Declare a class using `class`, `constructor`, and `this`
   - [ ] Instantiate objects from classes using `new`
   - [ ] Use Methods and constructors
@@ -21,19 +22,30 @@ By the end of this session, you will be able to:
   - [ ] Understand the difference between classes vs objects
 
 ```js
-// Example of declaring a Person class
-class Person {
-  constructor(name, age) {
-    this.name = name;
-    this.age = age;
+class Comment {
+  constructor(username, text) {
+    this.username = username;
+    this.text = text;
+    this.likes = 0;
   }
 
-  greet() {
-    console.log(`Hi, I'm ${this.name}`);
+  like() {
+    this.likes++;
+    this.render();
+  }
+
+  render() {
+    // data, behavior, and rendering live together
+    const div = document.createElement("div");
+    div.innerHTML = `
+      @${this.username}
+      

${this.text}

+ + `; + return div; } } -// Example of an actual person created from the Person class -const alice = new Person("Alice", 25); -alice.greet(); +const comment = new Comment("alice", "Great post!"); +document.body.appendChild(comment.render()); ``` diff --git a/courses/frontend/advanced-javascript/week4/assignment.md b/courses/frontend/advanced-javascript/week4/assignment.md index 34b00ad3..1374fbd2 100644 --- a/courses/frontend/advanced-javascript/week4/assignment.md +++ b/courses/frontend/advanced-javascript/week4/assignment.md @@ -1,27 +1,34 @@ # Assignment -For this week's assignment we will create a web applications that generates a screenshot of a website based on a url. We will combine two API's one to generate the screenshot and one to allow the user to save the screenshot. +For this week's assignment we will create a web application that generates a screenshot of a website based on a URL. We will combine two APIs: one to generate the screenshot and one to allow the user to save the screenshot. We use [Rapid API](https://rapidapi.com/apishub/api/website-screenshot6/?utm_source=RapidAPI.com%2Fguides&utm_medium=DevRel&utm_campaign=DevRel) to generate a screenshot and the [crudcrud API](https://crudcrud.com/) to save the screenshot. -Technical specifications. +## Technical specifications 1. User can enter a URL for a website and it will send back a screenshot of the website using the website-screenshot API 2. User can hit a button to save the screenshot. It will then save the screenshot and the URL as a resource on crudcrud 3. User can get a list of all screenshots that they have saved 4. User can delete a screenshot that they have saved -## Optional Tasks/Assignments +### Class requirements + +1. **Model your UI as classes** with a `render()` method that puts content on the page. +2. **Build an error system** with custom error classes that `extend Error`, each with a way to show a user-friendly message (e.g. `toUserMessage()`). +3. **Handle errors** with `try/catch` and use `instanceof` to treat different error types differently. + +Look at your interface and think about what parts can be modeled as classes — if something has data and behavior that go together, or if it can be reused, make it a class. For example, you could create a `Screenshot` class that holds the URL and image data, knows how to render itself as a card on the page, and has a method for deleting itself from crudcrud. -1. Create another resource called users which takes in an email and password. Create one user. -2. Get back a list of users -3. First show a login form -4. If the email and password matches the one user we created we show the applications else we show an error message. +For the error system, think about what kinds of errors can happen in your app — what if the user submits an empty URL? What if the API returns a bad response? What if the network is down? You might end up with classes like `ValidationError`, `ApiError`, or something else entirely — it's up to you. + +## Optional Tasks/Assignments -Extra +> **Note:** Users do not need to be stored in a database or API — just keep them in memory (e.g. an array of instances in your JavaScript). No need to persist them anywhere. -1. Create another user -2. When saving a screenshot also save the user email(or another unique identifier) -3. Make sure we are only showing screenshots that the user that is logged in has uploaded +1. Create a user object with an email and password. Keep it in a variable or array. +2. Show a login form first. +3. If the email and password match the user you created, show the application. Otherwise show an error message. +4. Create another user. When saving a screenshot, also save the user email (or another unique identifier). +5. Make sure you only show screenshots that the logged-in user has uploaded. -Keep in mind the API key for the website-screenshot and the uuid for crudcrud should be in a secret.js file which is not committed to git +Keep in mind the API key for the website-screenshot and the uuid for crudcrud should be in a secret.js file which is not committed to git. diff --git a/courses/frontend/advanced-javascript/week4/session-materials/code-inspiration.md b/courses/frontend/advanced-javascript/week4/session-materials/code-inspiration.md new file mode 100644 index 00000000..4df83aab --- /dev/null +++ b/courses/frontend/advanced-javascript/week4/session-materials/code-inspiration.md @@ -0,0 +1,189 @@ +# Code inspiration + +Snippets aligned with the in-session [demo](./demo/README.md): same `Comment` shape (`username`, `text`), plain-object motivation, `render()` / `like()` / `hasSwearWord()`, then `Error` / `ValidationError` and a Web Components sketch. Use these on the board or as copy-paste shortcuts; the demo files are the full runnable version. + +## Motivation (plain objects) + +Why classes: repeated object literals are easy to get wrong; rendering and behavior live outside the data. + +```js +const comment1 = { + username: "alice", + text: "Nice!", + date: new Date(), + likes: 0, +}; +const comment2 = { userName: "bob", text: "Hi" }; // typo: userName → UI shows undefined +const comment3 = { username: "carol", content: "Oops" }; // wrong key for text + +function renderComment(comment) { + const div = document.createElement("div"); + div.className = "comment"; + div.innerHTML = ` +

@${comment.username}

+

${comment.text}

+ + `; + return div; +} + +function likeComment(comment) { + comment.likes++; // data changes, but the button on screen does not update +} +``` + +## Constructor + +```js +class Comment { + constructor(username, text) { + this.username = username; + this.text = text; + this.date = new Date(); + this.likes = 0; + this.element = null; + } +} +``` + +## Instance + +```js +const c1 = new Comment("dave_dev", "Hello!"); +const c2 = new Comment("eve_codes", "Second comment"); + +console.log(c1); +``` + +## Methods + +`render()` keeps DOM in sync with state; `like()` updates and re-renders; `hasSwearWord()` drives a CSS class (e.g. `comment--flagged`) for moderation-style UI. + +```js +class Comment { + constructor(username, text) { + this.username = username; + this.text = text; + this.date = new Date(); + this.likes = 0; + this.element = null; + } + + like() { + this.likes++; + this.render(); + } + + hasSwearWord() { + const swearWords = ["crap", "damn", "stupid"]; + const words = this.text.toLowerCase().split(" "); + return swearWords.some((swear) => words.includes(swear)); + } + + render() { + if (!this.element) { + this.element = document.createElement("div"); + } + + this.element.className = this.hasSwearWord() + ? "comment comment--flagged" + : "comment"; + + this.element.innerHTML = ` +
+ @${this.username} + ${this.date.toLocaleDateString()} +
+

${this.text}

+ + `; + + this.element + .querySelector(".like-btn") + .addEventListener("click", () => this.like()); + + return this.element; + } +} + +document.getElementById("comments-class").appendChild(c1.render()); +``` + +## Static methods + +A **static method** belongs to the class itself, not to instances. Useful for factory functions (creating instances from raw data) or utility operations. + +```js +class Comment { + constructor(username, text) { + this.username = username; + this.text = text; + this.date = new Date(); + this.likes = 0; + } + + static fromJSON(data) { + return new Comment(data.username, data.text); + } + + // ... render(), like(), etc. +} + +// Called on the class, not on an instance: +const apiData = { username: "grace_api", text: "Loaded from JSON!" }; +const comment = Comment.fromJSON(apiData); +``` + +**You already use static methods — `Promise` is a class!** + +```js +// Instance methods (called on an instance): +const p = new Promise((resolve) => resolve("done")); +p.then((value) => console.log(value)); +p.catch((err) => console.log(err)); + +// Static methods (called on the class itself): +Promise.resolve("instant value"); +Promise.all([fetch("/a"), fetch("/b")]); +Promise.race([fetch("/a"), fetch("/b")]); +``` + +## (Optional) Extending built-ins: Error and Web Components + +`Error` is a built-in class; custom errors use `extends` and `super()` like any other subclass. Web Components apply the same “class + lifecycle + HTML” idea to the platform. + +```js +const err = new Error("something went wrong"); +console.log(err.message); +console.log(err.stack); + +class ValidationError extends Error { + constructor(field, message) { + super(message); + this.name = "ValidationError"; + this.field = field; + } + + toUserMessage() { + return `❌ ${this.field}: ${this.message}`; + } +} + +try { + throw new ValidationError("username", "Cannot be empty"); +} catch (error) { + console.log(error.toUserMessage()); + console.log(error instanceof ValidationError); + console.log(error instanceof Error); +} + +// Web Components — same pattern, browser APIs (not required to run in the demo): +// +// class CommentElement extends HTMLElement { +// connectedCallback() { +// this.innerHTML = `
...
`; +// } +// } +// customElements.define("my-comment", CommentElement); +// +``` diff --git a/courses/frontend/advanced-javascript/week4/session-materials/demo/README.md b/courses/frontend/advanced-javascript/week4/session-materials/demo/README.md new file mode 100644 index 00000000..77da1a73 --- /dev/null +++ b/courses/frontend/advanced-javascript/week4/session-materials/demo/README.md @@ -0,0 +1,63 @@ +# Mentors demo – Classes + +In-session live coding for **Week 4** (Advanced JavaScript). The page shows a **comment section** twice: first with plain objects (motivation: inconsistent keys, disconnected rendering), then with a **`Comment` class** (constructor, instances, `render()`, `like()`, `hasSwearWord()` and flagged styling). Part 4 walks through **built-in `Error`**, a **`ValidationError extends Error`** example, and a short **Web Components** comparison (comments only — not implemented in the browser). Implement the worksheet during class; the solution file is the finished version. + +--- + +## Files in this folder + +| File | Purpose | +| --------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| **index.js** | Worksheet: Part 1 runs on load; Parts 2–4 use `// Task:` lines, `// Next: Exercise N` pauses, and `// ==========` section banners. Use this file when leading the session. | +| **index-solution.js** | Full implementation: `Comment` with `render` / `like` / `hasSwearWord`, DOM append, `ValidationError` + `try` / `catch`, and the Web Components sketch as comments. | +| **index.html** | Two sections and containers for plain vs class comments; loads **index.js**. To preview the solution, change the ` + + diff --git a/courses/frontend/advanced-javascript/week4/session-materials/demo/index.js b/courses/frontend/advanced-javascript/week4/session-materials/demo/index.js new file mode 100644 index 00000000..4fdd8ff7 --- /dev/null +++ b/courses/frontend/advanced-javascript/week4/session-materials/demo/index.js @@ -0,0 +1,119 @@ +// ============================================================================= +// PART 1 — MOTIVATION: Comment section with plain objects +// ============================================================================= + +const comment1 = { + username: "alice_dev", + text: "Great post! Really enjoyed reading this.", + date: new Date("2025-03-15"), + likes: 3, +}; +const comment2 = { + userName: "bob_codes", + text: "Thanks for sharing, very helpful!", + date: new Date("2025-03-16"), + likes: 1, +}; +const comment3 = { + username: "carol_js", + content: "I have a question about the third paragraph.", + date: new Date("2025-03-17"), +}; + +const plainComments = [comment1, comment2, comment3]; + +function renderComment(comment) { + const div = document.createElement("div"); + div.className = "comment"; + div.innerHTML = ` +
+ @${comment.username} + ${comment.date.toLocaleDateString()} +
+

${comment.text}

+ + `; + return div; +} + +function likeComment(comment) { + comment.likes++; +} + +const plainContainer = document.getElementById("comments-plain"); + +plainComments.forEach(function (comment) { + plainContainer.appendChild(renderComment(comment)); +}); + +// ============================================================================= +// PART 2 — CONSTRUCTOR & INSTANCE +// ============================================================================= +// Task: Create a Comment class with constructor(username, text) +// It should also initialize: date = new Date(), likes = 0, element = null + +// Task: Create at least 3 instances using `new` and log them + +// Next: Exercise 1 +// Next: Exercise 2 + +// ============================================================================= +// PART 3 — METHODS +// ============================================================================= +// Task: Add a render() method that creates and returns a DOM element for the comment + +// Task: Add a like() method that increments likes and re-renders + +// Task: Add a hasSwearWord() method that checks the text against a list of banned words +// If true, render() should add a "comment--flagged" CSS class to highlight the comment + +// Task: Add a static method Comment.fromJSON(data) that takes a plain object +// (e.g. from an API response) and returns a new Comment instance + +// --- You already use static methods! --- +// Promise is a class. You've been using its static methods since Week 3: +// new Promise(...) — constructor (creates an instance) +// promise.then() — instance method (called on an instance) +// promise.catch() — instance method +// Promise.all([...]) — static method (called on the class itself) +// Promise.resolve("hello") — static method +// Promise.race([...]) — static method + +// Task: Display the class-based comments on the page (make sure at least one has a swear word!) +// const classContainer = document.getElementById("comments-class"); + +// Next: Exercise 3 + +// ============================================================================= +// PART 4 — CLASSES IN THE REAL WORLD +// ============================================================================= + +// --- Errors are classes! --- +// Error is a built-in class. You already use it: +// const err = new Error("something went wrong"); +// console.log(err.message); // constructor set this +// console.log(err.stack); // built-in method/property + +// Task: Create a ValidationError class that extends Error +// - constructor accepts (field, message), calls super(message), sets this.name and this.field +// - add a toUserMessage() method that returns a user-friendly string + +// Task: Try throwing and catching it — use instanceof to check the error type + +// --- Web Components use this exact pattern --- +// What we built (Comment class with render()) is very close to how Web Components work: +// +// class CommentElement extends HTMLElement { +// connectedCallback() { +// // called when the element appears on the page — similar to our render() +// this.innerHTML = `
...
`; +// } +// } +// customElements.define("my-comment", CommentElement); +// +// // Then in HTML: +// +// Same ideas: a class, extends a built-in, has lifecycle methods, renders HTML. +// Frameworks like React, Lit, and Angular all build on this mental model. + +// Next: Exercise 4 diff --git a/courses/frontend/advanced-javascript/week4/session-materials/demo/style.css b/courses/frontend/advanced-javascript/week4/session-materials/demo/style.css new file mode 100644 index 00000000..e572c553 --- /dev/null +++ b/courses/frontend/advanced-javascript/week4/session-materials/demo/style.css @@ -0,0 +1,130 @@ +/* ============================================================================= + Mentors demo – Classes + ============================================================================= */ + +* { + box-sizing: border-box; +} + +body { + font-family: "Segoe UI", system-ui, sans-serif; + margin: 0; + padding: 1.5rem 2rem; + background: #f8fafc; + color: #1e293b; +} + +h1 { + margin-top: 0; + font-size: 1.5rem; +} + +.intro { + color: #64748b; + margin-bottom: 2rem; + font-size: 0.95rem; +} + +/* ============================================================================= + Sections + ============================================================================= */ + +section { + margin-bottom: 2.5rem; +} + +section h2 { + font-size: 1.15rem; + color: #334155; + margin-bottom: 1rem; + padding-bottom: 0.5rem; + border-bottom: 2px solid #e2e8f0; +} + +/* ============================================================================= + Comment cards + ============================================================================= */ + +.comments { + display: flex; + flex-direction: column; + gap: 0.75rem; +} + +.comment { + background: #fff; + border-radius: 10px; + padding: 1rem 1.25rem; + box-shadow: 0 1px 4px rgba(0, 0, 0, 0.06); + border: 1px solid #e2e8f0; +} + +.comment-header { + display: flex; + align-items: center; + gap: 0.75rem; + margin-bottom: 0.5rem; +} + +.comment-username { + font-weight: 700; + color: #0f172a; + font-size: 0.95rem; +} + +.comment-date { + font-size: 0.8rem; + color: #94a3b8; +} + +.comment-text { + margin: 0 0 0.75rem; + font-size: 0.95rem; + line-height: 1.5; + color: #334155; +} + +/* ============================================================================= + Flagged comments (swear word detected) + ============================================================================= */ + +.comment--flagged { + border-color: #fca5a5; + background: #fef2f2; +} + +.comment--flagged .comment-text { + color: #991b1b; +} + +.comment--flagged .comment-header::after { + content: "⚠️ flagged"; + font-size: 0.75rem; + color: #dc2626; + font-weight: 600; + margin-left: auto; +} + +/* ============================================================================= + Like button + ============================================================================= */ + +.like-btn { + display: inline-flex; + align-items: center; + gap: 0.35rem; + padding: 0.3rem 0.75rem; + border: 1px solid #e2e8f0; + border-radius: 20px; + background: #fff; + font-size: 0.85rem; + cursor: pointer; + color: #64748b; + transition: all 0.15s ease; +} + +.like-btn:hover { + background: #fef2f2; + border-color: #fca5a5; + color: #ef4444; +} diff --git a/courses/frontend/advanced-javascript/week4/session-materials/exercises.md b/courses/frontend/advanced-javascript/week4/session-materials/exercises.md new file mode 100644 index 00000000..a375086f --- /dev/null +++ b/courses/frontend/advanced-javascript/week4/session-materials/exercises.md @@ -0,0 +1,89 @@ +# Exercises + +Work through these in order. + +## 1. Create a user class + +The class should have 2 properties: `firstName` and `lastName`. Hint: Use `this` and `constructor`. + +## 2. Create an instance of the class + +Use the `new` keyword and assign the instance in a variable. + +Add a **`renderUserCard(user)`** function that accepts a **`User`** instance and renders a user card on the page (e.g. a `div` with `firstName` and `lastName`). + +## 3. Create a class method + +1. Add **`getFullName`**: it should return the combined first and last name of the user. Use string concatenation or template literals and **`this`** to read the properties. + +2. Add **`render()`** on **`User`**: it should render the user card on the page (same job as **`renderUserCard(user)`** in exercise 2, but as an instance method). Use **`this.getFullName()`** for the name you show on the card. + +3. Call **`myUser.render()`** so the card appears on the page (you can stop using **`renderUserCard`** once this works). + +## 4. Creating a CV class + +The CV that we will be making uses three classes: `Job`, `Education` and +`CV`. The `CV` class we have made for you (with some missing functionality). The `Job` and `Education` classes you need to create. + +### Part 1 + +Create the classes `Job` and `Education`. + +- `Job` has five properties: `id`, `title`, `description`, `startDate` and `endDate` (the dates can be strings or actual `Date` objects). +- `Education` has six properties: `id`, `title`, `school`, `address`, `startDate` and `endDate`. + +```js +class Job { + ///... +} + +class Education { + ///... +} +``` + +### Part 2 + +Now add the functionality for the methods in the `CV` class. + +_Remember_: jobs and educations are just arrays of class instances. So use your array manipulation knowledge for the add and remove methods. + +```js +class CV { + constructor(email) { + this.jobs = []; + this.educations = []; + //this.email = ? + } + + addJob(job) { + // add functionality here + } + + removeJob(job) { + // add functionality here + } + + addEducation(education) { + // add functionality here + } + + removeEducation(education) { + // add functionality here + } +} +``` + +### Part 3 + +1. Create a new `CV` instance using the `new` keyword, and save it in a variable called `myCV`. + +2. Apply the methods you have created on the `myCV` object. Create a few `Job` and `Education` objects and add them to your CV. + +3. Remove a job and an education from `myCV`. + +4. Log `myCV` to the console, again, and check that the objects were removed correctly. + +### Part 4 + +Add a method to the `CV` class called `renderCV()`. This method should render out the CV using HTML. Make sure, that view updates, when data is changed. diff --git a/courses/frontend/advanced-javascript/week4/session-plan.md b/courses/frontend/advanced-javascript/week4/session-plan.md index 0b94a43f..977f9d03 100644 --- a/courses/frontend/advanced-javascript/week4/session-plan.md +++ b/courses/frontend/advanced-javascript/week4/session-plan.md @@ -2,12 +2,7 @@ ## Session Materials - - - +- [Demo](./session-materials/demo/) – In-session live coding: plain-object motivation, `Comment` class, methods, flagged comments, then Errors / Web Components as “real world” context. **index.js** = worksheet; **index-solution.js** = reference. [README](./session-materials/demo/README.md). ## Session Outline @@ -16,214 +11,25 @@ Start VERY simple. Just a class that has few fields, no methods. Explain the diff from object to class. Explain instance etc. When they get that move on to class methods. **Only teach extends if they really are on top of things** otherwise just get them comfortable with classes :) if you can repeat a bit of promise, maybe when working with class that would be great. - Constructor - - [Code inspiration](#constructor) - - [Exercise](#1-create-a-user-class) + - [Code inspiration](./session-materials/code-inspiration.md#constructor) + - [Exercise](./session-materials/exercises.md#1-create-a-user-class) - Instance - - [Code inspiration](#instance) - - [Exercise](#2-create-an-instance-of-the-class) -- Methods - - [Code inspiration](#methods) - - [Exercise](#3-create-a-class-method) + - [Code inspiration](./session-materials/code-inspiration.md#instance) + - [Exercise](./session-materials/exercises.md#2-create-an-instance-of-the-class) +- Methods (instance + static) + - [Code inspiration](./session-materials/code-inspiration.md#methods) + - [Code inspiration — static methods](./session-materials/code-inspiration.md#static-methods) (Promise as "you already use this") + - [Exercise](./session-materials/exercises.md#3-create-a-class-method) - `this` - Refers to the instance of the class. Do go into too much detail and edge cases. Avoid mentioning `bind`, `apply`, etc unless you find it super important, the trainees will just forget it anyway! -- [Exercise](#4-creating-a-cv-class) +- [Exercise](./session-materials/exercises.md#4-creating-a-cv-class) - Extend (Only if time!) - -### Constructor - -```js -class Comment { - constructor(username, content, time) { - this.username = username; - this.content = content; - this.time = time; - } -} -``` - -### Instance - -```js -const comment1 = new Comment("test", "post", new Date()); -``` - -### Methods - -```js -class Comment { - constructor(username, content, time) { - this.username = username; - this.content = content; - this.time = time; - } - - // Get help from trainees to write this method! - getTimeSincePost() { - return new Date().getTime() - this.time.getTime(); - } - - // Get help from trainees to write this method! - hasSwearWord() { - const swearWords = ["crap", "damn"]; - const postWords = this.content.split(" "); - const hasSwearWord = swearWords.find((swearWord) => - postWords.includes(swearWord), - ); - - return Boolean(hasSwearWord); - } -} - -const comment1 = new Comment("test", "post", new Date()); - -console.log(comment1.hasSwearWord()); -comment1.content = "shit crap"; -console.log(comment1.hasSwearWord()); -setTimeout(() => { - console.log(comment1.getTimeSincePost()); -}, 1000); - -// data -// username, content, time - -// functionality -// getTimeSincePost, hasSwearWord -``` - -### Class post - -```js -class Post { - // setup - constructor(username, content, postTime, likes, comments, shares) { - this.username = username; - this.content = content; - this.postTime = postTime; - this.likes = likes; - this.comments = comments; - this.shares = shares; - } - - addLike(username, time) { - const like = { - username: username, - time: time, - }; - - this.likes.push(like); - } - - addComment(username, content, time) { - this.comments.push(new Comment(username, content, time)); - } - - doShare() {} - - save() {} - - logThis() { - console.log(this.username); - } -} - -const post1 = new Post("benna100", "asd", "10/02/1019", [], [], []); -const post2 = new Post("habsdhjd", "asdajhdb", "10/02/1019", [], [], []); - -post1.addLike("bennaasdasd", "14:07"); -console.log(post1.likes); - -post1.addComment("ugg", "Great post", "14:16"); -console.log(post1.comments); - -post1.logThis(); -post2.logThis(); -``` + - [Code inspiration](./session-materials/code-inspiration.md#extending-built-ins-error-and-web-components) (`Error`, `ValidationError`, Web Components sketch — matches demo Part 4) ## Exercises - - -### 1. Create a user class - -The class should have 2 properties: firstName and lastName. Hint: Use `this` and `constructor`. - -### 2. Create an instance of the class - -Use the `new` keyword and assign the instance in a variable. - -- Try to log out the instance of the `User` to the console. -- Try to log out the users `firstName` - -### 3. Create a class method - -The method should be called `getFullName`, and should return the combined first name and last name of the user. Use string concatenation or template literals. Remember to use the `this` keyword to access the attributes on the class instance. - -Call the `getFullName` method and log the result to the console. - -### 4. Creating a CV class - -The CV that we will be making uses three classes: `Job`, `Education` and -`CV`. The `CV` class we have made for you (with some missing functionality). The `Job` and `Education` classes you need to create. - -#### Part 1 - -Create the classes `Job` and `Education`. - -- `Job` has five properties: `id`, `title`, `description`, `startDate` and `endDate` (the dates can be strings or actual `Date` objects). -- `Education` has six properties: `id`, `title`, `school`, `address`, `startDate` and `endDate`. - -```js -class Job { - ///... -} - -class Education { - ///... -} -``` - -#### Part 2 - -Now add the functionality for the methods in the `CV` class. - -_Remember_: jobs and educations are just arrays of class instances. So use your array manipulation knowledge for the add and remove methods. - -```js -class CV { - constructor(email) { - this.jobs = []; - this.educations = []; - //this.email = ? - } - - addJob(job) { - // add functionality here - } - - removeJob(job) { - // add functionality here - } - - addEducation(education) { - // add functionality here - } - - removeEducation(education) { - // add functionality here - } -} -``` - -#### Part 3 - -1. Create a new `CV` instance using the `new` keyword, and save it in a variable called `myCV`. - -2. Apply the methods you have created on the `myCV` object. Create a few `Job` and `Education` objects and add them to your CV. - -3. Remove a job and an education from `myCV`. - -4. Log `myCV` to the console, again, and check that the objects were removed correctly. +See the separate [Exercises](./session-materials/exercises.md) document. -#### Part 4 +## Code inspiration -Add a method to the `CV` class called `renderCV()`. This method should render out the CV using HTML. Use `document.getElementById("")` and `document.createElement("")`, as well as `element.appendChild()` to build your HTML using JavaScript. +See the separate [Code inspiration](./session-materials/code-inspiration.md) document.