From 03006366022252e83c8da5f24b8755fba9c6e792 Mon Sep 17 00:00:00 2001 From: oabdelnour <oabdelnour@takima.fr> Date: Tue, 27 Feb 2024 12:30:30 +0100 Subject: [PATCH] wip --- .../docs/docs/chapters/tp/arrow-functions.md | 136 ++++--- resources/docs/docs/chapters/tp/bundle.md | 215 +++++----- resources/docs/docs/chapters/tp/css-loader.md | 209 +++++----- .../docs/docs/chapters/tp/es6-classes.md | 367 +++++++++--------- .../chapters/tp/functionnal-programming.md | 226 ++++++----- .../docs/docs/chapters/tp/html-loader.md | 288 +++++++------- resources/docs/docs/chapters/tp/npm.md | 8 +- resources/docs/docs/chapters/tp/setup.md | 4 + resources/docs/docs/chapters/tp/spa.md | 8 +- resources/docs/docs/chapters/tp/variables.md | 201 +++++----- resources/docs/docs/chapters/tp/webpack.md | 4 + resources/docs/docs/stylesheets/global.css | 2 +- resources/docs/mkdocs.yml | 11 +- 13 files changed, 846 insertions(+), 833 deletions(-) diff --git a/resources/docs/docs/chapters/tp/arrow-functions.md b/resources/docs/docs/chapters/tp/arrow-functions.md index cf726c5..1e655fb 100644 --- a/resources/docs/docs/chapters/tp/arrow-functions.md +++ b/resources/docs/docs/chapters/tp/arrow-functions.md @@ -7,72 +7,75 @@ tag: _ES6_ brought a new syntax to declare functions: the [arrow functions](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions). -> **info** An arrow function expression is a compact alternative to a traditional function, with few differences: -> - They should not be used as method -> - They are tied to the parent scope. They cannot use `function.bind(...)` to be bound to another `this`. +!!! info "Infos" + An arrow function expression is a compact alternative to a traditional function, with few differences: + + - They should not be used as method + - They are tied to the parent scope. They cannot use `function.bind(...)` to be bound to another `this`. -## `functions` vs `() => {...}` <Diy /> +## `functions` vs `() => {...}` <span id="diy">:fontawesome-solid-wrench: Do it yourself</span> Open `game.js` and look at the `goToScore()` method. It is called once all card pairs are flipped and waits 750ms before redirecting the player to the Score page. -game.js - -```javascript -export class GameComponent { - goToScore() { - // ... - setTimeout( - function () { - const scorePage = "#score"; - scorePage + - "?name=" + - this._name + - "&size=" + - this._size + - "&time=" + - timeElapsedInSeconds; - }.bind(this), - 750 - ); - } -} -``` +=== "game.js" + + ```javascript linenums="1" + export class GameComponent { + goToScore() { + // ... + setTimeout( + function () { + const scorePage = "#score"; + scorePage + + "?name=" + + this._name + + "&size=" + + this._size + + "&time=" + + timeElapsedInSeconds; + }.bind(this), + 750 + ); + } + } + ``` Waiting a certain amount of time can be easily done using the [`setTimeout()`](https://developer.mozilla.org/en-US/docs/Web/API/setTimeout) function. -> **info** In JavaScript, long tasks (eg: waiting some time, doing an HTTP request, ...) are all asynchronous. -> <details><summary> Why? </summary> -> JavaScript is **mono-threaded**. That means, all the code is executed on the main thread and we cannot offload long jobs to a background thread. In other words, **long blocking tasks would freeze the application**. -> </details> +!!! info "Infos" + In JavaScript, long tasks (eg: waiting some time, doing an HTTP request, ...) are all asynchronous. +??? question "Why?" + JavaScript is **mono-threaded**. That means, all the code is executed on the main thread and we cannot offload long jobs to a background thread. In other words, **long blocking tasks would freeze the application**. `setTimeout()` accepts as first parameter an anonymous function called when the amount of time is elapsed. Such functions are called **callback functions**. - Edit `game.js` and `welcome.js` files. Replace all anonymous functions with _arrow functions_. - Follow `TODO #arrow-function` comments for instructions. - > Example: - > - > === "Anonymous function" - > - > ``` js - > setTimeout(function () { - > console.log("after 1s"); - > }, 1000); - > ``` - > - > === "Arrow function" - > - > ``` js - > setTimeout(() => { - > console.log("after 1s"); - > }, 1000); - > ``` - > - > === "Arrow function - single line" - > - > ``` js - > setTimeout(() => console.log("after 1s"), 1000); - > ``` - - > **danger** Regular functions declared with the **`function` keyword have `this` inherited from the caller**, whereas **arrow functions have a `this` inherited from the declaring scope**. + Follow `TODO #arrow-function` comments for instructions. <br> + +Example: +=== "Anonymous function" + + ``` js + setTimeout(function () { + console.log("after 1s"); + }, 1000); + ``` + +=== "Arrow function" + + ``` js + setTimeout(() => { + console.log("after 1s"); + }, 1000); + ``` + +=== "Arrow function - single line" + + ``` js + setTimeout(() => console.log("after 1s"), 1000); + ``` + +!!! danger "Danger" + Regular functions declared with the **`function` keyword have `this` inherited from the caller**, whereas **arrow functions have a `this` inherited from the declaring scope**. ??? note "Show the game.js file" @@ -140,24 +143,29 @@ Concatenating strings is not something we enjoy in any language. Fortunately, _E You have ${messages.length} message${messages.length ? "s" : ""} to read.`; ``` -### Use backquotes instead of quotes <Diy /> +### Use backquotes instead of quotes <span id="diy">:fontawesome-solid-wrench: Do it yourself</span> - Edit `game.js` and `welcome.js` files. Replace every string concatenation with template literals. Follow `TODO #template-literals` comments for instructions. -#### <i class="fas fa-folder-tree"></i> Modified files +#### :fontawesome-solid-folder-tree: Modified files ``` src/app/scripts/game.js src/app/scripts/welcome.js ``` -#### <i class="fa fa-list-check"></i> Checklist +#### :fontawesome-solid-list-check: Checklist + +- <input type="checkbox" /> I can write _arrow functions_ +- <input type="checkbox" /> I know the differences between _functions_ and _arrow functions_ +- <input type="checkbox" /> I know what is the _bound this_ of a function +- <input type="checkbox" /> I know about template literals +- <input type="checkbox" /> The application runs as usual + +<span id="commit">:fontawesome-brands-git-alt: Commit !</span> -- [ ] I can write _arrow functions_ -- [ ] I know the differences between _functions_ and _arrow functions_ -- [ ] I know what is the _bound this_ of a function -- [ ] I know about template literals -- [ ] The application runs as usual +## :fontawesome-solid-graduation-cap: Assessment +<iframe src="https://docs.google.com/forms/d/e/1FAIpQLSfeoczEA0VJINo3ty3W-gUzvkA8E26VKXM4U_TOKnZ5mXplkw/viewform?embedded=true" width="100%" height="640" frameborder="0" marginheight="0" marginwidth="0">Chargement…</iframe> diff --git a/resources/docs/docs/chapters/tp/bundle.md b/resources/docs/docs/chapters/tp/bundle.md index 9bd80cf..62063ac 100644 --- a/resources/docs/docs/chapters/tp/bundle.md +++ b/resources/docs/docs/chapters/tp/bundle.md @@ -18,139 +18,136 @@ Earlier in this chapter, we turned `router.js` into an _ES module_. It's time to After this chapter, thanks to the _ES modules_, there will be no more imported files in the `index.html`. **ES `import` statements will be the glue between files**. With the _bundler_, the importing file responsibility have shifted from _HTML_ to _JS_ files. -before bundler +=== "before bundler"  -after bundler +=== "after bundler"  -## <i class="fab fa-js"></i> **Bundle the JS** code <Diy /> <BadgeHard /> +## :fontawesome-brands-js: **Bundle the JS** code <span id="diy">:fontawesome-solid-wrench: Do it yourself</span> <span id="hard">:fontawesome-solid-star: :fontawesome-solid-star: :fontawesome-solid-star: Hard</span> - Edit `utils.js`. Look at the `TODO #export-functions` comments for instructions. - - Write an `export` statement to export the `parseUrl` function. - <details><summary> Show the utils.js file </summary> - utils.js + - Write an `export` statement to export the `parseUrl` function. + ??? note "Show the utils.js file" + === "utils.js" + + ```js linenums="1" + export function parseUrl() { + // ... + } + ``` - ```js - export function parseUrl() { - // ... - } - ``` - </details> - Edit `welcome.js`, `game.js` and `score.js`. Look at the `TODO #export-functions` comments for instructions. - - Remove the _IIFE_ - - Write an `export` statement to export the component's function. - - Write an `import` statement to import the `parseUrl` function if required. + - Remove the _IIFE_ + - Write an `export` statement to export the component's function. + - Write an `import` statement to import the `parseUrl` function if required. - <details><summary> Show the resulting files</summary> - welcome.js +??? note "Show the resulting files" +=== "welcome.js" - ```js - export function WelcomeComponent() {} + ```js linenums="1" + export function WelcomeComponent() {} - /* method WelcomeComponent.init */ - WelcomeComponent.prototype.init = function init() { - // ... - }; + /* method WelcomeComponent.init */ + WelcomeComponent.prototype.init = function init() { + // ... + }; - // ... - ``` + // ... + ``` - game.js + === "game.js" - ```js - import { parseUrl } from "./utils"; + ```js linenums="1" + import { parseUrl } from "./utils"; - var environment = { - api: { - host: "http://localhost:8081", - }, - }; + var environment = { + api: { + host: "http://localhost:8081", + }, + }; - export function GameComponent() { - // ... - } + export function GameComponent() { + // ... + } - /* method GameComponent.init */ - GameComponent.prototype.init = function init() { - // ... - }; - // ... - ``` + /* method GameComponent.init */ + GameComponent.prototype.init = function init() { + // ... + }; + // ... + ``` - score.js + === "score.js" - ```js - import { parseUrl } from "./utils"; + ```js linenums="1" + import { parseUrl } from "./utils"; - export function ScoreComponent() { - // ... - } - // ... - ``` - </details> + export function ScoreComponent() { + // ... + } + // ... + ``` - Edit `main.js`. Look at `TODO #import-components` for instructions. - - Write an `import` statement to import `WelcomeComponent`, `GameComponent` and `ScoreComponent` - - <details><summary> Show the main.js file </summary> - main.js +- Write an `import` statement to import `WelcomeComponent`, `GameComponent` and `ScoreComponent` - ```js - import { Router } from "./app/scripts/router"; - import { WelcomeComponent } from "./app/scripts/welcome"; - import { GameComponent } from "./app/scripts/game"; - import { ScoreComponent } from "./app/scripts/score"; +??? note "Show the main.js file" +=== "main.js" - const outlet = document.querySelector("#content-outlet"); - const router = new Router(outlet); - router.register("", { - component: WelcomeComponent, - templateUrl: "/src/app/views/welcome.html", - }); - // ... - ``` + ```js linenums="1" + import { Router } from "./app/scripts/router"; + import { WelcomeComponent } from "./app/scripts/welcome"; + import { GameComponent } from "./app/scripts/game"; + import { ScoreComponent } from "./app/scripts/score"; - </details> + const outlet = document.querySelector("#content-outlet"); + const router = new Router(outlet); + router.register("", { + component: WelcomeComponent, + templateUrl: "/src/app/views/welcome.html", + }); + // ... + ``` - Edit `index.html`. Remove all `<script>` tag as everything is now bundled by _Webpack_. - <details><summary> Show the index.html file </summary> - index.html - - ```html - <!DOCTYPE html> - <html lang="en"> - <head> - <meta charset="UTF-8" /> - <title>MÈME ory</title> - <!-- TODO #import-css: move style imports to src/main.js --> - <!-- TODO #npm-bootstrap: change css location to '/node_modules/bootstrap/dist/css/bootstrap.css' --> - <link rel="stylesheet" href="/src/app/styles/bootstrap.css" /> - - <link rel="stylesheet" href="/src/app/styles/style.css" /> - </head> - - <body> - <div id="content-outlet" class="h-100 w-100 d-flex flex-column"></div> - </body> - </html> - ``` - </details> + +??? note "Show the index.html file" +=== "index.html" + + ```html linenums="1" + <!DOCTYPE html> + <html lang="en"> + <head> + <meta charset="UTF-8" /> + <title>MÈME ory</title> + <!-- TODO #import-css: move style imports to src/main.js --> + <!-- TODO #npm-bootstrap: change css location to '/node_modules/bootstrap/dist/css/bootstrap.css' --> + <link rel="stylesheet" href="/src/app/styles/bootstrap.css" /> + + <link rel="stylesheet" href="/src/app/styles/style.css" /> + </head> + + <body> + <div id="content-outlet" class="h-100 w-100 d-flex flex-column"></div> + </body> + </html> + ``` - Test the application: navigate to [http://localhost:8080](http://localhost:8080), the application should work as usual and show no error in the console 🎉. - Build the application. - <details><summary> Show the build command </summary> - - ```bash - npm run build - ``` -</details> -#### <i class="fas fa-folder-tree"></i> Modified files +??? note "Show the build command" + + ```bash linenums="1" + npm run build + ``` + +#### :fontawesome-solid-folder-tree: Modified files ``` src/app/scripts/game.js @@ -161,20 +158,12 @@ src/main.js src/index.html ``` -#### <i class="fa fa-list-check"></i> Checklist - -- [ ] I know how to use the `import` keyword and do **named imports**. -- [ ] The codebase has no unresolved `TODO #export-functions` comments left. -- [ ] The codebase has no unresolved `TODO #import-components` comments left. -- [ ] The codebase has no _IIFE_ left. +#### :fontawesome-solid-list-check: Checklist -> **question** Look at the `dist/main.js` file, generated by the build command. What is its size? +- <input type="checkbox" /> I know how to use the `import` keyword and do **named imports**. +- <input type="checkbox" /> The codebase has no unresolved `TODO #export-functions` comments left. +- <input type="checkbox" /> The codebase has no unresolved `TODO #import-components` comments left. +- <input type="checkbox" /> The codebase has no _IIFE_ left. -<style> -summary { - color: #cb2468; - text-align: center; - margin-bottom: 20px; - margin-top: 20px -} -</style> \ No newline at end of file +!!! question "Question" + Look at the `dist/main.js` file, generated by the build command. What is its size? diff --git a/resources/docs/docs/chapters/tp/css-loader.md b/resources/docs/docs/chapters/tp/css-loader.md index fb64605..2e1b602 100644 --- a/resources/docs/docs/chapters/tp/css-loader.md +++ b/resources/docs/docs/chapters/tp/css-loader.md @@ -14,135 +14,133 @@ In the previous chapter, we used `import` statements to import `.js` files in ot For now, we still need the `<link rel="stylesheet">` tags to import the _CSS_ code, but this is about to change: with [`webpack-style-loader`](https://webpack.js.org/loaders/style-loader/), we will give _Webpack_ the ability to import _CSS_ files as well. -> **info** **_Webpack_ loaders** are official or community addons to change the way _Webpack_ imports files. -> They are often used to gain support for new file extensions and languages. -> - **[html-loader](https://webpack.js.org/loaders/html-loader/)**: to load _*.html_ files -> - **[babel-loader](https://webpack.js.org/loaders/babel-loader/)**: to transpile _*.js_ files -> - **[sass-loader](https://webpack.js.org/loaders/sass-loader/)**: to load _*.sass_ & _*.scss_ files -> - **[ts-loader](https://webpack.js.org/guides/typescript/)**: to load and transpile _*.ts_ files +!!! info "Infos" + **_Webpack_ loaders** are official or community addons to change the way _Webpack_ imports files. + They are often used to gain support for new file extensions and languages. -## <i class="fab fa-css3-alt"></i> **Bundle the CSS** code <BadgeHard /> + - **[html-loader](https://webpack.js.org/loaders/html-loader/)**: to load _*.html_ files + - **[babel-loader](https://webpack.js.org/loaders/babel-loader/)**: to transpile _*.js_ files + - **[sass-loader](https://webpack.js.org/loaders/sass-loader/)**: to load _*.sass_ & _*.scss_ files + - **[ts-loader](https://webpack.js.org/guides/typescript/)**: to load and transpile _*.ts_ files -### <i class="fa fa-file-download"></i> Install [`style-loader`](https://www.npmjs.com/package/style-loader) & [`css-loader`](https://www.npmjs.com/package/css-loader) <Diy/> +## :fontawesome-brands-css3-alt: **Bundle the CSS** code <span id="hard">:fontawesome-solid-star: :fontawesome-solid-star: :fontawesome-solid-star: Hard</span> -```bash +### :fontawesome-solid-file-arrow-down: Install [`style-loader`](https://www.npmjs.com/package/style-loader) & [`css-loader`](https://www.npmjs.com/package/css-loader) <span id="diy">:fontawesome-solid-wrench: Do it yourself</span> + +```bash linenums="1" npm i -D style-loader css-loader ``` - [`css-loader`](https://webpack.js.org/loaders/css-loader/) allows _Webpack_ to import and parse _CSS_ files. - [`style-loader`](https://webpack.js.org/loaders/style-loader/) creates `<link rel="stylesheet">` tags to link imported _CSS_ files to the document. -### <i class="fa fa-screwdriver-wrench"></i> Use the `style-loader` <Diy/> +### :fontawesome-solid-screwdriver-wrench: Use the `style-loader` <span id="diy">:fontawesome-solid-wrench: Do it yourself</span> - Edit `webpack.config.js` and configure the `style-loader` and the `css-loader` to load _CSS_ files: - webpack.config.js - - ```js{9-12} - // ... - module.exports = { - mode: "development", - devtool: "inline-source-map", - entry: "./src/main.js", - // ... - module: { - rules: [ - { - test: /\.(css)$/, - use: ["style-loader", "css-loader"], + === "webpack.config.js" + + ```js linenums="1" + // ... + module.exports = { + mode: "development", + devtool: "inline-source-map", + entry: "./src/main.js", + // ... + module: { + rules: [ + { + test: /\.(css)$/, + use: ["style-loader", "css-loader"], + }, + ], }, - ], - }, - }; - ``` + }; + ``` - Stop and restart the _Webpack dev server_. - <details><summary> Show the command </summary> + ??? note "Show the command" - ```bash - npm run start - ``` + ```bash linenums="1" + npm run start + ``` - </details> - > **danger** Remember to restart _Webpack_ whenever `webpack.config.js` changes, as it does not live-reload the configuration. +!!! danger "Danger" + Remember to restart _Webpack_ whenever `webpack.config.js` changes, as it does not live-reload the configuration. -### <i class="fa fa-file-import"></i> Write `imports` for the _CSS_ files +### :fontawesome-solid-file-import: Write `imports` for the _CSS_ files We want to import all the _CSS_ files that are still imported using a `<link>` tag in the `index.html` file. - Edit `index.html`, remove all `<link rel="stylesheet">` tags and write the corresponding [**side-effect import**](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import#import_a_module_for_its_side_effects_only) statements in `src/main.js`. - <details><summary> Show the index.html and main.js files </summary> - index.html - - ```html{6-8} - <!DOCTYPE html> - <html lang="en"> - <head> - <meta charset="UTF-8" /> - <title>MÈME ory</title> - <!-- removed --> - <!-- <link rel="stylesheet" href="/src/app/styles/bootstrap.css" /> --> - <!-- <link rel="stylesheet" href="/src/app/styles/style.css" /> --> - </head> - - <body> - <!-- ... --> - </body> - </html> - ``` - - main.js - ```js{6,7} - import { Router } from "./app/scripts/router"; - import { WelcomeComponent } from "./app/scripts/welcome"; - import { GameComponent } from "./app/scripts/game"; - import { ScoreComponent } from "./app/scripts/score"; - - import "./app/styles/bootstrap.css"; - import "./app/styles/style.css"; - - const outlet = document.querySelector("#content-outlet"); - const router = new Router(outlet); - // ... - ``` - - </details> +??? note "Show the index.html and main.js files" + === "index.html" + + ```html linenums="1" + <!DOCTYPE html> + <html lang="en"> + <head> + <meta charset="UTF-8" /> + <title>MÈME ory</title> + <!-- removed --> + <!-- <link rel="stylesheet" href="/src/app/styles/bootstrap.css" /> --> + <!-- <link rel="stylesheet" href="/src/app/styles/style.css" /> --> + </head> + + <body> + <!-- ... --> + </body> + </html> + ``` + + === "main.js" + ```js linenums="1" + import { Router } from "./app/scripts/router"; + import { WelcomeComponent } from "./app/scripts/welcome"; + import { GameComponent } from "./app/scripts/game"; + import { ScoreComponent } from "./app/scripts/score"; + + import "./app/styles/bootstrap.css"; + import "./app/styles/style.css"; + + const outlet = document.querySelector("#content-outlet"); + const router = new Router(outlet); + // ... + ``` - Browse [localhost:8080](localhost:8080) and test the application. It should have the same look and feel as usual. -### Install bootstrap with npm <Diy /> +### Install bootstrap with npm <span id="diy">:fontawesome-solid-wrench: Do it yourself</span> [_Bootstrap_](https://getbootstrap.com/) is [available on the _NPM_ repository](https://www.npmjs.com/package/bootstrap), so we can use the package manager to install it. - Go to the root of the `front-end/` directory and use the [`npm install`](https://docs.npmjs.com/cli/v8/commands/npm-install) command. - <details><summary> Show the command </summary> +??? note "Show the command" - verbose - ```bash - cd front-end - npm install bootstrap@5 - ``` + === "verbose" + ```bash linenums="1" + cd front-end + npm install bootstrap@5 + ``` - @tab alias - ```bash - cd front-end - npm i bootstrap@5 - ``` - </details> + === "alias" + ```bash linenums="1" + cd front-end + npm i bootstrap@5 + ``` - **Remove the hand-copied bootstrap sources** No need to keep our own copy of `bootstrap.css` now we have installed it with _NPM_. You can safely remove it. - <details><summary> Show the command </summary> - ```bash - rm src/app/styles/bootstrap.css - ``` - </details> +??? note "Show the command" + ```bash linenums="1" + rm src/app/styles/bootstrap.css + ``` - **Use the installed _Bootstrap_ package** @@ -152,7 +150,7 @@ so we can use the package manager to install it. Do it for the `game.html`, `score.html` and `welcome.html` files. -#### <i class="fas fa-folder-tree"></i> Modified files +#### :fontawesome-solid-folder-tree: Modified files ``` package.json @@ -165,25 +163,18 @@ src/scripts/score.html webpack.config.js ``` -#### <i class="fa fa-list-check"></i> Checklist - -- [ ] I know how to use ES **side-effects only imports**. -- [ ] I know how `style-loader` and `css-loader` work. -- [ ] `index.html` has no more `<link rel="stylesheet">` tags -- [ ] The codebase has no unresolved `TODO #import-css` comments left. -- [ ] I deleted the `styles/bootstrap.css`. -- [ ] My application uses bootstrap from `node_modules/`. -- [ ] There is no `TODO #npm-bootstrap` comments left. +#### :fontawesome-solid-list-check: Checklist +- <input type="checkbox" /> I know how to use ES **side-effects only imports**. +- <input type="checkbox" /> I know how `style-loader` and `css-loader` work. +- <input type="checkbox" /> `index.html` has no more `<link rel="stylesheet">` tags +- <input type="checkbox" /> The codebase has no unresolved `TODO #import-css` comments left. +- <input type="checkbox" /> I deleted the `styles/bootstrap.css`. +- <input type="checkbox" /> My application uses bootstrap from `node_modules/`. +- <input type="checkbox" /> There is no `TODO #npm-bootstrap` comments left. -> **question** Build the code with `npm run build` and look at the code generated in `dist/main.js`. -> What is this file size? +<span id="commit">:fontawesome-brands-git-alt: Commit !</span> -<style> -summary { - color: #cb2468; - text-align: center; - margin-bottom: 20px; - margin-top: 20px -} -</style> \ No newline at end of file +!!! question "Question" + Build the code with `npm run build` and look at the code generated in `dist/main.js`. + What is this file size? diff --git a/resources/docs/docs/chapters/tp/es6-classes.md b/resources/docs/docs/chapters/tp/es6-classes.md index 0d3bc19..c4d7802 100644 --- a/resources/docs/docs/chapters/tp/es6-classes.md +++ b/resources/docs/docs/chapters/tp/es6-classes.md @@ -14,47 +14,48 @@ In this chapter, we will learn how to update our code step by step with some new Have a look at `game.js`: -game.js +=== "game.js" -```js{2} -for (var i in this._config.ids) { - this._cards[i] = new CardComponent(this._config.ids[i]); -} // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ // -``` + ```js linenums="1" + for (var i in this._config.ids) { + this._cards[i] = new CardComponent(this._config.ids[i]); + } // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ // + ``` Here, we create a new `CardComponent` instance (ie: a _card_), although `CardComponent` is in fact a simple function. -game.js +=== "game.js" + + ```js linenums="1" + function CardComponent(id) { + this._flipped = false; + this.matched = false; + this._id = id; + // ... + } -```js{1,9,15} -function CardComponent(id) { - this._flipped = false; - this.matched = false; - this._id = id; - // ... -} + /* method CardComponent.flip */ + CardComponent.prototype.flip = function flip() { + this._imageElt.classList.toggle("flip"); + this._flipped = !this._flipped; + }; -/* method CardComponent.flip */ -CardComponent.prototype.flip = function flip() { - this._imageElt.classList.toggle("flip"); - this._flipped = !this._flipped; -}; + /* method CardComponent.equals */ + CardComponent.prototype.equals = function equals(card) { + return card._id === this._id; + }; + ``` -/* method CardComponent.equals */ -CardComponent.prototype.equals = function equals(card) { - return card._id === this._id; -}; -``` +!!! info "Infos" + _ES5_ is [Prototype oriented language](https://developer.mozilla.org/en-US/docs/Glossary/Prototype-based_programming). In other words, it has no notion of _classes_ like in _Object Oriented Programming_, but it uses simple functions, objects and the [`prototype` keyword](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Objects/Object_prototypes) to mimic inheritance. `XComponent.prototype` is an object in which you put variables and functions shared across all instances of `XComponent`. -> **info** _ES5_ is [Prototype oriented language](https://developer.mozilla.org/en-US/docs/Glossary/Prototype-based_programming). In other words, it has no notion of _classes_ like in _Object Oriented Programming_, but it uses simple functions, objects and the [`prototype` keyword](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Objects/Object_prototypes) to mimic inheritance. `XComponent.prototype` is an object in which you put variables and functions shared across all instances of `XComponent`. -> -> - **TLDR:** With _ES5_, we use `function` and `prototype` to mimic _Object Oriented Programming_ + - **TLDR:** With _ES5_, we use `function` and `prototype` to mimic _Object Oriented Programming_ With that in mind, you understand that `GameComponent`, `WelcomeComponent`, `CardComponent` and `ScoreComponent` are all kind-of a class, with a somewhat awkward syntax. -## From `prototype` to `class` <Diy /> +## From `prototype` to `class` <span id="diy">:fontawesome-solid-wrench: Do it yourself</span> In this step, we will rewrite `WelcomeComponent`, `GameComponent`, `CardComponent` and `ScoreComponent` to use the [_ES6_ `class` keyword](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes). @@ -64,196 +65,200 @@ In this step, we will rewrite `WelcomeComponent`, `GameComponent`, `CardComponen - Rewrite every `function XComponent() {...}` to `class XComponent {...}`. The function body itself goes in the `constructor() {}` - > Example: - > example.js - before - > - > ```js - > function ExampleComponent(name) { - > this.name = name; - > } - > ``` - > - > @tab example.js - after - > - > ```js - > class ExampleComponent { - > constructor(name) { - > this.name = name; - > } - > } - > ``` - > - - > **info** _ES6_ classes does not have proper encapsulation (eg: private methods or attributes). - > As a convention, we name properties/method with a leading underscore to identify them as private. +Example: +=== "example.js - before" + + ```js linenums="1" + function ExampleComponent(name) { + this.name = name; + } + ``` + +=== "example.js - after" + + ```js linenums="1" + class ExampleComponent { + constructor(name) { + this.name = name; + } + } + ``` + + +!!! info "Infos" + _ES6_ classes does not have proper encapsulation (eg: private methods or attributes). <br> + As a convention, we name properties/method with a leading underscore to identify them as private. ### Methods - Rewrite every `XComponent.prototype.<METHOD> = ...` to a method `<METHOD>` of the `XComponent` class: - > Example: - > example.js - before - > - > ```js - > ExampleComponent.prototype.doThis = function doThis(attr) { - > this.attr = attr; - > }; - > ``` - > - > @tab example.js - after - > - > ```js - > class ExampleComponent { - > doThis(attr) { - > this.attr = attr; - > } - > } - > ``` +Example: +=== "example.js - before" + + ```js + ExampleComponent.prototype.doThis = function doThis(attr) { + this.attr = attr; + }; + ``` + +=== "example.js - after" + + ```js + class ExampleComponent { + doThis(attr) { + this.attr = attr; + } + } + ``` ### Property accessors -> **info** [`Object.defineProperties`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperties) can dynamically add properties to an `object`, using a _property descriptor_. It can declare a property either with a value or a couple of `get/set` methods. +!!! info "Infos" + [`Object.defineProperties`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperties) can dynamically add properties to an `object`, using a _property descriptor_. It can declare a property either with a value or a couple of `get/set` methods. - Rewrite each `Object.defineProperties(XComponent.prototype, { <PROPERTY> : {...}} )` statement, to a property accessor `get <PROPERTY>` in the `XComponent` class. Do the same for `get <PROPERTY>`. - > Example: - > example.js - before - > - > ```js{3,6} - > Object.defineProperties(ExampleComponent.prototype, { - > property: { - > get: function () { - > return this.prop; - > }, - > set: function (prop) { - > this.prop = prop; - > }, - > }, - > }); - > ``` - > - > @tab example.js - after - > - > ```js{2,5} - > class ExampleComponent { - > get prop() { - > return this.prop; - > } - > - > set prop(prop) { - > this.prop = prop; - > } - > } - > ``` - > +Example: +=== "example.js - before" + + ```js linenums="1" + Object.defineProperties(ExampleComponent.prototype, { + property: { + get: function () { + return this.prop; + }, + set: function (prop) { + this.prop = prop; + }, + }, + }); + ``` + +=== "example.js - after" + + ```js linenums="1" + class ExampleComponent { + get prop() { + return this.prop; + } + + set prop(prop) { + this.prop = prop; + } + } + ``` + ### `extends` `WelcomeComponent`, `GameComponent` and `ScoreComponent` have in common that they are all JavaScript classes with a mandatory property `this.template` to store their _HTML template_. To save a little bit of code, we will leverage **Component inheritance**. + - Create a new file named `scripts/component.js` to define a class named `Component`. Its constructor has to take the _HTML_ template as a parameter and default to a blank string. - <details><summary> Show the component.js file </summary> - component.js +??? note "Show the component.js file" + === "component.js" - ```js - export class Component { - constructor(template = "") { - this.template = template; - } - } - ``` - </details> + ```js linenums="1" + export class Component { + constructor(template = "") { + this.template = template; + } + } + ``` - Edit `game.js`. Look at the `TODO #extends` comments for instructions: - Make `GameComponent` extends `Component`. - Make `CardComponent` extends `Component`. - Call `super()` in the constructor. -<details><summary> Show the resulting game.js </summary> - -game.js - with prototype - -```js{} -export function GameComponent() { - var params = parseUrl(); - this._name = params.name; - // ... -} - -GameComponent.prototype.init = function init() { /* ... */ }; -GameComponent.prototype.start = function start() { /* ... */ }; -GameComponent.prototype.fetchConfig = function fetchConfig(cb) { /* ... */ }; -GameComponent.prototype.goToScore = function goToScore() { /* .. */ }; -GameComponent.prototype._flipCard = function _flipCard(card) { /* ... */ }; - -function CardComponent(id) { - this._flipped = false; - // ... -} -CardComponent.prototype.getElement = function getElement() { /* ... */ }; -CardComponent.prototype.flip = function flip() { /* ... */}; -CardComponent.prototype.equals = function equals(card) { /* ... */}; -Object.defineProperties(CardComponent.prototype, { /* ... */}); - -``` - -game.js - with class - -```js{1,5} -import { Component } from "./component"; - -export class GameComponent extends Component { - constructor() { - super(template) - var params = parseUrl(); - this._name = params.name; - // ... - } - - init() { /* ... */ } - start() { /* ... */ } - fetchConfig(cb) { /* ... */ } - goToScore() { /* ... */ } - _flipCard(card) { /* ... */ } -} - -class CardComponent extends Component { - constructor(id) { - super(template) - this._flipped = false; - // ... - } - - getElement() { /* ... */ } - flip() { /* ... */ } - equals(card) { /* ... */ } - get flipped() { /* ... */ } -} -``` -</details> +??? note "Show the resulting game.js" + + === "game.js - with prototype" + + ```js linenums="1" + export function GameComponent() { + var params = parseUrl(); + this._name = params.name; + // ... + } + + GameComponent.prototype.init = function init() { /* ... */ }; + GameComponent.prototype.start = function start() { /* ... */ }; + GameComponent.prototype.fetchConfig = function fetchConfig(cb) { /* ... */ }; + GameComponent.prototype.goToScore = function goToScore() { /* .. */ }; + GameComponent.prototype._flipCard = function _flipCard(card) { /* ... */ }; + + function CardComponent(id) { + this._flipped = false; + // ... + } + CardComponent.prototype.getElement = function getElement() { /* ... */ }; + CardComponent.prototype.flip = function flip() { /* ... */}; + CardComponent.prototype.equals = function equals(card) { /* ... */}; + Object.defineProperties(CardComponent.prototype, { /* ... */}); + + ``` + + === "game.js - with class" + + ```js linenums="1" + import { Component } from "./component"; + + export class GameComponent extends Component { + constructor() { + super(template) + var params = parseUrl(); + this._name = params.name; + // ... + } + + init() { /* ... */ } + start() { /* ... */ } + fetchConfig(cb) { /* ... */ } + goToScore() { /* ... */ } + _flipCard(card) { /* ... */ } + } + + class CardComponent extends Component { + constructor(id) { + super(template) + this._flipped = false; + // ... + } + + getElement() { /* ... */ } + flip() { /* ... */ } + equals(card) { /* ... */ } + get flipped() { /* ... */ } + } + ``` - Navigate to [localhost:8080](http://localhost:8080) and test the application. It should work as usual. -#### <i class="fas fa-folder-tree"></i> Modified files +#### :fontawesome-solid-folder-tree: Modified files ``` src/app/scripts/component.js src/app/scripts/game.js ``` -#### <i class="fa fa-list-check"></i> Checklist +#### :fontawesome-solid-list-check: Checklist -- [ ] I know how to define _ES6_ classes. -- [ ] I know what get/set properties are. -- [ ] The application runs as usual. +- <input type="checkbox" /> I know how to define _ES6_ classes. +- <input type="checkbox" /> I know what get/set properties are. +- <input type="checkbox" /> The application runs as usual. The code seems much more clean and concise with classes, isn't it? Let's continue the modernization in the next chapter. +!!! warning "Warning" + As a _Java_ developer, an ES _class_ may look like a _Java_ _class_ to you, but they are actually very different. + + - no encapsulation (ie: private properties and methods) + - no method/constructor overload + - no abstract classes + - no interfaces -> **warning** As a _Java_ developer, an ES _class_ may look like a _Java_ _class_ to you, but they are actually very different. -> - no encapsulation (ie: private properties and methods) -> - no method/constructor overload -> - no abstract classes -> - no interfaces +<span id="commit">:fontawesome-brands-git-alt: Commit !</span> diff --git a/resources/docs/docs/chapters/tp/functionnal-programming.md b/resources/docs/docs/chapters/tp/functionnal-programming.md index 6343851..e4caeab 100644 --- a/resources/docs/docs/chapters/tp/functionnal-programming.md +++ b/resources/docs/docs/chapters/tp/functionnal-programming.md @@ -9,82 +9,80 @@ tag: _ES6_ adds a bit of _[functional programming](https://en.wikipedia.org/wiki/Functional_programming)_ too. -> **info** Functional programming is a programming paradigm where programs are constructed applying and composing functions +!!! info "Infos" + Functional programming is a programming paradigm where programs are constructed applying and composing functions With _arrow functions_ and the new [`Array` API](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array#instance_methods), arrays can now be processed like basic [java streams](https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html). > You might need some [JavaScript object property accessors](https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Operators/Property_Accessors) knowledge to understand this exemple. -:::code-tabs -@tab without the stream-like API - -```js -function countWords(string) { - const words = string.split(/\s|,|\&/g); // split against word separators - const count = {}; - for (let w of words) { - w = w.trim(); // remove leading & trailing spaces - if (w.length >= 3) { - // keep words that are long enough - count[w] = (count[w] ?? 0) + 1; // aggregate the results - } - } - return count; -} -countWords( - "How much wood would a woodchuck chuck if a woodchuck could chuck wood" -); -// { -// "How": 1, -// "much": 1, -// "wood": 2, -// "would": 1, -// "woodchuck": 2, -// "chuck": 2, -// "could": 1 -// } -``` - -@tab with the stream-like API - -```js -function countWords(string) { - return string - .split(/\s|,|\&/g) // split against word separators - .map((w) => w.trim()) // remove leading & trailing spaces - .filter((w) => w.length >= 3) // keep words that are long enough - .reduce((count, w) => { - return { - ...count, - [w]: (count[w] ?? 0) + 1, - }; - }, {}); // aggregate the results -} -countWords( - "How much wood would a woodchuck chuck if a woodchuck could chuck wood" -); -// { -// "How": 1, -// "much": 1, -// "wood": 2, -// "would": 1, -// "woodchuck": 2, -// "chuck": 2, -// "could": 1 -// } -``` - -::: +=== "without the stream-like API" + + ```js linenums="1" + function countWords(string) { + const words = string.split(/\s|,|\&/g); // split against word separators + const count = {}; + for (let w of words) { + w = w.trim(); // remove leading & trailing spaces + if (w.length >= 3) { + // keep words that are long enough + count[w] = (count[w] ?? 0) + 1; // aggregate the results + } + } + return count; + } + countWords( + "How much wood would a woodchuck chuck if a woodchuck could chuck wood" + ); + // { + // "How": 1, + // "much": 1, + // "wood": 2, + // "would": 1, + // "woodchuck": 2, + // "chuck": 2, + // "could": 1 + // } + ``` + +=== "with the stream-like API" + + ```js linenums="1" + function countWords(string) { + return string + .split(/\s|,|\&/g) // split against word separators + .map((w) => w.trim()) // remove leading & trailing spaces + .filter((w) => w.length >= 3) // keep words that are long enough + .reduce((count, w) => { + return { + ...count, + [w]: (count[w] ?? 0) + 1, + }; + }, {}); // aggregate the results + } + countWords( + "How much wood would a woodchuck chuck if a woodchuck could chuck wood" + ); + // { + // "How": 1, + // "much": 1, + // "wood": 2, + // "would": 1, + // "woodchuck": 2, + // "chuck": 2, + // "could": 1 + // } + ``` At the beginning, functional programming may be unnecessarily complicated, however it is way less error prone than `for` loops. -#### Your job <Diy /> +#### Your job <span id="diy">:fontawesome-solid-wrench: Do it yourself</span> - Edit `utils.js`. Look at the comments `TODO #functional-programming` for instructions. The `parseUrl` function is required by `game.js` and `score.js`. It takes every parameter in the URL and puts it in a JSON object. - ```js + ```js linenums="1" parseUrl("?param1=1¶m2=2"); // { @@ -99,73 +97,69 @@ At the beginning, functional programming may be unnecessarily complicated, howev - [`Array.map`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Map) - [`Array.reduce`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce) - ::::spoiler Show the utils.js file - :::code-tabs - @tab utils.js - procedural - - ```js - function parseUrl(url = window.location.href) { - const query = url.split("?")[1] || ""; - const result = {}; - - const parts = query.split("&"); - for (var item of parts) { - var kv = item.split("="); - result[kv[0]] = kv[1]; - } - - return result; - } - ``` - - @tab utils.js - functional - - ```js - export function parseUrl(url = window.location.href) { - return (url.split("?")[1] ?? "") - .split("&") - .map((q) => q.split("=")) - .reduce((params, keyValue) => { - const [k, v] = keyValue; - params[k] = v; - return params; - }, {}); - } - ``` - - @tab utils.js - functional ✨😎✨ - - ```js - export function parseUrl(url = window.location.href) { - return (url.split("?")[1] ?? "") - .split("&") - .map((q) => q.split("=")) - .reduce((params, [k, v]) => ({ ...params, [k]: v }), {}); - } - ``` - - ::: - :::: +??? note "Show the utils.js file" + === "utils.js - procedural" + + ```js linenums="1" + function parseUrl(url = window.location.href) { + const query = url.split("?")[1] || ""; + const result = {}; + + const parts = query.split("&"); + for (var item of parts) { + var kv = item.split("="); + result[kv[0]] = kv[1]; + } + + return result; + } + ``` + + === "utils.js - functional" + + ```js linenums="1" + export function parseUrl(url = window.location.href) { + return (url.split("?")[1] ?? "") + .split("&") + .map((q) => q.split("=")) + .reduce((params, keyValue) => { + const [k, v] = keyValue; + params[k] = v; + return params; + }, {}); + } + ``` + + === "utils.js - functional ✨😎✨" + + ```js linenums="1" + export function parseUrl(url = window.location.href) { + return (url.split("?")[1] ?? "") + .split("&") + .map((q) => q.split("=")) + .reduce((params, [k, v]) => ({ ...params, [k]: v }), {}); + } + ``` - Edit `game.js` and `score.js`. Look at `TODO #functional-programming` comments for instructions. - Replace any `for` or `while` loops with [`Array.forEach`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/ForEach), [`Array.map`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Map), [`Array.filter`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Filter) and/or [`Array.reduce`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce) -#### <i class="fas fa-folder-tree"></i> Modified files +#### :fontawesome-solid-folder-tree: Modified files ``` src/app/scripts/game.js src/app/scripts/utils.js ``` -#### <i class="fa fa-list-check"></i> Checklist +#### :fontawesome-solid-list-check: Checklist -- [ ] I know the _ES6+_ **`Array` API**. -- [ ] The `parseUrl()` function works as usual. +- <input type="checkbox" /> I know the _ES6+_ **`Array` API**. +- <input type="checkbox" /> The `parseUrl()` function works as usual. -<GitCommit /> +<span id="commit">:fontawesome-brands-git-alt: Commit !</span> -## <i class="fas fa-graduation-cap"></i> Assessment +## :fontawesome-solid-graduation-cap: Assessment <iframe src="https://docs.google.com/forms/d/e/1FAIpQLScccqQBlsZP5xkR609quwwWTMR-UgEG6pGgV4m7ywEC9w-y3w/viewform?embedded=true" width="100%" height="640" frameborder="0" marginheight="0" marginwidth="0">Chargement…</iframe> diff --git a/resources/docs/docs/chapters/tp/html-loader.md b/resources/docs/docs/chapters/tp/html-loader.md index 00b10c5..7c671b4 100644 --- a/resources/docs/docs/chapters/tp/html-loader.md +++ b/resources/docs/docs/chapters/tp/html-loader.md @@ -13,118 +13,116 @@ tag: In the previous chapter, we managed to _bundle_ all the _CSS_ code. Now, it's time to do the same for the _HTML_ templates. Look at `src/main.js`: -main.js - -```js{3,5} -// ... -router - .register("welcome", { - component: WelcomeComponent, - templateUrl: "/src/app/views/welcome.html", - }) -// ... -``` +=== "main.js" + + ```js linenums="1" + // ... + router + .register("welcome", { + component: WelcomeComponent, + templateUrl: "/src/app/views/welcome.html", + }) + // ... + ``` For now, we configure each path with a `templateUrl`, so the _Router_ can fetch the right template at runtime and display it. With `html-loader`, we could statically `import` this template at _compile-time_ and add it to the _bundle_. -## <i class="fab fa-html5"></i> Bundle the HTML code <BadgeHard /> +## :fontawesome-brands-html5: Bundle the HTML code <span id="hard">:fontawesome-solid-star: :fontawesome-solid-star: :fontawesome-solid-star: Hard</span> To bundle the _HTML_ code, we will use the [`html-loader`](https://webpack.js.org/loaders/html-loader/) that allows _Webpack_ to import and parse _HTML_ files. -### <i class="fa fa-file-download"></i> Install the `html-loader` <Diy/> +### :fontawesome-solid-file-arrow-down: Install the `html-loader` <span id="diy">:fontawesome-solid-wrench: Do it yourself</span> - Install `html-loader` using _NPM_. -<details><summary> Show the command </summary> +??? note "Show the command" -```bash -npm i -D html-loader -``` + ```bash linenums="1" + npm i -D html-loader + ``` - Edit `webpack.config.js` and configure the `html-loader` to load _HTML_ files: - webpack.config.js - - ```js{10-13} - // ... - module.exports = { - mode: "development", - devtool: "inline-source-map", - entry: "./src/main.js", - // ... - module: { - rules: [ +=== "webpack.config.js" + + ```js linenums="1" + // ... + module.exports = { + mode: "development", + devtool: "inline-source-map", + entry: "./src/main.js", // ... - { - test: /\.(html)$/, - use: ["html-loader"], + module: { + rules: [ + // ... + { + test: /\.(html)$/, + use: ["html-loader"], + }, + ], }, - ], - }, - }; - ``` + }; + ``` - Stop and restart the _Webpack dev server_. - :::spoiler Show the command +??? note "Show the command" - ```bash - npm run start - ``` - </details> + ```bash linenums="1" + npm run start + ``` - > **danger** Remember to restart _Webpack_ whenever `webpack.config.js` changes, as it does not live-reload the configuration. +!!! danger "Danger" + Remember to restart _Webpack_ whenever `webpack.config.js` changes, as it does not live-reload the configuration. -### <i class="fa fa-file-import"></i> Write _HTML_ files `import` statements +### :fontawesome-solid-file-import: Write _HTML_ files `import` statements - Edit `main.js`. Look at the `TODO #import-html` comments for instructions to remove all `templateUrl` properties. - <details><summary> Show the main.js file </summary> - main.js - - ```js - // ... - - const outlet = document.querySelector("#content-outlet"); - const router = new Router(outlet); - router - .register("", { - component: WelcomeComponent, - }) - .register("welcome", { - component: WelcomeComponent, - }) - .register("game", { - component: GameComponent, - }) - .register("score", { - component: ScoreComponent, - }); - ``` - </details> +??? note "Show the main.js file" + === "main.js" + + ```js linenums="1" + // ... + + const outlet = document.querySelector("#content-outlet"); + const router = new Router(outlet); + router + .register("", { + component: WelcomeComponent, + }) + .register("welcome", { + component: WelcomeComponent, + }) + .register("game", { + component: GameComponent, + }) + .register("score", { + component: ScoreComponent, + }); + ``` - Edit `game.js`. Look at the `TODO #import-html` comments for instructions. - Import `game.html` template as `template` with a [**default import**](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import#default_import). - Edit the `GameComponent` function and assign `template` to a new attribute `this.template` - <details><summary> Show the game.js file </summary> - game.js - ```js - import template from "../views/game.html"; +??? note "Show the game.js file" + === "game.js" - function GameComponent() { - // ... - this.template = template; - } - ``` + ```js linenums="1" + import template from "../views/game.html"; - </details> + function GameComponent() { + // ... + this.template = template; + } + ``` - Browse to [localhost:8080](http://localhost:8080) and test the application. It should work as usual. -#### <i class="fas fa-folder-tree"></i> Modified files +#### :fontawesome-solid-folder-tree: Modified files ``` package.json @@ -133,87 +131,88 @@ src/main.js webpack.config.js ``` -#### <i class="fa fa-list-check"></i> Checklist +#### :fontawesome-solid-list-check: Checklist -- [ ] I know how to use ES **default imports**. -- [ ] I know how `html-loader` works. -- [ ] The codebase has no unresolved `TODO #import-html` comments left. +- <input type="checkbox" /> I know how to use ES **default imports**. +- <input type="checkbox" /> I know how `html-loader` works. +- <input type="checkbox" /> The codebase has no unresolved `TODO #import-html` comments left. -> **question** Build the code with `npm run build` and look at the code generated in `dist/main.js`. -> -> - What is this file size? -> - How many files have been transferred after going through the three views, with `size=2`? +!!! question "Question" + Build the code with `npm run build` and look at the code generated in `dist/main.js`. -## <i class="fab fa-html5"></i> Bundle the assets <Optional /> + - What is this file size? + - How many files have been transferred after going through the three views, with `size=2`? + +## :fontawesome-brands-html5: Bundle the assets +<div id="optional">+ Optional</div> With _Webpack_ it is even possible to use ES imports to import assets, such as `.png` files. -### <i class="fa fa-screwdriver-wrench"></i> Configuration <Diy/> +### :fontawesome-solid-screwdriver-wrench: Configuration <span id="diy">:fontawesome-solid-wrench: Do it yourself</span> Edit `webpack.config.js` and add a new rule to load images: -webpack.config.js +=== "webpack.config.js" -```js{10-13} -// ... -module.exports = { - mode: "development", - devtool: "inline-source-map", - entry: "./src/main.js", - // ... - module: { - rules: [ + ```js linenums="1" // ... - { - test: /\.(png|jpg|gif)$/i, - type: 'asset/resource' - } - ], - }, -}; -``` + module.exports = { + mode: "development", + devtool: "inline-source-map", + entry: "./src/main.js", + // ... + module: { + rules: [ + // ... + { + test: /\.(png|jpg|gif)$/i, + type: 'asset/resource' + } + ], + }, + }; + ``` -### <i class="fa fa-file-import"></i> Write `imports` for the _png_ files <Diy/> +### :fontawesome-solid-file-import: Write `imports` for the _png_ files <span id="diy">:fontawesome-solid-wrench: Do it yourself</span> - Edit `game.js`. Look at the `TODO #import-assets` comments for instructions. - Locate the variable named `CARDS_IMAGE`. It is an array containing the card images paths. Use ES default imports to import the images and replace the `CARD_IMAGES` content with the imported modules. - <details><summary> Show the game.js file </summary> - - ```js - import back from "/src/assets/cards/back.png"; - import card0 from "/src/assets/cards/card-0.png"; - import card1 from "/src/assets/cards/card-1.png"; - import card2 from "/src/assets/cards/card-2.png"; - import card3 from "/src/assets/cards/card-3.png"; - import card4 from "/src/assets/cards/card-4.png"; - import card5 from "/src/assets/cards/card-5.png"; - import card6 from "/src/assets/cards/card-6.png"; - import card7 from "/src/assets/cards/card-7.png"; - import card8 from "/src/assets/cards/card-8.png"; - import card9 from "/src/assets/cards/card-9.png"; - - var CARDS_IMAGE = [ - back, - card0, - card1, - card2, - card3, - card4, - card5, - card6, - card7, - card8, - card9, - ]; - ``` - - </details> - -#### <i class="fas fa-folder-tree"></i> Modified files +??? note "Show the game.js file" + + ```js linenums="1" + import back from "/src/assets/cards/back.png"; + import card0 from "/src/assets/cards/card-0.png"; + import card1 from "/src/assets/cards/card-1.png"; + import card2 from "/src/assets/cards/card-2.png"; + import card3 from "/src/assets/cards/card-3.png"; + import card4 from "/src/assets/cards/card-4.png"; + import card5 from "/src/assets/cards/card-5.png"; + import card6 from "/src/assets/cards/card-6.png"; + import card7 from "/src/assets/cards/card-7.png"; + import card8 from "/src/assets/cards/card-8.png"; + import card9 from "/src/assets/cards/card-9.png"; + + var CARDS_IMAGE = [ + back, + card0, + card1, + card2, + card3, + card4, + card5, + card6, + card7, + card8, + card9, + ]; + ``` + + +#### :fontawesome-solid-folder-tree: Modified files ``` package.json @@ -223,11 +222,8 @@ src/app/scripts/game.js webpack.config.js ``` -<style> -summary { - color: #cb2468; - text-align: center; - margin-bottom: 20px; - margin-top: 20px -} -</style> \ No newline at end of file +<span id="commit">:fontawesome-brands-git-alt: Commit !</span> + +## :fontawesome-solid-graduation-cap: Assessment + +<iframe src="https://docs.google.com/forms/d/e/1FAIpQLSdzd5BXpKEJ5F1mtPcSuM3pn8ozs5WzHHllvsdVUavYQMCn5A/viewform?embedded=true" width="100%" height="640" frameborder="0" marginheight="0" marginwidth="0">Chargement…</iframe> diff --git a/resources/docs/docs/chapters/tp/npm.md b/resources/docs/docs/chapters/tp/npm.md index 933ea9a..dc95fba 100644 --- a/resources/docs/docs/chapters/tp/npm.md +++ b/resources/docs/docs/chapters/tp/npm.md @@ -381,9 +381,13 @@ front-end/ ├── package.json ``` -<span id="commit">:fontawesome-brands-git-alt: Commit !</span> - #### :fontawesome-solid-list-check: Checklist - <input type="checkbox" /> I know what _NPM scripts_ are. - <input type="checkbox" /> I can run my application running the `npm start` command. + +<span id="commit">:fontawesome-brands-git-alt: Commit !</span> + +## :fontawesome-solid-graduation-cap: Assessment + +<iframe src="https://docs.google.com/forms/d/e/1FAIpQLScHVcKj7HiXR0CJvkX5MC49GlUGPk-bPJuYQHWinOErGnThGA/viewform?embedded=true" width="100%" height="640" frameborder="0" marginheight="0" marginwidth="0">Chargement…</iframe> diff --git a/resources/docs/docs/chapters/tp/setup.md b/resources/docs/docs/chapters/tp/setup.md index c621f09..4619817 100644 --- a/resources/docs/docs/chapters/tp/setup.md +++ b/resources/docs/docs/chapters/tp/setup.md @@ -141,5 +141,9 @@ front-end/ - <input type="checkbox" /> I can play _MEME-Ory_ game on [http://localhost:8080/src](http://localhost:8080/src) +## :fontawesome-solid-graduation-cap: Assessment + +<iframe src="https://docs.google.com/forms/d/e/1FAIpQLSf5PqALBMoJlCeF-F9Y0fOyfExredhgncpoyb_muAUu9rwgjQ/viewform?embedded=true" width="100%" height="640" frameborder="0" marginheight="0" marginwidth="0">Chargement…</iframe> + [game-mockup]: ../../assets/mockup.png [flag]: ../../icons/solid/flag-checkered.svg diff --git a/resources/docs/docs/chapters/tp/spa.md b/resources/docs/docs/chapters/tp/spa.md index 35c3e5e..b4dedfe 100644 --- a/resources/docs/docs/chapters/tp/spa.md +++ b/resources/docs/docs/chapters/tp/spa.md @@ -200,10 +200,14 @@ src/app/views/score.html src/app/views/welcome.html ``` -<span id="commit">:fontawesome-brands-git-alt: Commit !</span> - #### :fontawesome-solid-list-check: Checklist - <input type="checkbox" /> I know how to open and use the browser developer console. - <input type="checkbox" /> I know what is a _SPA_ and why it may be a better choice. - <input type="checkbox" /> The application runs as usual. + +<span id="commit">:fontawesome-brands-git-alt: Commit !</span> + +## :fontawesome-solid-graduation-cap: Assessment + +<iframe src="https://docs.google.com/forms/d/e/1FAIpQLScjqa2rG-bBbDAId1iaHMVPrrgi0C1uRZTm5kht4fxuNP4ilQ/viewform?embedded=true" width="100%" height="640" frameborder="0" marginheight="0" marginwidth="0">Chargement…</iframe> diff --git a/resources/docs/docs/chapters/tp/variables.md b/resources/docs/docs/chapters/tp/variables.md index ee6715d..d2cf14d 100644 --- a/resources/docs/docs/chapters/tp/variables.md +++ b/resources/docs/docs/chapters/tp/variables.md @@ -7,134 +7,139 @@ tag: With _ES5_, we used to define each variable along with the [`var` keyword](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/var). -> **danger** A variable defined with the `var` keyword is **[hoisted](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/var)** and its scope is the global function scope in which it is defined. For this reason, a variable declared with `var` may have a strange behavior and `let`/`const` should be preferred over `var`. -> -> <details><summary> Show some examples </summary> -> var -> -> ```js{2} -> if (true) { -> var result = "done"; -> } -> -> console.log(result); // => done -> ``` -> -> @tab let -> -> ```js{2} -> if (true) { -> let result = "done"; -> } -> -> console.log(result); // => error -> ``` -> - -## `let` / `const` <BadgeEasy /> +!!! danger "Danger" + A variable defined with the `var` keyword is **[hoisted](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/var)** and its scope is the global function scope in which it is defined. For this reason, a variable declared with `var` may have a strange behavior and `let`/`const` should be preferred over `var`. + +??? note "Show some examples" + === "var" + + ```js linenums="1" + if (true) { + var result = "done"; + } + + console.log(result); // => done + ``` + + === "let" + + ```js linenums="1" + if (true) { + let result = "done"; + } + + console.log(result); // => error + ``` + + +## `let` / `const` <span id="easy">:fontawesome-solid-star: Easy</span> Since _ES6_, [`let`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let) and [`const`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/const) have replaced `var`, with a better handling of the [variable scope](https://developer.mozilla.org/en-US/docs/Glossary/Scope). -### `var` <Diy/> +### `var` <span id="diy">:fontawesome-solid-wrench: Do it yourself</span> - Locate the methods `init` and `_appendCard` in `GameComponent` - `init()` fetches the game configuration (ie: the card pairs) and calls `_appendCard()` - `_appendCard()` adds the cards one by one to the document and adds an `eventListener` on cards click. - Edit `game.js`. Let's try to remove the `_appendCard()` function and add its content right in the `init()` function as this code is only used once... - game.js - before - - ```js{10,19-25} - export class GameComponent { - // ... - init() { - // fetch the cards configuration from the server - this.fetchConfig( - function (config) { - // ... - for (var i in this._cards) { - var card = this._cards[i]; - this._appendCard(card); - } - - this.start(); - }.bind(this) - ); - } - - _appendCard(card) { +=== "game.js - before" + ```js linenums="1" + export class GameComponent { + // ... + init() { + // fetch the cards configuration from the server + this.fetchConfig( + function (config) { + // ... + for (var i in this._cards) { + var card = this._cards[i]; + this._appendCard(card); + } + this.start(); + }.bind(this) + ); + } + + _appendCard(card) { this._boardElement.appendChild(card.getElement()); card.getElement().addEventListener( - "click", - function () { - this._flipCard(card); - }.bind(this) - ); + "click", + function () { + this._flipCard(card); + }.bind(this) + ); + } } - } - ``` - - game.js - after - - ```js{9-17,25} - export class GameComponent { - // ... - init() { - // fetch the cards configuration from the server - this.fetchConfig( - function (config) { - // ... - for (var i in this._cards) { - var card = this._cards[i]; - this._boardElement.appendChild(card.getElement()); - - card.getElement().addEventListener( - "click", - function () { - this._flipCard(card); - }.bind(this) - ); - } - - this.start(); - }.bind(this) - ); + ``` + +=== "game.js - after" + ```js linenums="1" + export class GameComponent { + // ... + init() { + // fetch the cards configuration from the server + this.fetchConfig( + function (config) { + // ... + for (var i in this._cards) { + var card = this._cards[i]; + this._boardElement.appendChild(card.getElement()); + + card.getElement().addEventListener( + "click", + function () { + this._flipCard(card); + }.bind(this) + ); + } + + this.start(); + }.bind(this) + ); + } + // removed _appendCard(card) { } } - - // removed _appendCard(card) { } - } - ``` + ``` - Try to flip a card again and you should realize that **things are broken**. -### `let`, `const` <Diy/> +### `let`, `const` <span id="diy">:fontawesome-solid-wrench: Do it yourself</span> - Replace the `var` keyword by `let` or `const`, to fix the issue. Look at the `TODO #let-const` comments for instructions. - > **question** **Why does this make it work again?** - > <details><summary> Show the answer </summary> - > - `var` is scoped to the function (eg: it is _hoisted_ at the top of the _function's brackets pair_) - > - `var card` variable is reused in each loop iteration - > - when a card is clicked, the `card` variable is already assigned to its last value (the last card) and passed to `this._flipCard(card)`. - > </details> +!!! question "Question" + **Why does this make it work again?** + +??? note "Show the answer" + + - `var` is scoped to the function (eg: it is _hoisted_ at the top of the _function's brackets pair_) + - `var card` variable is reused in each loop iteration + - when a card is clicked, the `card` variable is already assigned to its last value (the last card) and passed to `this._flipCard(card)`. - Edit `game.js`: whenever you see the `var` keyword in the whole file, replace it with either `let` or `const`. - Test the game. Everything should work as usual. - Apply the same changes in `welcome.js` and `score.js` files. -#### <i class="fas fa-folder-tree"></i> Modified files +#### :fontawesome-solid-folder-tree: Modified files ``` src/app/scripts/game.js ``` -#### <i class="fa fa-list-check"></i> Checklist +#### :fontawesome-solid-list-check: Checklist + +- <input type="checkbox" /> I know the differences between `var` and `let`/`const`. +- <input type="checkbox" /> I know what do _scope_ and _hoisting_ mean. +- <input type="checkbox" /> All `var` have been replaced with either `let` or `const`. +- <input type="checkbox" /> The codebase has no unresolved `TODO #let-const` comments left. +- <input type="checkbox" /> The application runs as usual. + +<span id="commit">:fontawesome-brands-git-alt: Commit !</span> + +## :fontawesome-solid-graduation-cap: Assessment -- [ ] I know the differences between `var` and `let`/`const`. -- [ ] I know what do _scope_ and _hoisting_ mean. -- [ ] All `var` have been replaced with either `let` or `const`. -- [ ] The codebase has no unresolved `TODO #let-const` comments left. -- [ ] The application runs as usual. +<iframe src="https://docs.google.com/forms/d/e/1FAIpQLScwuzXwjtf2IaP_NGB-GbNOMRrfsscnBGZtVz2ztmpol-TctA/viewform?embedded=true" width="100%" height="640" frameborder="0" marginheight="0" marginwidth="0">Chargement…</iframe> diff --git a/resources/docs/docs/chapters/tp/webpack.md b/resources/docs/docs/chapters/tp/webpack.md index 6de5149..dae4f22 100644 --- a/resources/docs/docs/chapters/tp/webpack.md +++ b/resources/docs/docs/chapters/tp/webpack.md @@ -445,3 +445,7 @@ package-lock.json - What is the size of this file? - The source code for `welcome.js`, `game.js` and `score.js` has not been bundled at all. Why? + +## :graduation-cap: Assessment + +<iframe src="https://docs.google.com/forms/d/e/1FAIpQLSdzd5BXpKEJ5F1mtPcSuM3pn8ozs5WzHHllvsdVUavYQMCn5A/viewform?embedded=true" width="100%" height="640" frameborder="0" marginheight="0" marginwidth="0">Chargement…</iframe> diff --git a/resources/docs/docs/stylesheets/global.css b/resources/docs/docs/stylesheets/global.css index cd933f1..01c97c2 100644 --- a/resources/docs/docs/stylesheets/global.css +++ b/resources/docs/docs/stylesheets/global.css @@ -2,7 +2,7 @@ h2, h3 { color: #cb2468; font-weight: 500 !important; ; } -#getting-started, #the-package-manager, #single-page-application-spa, #webpack { +#getting-started, #the-package-manager, #single-page-application-spa, #webpack, #the-bundle, #css-loader, #classes, #variables, #arrow-functions-template-literals, #functional-programming { color: #cb2468; font-weight: 700; } diff --git a/resources/docs/mkdocs.yml b/resources/docs/mkdocs.yml index 9303815..1375ae6 100644 --- a/resources/docs/mkdocs.yml +++ b/resources/docs/mkdocs.yml @@ -27,7 +27,16 @@ theme: name: "material" logo: assets/logo.png palette: - primary: pink + - scheme: default + primary: pink + toggle: + icon: material/brightness-7 + name: Switch to dark mode + - scheme: slate + primary: pink + toggle: + icon: material/brightness-4 + name: Switch to light mode features: - navigation.tabs - navigation.instant -- GitLab