Skip to content

Commit

Permalink
Merge branch 'main' into dwh-mentored-pair-programming
Browse files Browse the repository at this point in the history
  • Loading branch information
SallyMcGrath authored Oct 26, 2024
2 parents 1d2845c + 21a309f commit 87cea33
Show file tree
Hide file tree
Showing 38 changed files with 409 additions and 234 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
+++
title="Overview"
description="tl;dr How Our Curriculum Works"
emoji="🦌"
time=2
[objectives]
1="Explain the structure of our curriculum"
[build]
render = 'never'
list = 'local'
publishResources = false

+++

The course is divided into modules, each with a theme and learning objectives. Modules are divided into week-long sprints. Each sprint contains these activities in this order:

##### 1. Start with the: **🧑🏾‍💻 Prep**, which explains the main concepts of the sprint.

**Learners** complete prep before class. **Mentors** browse prep to know what learners are learning.

##### 2. Then go to: **🏷️ Backlog**, a list of coursework assignments as issues.

**Learners** clone issues to project boards and work on them. **Mentors** browse issues to know what learners are doing.

##### 3. For class it's the: **🧑🏾‍🤝‍🧑🏾 Day Plan**, a timestamped agenda for class day.

Usually a lively morning workshop and quieter afternoon study-group. **Everyone** should review the plan to prepare for class.

##### 4. Review with: **✅ Success**, the learning objectives for the sprint.

**Learners** check off goals. **Mentors** help us focus on the sprint goals.

### Still lost?

1. 🔍 Search: filter by module, sprint, and view.
1. 🦉 [Overview](/overview): your high level map with themes.
1. 📚 [How this works](/how-this-works): our programme in detail.
45 changes: 45 additions & 0 deletions common-content/en/module/js3/actually-re-rendering/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
+++
title = '🔁 Actually re-rendering'

time = 30
facilitation = false
emoji= '🔁'
[objectives]
1='Group UI components by whether they need to re-render'
2='Control which UI components are re-rendered'
[build]
render = 'never'
list = 'local'
publishResources = false

+++

We have seen that when we search, we're only _adding_ new elements, and not removing existing elements from the page.

We previously identified our strategy of clearing old elements before adding new ones. But we are not doing this.

We can clear out the existing children of an element by setting its `textContent` propery to the empty string:

```js
document.body.textContent = "";
```

Add this to your `render` function before you add new elements. Try using your page. Try searching for a particular film.

Oh no, our search box is gone!

{{<note type="exercise">}}
Work out why our search box is gone. Remember what we just changed, and what we were trying to do by making that change.
{{</note>}}

We removed our search box from the page because we removed everything from the entire document body.

This was not our intention - we only wanted to remove any films we had previously rendered.

A way to solve this is to introduce a container element which our `render` function will re-fill every time it's called.

We should identify which elements in our page should be re-drawn every time we render, and which should always be present.

Introduce a new container, and keep any "always present" UI elements outside of it. Update your `render` function to clear and append to the container, not the whole body.

Remember to use semantic HTML. Your container should be an appropriate tag for the contents it will have.
73 changes: 32 additions & 41 deletions common-content/en/module/js3/async-await/index.md
Original file line number Diff line number Diff line change
@@ -1,31 +1,50 @@
+++
title = '🍬 async/await'

time = 20
time = 30
facilitation = false
emoji= '🧩'
[objectives]
1='Define syntactic sugar'
2='Write a function using the async keyword'
2='Write a function using async/await'
3='Explain what happens when an async function is awaited'
[build]
render = 'never'
list = 'local'
publishResources = false

+++

```mermaid
graph LR
Promise{{🤝 Promises}} --> |syntax| async{{🏃‍♂️ async}}
async --> |syntax| await{{📭 await}}
await --> |resolves to| Response{{📤 Response}}
These two blocks of code do exactly the same thing:

```js
const getProfile = async (url) => {
const response = await fetch(url);
const data = await response.json();
const htmlUrl = data.html_url;
console.log(htmlUrl);
}

getProfile("https://api.github.com/users/SallyMcGrath");
```

Async/await is {{<tooltip title="syntactic sugar">}}A simpler, or "sweeter" way to write the same thing. The code works the same under the hood, but it's easier to read. {{</tooltip>}} for Promises. We group them together: async/await, because we {{<tooltip title="use them together. ">}}We can only use `await` inside an `async` function or at the [top level](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await#top_level_await) of a module.{{</tooltip>}}
```js
const getProfile = (url) => {
return fetch(url)
.then((response) => response.json())
.then((data) => data.html_url)
.then((htmlUrl) => console.log(htmlUrl));
};
getProfile("https://api.github.com/users/SallyMcGrath");
```

Async/await is {{<tooltip title="syntactic sugar">}}A simpler, or "sweeter" way to write the same thing. The code works the same under the hood, but it's easier to read. {{</tooltip>}} for Promises.

We group `async` and `await` together: async/await, because we {{<tooltip title="use them together. ">}}We can only use `await` inside an `async` function or at the [top level](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await#top_level_await) of a module.{{</tooltip>}}

We use the [`async`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function) [keyword](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Lexical_grammar#keywords) to define a function that returns a Promise. An async function always returns a Promise.

We can see this with a simple function which doesn't need to await anything:
We can see this with a simple function which doesn't need to await anything. Save this in a file and run it with node:

```js
const getProfile = async (url) => url;
Expand All @@ -45,44 +64,16 @@ const getProfile = async (url) => {
};
```

We use the [`await`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await) operator to _wait_ for a Promise to resolve. This allows us to write code that looks like it's happening in time order, but doesn't block our main thread.
We use the [`await`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await) operator to say "don't move on until this is done". Importantly, we are not actually _waiting_ for a Promise to resolve. We are scheduling a callback that will be called when the Promise resolves. But this allows us to write code that looks like it's happening in time order (as if we _are_ waiting), without actually blocking our main thread.

```js
const getProfile = async (url) => {
const response = await fetch(url);
return response.json();
};
```

Go ahead and call this in your Node REPL in your terminal: `getProfile("https://api.github.com/users/SallyMcGrath").then(console.log)`. It works the same as before.

### 🫠 Handling errors

When we use `await`, we are saying, "Wait for this Promise to resolve before moving on to the next line of code." But if the Promise _doesn't_ resolve, the next line of code will never run and an [error](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error) will be thrown.

Let's try this. Call `getProfile` with a url that doesn't exist: `getProfile("invalid_url");`

You will get a curious response:

<details><summary>Uncaught (in promise) TypeError...</summary>

```js
getProfile("invalid_url")
Promise {
<pending>,
[...]
}
> Uncaught [TypeError: Failed to parse URL from invalid_url] {
[cause]: TypeError: Invalid URL
[...] {
code: 'ERR_INVALID_URL',
input: 'invalid_url'
}
}
getProfile("https://api.github.com/users/SallyMcGrath")
.then((response) => console.log(response))
```

_Some lines redacted [...] for clarity._

</details>

JavaScript is telling us we need to `catch` the error, but how, and why?
Save this to a file and run with with node. It works the same as before.
15 changes: 12 additions & 3 deletions common-content/en/module/js3/asynchrony/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ emoji= '🧩'

+++

We can handle latency using {{<tooltip title="asynchronous execution">}}run code in a different order.{{</tooltip>}} To understand asynchrony we first need to be clear about {{<tooltip title="synchronous execution">}}run code in the order it is written.{{</tooltip>}}.
We can handle latency using {{<tooltip title="asynchronous execution">}}Asynchronous execution is running code in a different order than it was written.{{</tooltip>}} To understand asynchrony we first need to be clear about {{<tooltip title="synchronous execution">}}Synchronous execution is running code in the order it is written.{{</tooltip>}}.

We have written a lot of JavaScript programs that execute sequentially. This means that each line of code is run in order, one after the other.
{{<columns>}}
Expand Down Expand Up @@ -47,7 +47,16 @@ When we call a function, the function will run to completion before the next lin

### Event Loop

We have already used asynchronous execution. We have defined `eventListener`s that _listen_ for events to happen, _then_ execute a callback function. But here's a new idea: eventListeners are part of the [Event API](https://developer.mozilla.org/en-US/docs/Web/API/Event). They are not part of JavaScript! 🤯 This means you can't use them in a Node REPL, but they are implemented in web browsers. The core of JavaScript is the same everywhere, but different contexts may add extra APIs.
We have already used asynchronous execution. We have defined `eventListener`s that _listen_ for events to happen, _then_ execute a callback function.

```js
const search = document.getElementById("search");
search.addEventListener("input", handleInput);
```

When we called `addEventListener` it didn't immediately call `handleInput`.

But here's a new idea: eventListeners are part of the [Event API](https://developer.mozilla.org/en-US/docs/Web/API/Event). They are not part of the JavaScript language! 🤯 This means you can't use them in a Node REPL. But they are implemented in web browsers. The core of JavaScript (e.g. strings and functions) is the same everywhere, but different contexts may add extra APIs.

When you set an eventListener you are really sending a call to a Web API and asking it do something for you.

Expand All @@ -56,7 +65,7 @@ const search = document.getElementById("search");
search.addEventListener("input", handleInput);
```

The callback `handleInput` cannot run until the user types. With `fetch`, the callback function cannot run until the data arrives. In both cases, we are waiting for something to happen before we can run our code.
The callback `handleInput` does not run until the user types. With `fetch`, the callback function does not run until the data arrives. In both cases, we are waiting for something to happen before we can run our code.

We use a function as a way of wrapping up the code that needs to be run later on. This means we can tell the browser _what_ to do when we're done waiting.

Expand Down
11 changes: 7 additions & 4 deletions common-content/en/module/js3/break-down/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ title = '🧩 Break down the problem'
time = "30"
facilitation = false
emoji= '🧩'
hide_from_overview = true
[objectives]
1='Identify and sequence sub tasks'
[build]
Expand All @@ -13,11 +14,13 @@ emoji= '🧩'

+++

We already have a website for displaying film listings.

Let's think through building this film search interface step-by-step. Write down your sequence of steps to build this interface.

> _Given_ a view of film cards and search box
> _When_ a user types in the search box
> _Then_ the view should update to show only matching films
> _Given_ a view of film cards and search box
> _When_ a user types in the search box
> _Then_ the view should update to show only matching films.
{{<tabs name="Decomposition">}}
{{<tab name="Draw your plan">}}
Expand Down Expand Up @@ -47,7 +50,7 @@ The key aspects we need to handle are capturing input and updating UI.

### 👂🏿 Capturing Input

We need to listen for the `input` event on the search box to react as the user types. When the event fires, we can read the updated string value from the search box input element.
We need to listen for the [`input`](https://developer.mozilla.org/en-US/docs/Web/API/Element/input_event) event on the search box to react as the user types. When the event fires, we can read the updated string value from the search box input element.

### 🎬 Filtering Data

Expand Down
13 changes: 7 additions & 6 deletions common-content/en/module/js3/callbacks/index.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
+++
title = '🪃 Callbacks'

time = 20
time = 30
facilitation = false
emoji= '🧩'
[objectives]
Expand All @@ -19,9 +19,9 @@ Consider this visualisation of an asynchronous program:

**👉🏽 [Code running out of order and off the thread](http://latentflip.com/loupe/?code=c2V0VGltZW91dChmdW5jdGlvbiB0aW1lb3V0KCkgewogICAgY29uc29sZS5sb2coIjEiKTsKfSwgMjAwMCk7CnNldFRpbWVvdXQoZnVuY3Rpb24gdGltZW91dCgpIHsKICAgIGNvbnNvbGUubG9nKCIyIik7Cn0sIDUwMCk7CnNldFRpbWVvdXQoZnVuY3Rpb24gdGltZW91dCgpIHsKICAgIGNvbnNvbGUubG9nKCIzIik7Cn0sIDApOwo%3D!!!)**

When we call [`setTimeout`](https://developer.mozilla.org/en-US/docs/Web/API/setTimeout) we send **a function call** to a client side Web API. The code isn't executing in our single thread any more, so we can run the next line. The countdown _is_ happening, but it's not happening _in our thread_.
When we call [`setTimeout`](https://developer.mozilla.org/en-US/docs/Web/API/setTimeout) we send **a function call** to a client side Web API. The code isn't executing in our single thread any more, so we can run the next line. The countdown _is_ happening, but it's not happening _in code we control_.

When the time runs out, our Web API sends a message to our program to let us know. This is called an {{<tooltip title="event">}}An event is a signal that something has happened.{{</tooltip>}}. Our API sends its message to our {{<tooltip title="event loop">}}The event loop is a JavaScript mechanism that handles asynchronous callbacks.{{</tooltip>}}. And what message does the event loop send? It sends a **callback**. It sends _our_ call _back_. It tells our thread to run the code in that function.
When the time runs out, the Web API sends a message to our program to let us know. This is called an {{<tooltip title="event">}}An event is a signal that something has happened.{{</tooltip>}}. The API sends its message to our {{<tooltip title="event loop">}}The event loop is a JavaScript mechanism that handles asynchronous callbacks.{{</tooltip>}}. And what message does the event loop send? It sends a **callback**. It sends _our_ call _back_. It tells our thread to run the code in that function.

{{<note type="tip" title="Our call is back">}}
A callback is our function call, sent back to us through the event loop, for us to run.
Expand All @@ -34,15 +34,16 @@ A callback is our function call, sent back to us through the event loop, for us
Use your model to predict the order of logged numbers in the following code snippet:

```js
setTimeout(function timeout() {
setTimeout(function timeout1() {
console.log("1");
}, 2000);
setTimeout(function timeout() {
setTimeout(function timeout2() {
console.log("2");
}, 500);
setTimeout(function timeout() {
setTimeout(function timeout3() {
console.log("3");
}, 0);
console.log("4");
```

{{</tab>}}
Expand Down
3 changes: 1 addition & 2 deletions common-content/en/module/js3/capturing-events/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ So our key steps are:
1. Add an input event listener to the search box
2. In the handler, get `value` of input element
3. Set the new state based on this value.
4. Call our `render` function again.

{{<note type="warning" title="One thing at a time!">}}
But we're not going to do all of these at once! Stop and implement just the first two steps (adding the event listener, and getting the value), and `console.log` the search term.
Expand All @@ -56,5 +57,3 @@ function handleSearchInput(event) {
console.log(searchTerm);
}
```

Now that we've shown we can log the search text, we can set the new value of the searchTerm state, and re-render the page.
Loading

0 comments on commit 87cea33

Please sign in to comment.