Merge remote-tracking branch 'upstream/main'

pull/203/head
Eugene Goh 4 years ago
commit 9b45a7bd39

@ -88,7 +88,7 @@ back add r0,r1
信じられないかもしれませんが、両者は同じ処理をしています。
いずれもフィボナッチ数を順番に10個出力します。
✅ [フィボナッチ数](https://en.wikipedia.org/wiki/Fibonacci_number)とは、各数がその手前の二つの値の和である値の集合です。
✅ [フィボナッチ数](https://ja.wikipedia.org/wiki/%E3%83%95%E3%82%A3%E3%83%9C%E3%83%8A%E3%83%83%E3%83%81%E6%95%B0)とは、各数がその手前の二つの値の和である値の集合です。
## プログラムの基本要素
@ -244,4 +244,4 @@ COBOLとGoについては
## 課題
[assignment.md](assignment.ja.md)
[ドキュメントを読む](assignment.ja.md)

@ -63,7 +63,7 @@ Let's say you have a folder locally with some code project and you want to start
git init
```
1. **Check status**. To check the status if your repository type:
1. **Check status**. To check the status of your repository type:
```bash
git status

@ -1,6 +1,6 @@
# アクセシブルな Web ページの作成
![All About Accessibility](webdev101-a11y.png)
![All About Accessibility](../webdev101-a11y.png)
> Sketchnote by [Tomomi Imura](https://twitter.com/girlie_mac)
## レッスン前の小テスト
@ -84,7 +84,7 @@ CSS は、ページ上のあらゆる要素の外観を完全に制御するこ
ご想像の通り、スクリーンリーダーはリンクテキストをページ上の他のテキストと同じように読みます。このことを念頭に置いて、以下に示すテキストは完全に許容できると感じるかもしれません。
> 妖精ペンギンと呼ばれることもあるコガタペンギンは、世界最小のペンギンです。[詳しくはこちら](https://en.wikipedia.org/wiki/Little_penguin) をご覧ください。
> 妖精ペンギンと呼ばれることもあるコガタペンギンは、世界最小のペンギンです。[詳しくはこちら](https://ja.wikipedia.org/wiki/%E3%82%B3%E3%82%AC%E3%82%BF%E3%83%9A%E3%83%B3%E3%82%AE%E3%83%B3) をご覧ください。
> 妖精ペンギンと呼ばれることもあるリトルペンギンは、世界最小のペンギンです。詳しくは https://ja.wikipedia.org/wiki/%E3%82%B3%E3%82%AC%E3%82%BF%E3%83%9A%E3%83%B3%E3%82%AE%E3%83%B3 をご覧ください。
@ -224,6 +224,6 @@ CSS は、ページ上のあらゆる要素の外観を完全に制御するこ
## 課題
[Analyze a non-accessible web site](assignment.ja.md)
[アクセスできない Web サイトを分析する](assignment.ja.md)
Credits: [Turtle Ipsum](https://github.com/Instrument/semantic-html-sample) by Instrument

@ -4,7 +4,7 @@
> Sketchnote di [Tomomi Imura](https://twitter.com/girlie_mac)
## Quiz pre-lezione
[Quiz pre-lezione](https://nice-beach-0fe9e9d0f.azurestaticapps.net/quiz/7)
[Quiz pre-lezione](https://nice-beach-0fe9e9d0f.azurestaticapps.net/quiz/7?loc=it)
Questa lezione copre le basi di JavaScript, il linguaggio che fornisce l'interattività sul web.
@ -116,13 +116,13 @@ Le variabili possono memorizzare tutti i tipi di numero, inclusi decimali o nume
Esistono diversi tipi di operatori da utilizzare quando si eseguono funzioni aritmetiche e alcuni sono elencati qui:
| Simbolo | Descrizione | Esempio |
| ------ | ------------------------------------------------------------------------ | -------------------------------- |
| `+` | **Addizione**: calcola la somma di due numeri | `1 + 2 // la risposta attesa è 3` |
| `-` | **Sottrazione**: calcola la differenza tra due numeri | `1-2 // la risposta attesa è -1` |
| `*` | **Moltiplicazione**: calcola il prodotto di due numeri | `1 * 2 // la risposta attesa è 2` |
| `/` | **Divisione**: calcola il quoziente di due numeri | `1/2 // la risposta attesa è 0,5` |
| `%` | **Resto**: calcola il resto dalla divisione di due numeri | `1 % 2 // la risposta attesa è 1` |
| Simbolo | Descrizione | Esempio |
| ------- | --------------------------------------------------------- | --------------------------------- |
| `+` | **Addizione**: calcola la somma di due numeri | `1 + 2 // la risposta attesa è 3` |
| `-` | **Sottrazione**: calcola la differenza tra due numeri | `1-2 // la risposta attesa è -1` |
| `*` | **Moltiplicazione**: calcola il prodotto di due numeri | `1 * 2 // la risposta attesa è 2` |
| `/` | **Divisione**: calcola il quoziente di due numeri | `1/2 // la risposta attesa è 0,5` |
| `%` | **Resto**: calcola il resto dalla divisione di due numeri | `1 % 2 // la risposta attesa è 1` |
✅ Proviamolo! Provare un'operazione aritmetica nella console del proprio browser. I risultati sorprendono?
@ -184,7 +184,7 @@ I booleani possono avere solo due valori: vero (`true`) o falso (`false`). I bo
JavaScript è noto per i suoi modi sorprendenti di gestire talvolta i tipi di dato. Effettuare un po' di ricerca su questi "trabocchetti". Ad esempio: la distinzione tra maiuscole e minuscole può mordere! Provare questo nella propria console: `let age = 1; let Age = 2; age == Age` (risulta `false` - perché?). Quali altri trabocchetti si riescono a trovare?
## quiz post-lezione
[Quiz post-lezione](https://nice-beach-0fe9e9d0f.azurestaticapps.net/quiz/8)
[Quiz post-lezione](https://nice-beach-0fe9e9d0f.azurestaticapps.net/quiz/8?loc=it)
## Revisione e auto apprendimento

@ -1,6 +1,6 @@
# JavaScript の基本: データ型
![JavaScript Basics - Data types](images/webdev101-js-datatypes.png)
![JavaScript Basics - Data types](../images/webdev101-js-datatypes.png)
> Sketchnote by [Tomomi Imura](https://twitter.com/girlie_mac)
## レッスン前の小テスト
@ -111,7 +111,7 @@ const MY_VARIABLE = 123;
`let myVariable = 123;`
変数は、小数や負の数を含むあらゆるタイプの数値を格納することができます。数値は、[次のセクション](#operators)で説明する算術演算子と一緒に使用することもできます。
変数は、小数や負の数を含むあらゆるタイプの数値を格納することができます。数値は、[次のセクション](#算術演算子)で説明する算術演算子と一緒に使用することもできます。
### 算術演算子
@ -171,7 +171,7 @@ let myString2 = "World";
### ブール値
ブール値は2つの値だけを持つことができます。`true` か `false` です。ブール値は、特定の条件が満たされたときにどの行のコードを実行するかを決定するのに役立ちます。多くの場合、[演算子](#operators) がブール値の設定を支援し、変数が初期化されたり、演算子で値が更新されたりしていることに気付いたり、書いたりすることが多いでしょう。
ブール値は2つの値だけを持つことができます。`true` か `false` です。ブール値は、特定の条件が満たされたときにどの行のコードを実行するかを決定するのに役立ちます。多くの場合、[演算子](#算術演算子) がブール値の設定を支援し、変数が初期化されたり、演算子で値が更新されたりしていることに気付いたり、書いたりすることが多いでしょう。
- `let myTrueBool = true`
- `let myFalseBool = false`

@ -5,7 +5,7 @@
## Quiz pre-lezione
[Quiz Pre-Lezione](https://nice-beach-0fe9e9d0f.azurestaticapps.net/quiz/9)
[Quiz Pre-Lezione](https://nice-beach-0fe9e9d0f.azurestaticapps.net/quiz/9?loc=it)
Quando si pensa di scrivere codice, ci si vuole sempre assicurare che il proprio codice sia leggibile. Anche se questo sembra controintuitivo, il codice viene letto molte più volte di quanto non venga scritto. Uno strumento base nella cassetta degli attrezzi dello sviluppatore è la **funzione**
@ -184,7 +184,7 @@ Si riesce ad articolare in una frase la differenza tra funzioni e metodi? Fare u
## Quiz post-lezione
[Quiz post-lezione](https://nice-beach-0fe9e9d0f.azurestaticapps.net/quiz/10)
[Quiz post-lezione](https://nice-beach-0fe9e9d0f.azurestaticapps.net/quiz/10?loc=it)
## Revisione e auto apprendimento

@ -1,6 +1,6 @@
# JavaScript の基本: メソッドと関数
![JavaScript Basics - Functions](images/webdev101-js-functions.png)
![JavaScript Basics - Functions](../images/webdev101-js-functions.png)
> Sketchnote by [Tomomi Imura](https://twitter.com/girlie_mac)
## レッスン前の小テスト

@ -162,7 +162,7 @@ Creare un programma che viene scritto prima con operatori logici, quindi riscriv
---
## Quiz Post-Lezione
[Quiz post-lezione](https://nice-beach-0fe9e9d0f.azurestaticapps.net/quiz/12?loc=12)
[Quiz post-lezione](https://nice-beach-0fe9e9d0f.azurestaticapps.net/quiz/12?loc=it)
## Revisione e Auto Apprendimento

@ -1,6 +1,6 @@
# JavaScript の基本: 意思決定
![JavaScript Basics - Making decisions](images/webdev101-js-decisions.png)
![JavaScript Basics - Making decisions](../images/webdev101-js-decisions.png)
> Sketchnote by [Tomomi Imura](https://twitter.com/girlie_mac)
## レッスン前の小テスト

@ -4,7 +4,7 @@
> Sketchnote by [Tomomi Imura](https://twitter.com/girlie_mac)
## 강의 전 퀴즈
[Pre-lecture quiz](https://nice-beach-0fe9e9d0f.azurestaticapps.net/quiz/11&loc=ko)
[Pre-lecture quiz](https://nice-beach-0fe9e9d0f.azurestaticapps.net/quiz/11?loc=ko)
결정을 내리고 코드가 실행되는 순서를 제어하면 코드를 재사용하며 강력하게 만들 수 있습니다. 이 강의에서는 JavaScript에서 데이터 흐름을 제어하기 위한 구문과 논리 자료형 데이터 타입을 함께 사용하는 중요성을 다룹니다.

@ -113,7 +113,7 @@ for (let i = 0; i < iceCreamFlavors.length; i++) {
There are other ways of looping over arrays other than for and while loops. There are [forEach](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach), [for-of](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...of), and [map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map). Rewrite your array loop using one of these techniques.
## Post-Lecture Quiz
[Pre-lecture quiz](https://nice-beach-0fe9e9d0f.azurestaticapps.net/quiz/14)
[Post-lecture quiz](https://nice-beach-0fe9e9d0f.azurestaticapps.net/quiz/14)
## Review & Self Study

@ -113,7 +113,7 @@ for (let i = 0; i < iceCreamFlavors.length; i++) {
Esistono altri modi per eseguire un ciclo sugli array. diversi dai cicli for e while. Ci sono [forEach](https://developer.mozilla.org/it/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach), [for-of](https://developer.mozilla.org/it/docs/Web/JavaScript/Reference/Statements/for...of) e [map](https://developer.mozilla.org/it/docs/Web/JavaScript/Reference/Global_Objects/Array/map). Riscrivere il ciclo sull'array usando una di queste tecniche.
## Quiz Post-Lezione
[Quiz Post-Lezione](https://nice-beach-0fe9e9d0f.azurestaticapps.net/quiz/14?loc=14)
[Quiz Post-Lezione](https://nice-beach-0fe9e9d0f.azurestaticapps.net/quiz/14?loc=it)
## Revisione e Auto Apprendimento

@ -1,6 +1,6 @@
# JavaScript の基本: 配列とループ
![JavaScript Basics - Arrays](images/webdev101-js-arrays.png)
![JavaScript Basics - Arrays](../images/webdev101-js-arrays.png)
> Sketchnote by [Tomomi Imura](https://twitter.com/girlie_mac)
## レッスン前の小テスト
@ -89,7 +89,7 @@ while (i < 10) {
}
```
✅ for loop と while loop のどちらかを選ぶ理由は何ですか? 17K の視聴者が StackOverflow で同じ質問をしていましたが、その意見の中には[あなたにとって興味深いものもあるかもしれません](https://stackoverflow.com/questions/39969145/while-loops-vs-for-loops-in-javascript)。
✅ for loop と while loop のどちらかを選ぶ理由は何ですか? 同じ質問が StackOverflow で約2万回閲覧されていましたが、その意見の中には[あなたにとって興味深いものもあるかもしれません](https://stackoverflow.com/questions/39969145/while-loops-vs-for-loops-in-javascript)。
## ループと配列

@ -4,7 +4,7 @@
> Sketchnote by [Tomomi Imura](https://twitter.com/girlie_mac)
## 강의 전 퀴즈
[Pre-lecture quiz](https://nice-beach-0fe9e9d0f.azurestaticapps.net/quiz/13&loc=ko)
[Pre-lecture quiz](https://nice-beach-0fe9e9d0f.azurestaticapps.net/quiz/13?loc=ko)
이 강의에서는 웹에서 상호 작용을 제공하는 언어인 JavaScript의 기본 사항을 다룹니다. 데이터를 컨트롤하는 데 사용하는 배열과 반복문에 대해 알아 봅니다.
@ -112,7 +112,7 @@ for (let i = 0; i < iceCreamFlavors.length; i++) {
for문과 while문 외에 배열을 반복하는 다른 방법이 있습니다. [forEach](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach), [for-of](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...of) 그리고 [map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map)도 있습니다. 해당 기술 중 하나를 사용하여 배열 반복을 다시 작성하십시오.
## 강의 후 퀴즈
[Post-lecture quiz](https://nice-beach-0fe9e9d0f.azurestaticapps.net/quiz/14&loc=ko)
[Post-lecture quiz](https://nice-beach-0fe9e9d0f.azurestaticapps.net/quiz/14?loc=ko)
## 리뷰 & 자기주도 학습

@ -4,10 +4,10 @@ JavaScript ist die Sprache des Webs. In diesen vier Lektionen lernen Sie die Gru
### Themen
1. [Variablen und Datentypen](1-Datentypen/README.md)
2. [Funktionen und Methoden](2-functions-methods/README.md)
3. [Entscheidungen mit JavaScript treffen](3-making-decisions/README.md)
4. [Arrays und Loops](4-arrays-loops/README.md)
1. [Variablen und Datentypen](../1-data-types/translations/README.de.md)
2. [Funktionen und Methoden](../2-functions-methods/translations/README.de.md)
3. [Entscheidungen mit JavaScript treffen](../3-making-decisions/translations/README.de.md)
4. [Arrays und Loops](../4-arrays-loops/translations/README.de.md)
### Credits

@ -4,10 +4,10 @@ JavaScript es el lenguaje de la web. En estas cuatro lecciones, aprenderá sus c
### Temas
1. [Variables y tipos de datos](data-types/README.md)
2. [Funciones y métodos](variables-datatypes/README.md)
3. [Toma de decisiones con JavaScript](making-decisions/README.md)
4. [Arrays and Loops](arrays-loops/README.md)
1. [Variables y tipos de datos](../1-data-types/translations/README.es.md)
2. [Funciones y métodos](../2-functions-methods/translations/README.es.md)
3. [Toma de decisiones con JavaScript](../3-making-decisions/translations/README.es.md)
4. [Arrays and Loops](../4-arrays-loops/translations/README.es.md)
### Créditos

@ -4,10 +4,10 @@ JavaScript è il linguaggio del web. In queste quattro lezioni, si impareranno l
### Argomenti
1. [Variabili e Tipi di Dato](1-data-types/translations/README.it.md)
2. [Funzioni e Metodi](2-functions-methods/translations/README.it.md)
3. [Prendere Decisioni con JavaScript](3-making-decisions/translations/README.it.md)
4. [Array e Cicli](4-arrays-loops/translations/README.it.md)
1. [Variabili e Tipi di Dato](../1-data-types/translations/README.it.md)
2. [Funzioni e Metodi](../2-functions-methods/translations/README.it.md)
3. [Prendere Decisioni con JavaScript](../3-making-decisions/translations/README.it.md)
4. [Array e Cicli](../4-arrays-loops/translations/README.it.md)
### Crediti

@ -4,10 +4,10 @@ JavaScript は Web の言語です。この4つのレッスンでは、その基
### トピック
1. [変数とデータ型](1-data-types/README.md)
2. [関数とメソッド](2-functions-methods/README.md)
3. [JavaScript での意思決定](3-making-decisions/README.md)
4. [配列とループ](4-arrays-loops/README.md)
1. [変数とデータ型](../1-data-types/translations/README.ja.md)
2. [関数とメソッド](../2-functions-methods/translations/README.ja.md)
3. [JavaScript での意思決定](../3-making-decisions/translations/README.ja.md)
4. [配列とループ](../4-arrays-loops/translations/README.ja.md)
### Credits

@ -4,10 +4,10 @@ JavaScript adalah bahasa web. Dalam empat pelajaran ini, anda akan mengetahui as
### Topik
1. [Pemboleh ubah dan Jenis Data](1-data-types/README.md)
2. [Fungsi dan Kaedah](2-functions-methods/README.md)
3. [Membuat Keputusan dengan JavaScript](3-making-decisions/README.md)
4. [Susunan dan Gelung](4-arrays-loops/README.md)
1. [Pemboleh ubah dan Jenis Data](../1-data-types/translations/README.ms.md)
2. [Fungsi dan Kaedah](../2-functions-methods/translations/README.ms.md)
3. [Membuat Keputusan dengan JavaScript](../3-making-decisions/translations/README.ms.md)
4. [Susunan dan Gelung](../4-arrays-loops/translations/README.ms.md)
### Kredit

@ -1,6 +1,6 @@
# テラリウムプロジェクト その1: HTML 入門
![Introduction to HTML](images/webdev101-html.png)
![Introduction to HTML](../images/webdev101-html.png)
> Sketchnote by [Tomomi Imura](https://twitter.com/girlie_mac)
## レッスン前の小テスト
@ -17,7 +17,7 @@ HTML (HyperText Markup Language) は、Web の「骨格」です。CSS が HTML
コンピュータ上に 'terrarium' というフォルダを作成し、その中に 'index.html' というファイルを作成します。テラリウムのフォルダを作成した後、Visual Studio Code で新しい VS Code ウィンドウを開き、「フォルダを開く」をクリックして新しいフォルダに移動することで、この作業を行うことができます。エクスプローラペインの「ファイル」ボタンをクリックして、新しいファイルを作成してください。
![explorer in VS Code](images/vs-code-index.png)
![explorer in VS Code](../images/vs-code-index.png)
もしくは

@ -1,6 +1,6 @@
# テラリウムプロジェクト その2: CSS 入門
![Introduction to CSS](images/webdev101-css.png)
![Introduction to CSS](../images/webdev101-css.png)
> Sketchnote by [Tomomi Imura](https://twitter.com/girlie_mac)
## レッスン前の小テスト
@ -11,7 +11,7 @@
CSS (カスケードスタイルシート) は、Web 開発の重要な問題である、Web サイトの見栄えを良くする方法を解決します。また、CSS を使用してレスポンシブ ウェブ デザイン (RWD) を作成することもできます。CSS の仕様には、アプリケーションの洗練されたインタラクションを可能にするアニメーションや変換が含まれています。CSS Working Group は現在の CSS 仕様の維持を支援しており、[World Wide Web Consortium のサイト](https://www.w3.org/Style/CSS/members) でその活動を追うことができます。
> 注: CSS は Web 上のすべてのものと同様に進化する言語であり、すべてのブラウザが仕様の新しい部分をサポートしているわけではありません。常に [CanIUse.com](caniuse.com) に相談して実装を確認してください。
> 注: CSS は Web 上のすべてのものと同様に進化する言語であり、すべてのブラウザが仕様の新しい部分をサポートしているわけではありません。常に [CanIUse.com](https://caniuse.com) を参照して実装を確認してください。
このレッスンでは、オンラインテラリウムにスタイルを追加し、カスケード、継承、セレクタの使用、配置、CSS を使ったレイアウトの作成など、CSS の概念について学びます。その過程で、テラリウムをレイアウトし、実際のテラリウムを作成していきます。
@ -37,13 +37,13 @@ CSS (カスケードスタイルシート) は、Web 開発の重要な問題で
インラインスタイル "color: red" を `<h1>` タグに追加します。
```HTML
```html
<h1 style="color: red">My Terrarium</h1>
```
次に、以下のコードを `style.css` ファイルに追加します。
```CSS
```css
h1 {
color: blue;
}
@ -61,7 +61,7 @@ h1 {
body のフォントを指定されたフォントに設定し、入れ子になっている要素のフォントを確認します。
```
```css
body {
font-family: helvetica, arial, sans-serif;
}
@ -69,7 +69,7 @@ body {
ブラウザのコンソールから「要素」タブを開き、h1 のフォントを観察してください。ブラウザ内で述べられているように、フォントは body から継承されています。
![inherited font](images/1.png)
![inherited font](../images/1.png)
✅ 入れ子になったスタイルを別のプロパティを継承させることはできますか?
@ -81,7 +81,7 @@ body {
これまでのところ、`style.css` ファイルにはいくつかのタグがスタイリングされているだけで、アプリはかなり変な感じになっています。
```
```css
body {
font-family: helvetica, arial, sans-serif;
}
@ -94,12 +94,11 @@ h1 {
このようにタグをスタイリングすることで、ユニークな要素をコントロールすることができますが、テラリウム内の多くの植物のスタイルをコントロールする必要があります。そのためには、CSS セレクタを利用する必要があります。
### Id
### id
Add some style to layout the left and right containers. Since there is only one left container and only one right container, they are given ids in the markup. To style them, use `#`:
左と右のコンテナをレイアウトするためのスタイルを追加します。左のコンテナと右のコンテナは1つしかないので、マークアップでは id が与えられます。スタイルを設定するには、`#` を使用します。
```
```css
#left-container {
background-color: #eee;
width: 15%;
@ -247,7 +246,7 @@ HTML マークアップの各植物には、id とクラスの組み合わせが
瓶の左下の部分に「泡」のような輝きを加えて、よりガラスのように見えるようにします。`.jar-glossy-long` と `.jar-glossy-short` は反射光のように見えるようにスタイリングします。このようになります。
![finished terrarium](./images/terrarium-final.png)
![finished terrarium](../images/terrarium-final.png)
レッスン後の小テストを完成させるには、この Learn モジュールを通ってください: [HTML アプリを CSS でスタイルを整える](https://docs.microsoft.com/ja-jp/learn/modules/build-simple-website/4-css-basics?WT.mc_id=academic-13441-cxa)

@ -1,6 +1,6 @@
# テラリウムプロジェクト その3: DOM の操作とクロージャ
![DOM and a closure](images/webdev101-js.png)
![DOM and a closure](../images/webdev101-js.png)
> Sketchnote by [Tomomi Imura](https://twitter.com/girlie_mac)
## レッスン前の小テスト
@ -19,7 +19,7 @@ DOM (Document Object Model) を操作することは、Web 開発の重要な
DOM をツリーと考え、Web ページのドキュメントを操作できるすべての方法を表しています。様々な API (Application Program Interfaces) が書かれており、プログラマーが自分の好きなプログラミング言語を使って DOM にアクセスし、編集、変更、再配置、その他の管理ができるようになっています。
![DOM tree representation](./images/dom-tree.png)
![DOM tree representation](../images/dom-tree.png)
> DOM とそれを参照する HTML マークアップの表現。[Olfa Nasraoui](https://www.researchgate.net/publication/221417012_Profile-Based_Focused_Crawler_for_Social_Media-Sharing_Websites) より
@ -34,7 +34,7 @@ DOM をツリーと考え、Web ページのドキュメントを操作できる
terrarium フォルダ内に `script.js` というファイルを作成します。このファイルを `<head>` セクションにインポートします。
```html
<script src="./script.js" defer></script>
<script src="./script.js" defer></script>
```
> 注: HTML ファイルが完全に読み込まれた後にのみ JavaScript を実行できるようにするために、外部の JavaScript ファイルを HTML ファイルにインポートする際には `defer` を使用してください。また、`async` 属性を使用することもできます。これは HTML ファイルのパース中にスクリプトを実行できるようにしますが、私たちの場合は、ドラッグスクリプトを実行する前に HTML 要素を完全に利用できるようにしておくことが重要です。
@ -46,7 +46,7 @@ terrarium フォルダ内に `script.js` というファイルを作成します
### タスク
```html
```javascript
dragElement(document.getElementById('plant1'));
dragElement(document.getElementById('plant2'));
dragElement(document.getElementById('plant3'));
@ -141,7 +141,7 @@ function pointerDrag(e) {
`pos4 = e.clientY` の下にポインタイベントの操作を2つ追加して、初期関数を完成させます。
```html
```javascript
document.onpointermove = elementDrag;
document.onpointerup = stopElementDrag;
```
@ -191,7 +191,7 @@ function stopElementDrag() {
これでプロジェクトが完成しました!
🥇おめでとうございます。素敵なテラリウムが完成しましたね。 ![finished terrarium](./images/terrarium-final.png)
🥇おめでとうございます。素敵なテラリウムが完成しましたね。 ![finished terrarium](../images/terrarium-final.png)
---

@ -2,13 +2,13 @@
ちょっとしたドラッグ&ドロップのコードメディテーション。少しの HTML、JS、CSS で、Web インターフェースを構築し、スタイルを設定し、インタラクションを追加することができます。
![my terrarium](images/screenshot_gray.png)
![my terrarium](../images/screenshot_gray.png)
# レッスン
1. [HTML 入門](./1-intro-to-html/README.md)
2. [CSS 入門](./2-intro-to-css/README.md)
3. [DOM と JS Closures の紹介](./3-intro-to-DOM-and-closures/README.md)
1. [HTML 入門](../1-intro-to-html/translations/README.ja.md)
2. [CSS 入門](../2-intro-to-css/translations/README.ja.md)
3. [DOM の操作とクロージャ](../3-intro-to-DOM-and-closures/translations/README.ja.md)
## クレジット

@ -23,7 +23,7 @@
## レッスン
[イベント駆動型プログラミングを用いたタイピングゲームの作成](./typing-game/translations/README.ja.md)
[イベント駆動型プログラミングを用いたタイピングゲームの作成](../typing-game/translations/README.ja.md)
## クレジット

@ -2,7 +2,7 @@
## Quiz Pre-Lezione
[Quiz Pre-Lezione](https://nice-beach-0fe9e9d0f.azurestaticapps.net/quiz/21)
[Quiz Pre-Lezione](https://nice-beach-0fe9e9d0f.azurestaticapps.net/quiz/21?loc=it)
## Programmazione guidata dagli eventi
@ -328,7 +328,7 @@ Aggiungere più funzionalità
## Quiz Post-Lezione
[Quiz post-lezione](https://nice-beach-0fe9e9d0f.azurestaticapps.net/quiz/22)
[Quiz post-lezione](https://nice-beach-0fe9e9d0f.azurestaticapps.net/quiz/22?loc=it)
## Revisione e Auto Apprendimento

@ -5,7 +5,7 @@
## Quiz Pre-Lezione
[Quiz Pre-Lezione](https://nice-beach-0fe9e9d0f.azurestaticapps.net/quiz/23)
[Quiz Pre-Lezione](https://nice-beach-0fe9e9d0f.azurestaticapps.net/quiz/23?loc=it)
### Introduzione
@ -152,7 +152,7 @@ Si dia un'occhiata alle estensioni del browser in uno store e se ne installi una
## Quiz Post-Lezione
[Quiz post-lezione](https://nice-beach-0fe9e9d0f.azurestaticapps.net/quiz/24)
[Quiz post-lezione](https://nice-beach-0fe9e9d0f.azurestaticapps.net/quiz/24?loc=it)
## Revisione e Auto Apprendimento

@ -2,7 +2,7 @@
## Quiz Pre-Lezione
[Quiz Pre-Lezione](https://nice-beach-0fe9e9d0f.azurestaticapps.net/quiz/25)
[Quiz Pre-Lezione](https://nice-beach-0fe9e9d0f.azurestaticapps.net/quiz/25?loc=it)
### Introduzione
@ -212,8 +212,7 @@ Finora si è discusso sui diversi tipi di API in queste lezioni. Scegliere un'AP
## Quiz Post-Lezione
[
Quiz post-lezione](https://nice-beach-0fe9e9d0f.azurestaticapps.net/quiz/26)
[Quiz post-lezione](https://nice-beach-0fe9e9d0f.azurestaticapps.net/quiz/26?loc=it)
## Revisione e Auto Apprendimento

@ -12,11 +12,11 @@
### 拡張機能で操作する要素の設定:
この時点で、フォーム用の HTML とブラウザ拡張機能用の結果 `<div>` が構築されています。これからは、`/src/index.js` ファイルを使って少しずつ拡張機能を構築していく必要があります。プロジェクトのセットアップとビルドのプロセスについては [前のレッスン](.../.../1-about-browsers/translations/README.ja.md) を参照してください。
この時点で、フォーム用の HTML とブラウザ拡張機能用の結果 `<div>` が構築されています。これからは、`/src/index.js` ファイルを使って少しずつ拡張機能を構築していく必要があります。プロジェクトのセットアップとビルドのプロセスについては [前のレッスン](/5-browser-extension/1-about-browsers/translations/README.ja.md) を参照してください。
`index.js` ファイルの中で作業を行うには、様々なフィールドに関連付けられた値を保持するための `const` 変数を作成することから始めます。
```JavaScript
```javascript
// フォームフィールド
const form = document.querySelector('.form-data');
const region = document.querySelector('.region-name');
@ -38,7 +38,7 @@ const clearBtn = document.querySelector('.clear-btn');
次に、フォームにイベントリスナーを追加し、フォームをリセットするクリアボタンを追加します。ユーザーがフォームを送信したり、リセットボタンをクリックしたりした場合に、そのリセットボタンをクリックするようにします。何かが起こるときのため、ファイルの一番下にアプリを初期化するための呼び出しを追加します。
```JavaScript
```javascript
form.addEventListener('submit', (e) => handleSubmit(e));
clearBtn.addEventListener('click', (e) => reset(e));
init();
@ -50,7 +50,7 @@ init();
今度は拡張機能を初期化する関数を作ります。これは init() と呼ばれています。
```JavaScript
```javascript
function init() {
//何かがローカルストレージにある場合は、それをピックアップします。
const storedApiKey = localStorage.getItem('apiKey');
@ -112,7 +112,7 @@ API キーに文字列の値を設定して、例えば Edge では Web ペー
イベント引数 `(e)` を受け取る関数 `handleSubmit` を作成します。イベントの伝播を停止し (この場合、ブラウザの更新を停止したい)、新しい関数 `setUpUser` を呼び出し、引数 `apiKey.value``region.value` を渡します。このようにして、適切なフィールドが入力されたときに初期フォームから取得される2つの値を利用します。
```JavaScript
```javascript
function handleSubmit(e) {
e.preventDefault();
setUpUser(apiKey.value, region.value);
@ -124,7 +124,7 @@ function handleSubmit(e) {
次に `setUpUser` 関数に進み、ここで apiKey と regionName のローカルストレージの値を設定します。新しい関数を追加します。
```JavaScript
```javascript
function setUpUser(apiKey, regionName) {
localStorage.setItem('apiKey', apiKey);
localStorage.setItem('regionName', regionName);
@ -153,7 +153,7 @@ function setUpUser(apiKey, regionName) {
C02Signal API に問い合わせを行うための新しい関数を作成します:
```JavaScript
```javascript
import axios from '../node_modules/axios';
async function displayCarbonUsage(apiKey, region) {

@ -2,7 +2,7 @@
## Quiz Pre-Lezione
[Quiz Pre-Lezione](https://nice-beach-0fe9e9d0f.azurestaticapps.net/quiz/27)
[Quiz Pre-Lezione](https://nice-beach-0fe9e9d0f.azurestaticapps.net/quiz/27?loc=it)
### Introduzione
@ -147,7 +147,7 @@ Esaminare alcuni siti web open source che esistono da molto tempo e, in base all
## Quiz Post-Lezione
[Quiz post-lezione](https://nice-beach-0fe9e9d0f.azurestaticapps.net/quiz/28)
[Quiz post-lezione](https://nice-beach-0fe9e9d0f.azurestaticapps.net/quiz/28?loc=it)
## Revisione e Auto Apprendimento

@ -60,7 +60,7 @@
`src/index.js` の中で作業し、`calculateColor()` という関数を、DOM にアクセスするために設定した一連の変数 `const` の後に追加します。
```JavaScript
```javascript
function calculateColor(value) {
let co2Scale = [0, 150, 600, 750, 800];
let colors = ['#2AA364', '#F5EB4D', '#9E4229', '#381D02', '#381D02'];
@ -93,7 +93,7 @@ chrome.runtime には、あらゆる種類のバックグラウンドタスク
ここで、`init()` 関数の中で、アイコンをジェネリックグリーンに設定して、chrome の `updateIcon` アクションを再度呼び出して起動します。
```JavaScript
```javascript
chrome.runtime.sendMessage({
action: 'updateIcon',
value: {
@ -105,14 +105,14 @@ chrome.runtime.sendMessage({
次に、C02Signal API が返すプロミスに追加して、先ほど作成した関数を呼び出します。
```JavaScript
```javascript
//let CO2...
calculateColor(CO2);
```
最後に、`/dist/background.js` で、これらのバックグラウンドアクションの呼び出し用のリスナーを追加します。
```JavaScript
```javascript
chrome.runtime.onMessage.addListener(function (msg, sender, sendResponse) {
if (msg.action === 'updateIcon') {
chrome.browserAction.setIcon({ imageData: drawIcon(msg.value) });

@ -20,6 +20,6 @@
"webpack-cli": "^3.3.12"
},
"dependencies": {
"axios": "^0.19.2"
"axios": "^0.21.1"
}
}

@ -8,13 +8,13 @@ Edge、Chrome、Firefox で動作するブラウザ拡張機能を構築しま
### トピック
1. [ブラウザについて](1-about-browsers/translations/README.ja.md)
2. [フォームとローカルストレージ](2-forms-browsers-local-storage/translations/README.ja.md)
3. [バックグラウンドタスクとパフォーマンス](3-background-tasks-and-performance/translations/README.ja.md)
1. [ブラウザのすべて](../1-about-browsers/translations/README.ja.md)
2. [API の呼び出し、ローカルストレージの使用](../2-forms-browsers-local-storage/translations/README.ja.md)
3. [バックグラウンドタスクとパフォーマンスについて学ぶ](../3-background-tasks-and-performance/translations/README.ja.md)
### クレジット
![a green browser extension](extension-screenshot.png)
![a green browser extension](../extension-screenshot.png)
## クレジット

@ -211,7 +211,7 @@ eventEmitter.on(Messages.HERO_MOVE_LEFT, () => {
## लेक्चर बाद की क्विज
[लेक्चर बाद की क्विज](https://nice-beach-0fe9e9d0f.azurestaticapps.net/quiz/30?loc=30)
[लेक्चर बाद की क्विज](https://nice-beach-0fe9e9d0f.azurestaticapps.net/quiz/30?loc=hi)
## समीक्षा और स्व अध्ययन

@ -0,0 +1,224 @@
# Costruire un Gioco Spaziale Parte 1: Introduzione
![video](../../images/pewpew.gif)
## Quiz Pre-Lezione
[Quiz Pre-Lezione](https://nice-beach-0fe9e9d0f.azurestaticapps.net/quiz/29?loc=it)
### Ereditarietà e Composizione nello sviluppo del gioco
Nelle lezioni precedenti, non c'era molto bisogno di preoccuparsi dell'architettura di progettazione delle app che sono state create, poiché i progetti erano di portata molto ridotta. Tuttavia, quando le dimensioni e l'ambito delle applicazioni aumentano, le decisioni sull'architettura diventano una preoccupazione maggiore. Esistono due approcci principali per creare applicazioni più grandi in JavaScript: *composizione* o *ereditarietà*. Ci sono pro e contro in entrambi, che verranno spiegiati dall'interno del contesto di un gioco.
✅ Uno dei libri di programmazione più famosi mai scritti ha a che fare con [i modelli di progettazione](https://it.wikipedia.org/wiki/Design_Patterns).
In un gioco ci sono `oggetti di gioco` che esistono su uno schermo. Ciò significa che hanno una posizione su un sistema di coordinate cartesiane, caratterizzati dall'avere una coordinata `x` e una `y` . Man mano che si sviluppa un gioco si noterà che tutti gli oggetti di gioco hanno una proprietà standard, comune a ogni gioco che si crea, ovvero elementi che sono:
- **basati sulla posizione** La maggior parte, se non tutti, gli elementi del gioco sono basati sulla posizione. Ciò significa che hanno una posizione, una `x` e una `y`.
- **mobili** Questi sono oggetti che possono spostarsi in una nuova posizione. Tipicamente un eroe, un mostro o un personaggio non giocante (NPC - Non Player Character), ma non, ad esempio, un oggetto statico come un albero.
- **autodistruggenti** Questi oggetti esistono solo per un determinato periodo di tempo prima che vengano contrassegnati per l'eliminazione. Di solito questo è rappresentato da un booleano `morto` o `distrutto` che segnala al motore di gioco che questo oggetto non dovrebbe più essere renderizzato.
- **raffreddamento** Il "raffreddamento" è una proprietà tipica tra gli oggetti di breve durata. Un classico esempio è un pezzo di testo o un effetto grafico come un'esplosione che dovrebbe essere visto solo per pochi millisecondi.
✅ Si pensi a un gioco come Pac-Man. Si riescono a identificare i quattro tipi di oggetti sopra elencati in questo gioco?
### Esprimere il comportamento
Quanto descritto sopra è il comportamento che possono avere gli oggetti di gioco. Allora come vanno codificati? Si può esprimere questo comportamento tramite metodi associati a classi o oggetti.
**Classi**
L'idea è di usare `classi` insieme all'`ereditarietà` per ottenere l'aggiunta di un determinato comportamento a una classe.
✅ L'ereditarietà è un concetto importante da comprendere. Ulteriori informazioni sull'[articolo di MDN sull'ereditarietà](https://developer.mozilla.org/it/docs/Web/JavaScript/Inheritance_and_the_prototype_chain).
Espresso tramite codice, un oggetto di gioco può tipicamente avere questo aspetto:
```javascript
//Imposta la classe GameObject
class GameObject {
constructor(x, y, type) {
this.x = x;
this.y = y;
this.type = type;
}
}
//Questa classe estenderà le proprietà di classe di GameObject
class Movable extends GameObject {
constructor(x,y, type) {
super(x,y, type)
}
//questo oggetto movibile può essere spostato nello schermo
moveTo(x, y) {
this.x = x;
this.y = y;
}
}
//classe specifica che estende la classe Movable, per poter approfittare di tutte le proprietà che eredita
class Hero extends Movable {
constructor(x,y) {
super(x,y, 'Hero')
}
}
//questa classe, d'altro canto, eredita solo le proprietà di GameObject
class Tree extends GameObject {
constructor(x,y) {
super(x,y, 'Tree')
}
}
//un eroe può spostarsi...
const hero = new Hero();
hero.moveTo(5,5);
//ma un albero no
const tree = new Tree();
```
✅ Ci si prenda qualche minuto per rivedere un eroe di Pac-Man (Inky, Pinky o Blinky, per esempio) e come sarebbe scritto in JavaScript.
**Composizione**
Un modo diverso di gestire l'ereditarietà degli oggetti consiste nell'usare la *composizione*. Con questo sistema gli oggetti esprimono il loro comportamento in questo modo:
```javascript
//crea una costante gameObject
const gameObject = {
x: 0,
y: 0,
type: ''
};
//...e una costante movable
const movable = {
moveTo(x, y) {
this.x = x;
this.y = y;
}
}
//poi la costante movableObject viene composta dalle costanti gameObject e movable
const movableObject = {...gameObject, ...movable};
//quindi si scrive una funzione per crear un nuovo eroe che eredita le proprietà di movableObject
function createHero(x, y) {
return {
...movableObject,
x,
y,
type: 'Hero'
}
}
//...e un oggetto statico che eredita solo le proprietà di gameObject
function createStatic(x, y, type) {
return {
...gameObject
x,
y,
type
}
}
//crea l'eroe e lo muove
const hero = createHero(10,10);
hero.moveTo(5,5);
//e crea un albero statico immobile
const tree = createStatic(0,0, 'Tree');
```
**Quale modello si dovrebbe usare?**
Dipende dallo sviluppatore quale modello scegliere. JavaScript supporta entrambi questi paradigmi.
--
Un altro modello comune nello sviluppo di giochi affronta il problema della gestione dell'esperienza utente e delle prestazioni del gioco.
## Modello pub/sub
✅ Pub/Sub sta per pubblica/sottoscrivi ('publish-subscribe')
Questo modello indirizza l'idea che parti disparate della propria applicazione non dovrebbero sapere l'una dell'altra. Perché? Rende molto più facile vedere cosa sta succedendo in generale se le varie parti sono separate. Inoltre, è più facile cambiare improvvisamente un comportamento se necessario. Come si realizza? Si fa stabilendo alcuni concetti:
- **messaggio**: un messaggio è solitamente una stringa di testo accompagnata da un payload opzionale (un dato che chiarisce di cosa tratta il messaggio). Un messaggio tipico in un gioco può essere `KEY_PRESSED_ENTER`.
- **publisher**: questo elemento *pubblica* un messaggio e lo invia a tutti i sottoscrittori.
- **subscriber**: questo elemento *ascolta* messaggi specifici e svolge alcune attività come risultato della ricezione di questo messaggio, come sparare con un laser.
L'implementazione è di dimensioni piuttosto ridotte ma è un modello molto potente. Ecco come può essere implementato:
```javascript
//imposta la classe EventEmitter che contiene i listener
class EventEmitter {
constructor() {
this.listeners = {};
}
//quando un messaggio viene ricevuto, si fa gestire il suo payload al listener
on(message, listener) {
if (!this.listeners[message]) {
this.listeners[message] = [];
}
this.listeners[message].push(listener);
}
//quando un messaggio viene ricevuto, si invia a un listener con un payload
emit(message, payload = null) {
if (this.listeners[message]) {
this.listeners[message].forEach(l => l(message, payload))
}
}
}
```
Per utilizzare il codice qui sopra si può creare un'implementazione molto piccola:
```javascript
//impostazione di una struttura di messaggio
const Messages = {
HERO_MOVE_LEFT: 'HERO_MOVE_LEFT'
};
//invoca l'eventEmitter impostato sopra
const eventEmitter = new EventEmitter();
//imposta un eroe
const hero = createHero(0,0);
//fa in modo che eventEmitter sappia come monitorare i messages di pertinenza dell'eroe per spostarsi a sinistra, e agisce di conseguenza
eventEmitter.on(Messages.HERO_MOVE_LEFT, () => {
hero.move(5,0);
});
//imposta window per ascoltare un evento keyup, nello specifico se viene premuto freccia sinistra emette un messaggio che fa spostare l'eroe a sinistra
window.addEventListener('keyup', (evt) => {
if (evt.key === 'ArrowLeft') {
eventEmitter.emit(Messages.HERO_MOVE_LEFT)
}
});
```
Sopra si collega un evento della tastiera, `ArrowLeft` e si invia il messaggio `HERO_MOVE_LEFT` (spostamento a sinistra - n.d.t.). Questo messaggio viene recepito e come risultato si sposta l'eroe (`hero`). Il punto di forza di questo modello è che l'event listener e l'eroe non sanno nulla l'uno dell'altro. Si può rimappare `ArrowLeft` sul tasto `A`. Inoltre sarebbe possibile fare qualcosa di completamente diverso su `ArrowLeft` apportando alcune modifiche alla funzione `on` di eventEmitter:
```javascript
eventEmitter.on(Messages.HERO_MOVE_LEFT, () => {
hero.move(5,0);
});
```
Man mano che le cose diventano più complicate quando il gioco cresce, questo modello rimane lo stesso in termini di complessità e il proprio codice rimane pulito. Si consiglia vivamente di adottare questo modello.
---
## 🚀 Sfida
Pensare a come il modello pub/sub può migliorare un gioco. Quali parti dovrebbero emettere eventi e come dovrebbe reagire a questi il gioco? Ora si ha la possibilità di essere creativi, pensando a un nuovo gioco e a come potrebbero comportarsi le sue parti.
## Quiz Post-Lezione
[Quiz post-lezione](https://nice-beach-0fe9e9d0f.azurestaticapps.net/quiz/30?loc=it)
## Revisione e Auto Apprendimento
Saperne di più su Pub/Sub [leggendo qui](https://docs.microsoft.com/it-it/azure/architecture/patterns/publisher-subscriber?WT.mc_id=academic-4621-cxa).
## Compito
[Produrre uno schizzo di un gioco](assignment.it.md)

@ -29,7 +29,7 @@
クラスに特定の振る舞いを追加するために `クラス``継承` と組み合わせて使うという考え方です。
✅ 継承は理解しておくべき重要な概念です。[継承に関する MdN の記事](https://developer.mozilla.org/ja/docs/Web/JavaScript/Inheritance_and_the_prototype_chain)で詳しく解説しています。
✅ 継承は理解しておくべき重要な概念です。[継承に関する MDN の記事](https://developer.mozilla.org/ja/docs/Web/JavaScript/Inheritance_and_the_prototype_chain)で詳しく解説しています。
コードで表現すると、ゲームオブジェクトは通常このようになります。
@ -136,7 +136,7 @@ const tree = createStatic(0,0, 'Tree');
ゲーム開発に共通するもう一つのパターンは、ゲームのユーザーエクスペリエンスとパフォーマンスを処理する問題を扱っています。
## Pub/sub パターン
## Pub/Sub パターン
✅ Pub/Sub は 'publish-subscribe' の略です。

@ -0,0 +1,11 @@
# Produrre uno schizzo di un gioco
## Istruzioni
Utilizzando gli esempi di codice nella lezione, scrivere una rappresentazione di un gioco che piace. Dovrà essere un gioco semplice, ma l'obiettivo è usare la classe o il modello di composizione e il modello pub/sub per mostrare come potrebbe essere avviato un gioco. Si dia sfogo alla propria creatività!
## Rubrica
| Criteri | Ottimo | Adeguato | Necessita miglioramento |
| -------- | ------------------------------------------------------- | ----------------------------------------------------- | --------------------------------------------------- |
| | Tre elementi vengono posizionati sullo schermo e manipolati | Due elementi vengono posizionati sullo schermo e manipolati | Un elemento viene posizionato sullo schermo e manipolato |

@ -0,0 +1,216 @@
# Costruire un Gioco Spaziale Parte 2: Disegnare Eroi e Mostri sull'elemento Canvas
## Quiz Pre-Lezione
[Quiz Pre-Lezione](https://nice-beach-0fe9e9d0f.azurestaticapps.net/quiz/31?loc=it)
## Canvas
Canvas è un elemento HTML che per impostazione predefinita non ha contenuto; è una lavagna vuota. Si può riempirla disegnandoci sopra.
✅ [Ulteriori informazioni sull'API Canvas](https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API) su MDN.
Ecco come viene tipicamente dichiarato, come parte dell'elemento body della pagina:
```html
<canvas id="myCanvas" width="200" height="100"></canvas>
```
Sopra si è impostato l'`id`entificativo, la larghezza `(width)` e l'altezza (`height`).
- `id`: va impostato in modo da poter ottenere un riferimento quando si deve interagire con l'elemento.
- `width`: questa è la larghezza dell'elemento.
- `height`: questa è l'altezza dell'elemento.
## Disegnare una geometria semplice
Canvas utilizza un sistema di coordinate cartesiane per disegnare le cose. Quindi utilizza un asse x e un asse y per esprimere dove si trova qualcosa. La posizione `0,0` è la posizione in alto a sinistra e quella in basso a destra è ciò che si è determinato come larghezza (WIDTH) e altezza (HEIGHT) del canvas
![la griglia del canvas](../canvas_grid.png)
> Immagine da [MDN](https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Drawing_shapes)
Per disegnare sull'elemento canvas si dovranno eseguire i seguenti passaggi:
1. **Ottenere un riferimento** all'elemento Canvas.
1. **Ottenere un riferimento** all'elemento Context che si trova sull'elemento canvas.
1. **Eseguire un'operazione di disegno** utilizzando l'elemento context.
Il codice per i passaggi precedenti di solito ha questo aspetto:
```javascript
// disegna un rettangolo rosso
//1. ottiene il riferimento per il canvas
canvas = document.getElementById("myCanvas");
//2. ottiene l'oggetto context per disegnare forme basiche in 2D
ctx = canvas.getContext("2d");
//3. lo riempie con il colore rosso
ctx.fillStyle = 'red';
//4. e disegna un rettangolo con questi parametri, impostando posizione e dimensione
ctx.fillRect(0,0, 200, 200) // x,y,larghezza, altezza
```
✅ L'API Canvas si concentra principalmente su forme 2D, ma si possono anche disegnare elementi 3D su un sito web; per questo, si potrebbe utilizzare l' [API WebGL](https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API).
Si può disegnare ogni sorta di cose con l'API Canvas come:
- **Forme geometriche**, è già stato mostrato come disegnare un rettangolo, ma c'è molto di più che si può disegnare.
- **Testo**, si può disegnare un testo con qualsiasi carattere e colore si desideri.
- **Immagini**, si puòdisegnare un'immagine basandosi su una risorsa immagine come .jpg o .png, ad esempio.
Si faccia una prova! Si sa come disegnare un rettangolo, si può disegnare un cerchio su una pagina? Si dia un'occhiata ad alcuni interessanti disegni su canvas su CodePen. Ecco un [esempio particolarmente degno di nota](https://codepen.io/dissimulate/pen/KrAwx).
## Caricare e disegnare una risorsa immagine
Si carica una risorsa immagine creando un oggetto `Image` e impostando la sua proprietà `src` . Quindi ci si mette in ascolto per l'evento di caricamento (`load`) per sapere quando è pronto per essere utilizzato. Il codice si presenta cosí:
### Caricamento risorsa
```javascript
const img = new Image();
img.src = 'path/to/my/image.png';
img.onload = () => {
// immagine caricata e pronta all'uso
}
```
### Modello di Caricamento Risorsa
Si consiglia di racchiudere quanto sopra in un costrutto come questo, così è più facile da usare e si tenta di manipolarlo solo quando è completamente caricato:
```javascript
function loadAsset(path) {
return new Promise((resolve) => {
const img = new Image();
img.src = path;
img.onload = () => {
// immagine caricata e pronta all'uso
resolve(img);
}
})
}
// usarlo in questo modo
async function run() {
const heroImg = await loadAsset('hero.png')
const monsterImg = await loadAsset('monster.png')
}
```
Per disegnare risorse di gioco su uno schermo, il codice sarà simile a questo:
```javascript
async function run() {
const heroImg = await loadAsset('hero.png')
const monsterImg = await loadAsset('monster.png')
canvas = document.getElementById("myCanvas");
ctx = canvas.getContext("2d");
ctx.drawImage(heroImg, canvas.width/2,canvas.height/2);
ctx.drawImage(monsterImg, 0,0);
}
```
## Ora è il momento di iniziare a costruire il gioco
### Cosa costruire
Si costruirà una pagina web con un elemento Canvas. Si dovrebbe rendere uno schermo nero `1024 * 768`. Sono state fornite due immagini:
- Astronave dell'eroe
![Nave dell'eroe](../solution/assets/player.png)
- Mostro 5*5
![Nave del mostro](../solution/assets/enemyShip.png)
### Passaggi consigliati per iniziare lo sviluppo
Individuare i file che già sono stati creati nella sottocartella `your-work` della cartella di lavoro Dovrebbe contenere le seguenti informazioni:
```bash
-| assets
-| enemyShip.png
-| player.png
-| index.html
-| app.js
-| package.json
```
Aprire una copia di questa cartella in Visual Studio Code. È necessario disporre di una configurazione di ambiente di sviluppo locale, preferibilmente con Visual Studio Code con NPM e Node installati. Se non si è impostato `npm` sul proprio computer, [ecco come farlo](https://www.npmjs.com/get-npm).
Inizializzare il proprio progetto accedendo alla cartella `your_work` :
```bash
cd your-work
npm start
```
Quanto sopra avvierà un server HTTP sull'indirizzo `http://localhost:5000`. Aprire un browser e inserire quell'indirizzo. Al momento è una pagina vuota, ma cambierà
> Nota: per vedere le modifiche sullo schermo, aggiornare il contenuto del browser.
### Aggiungere codice
Aggiungi il codice necessario al file `your-work/app.js` per risolvere quanto segue
1. **Disegnare** un oggetto canvas con sfondo nero
> suggerimento: aggiungere due righe sotto il TODO appropriato in `/app.js`, impostando l'elemento `ctx` in modo che sia nero e le coordinate alto/sinistra a 0,0 e l'altezza e la larghezza uguali a quelle del canvas.
2. **Caricare** le strutture di gioco
> suggerimento: aggiungere le immagini del giocatore e del nemico usando `await loadTexture`, passando il percorso dell'immagine. Non saranno ancora visibili sullo schermo!
3. **Disegnare** l'eroe al centro dello schermo nella metà inferiore
> suggerimento: usare l'API `drawImage` per disegnare `heroImg` sullo schermo, impostando `canvas.width / 2 - 45` e `canvas.height - canvas.height / 4` come valori di coordinate x, y
4. **Disegnare** mostri 5*5
> suggerimento: ora si può rimuovere il commento dal codice per disegnare i nemici sullo schermo. Successivamente, passare alla funzione `createEnemies` e crearla.
Per prima cosa, impostare alcune costanti:
```javascript
const MONSTER_TOTAL = 5;
const MONSTER_WIDTH = MONSTER_TOTAL * 98;
const START_X = (canvas.width - MONSTER_WIDTH) / 2;
const STOP_X = START_X + MONSTER_WIDTH;
```
quindi, creare un ciclo per disegnare l'array di mostri sullo schermo:
```javascript
for (let x = START_X; x < STOP_X; x += 98) {
for (let y = 0; y < 50 * 5; y += 50) {
ctx.drawImage(enemyImg, x, y);
}
}
```
## Risultato
Il risultato finale dovrebbe essere così:
![Schermo nero con un eroe e mostri 5*5](../partI-solution.png)
## Soluzione
Per favore provare a risolverlo da soli, ma se si rimane bloccati, dare un'occhiata alla [soluzione](../solution/app.js)
---
## 🚀 Sfida
Si è imparato a disegnare con l'API Canvas incentrata sul 2D; dare un'occhiata all['API WebGL API](https://developer.mozilla.org/it/docs/Web/API/WebGL_API) e provare a disegnare un oggetto 3D.
## Quiz Post-Lezione
[Quiz post-lezione](https://nice-beach-0fe9e9d0f.azurestaticapps.net/quiz/32?loc=it)
## Revisione e Auto Apprendimento
Scoprire di più sull'API Canvas raccogliendo [informazioni su di essa](https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API).
## Compito
[Giocare con l'API Canvas](assignment.it.md)

@ -0,0 +1,11 @@
# Giocare con l'API Canvas
## Istruzioni
Sceglire un elemento dell'API Canvas e creare qualcosa di interessante attorno ad esso. Si è in grado di creare una piccola galassia di stelle ripetute? Si riesce a creare una interessante struttura di linee colorate? Si puoi guardare CodePen per l'ispirazione (ma non copiare)
## Rubrica
| Criteri | Ottimo | Adeguato | Necessita miglioramento |
| -------- | --------------------------------------------------------- | ----------------------------------- | --------------------- |
| | Il codice viene inviato mostrando una struttura o una forma interessante | Il codice viene inviato, ma non viene eseguito | Il codice non è stato inviato |

@ -1,24 +1,24 @@
function loadTexture(path) {
return new Promise((resolve) => {
const img = new Image();
img.src = path;
img.onload = () => {
resolve(img);
};
});
return new Promise((resolve) => {
const img = new Image()
img.src = path
img.onload = () => {
resolve(img)
}
})
}
function createEnemies(ctx, canvas, enemyImg) {
// TODO draw enemies
// TODO draw enemies
}
window.onload = async () => {
canvas = document.getElementById('canvas');
ctx = canvas.getContext('2d');
// TODO load textures
canvas = document.getElementById('canvas')
ctx = canvas.getContext('2d')
// TODO load textures
// TODO draw black background
// TODO draw hero
// TODO uncomment the next line when you add enemies to screen
//createEnemies(ctx, canvas, enemyImg);
};
// TODO draw black background
// TODO draw hero
// TODO uncomment the next line when you add enemies to screen
//createEnemies(ctx, canvas, enemyImg);
}

@ -0,0 +1,388 @@
# Costruire un Gioco Spaziale parte 3: Aggiungere il Movimento
## Quiz Pre-Lezione
[Quiz Pre-Lezione](https://nice-beach-0fe9e9d0f.azurestaticapps.net/quiz/33?loc=it)
I giochi non sono molto divertenti finché non si hanno alieni che scorazzano per lo schermo! In questo gioco, si utilizzeranno due tipi di movimenti:
- **Movimento tastiera/mouse**: quando l'utente interagisce con la tastiera o il mouse per spostare un oggetto sullo schermo.
- **Movimento indotto dal gioco**: quando il gioco sposta un oggetto con un certo intervallo di tempo.
Quindi come si spostano le cose su uno schermo? Dipende tutto dalle coordinate cartesiane: si cambia la posizione (x, y) di un oggetto, poi si ridisegna lo schermo.
In genere sono necessari i seguenti passaggi per eseguire il *movimento* su uno schermo:
1. **Impostare una nuova posizione** per un oggetto; questo è necessario per percepire l'oggetto come se si fosse spostato.
2. **Cancellare lo schermo, lo** schermo deve essere cancellato tra un disegno e un altro. Si può cancellarlo disegnando un rettangolo che viene riempito con un colore di sfondo.
3. **Ridisegnare l'oggetto** in una nuova posizione. In questo modo si può finalmente spostare l'oggetto da una posizione all'altra.
Ecco come può apparire nel codice:
```javascript
//imposta la posizione dell'eroe
hero.x += 5;
// pulisce il rettangolo che ospita l'eroe
ctx.clearRect(0, 0, canvas.width, canvas.height);
// ridisegna lo sfondo del gioco e l'eroe
ctx.fillRect(0, 0, canvas.width, canvas.height)
ctx.fillStyle = "black";
ctx.drawImage(heroImg, hero.x, hero.y);
```
✅ Si riesce a pensare a un motivo per cui ridisegnare il proprio eroe con molti fotogrammi al secondo potrebbe far aumentare i costi delle prestazioni? Leggere le [alternative a questo modello](https://www.html5rocks.com/en/tutorials/canvas/performance/).
## Gestire eventi da tastiera
Gli eventi si gestiscono allegando eventi specifici al codice. Gli eventi della tastiera vengono attivati sull'intera finestra mentre gli eventi del mouse come un `clic` possono essere collegati al clic su un elemento specifico. Si useranno gli eventi della tastiera durante questo progetto.
Per gestire un evento è necessario utilizzare il metodo `addEventListener()` dell'oggetto window e fornirgli due parametri di input. Il primo parametro è il nome dell'evento, ad esempio `keyup`. Il secondo parametro è la funzione che dovrebbe essere invocata come risultato dell'evento in corso.
Ecco un esempio:
```javascript
window.addEventListener('keyup', (evt) => {
// `evt.key` = rappresentazione stringa del tasto
if (evt.key === 'ArrowUp') {
// fa qualcosa
}
})
```
Per gli eventi da tastiera ci sono due proprietà sull'evento che si possono usare usare per vedere quale tasto è stato premuto:
- `key`, questa è una rappresentazione di stringa del tasto premuto, ad esempio `ArrowUp`
- `keyCode`, questa è una rappresentazione numerica, ad esempio `37`, corrisponde a `ArrowLeft`.
✅ La manipolazione degli eventi da tastiera è utile al di fuori dello sviluppo del gioco. Quali altri usi possono venire in mente per questa tecnica?
### Tasti speciali: un avvertimento
Ci sono alcuni tasti *speciali* che influenzano la finestra. Ciò significa che se si sta ascoltando un evento `keyup` e si usano questi tasti speciali per muovere l'eroe, verrà eseguito anche lo scorrimento orizzontale. Per questo motivo si potrebbe voler *disattivare* questo comportamento del browser integrato mentre si sviluppa il gioco. Serve un codice come questo:
```javascript
let onKeyDown = function (e) {
console.log(e.keyCode);
switch (e.keyCode) {
case 37:
case 39:
case 38:
case 40: // Tasti freccia
case 32:
e.preventDefault();
break; // Barra spazio
default:
break; // non bloccare altri tasti
}
};
window.addEventListener('keydown', onKeyDown);
```
Il codice precedente assicurerà che i tasti freccia e la barra spaziatrice abbiano il loro comportamento *predefinito* disattivato. Il meccanismo *di disattivazione* si verifica quando si chiama `e.preventDefault()`.
## Movimento indotto dal gioco
E' possibile far muovere le cose da sole utilizzando timer come la funzione `setTimeout()` o `setInterval()` che aggiornano la posizione dell'oggetto a ogni tick o intervallo di tempo. Ecco come può apparire:
```javascript
let id = setInterval(() => {
//sposta il nemico sull'asse y
enemy.y += 10;
})
```
## Il ciclo di gioco
Il ciclo di gioco è un concetto che è essenzialmente una funzione che viene invocata a intervalli regolari. Si chiama ciclo di gioco poiché tutto ciò che dovrebbe essere visibile all'utente viene disegnato nel ciclo. Il ciclo di gioco utilizza tutti gli oggetti che fanno parte del gioco, disegnandoli tutti a meno che per qualche motivo non debbano più far parte del gioco. Ad esempio, se un oggetto è un nemico che è stato colpito da un laser ed esplode, non fa più parte del ciclo di gioco corrente (maggiori informazioni nelle lezioni successive).
Ecco come può apparire tipicamente un ciclo di gioco, espresso in codice:
```javascript
let gameLoopId = setInterval(() =>
function gameLoop() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "black";
ctx.fillRect(0, 0, canvas.width, canvas.height);
drawHero();
drawEnemies();
drawStaticObjects();
}, 200);
```
Il ciclo precedente viene richiamato ogni `200` millisecondi per ridisegnare il canvas. Si ha la possibilità di scegliere l'intervallo migliore che abbia senso per il proprio gioco.
## Continuare il Gioco Spaziale
Si prenderà il codice esistente per estenderlo. Si inizia con il codice che si è completato durante la parte I o si usa il codice nella [parte II-starter](../your-work).
- **Muovere l'eroe**: si aggiungerà un codice per assicurarsi di poter muovere l'eroe usando i tasti freccia.
- **Muovere i nemici**: si dovrà anche aggiungere del codice per assicurarsi che i nemici si muovano dall'alto verso il basso a una determinata velocità.
## Passaggi consigliati
Individuare i file che già sono stati creati nella sottocartella `your-work` Dovrebbe contenere quanto segue:
```bash
-| assets
-| enemyShip.png
-| player.png
-| index.html
-| app.js
-| package.json
```
Si fa partire il progetto nella cartella `your_work` digitando:
```bash
cd your-work
npm start
```
Quanto sopra avvierà un server HTTP all'indirizzo `http://localhost:5000`. Aprire un browser e inserire quell'indirizzo, in questo momento dovrebbe rendere l'eroe e tutti i nemici; niente si muove - ancora!
### Aggiungere codice
1. **Aggiungere oggetti dedicati** per `eroe`, `nemico` e `oggetto di gioco`, dovrebbero avere proprietà `x` e `y` . (Ricorda la parte su [ereditarietà o composizione](../../1-introduction/translations/README.it.md).
*SUGGERIMENTO* l'`oggetto di gioco` (GameObject) dovrebbe essere quello con `x` e `y` e la capacità di disegnare se stesso sul canvas.
> suggerimento: iniziare aggiungendo una nuova classe GameObject con il suo costruttore delineato come di seguito, quindi disegnarlo sul canvas:
```javascript
class GameObject {
constructor(x, y) {
this.x = x;
this.y = y;
this.dead = false;
this.type = "";
this.width = 0;
this.height = 0;
this.img = undefined;
}
draw(ctx) {
ctx.drawImage(this.img, this.x, this.y, this.width, this.height);
}
}
```
Ora, si estende questo GameObject per creare eroe (classe Hero) e nemico (clsse Enemy).
```javascript
class Hero extends GameObject {
constructor(x, y) {
...servono x, y, tipo, e velocità
}
}
```
```javascript
class Enemy extends GameObject {
constructor(x, y) {
super(x, y);
(this.width = 98), (this.height = 50);
this.type = "Enemy";
let id = setInterval(() => {
if (this.y < canvas.height - this.height) {
this.y += 5;
} else {
console.log('Stopped at', this.y)
clearInterval(id);
}
}, 300)
}
}
```
2. **Aggiungere gestori di eventi di tastiera** per gestire la navigazione con i tasti (spostare l'eroe su/giù, sinistra/destra)
*RICORDARE* che è un sistema cartesiano, la posizione in alto a sinistra è `0,0`. Ricordare anche di aggiungere il codice per interrompere *il comportamento predefinito*
> suggerimento: creare la funzione onKeyDown e attaccarla all'oggetto window:
```javascript
let onKeyDown = function (e) {
console.log(e.keyCode);
...aggiungere il codice dalla lezione più sopra per fermare il comportamento predefinito
}
};
window.addEventListener("keydown", onKeyDown);
```
Controllare la console del browser a questo punto e osservare le sequenze di tasti che vengono registrate.
3. **Implementare** il [modello Pub/Sub](../../1-introduction/translations/README.it.md), questo manterrà il codice pulito mentre si seguono le parti rimanenti.
Per fare quest'ultima parte, si può:
1. **Aggiungere un event listener** all'oggetto window:
```javascript
window.addEventListener("keyup", (evt) => {
if (evt.key === "ArrowUp") {
eventEmitter.emit(Messages.KEY_EVENT_UP);
} else if (evt.key === "ArrowDown") {
eventEmitter.emit(Messages.KEY_EVENT_DOWN);
} else if (evt.key === "ArrowLeft") {
eventEmitter.emit(Messages.KEY_EVENT_LEFT);
} else if (evt.key === "ArrowRight") {
eventEmitter.emit(Messages.KEY_EVENT_RIGHT);
}
});
```
1. **Creare una classe EventEmitter** per pubblicare e sottoscrivere i messaggi:
```javascript
class EventEmitter {
constructor() {
this.listeners = {};
}
on(message, listener) {
if (!this.listeners[message]) {
this.listeners[message] = [];
}
this.listeners[message].push(listener);
}
emit(message, payload = null) {
if (this.listeners[message]) {
this.listeners[message].forEach((l) => l(message, payload));
}
}
}
```
1. **Aggiungere costanti** e impostare EventEmitter:
```javascript
const Messages = {
KEY_EVENT_UP: "KEY_EVENT_UP",
KEY_EVENT_DOWN: "KEY_EVENT_DOWN",
KEY_EVENT_LEFT: "KEY_EVENT_LEFT",
KEY_EVENT_RIGHT: "KEY_EVENT_RIGHT",
};
let heroImg,
enemyImg,
laserImg,
canvas, ctx,
gameObjects = [],
hero,
eventEmitter = new EventEmitter();
```
1. **Inizializzare il gioco**
```javascript
function initGame() {
gameObjects = [];
createEnemies();
createHero();
eventEmitter.on(Messages.KEY_EVENT_UP, () => {
hero.y -=5 ;
})
eventEmitter.on(Messages.KEY_EVENT_DOWN, () => {
hero.y += 5;
});
eventEmitter.on(Messages.KEY_EVENT_LEFT, () => {
hero.x -= 5;
});
eventEmitter.on(Messages.KEY_EVENT_RIGHT, () => {
hero.x += 5;
});
}
```
1. **Impostare il ciclo di gioco**
Rifattorizzare la funzione window.onload per inizializzare il gioco e impostare un ciclo di gioco su un buon intervallo. Aggiungere anche un raggio laser:
```javascript
window.onload = async () => {
canvas = document.getElementById("canvas");
ctx = canvas.getContext("2d");
heroImg = await loadTexture("assets/player.png");
enemyImg = await loadTexture("assets/enemyShip.png");
laserImg = await loadTexture("assets/laserRed.png");
initGame();
let gameLoopId = setInterval(() => {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "black";
ctx.fillRect(0, 0, canvas.width, canvas.height);
drawGameObjects(ctx);
}, 100)
};
```
5. **Aggiungere il codice** per spostare i nemici a un certo intervallo
Rifattorizzare la funzione `createEnemies()` per creare i nemici e inserirli nella nuova classe gameObjects:
```javascript
function createEnemies() {
const MONSTER_TOTAL = 5;
const MONSTER_WIDTH = MONSTER_TOTAL * 98;
const START_X = (canvas.width - MONSTER_WIDTH) / 2;
const STOP_X = START_X + MONSTER_WIDTH;
for (let x = START_X; x < STOP_X; x += 98) {
for (let y = 0; y < 50 * 5; y += 50) {
const enemy = new Enemy(x, y);
enemy.img = enemyImg;
gameObjects.push(enemy);
}
}
}
```
e aggiungere una `funzione createHero()` per eseguire un processo simile per l'eroe.
```javascript
function createHero() {
hero = new Hero(
canvas.width / 2 - 45,
canvas.height - canvas.height / 4
);
hero.img = heroImg;
gameObjects.push(hero);
}
```
infine, aggiungere una funzione `drawGameObjects()` per avviare il disegno:
```javascript
function drawGameObjects(ctx) {
gameObjects.forEach(go => go.draw(ctx));
}
```
I nemici dovrebbero iniziare ad avanzare verso l'astronave dell'eroe!
---
## 🚀 Sfida
Come si può vedere, il proprio codice può trasformarsi in ["spaghetti code"](https://it.wikipedia.org/wiki/Spaghetti_code) quando si inizia ad aggiungere funzioni, variabili e classi. Come si puo organizzare meglio il codice in modo che sia più leggibile? Disegnare un sistema per organizzare il proprio codice, anche se risiede ancora in un file.
## Quiz Post-Lezione
[Quiz post-lezione](https://nice-beach-0fe9e9d0f.azurestaticapps.net/quiz/34?loc=it)
## Revisione e Auto Apprendimento
Mentre questo gioco viene scritto senza utilizzare infrastutture Javascript (framework), ci sono molti framework canvas basati su JavaScript per lo sviluppo di giochi. Ci si prenda un po' di tempo per [leggere qualcosa su questi](https://github.com/collections/javascript-game-engines).
## Compito
[Commentare il proprio codice](assignment.it.md)

@ -0,0 +1,11 @@
# Commentare il proprio codice
## Istruzioni
Eseminare il file /app.js corrente nella cartella del gioco e trovare i modi per commentarlo e riordinarlo. È molto facile che il codice sfugga al controllo e ora è una buona occasione per aggiungere commenti per assicurarsi di avere codice leggibile in modo da poterlo utilizzare in seguito.
## Rubrica
| Criteri | Ottimo | Adeguato | Necessita miglioramento |
| -------- | ------------------------------------------------------------------ | ------------------------------------- | -------------------------------------------------------------- |
| | Il codice in `app.js` è completamente commentato e organizzato in blocchi logici | Il codice in `app.js` è adeguatamente commentato | Il codice in `app.js` è in qualche modo disorganizzato e manca di buoni commenti |

@ -0,0 +1,297 @@
# Costruire un Gioco Spaziale Parte 4: Aggiungere un Laser e Rilevare le Collisioni
## Quiz Pre-Lezione
[Quiz Pre-Lezione](https://nice-beach-0fe9e9d0f.azurestaticapps.net/quiz/35?loc=it)
In questa lezione si imparererà come fare fuoco con i laser con JavaScript! Verranno aggiunte due cose al gioco:
- **Un laser**: questo laser viene sparato dall'astronave del proprio eroe e verticalmente verso l'alto
- **Rilevamento delle collisioni**, come parte dell'implementazione della capacità di *sparare* , si aggiungeranno anche alcune simpatiche regole di gioco:
- Il **laser colpisce il nemico**: il nemico muore se colpito da un laser
- Il **laser raggiunge lo schermo superiore**: un laser viene distrutto se raggiunge la parte superiore dello schermo
- **Collisione tra nemico ed eroe**: un nemico e l'eroe vengono distrutti se entrano in collisione
- Il **nemico raggiunge la parte inferiore dello schermo**: un nemico e un eroe vengono distrutti se il nemico raggiunge la parte inferiore dello schermo
In breve, il giocatore - *l'eroe* - deve colpire tutti i nemici con un laser prima che riescano a raggiungere la parte inferiore dello schermo.
✅ Fare una piccola ricerca sul primissimo gioco per computer mai scritto. Qual era la sua funzionalità?
Diventiamo eroici insieme!
## Rilevamento della collisione.
Come si rilevano le collisioni? Occorre pensare agli oggetti di gioco come rettangoli che si muovono. Perché, ci si potrebbe chiedere? Bene, l'immagine usata per disegnare un oggetto di gioco è un rettangolo: ha una `x`, una `y`, una larghezza (`width`) e un'altezza (`height`).
Se due rettangoli, cioè un eroe e un nemico *si intersecano*, si verifica una collisione. Ciò che dovrebbe accadere dipende dalle regole del gioco. Per implementare il rilevamento delle collisioni è quindi necessario quanto segue:
1. Un modo per ottenere una rappresentazione rettangolare di un oggetto di gioco, qualcosa del genere:
```javascript
rectFromGameObject() {
return {
top: this.y,
left: this.x,
bottom: this.y + this.height,
right: this.x + this.width
}
}
```
2. Una funzione di confronto, questa funzione può assomigliare a questa:
```javascript
function intersectRect(r1, r2) {
return !(r2.left > r1.right ||
r2.right < r1.left ||
r2.top > r1.bottom ||
r2.bottom < r1.top);
}
```
## Come si distruggono le cose
Per distruggere le cose in un gioco si deve far sapere al gioco stesso che non dovrebbe più disegnare un certo oggetto nel ciclo di gioco che si attiva in un certo intervallo. Un modo per farlo è contrassegnare un oggetto del gioco come *morto* quando succede qualcosa, in questo modo:
```javascript
// si è verificata una collisione
enemy.dead = true
```
Quindi si procede a filtrare gli oggetti *morti* prima di ridipingere lo schermo, in questo modo:
```javascript
gameObjects = gameObject.filter(go => !go.dead);
```
## Come si spara un laser
Sparare un laser si traduce nel rispondere a un evento da tastiera e creare un oggetto che si muove in una certa direzione. Occorre quindi eseguire i seguenti passaggi:
1. **Creare un oggetto laser**: dalla cima dell' astronave dell'eroe, che al momento della creazione inizia a muoversi in alto verso la parte superiore dello schermo.
2. **Allegare codice a un evento da tastiera**: si deve scegliere un tasto sulla tastiera che rappresenti il giocatore che spara il laser.
3. **Creare un oggetto di gioco che assomigli a un laser** quando viene premuto il tasto.
## Raffreddamento del laser
Il laser deve attivarsi ogni volta che si preme un tasto, come ad esempio la *barra spazio*. Per evitare che il gioco produca troppi laser in breve tempo, si deve risolvere questo problema. La soluzione è implementando un cosiddetto raffreddamento (*cooldown*), un timer, che garantisce che un laser possa essere sparato solo a intervallo determinato. Su può implementare nel modo seguente:
```javascript
class Cooldown {
constructor(time) {
this.cool = false;
setTimeout(() => {
this.cool = true;
}, time)
}
}
class Weapon {
constructor {
}
fire() {
if (!this.cooldown || this.cooldown.cool) {
// produce un laser
this.cooldown = new Cooldown(500);
} else {
// non fa nulla - non si è ancora raffreddato
}
}
}
```
✅ Fare riferimento alla lezione 1 della serie gioco spaziale per quanto riguarda i tempi di *cooldown* (raffreddamento).
## Cosa costruire
Si prende il codice esistente (che dovrebbe essere stato ripulito e rifattorizzato) dalla lezione precedente e lo si estende Si inizia con il codice della parte II o si usa il codice [della parte III-starter](../your-work).
> suggerimento: il laser con cui lavorare è già nella cartella asset ed è referenziato nel proprio codice
- **Aggiungere il rilevamento delle collisioni**, quando un laser entra in collisione con qualcosa dovrebbero essere applicate le seguenti regole:
1. Il **laser colpisce il nemico**: il nemico muore se colpito da un laser
2. Il **laser raggiunge lo schermo superiore**: un laser viene distrutto se raggiunge la parte superiore dello schermo
3. **Collisione tra nemico ed eroe**: un nemico e l'eroe vengono distrutti se entrano in collisione
4. Il **nemico colpisce la parte inferiore dello schermo**: un nemico e un eroe vengono distrutti se il nemico raggiunge la parte inferiore dello schermo
## Passaggi consigliati
Individuare i file che già sono stati creati nella sottocartella `your-work`. Dovrebbe contenere quanto segue:
```bash
-| assets
-| enemyShip.png
-| player.png
-| laserRed.png
-| index.html
-| app.js
-| package.json
```
Si fa partire il progetto nella cartella `your_work` digitando:
```bash
cd your-work
npm start
```
Quanto sopra avvierà un server HTTP sull'indirizzo `http://localhost:5000`. Aprire un browser e inserire quell'indirizzo, in questo momento dovrebbe rendere l'eroe e tutti i nemici.
### Aggiungere codice
1. **Impostare una rappresentazione di un rettangolo di un oggetto di gioco, per gestire la collisione**. Il codice seguente consente di ottenere una rappresentazione di un rettangolo di un `GameObject`. Modificare la classe GameObject per estenderla:
```javascript
rectFromGameObject() {
return {
top: this.y,
left: this.x,
bottom: this.y + this.height,
right: this.x + this.width,
};
}
```
2. **Aggiungere il codice che controlla la collisione** Questa sarà una nuova funzione che verifica se due rettangoli si intersecano:
```javascript
function intersectRect(r1, r2) {
return !(
r2.left > r1.right ||
r2.right < r1.left ||
r2.top > r1.bottom ||
r2.bottom < r1.top
);
}
```
3. **Aggiungere capacità di fuoco laser**
1. **Aggiungere messaggio per un evento da tastiera**. Il tasto spazio (*space*) dovrebbe creare un laser appena sopra la astronave dell'eroe. Aggiungere tre costanti nell'oggetto Messages:
```javascript
KEY_EVENT_SPACE: "KEY_EVENT_SPACE",
COLLISION_ENEMY_LASER: "COLLISION_ENEMY_LASER",
COLLISION_ENEMY_HERO: "COLLISION_ENEMY_HERO",
```
1. **Gestire il tasto barra spazio** Modificare la funzione keyup di `window.addEventListener` per gestire la barra spazio:
```javascript
} else if(evt.keyCode === 32) {
eventEmitter.emit(Messages.KEY_EVENT_SPACE);
}
```
1. **Aggiungere listener**. Modificare la funzione `initGame()` per assicurarsi che l'eroe possa sparare quando viene premuta la barra spazio:
```javascript
eventEmitter.on(Messages.KEY_EVENT_SPACE, () => {
if (hero.canFire()) {
hero.fire();
}
```
e aggiungere una nuova funzione `eventEmitter.on()` per garantire il comportamento quando un nemico si scontra con un laser:
```javascript
eventEmitter.on(Messages.COLLISION_ENEMY_LASER, (_, { first, second }) => {
first.dead = true;
second.dead = true;
})
```
1. **Spostare l'oggetto**, assicurarsi che il laser si muova gradualmente verso parte superiore dello schermo. Creare una nuova classe Laser che estende `GameObject`, come fatto precedentemente:
```javascript
class Laser extends GameObject {
constructor(x, y) {
super(x,y);
(this.width = 9), (this.height = 33);
this.type = 'Laser';
this.img = laserImg;
let id = setInterval(() => {
if (this.y > 0) {
this.y -= 15;
} else {
this.dead = true;
clearInterval(id);
}
}, 100)
}
}
```
1. **Gestire le collisioni**, implementare le regole di collisione per il laser. Aggiungere una funzione `updateGameObjects()` che verifica gli oggetti in collisione per quelli colpiti
```javascript
function updateGameObjects() {
const enemies = gameObjects.filter(go => go.type === 'Enemy');
const lasers = gameObjects.filter((go) => go.type === "Laser");
// il laser ha colpito qualcosa
lasers.forEach((l) => {
enemies.forEach((m) => {
if (intersectRect(l.rectFromGameObject(), m.rectFromGameObject())) {
eventEmitter.emit(Messages.COLLISION_ENEMY_LASER, {
first: l,
second: m,
});
}
});
});
gameObjects = gameObjects.filter(go => !go.dead);
}
```
Assicurarsi di aggiungere `updateGameObjects()` nel ciclo di gioco in `window.onload`.
4. **Implementare il** raffreddamento sul laser, in modo che possa essere sparato solo a determinati intervalli.
Infine, modificare la classe Hero in modo che possa eseguire il raffreddamento:
```javascript
class Hero extends GameObject {
constructor(x, y) {
super(x, y);
(this.width = 99), (this.height = 75);
this.type = "Hero";
this.speed = { x: 0, y: 0 };
this.cooldown = 0;
}
fire() {
gameObjects.push(new Laser(this.x + 45, this.y - 10));
this.cooldown = 500;
let id = setInterval(() => {
if (this.cooldown > 0) {
this.cooldown -= 100;
} else {
clearInterval(id);
}
}, 200);
}
canFire() {
return this.cooldown === 0;
}
}
```
A questo punto, il tuo gioco ha alcune funzionalità! Si può navigare con i tasti freccia, sparare un laser con la barra spaziatrice e i nemici scompaiono quando colpiti. Ottimo lavoro!
---
## 🚀 Sfida
Aggiungere un'esplosione! Dare un'occhiata alle risorse di gioco [nel repository Space Art](../solution/spaceArt/readme.txt) e provare ad aggiungere un'esplosione quando il laser colpisce un alieno
## Quiz Post-Lezione
[Quiz post-lezione](https://nice-beach-0fe9e9d0f.azurestaticapps.net/quiz/36?loc=it)
## Revisione e Auto Apprendimento
Sperimentare con gli intervalli nel proprio gioco fino ad ora. Cosa succede quando si cambiano? Ulteriori informazioni sugli [eventi di temporizzazione JavaScript](https://www.freecodecamp.org/news/javascript-timing-events-settimeout-and-setinterval/).
## Compito
[Esplorare le collisioni](assignment.it.md)

@ -0,0 +1,11 @@
# Esplorare Collisioni
## Istruzioni
Per una migliore comprensione del funzionamento delle collisioni, costruire un gioco molto piccolo con alcuni elementi che collidono. Farli muovere tramite pressioni di tasto o clic di mouse, fare accadere qualcosa a uno di questi elementi quando viene colpito. Potrebbe essere qualcosa tipo un meteorite che colpisce la terra oppure un autoscontro. Si usi la propria creatività!
## Rubrica
| Criteri | Ottimo | Adeguato | Necessita miglioramento |
| -------- | ------------------------------------------------------------------ | ------------------------------------- | -------------------------------------------------------------- |
| | Viene prodotto un campione di codice completo e funzionante con elementi disegnati sul canvas, verificarsi di collisioni basiche e reazioni| Il codice è incompleto in qualche modo | Malfunzionamenti del codice |

@ -0,0 +1,189 @@
# Costruire un Gioco Spaziale parte 5: Punteggio e Vite
## Quiz Pre-Lezione
[Quiz Pre-Lezione](https://nice-beach-0fe9e9d0f.azurestaticapps.net/quiz/37?loc=it)
In questa lezione si imparerà come aggiungere punteggi a una partita e calcolare le vite.
## Disegnare testo sullo schermo
Per poter visualizzare il punteggio di una partita sullo schermo, serve sapere come posizionare il testo sullo schermo. La risposta è usando il metodo `fillText()` sull'oggetto canvas. Si possono anche controllare altri aspetti come il tipo di carattere da usare, il colore del testo e anche il suo allineamento (sinistra, destra, centro). Di seguito è riportato del codice che disegna testo sullo schermo.
```javascript
ctx.font = "30px Arial";
ctx.fillStyle = "red";
ctx.textAlign = "right";
ctx.fillText("show this on the screen", 0, 0);
```
✅ Si legga di più su [come aggiungere testo a un oggetto canvas](https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Drawing_text), ci si senta liberi di far sembrare il proprio più elaborato!
## La vita, come concetto di gioco
Il concetto di avere una vita in un gioco è solo un numero. Nel contesto di un gioco spaziale è comune assegnare una serie di vite che vengono detratte una per una quando la propria astronave subisce danni. Sarebbe bello se si potesse mostrare una rappresentazione grafica di questo come mini astronavi o cuori invece di un numero.
## Cosa costruire
Si aggiunge quanto segue al gioco:
- **Punteggio del gioco**: per ogni astronave nemica che viene distrutta, l'eroe dovrebbe ricevere alcuni punti, si suggerisce 100 punti per astronave. Il punteggio del gioco dovrebbe essere mostrato in basso a sinistra.
- **Vita**: la propria astronave ha tre vite. Si perde una vita ogni volta che una astronave nemica si scontra con la propria. Un punteggio di vita dovrebbe essere visualizzato in basso a destra ed essere ricavato dalla seguente immagine ![immagine grafica di una vita](../solution/assets/life.png).
## Passaggi consigliati
Individuare i file che già sono stati creati nella sottocartella `your-work`. Dovrebbe contenere le seguenti informazioni:
```bash
-| assets
-| enemyShip.png
-| player.png
-| laserRed.png
-| index.html
-| app.js
-| package.json
```
Si fa partire il progetto dalla cartella `your_work` digitando:
```bash
cd your-work
npm start
```
Quanto sopra avvierà un server HTTP all'indirizzo `http://localhost:5000`. Aprire un browser e inserire quell'indirizzo, in questo momento dovrebbe rendere l'eroe e tutti i nemici, e premendo le frecce sinistra e destra, si fa muovere l'eroe che può abbattere i nemici.
### Aggiungere codice
1. **Copiare le risorse necessarie** dalla cartella `solution/assets` nella cartella `your-work`; aggiungere una risorsa `life.png`. Aggiungere lifeImg alla funzione window.onload:
```javascript
lifeImg = await loadTexture("assets/life.png");
```
1. Aggiungere `lifeImg` all'elenco delle risorse:
```javascript
let heroImg,
...
lifeImg,
...
eventEmitter = new EventEmitter();
```
2. **Aggiungere variabili**. Aggiungere il codice che rappresenta il punteggio totale (0) e le vite rimaste (3), visualizzare questi punteggi sullo schermo.
3. **Estendere la funzione `updateGameObjects()`** . Estendere la funzione `updateGameObjects()` per gestire le collisioni con il nemico:
```javascript
enemies.forEach(enemy => {
const heroRect = hero.rectFromGameObject();
if (intersectRect(heroRect, enemy.rectFromGameObject())) {
eventEmitter.emit(Messages.COLLISION_ENEMY_HERO, { enemy });
}
})
```
4. **Aggiungere vita (`life`) e punti (`points`)**.
1. **Inizializzare le variabili**. Sotto `this.cooldown = 0` nella classe `Hero`, impostare la vita e i punti:
```javascript
this.life = 3;
this.points = 0;
```
1. **Disegnare variabili sullo schermo**. Disegnare questi valori sullo schermo:
```javascript
function drawLife() {
// TODO, 35, 27
const START_POS = canvas.width - 180;
for(let i=0; i < hero.life; i++ ) {
ctx.drawImage(
lifeImg,
START_POS + (45 * (i+1) ),
canvas.height - 37);
}
}
function drawPoints() {
ctx.font = "30px Arial";
ctx.fillStyle = "red";
ctx.textAlign = "left";
drawText("Points: " + hero.points, 10, canvas.height-20);
}
function drawText(message, x, y) {
ctx.fillText(message, x, y);
}
```
1. **Aggiungere metodi al ciclo di gioco**. Assicurarsi di aggiungere queste funzioni alla funzione window.onload sotto `updateGameObjects()`:
```javascript
drawPoints();
drawLife();
```
1. **Implementare le regole del gioco**. Implementare le seguenti regole di gioco:
1. **Per ogni collisione di eroi e nemici**, togliere una vita.
Estendere la classe `Hero` per eseguire questa sottrazione:
```javascript
decrementLife() {
this.life--;
if (this.life === 0) {
this.dead = true;
}
}
```
2. **Per ogni laser che colpisce un nemico**, aumentare il punteggio del gioco di 100 punti.
Estendere la classe Hero per fare questo incremento:
```javascript
incrementPoints() {
this.points += 100;
}
```
Aggiungere queste funzioni agli event listener di Collision:
```javascript
eventEmitter.on(Messages.COLLISION_ENEMY_LASER, (_, { first, second }) => {
first.dead = true;
second.dead = true;
hero.incrementPoints();
})
eventEmitter.on(Messages.COLLISION_ENEMY_HERO, (_, { enemy }) => {
enemy.dead = true;
hero.decrementLife();
});
```
✅ Fare una piccola ricerca per scoprire altri giochi creati utilizzando JavaScript/Canvas. Quali sono i loro tratti comuni?
Alla fine di questo lavoro, si dovrebbero vedere le piccole astronavi che rappresentano le vite in basso a destra, i punti in basso a sinistra, e si dovrebbe vedere il numero di vite diminuire quando si entra in collisione con i nemici e i punti aumentare quando si colpiscono i nemici. Ottimo lavoro! Il gioco è quasi completo.
---
## 🚀 Sfida
Il codice è quasi completo. Si riescono a immaginare i prossimi passi?
## Quiz Post-Lezione
[Quiz post-lezione](https://nice-beach-0fe9e9d0f.azurestaticapps.net/quiz/38?loc=it)
## Revisione e Auto Apprendimento
Cercare alcuni modi per aumentare e diminuire i punteggi e le vite del gioco. Ci sono alcuni motori di gioco interessanti come [PlayFab](https://playfab.com). In che modo l'utilizzo di uno di questi potrebbe migliorare il proprio gioco?
## Compito
[Costruire un Gioco di Punteggio](assignment.it.md)

@ -0,0 +1,11 @@
# Costruire un Gioco di Punteggio
## Istruzioni
Creare un gioco in cui si mostrano vite e punti in modo creativo. Un suggerimento è quello di mostrare le vite come cuori e i punti come un numero grande nella parte centrale in basso dello schermo. Dare un'occhiata qui per [risorse di gioco gratuite](https://www.kenney.nl/)
# Rubrica
| Criteri | Ottimo | Adeguato | Necessita miglioramento |
| -------- | ---------------------- | --------------------------- | -------------------------- |
| | Viene presentato un gioco completo | Il gioco è presentato parzialmente | il gioco parziale contiene bug |

@ -0,0 +1,222 @@
# Costruire un Gioco Spaziale Parte 6: Fine e Riavvio
## Quiz Pre-Lezione
[Quiz Pre-Lezione](https://nice-beach-0fe9e9d0f.azurestaticapps.net/quiz/39?loc=it)
Esistono diversi modi per esprimere una *condizione di fine gioco*. Spetta al creatore del gioco dire perché il gioco è finito. Ecco alcuni motivi, si supponga di parlare del gioco spaziale costruito finora:
- Un **numero `N` di astronavi nemiche sono state distrutte**: è abbastanza comune se il gioco viene diviso in diversi livelli che si debba distruggere `N` astronavi nemiche per completare un livello
- **La propria nave è stata distrutta**: ci sono sicuramente giochi in si perde la partita se la propria astronave viene distrutta. Un altro approccio comune è il concetto di vite. Ogni volta che una propria astronave viene distrutta, sottrae una vita. Una volta perse tutte le vite, si perde la partita.
- **Sono stati raccolti `N` punti**: un'altra condizione finale comune è che il giocatore raccolga punti. Il modo in cui ottenere punti dipende dallo sviluppatore, ma è abbastanza comune assegnare punti a varie attività come distruggere una astronave nemica o forse raccogliere elementi che vengono *rilasciati* quando gli oggetti vengono distrutti.
- **Completare un livello**: questo potrebbe coinvolgere diverse condizioni come `X` astronavi nemiche distrutte, `Y` punti raccolti o forse che è stato raccolto un oggetto specifico.
## Riavvio
Se le persone apprezzano il gioco, è probabile che vogliano rigiocarlo. Una volta che il gioco finisce per qualsiasi motivo, si dovrebbe offrire un'alternativa per il riavvio.
✅ Si pensi alle condizioni per le quali si ritiene che un gioco finisca e poi a come viene chiesto di riavviare
## Cosa costruire
Aggiungere queste regole al gioco:
1. **Vincere il gioco**. Una volta che tutte le navi nemiche sono state distrutte, si vince la partita. Mostrare inoltre una sorta di messaggio di vittoria.
1. **Riavvio**. Una volta perse tutte le vite o si è vinto il gioco, si dovrebbe offrire un modo per riavviare il gioco. Ricordare! Si dovrà reinizializzare il gioco e lo stato del gioco precedente dovrebbe essere cancellato.
## Passaggi consigliati
Individuare i file che già sono stati creati nella sottocartella `your-work`. Dovrebbe contenere le seguenti informazioni:
```bash
-| assets
-| enemyShip.png
-| player.png
-| laserRed.png
-| life.png
-| index.html
-| app.js
-| package.json
```
Si fa partire il progetto dalla cartella `your_work` digitando:
```bash
cd your-work
npm start
```
Quanto sopra avvierà un server HTTP all'indirizzo `http://localhost:5000`. Aprire un browser e inserire quell'indirizzo. Il gioco dovrebbe essere in uno stato giocabile.
> suggerimento: per evitare avvertimenti in Visual Studio Code, modificare la funzione `window.onload` per chiamare `gameLoopId` così com'è (senza `let`) e dichiarare gameLoopId all'inizio del file, indipendentemente: `let gameLoopId;`
### Aggiungere codice
1. **Tracciare la condizione di fine**. Aggiungere codice che tenga traccia del numero di nemici o se l'astronave dell'eroe è stata distrutta aggiungendo queste due funzioni:
```javascript
function isHeroDead() {
return hero.life <= 0;
}
function isEnemiesDead() {
const enemies = gameObjects.filter((go) => go.type === "Enemy" && !go.dead);
return enemies.length === 0;
}
```
1. **Aggiungere logica ai gestori di messaggi**. Modificare `eventEmitter` per gestire queste condizioni:
```javascript
eventEmitter.on(Messages.COLLISION_ENEMY_LASER, (_, { first, second }) => {
first.dead = true;
second.dead = true;
hero.incrementPoints();
if (isEnemiesDead()) {
eventEmitter.emit(Messages.GAME_END_WIN);
}
});
eventEmitter.on(Messages.COLLISION_ENEMY_HERO, (_, { enemy }) => {
enemy.dead = true;
hero.decrementLife();
if (isHeroDead()) {
eventEmitter.emit(Messages.GAME_END_LOSS);
return; // loss before victory
}
if (isEnemiesDead()) {
eventEmitter.emit(Messages.GAME_END_WIN);
}
});
eventEmitter.on(Messages.GAME_END_WIN, () => {
endGame(true);
});
eventEmitter.on(Messages.GAME_END_LOSS, () => {
endGame(false);
});
```
1. **Aggiungere nuovi tipi di messaggi**. Aggiungere questi messaggi alle costanti assegnate agli oggetti:
```javascript
GAME_END_LOSS: "GAME_END_LOSS",
GAME_END_WIN: "GAME_END_WIN",
```
2. **Aggiungere il codice di riavvio** che fa ripartire il gioco con la semplice pressione di un pulsante selezionato.
1. **Mettersi in ascolto per la pressione del tasto `Invio`**. Modificare l'eventListener di window per ascoltare questa pressione
```javascript
else if(evt.key === "Enter") {
eventEmitter.emit(Messages.KEY_EVENT_ENTER);
}
```
1. **Aggiungere messaggio di riavvio**. Aggiungere questo messaggio alle costanti in Messages:
```javascript
KEY_EVENT_ENTER: "KEY_EVENT_ENTER",
```
1. **Implementare le regole del gioco**. Implementare le seguenti regole di gioco:
1. **Condizione di vittoria del giocatore**. Quando tutte le astronavi nemiche vengono distrutte, mostrare un messaggio di vittoria.
1. Innanzitutto, creare una funzione `displayMessage()` :
```javascript
function displayMessage(message, color = "red") {
ctx.font = "30px Arial";
ctx.fillStyle = color;
ctx.textAlign = "center";
ctx.fillText(message, canvas.width / 2, canvas.height / 2);
}
```
1. Creare una funzione `endGame()` :
```javascript
function endGame(win) {
clearInterval(gameLoopId);
// imposta un ritardo per assicurarsi che tutte sia stato disegnato
setTimeout(() => {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "black";
ctx.fillRect(0, 0, canvas.width, canvas.height);
if (win) {
displayMessage(
"Victory!!! Pew Pew... - Press [Enter] to start a new game Captain Pew Pew",
"green"
);
} else {
displayMessage(
"You died !!! Press [Enter] to start a new game Captain Pew Pew"
);
}
}, 200)
}
```
1. **Logica di riavvio**. Quando tutte le vite sono perse o il giocatore ha vinto la partita, mostrare che il gioco può essere riavviato. Inoltre, riavviare il gioco quando viene premuto il tasto di *riavvio* (si può decidere quale tasto deve essere mappato per il riavvio).
1. Creare la funzione `resetGame()` :
```javascript
function resetGame() {
if (gameLoopId) {
clearInterval(gameLoopId);
eventEmitter.clear();
initGame();
gameLoopId = setInterval(() => {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "black";
ctx.fillRect(0, 0, canvas.width, canvas.height);
drawPoints();
drawLife();
updateGameObjects();
drawGameObjects(ctx);
}, 100);
}
}
```
1. Aggiungere una chiamata a `eventEmitter` per riazzerare il gioco in `initGame()`:
```javascript
eventEmitter.on(Messages.KEY_EVENT_ENTER, () => {
resetGame();
});
```
1. Aggiungere una funzione `clear()` a EventEmitter:
```javascript
clear() {
this.listeners = {};
}
```
👽 💥 🚀 Congratulazioni, Capitano! Il gioco è completo! Ottimo lavoro! 🚀 💥 👽
---
## 🚀 Sfida
Aggiungere un suono! Si può aggiungere un suono per migliorare il gioco, magari quando un colpo di laser va a segno, o l'eroe muore o vince? Si dia un'occhiata a questo [sandbox](https://www.w3schools.com/jsref/tryit.asp?filename=tryjsref_audio_play) per imparare a riprodurre il suono utilizzando JavaScript
## Quiz Post-Lezione
[Quiz post-lezione](https://nice-beach-0fe9e9d0f.azurestaticapps.net/quiz/40?loc=it)
## Revisione e Auto Apprendimento
Il compito è creare un nuovo gioco di esempio, quindi esplorare alcuni dei giochi interessanti esistenti per vedere che tipo di gioco si potrebbe costruire.
## Compito
[Creare un Gioco di Esempio](assignment.it.md)

@ -0,0 +1,19 @@
# Creare un Gioco di Esempio
## Istruzioni
Provare a costruire un piccolo gioco in cui esercitarsi in diverse condizioni di fine. Variare tra ottenere un numero di punti, l'eroe perde tutte le vite o tutti i mostri vengono sconfitti. Costruire qualcosa di semplice come un gioco di avventura basato su console. Usare il seguente flusso di gioco come ispirazione:
```
Eroe> Colpisce con lo spadone - l'orco riceve 3 punti di danno
Orco> Colpisce con la mazza - l'eroe riceve 2 punti di danno
Eroe> Calcia - l'orco takes 1 punto di danno
Gioco> l'orco è sconfitto - L'eroe guadagna 2 monete
Gioco> ****Non ci sono più mostri, la fortezza del male è stata conquistata****
```
## Rubrica
| Criteri | Ottimo | Adeguato | Necessita miglioramento |
| -------- | ---------------------- | --------------------------- | -------------------------- |
| | Viene presentato un gioco completo | Il gioco è presentato parzialmente | il gioco parziale contiene bug |

@ -0,0 +1,31 @@
# Costruire un Gioco Spaziale
Un gioco spaziale per insegnare fondamenti di JavaScript più avanzati
In questa lezione si imparerà come costruire un gioco spaziale. Se mai si è giocato a "Space Invaders", questo gioco ha la stessa idea: guidare un'astronave e sparare sui mostri che scendono dall'alto. Ecco come apparirà il gioco finito:
![Gioco completato](../images/pewpew.gif)
In queste sei lezioni si imparerà quanto segue:
- **Interagire** con l'elemento Canvas per disegnare oggetti su uno schermo
- **Comprendere** il sistema di coordinate cartesiane
- **Imparare** il modello Pub-Sub per creare una solida architettura di gioco più facile da mantenere ed estendere
- **Sfruttare** Async/Await per caricare le risorse di gioco
- **Gestire** gli eventi da tastiera
## Panoramica
- Teoria
- [Introduzione alla creazione di giochi con JavaScript](1-introduction/translations/README.it.md)
- Esercitazione
- [Disegnare sull'elemento canvas](2-drawing-to-canvas/translations/README.it.md)
- [Spostamento di elementi sullo schermo](3-moving-elements-around/translations/README.it.md)
- [Rilevamento della collisione.](4-collision-detection/translations/README.it.md)
- [Tenere il punteggio](5-keeping-score/translations/README.it.md)
- [Terminare e riavviare il gioco](6-end-condition/translations/README.it.md)
## Crediti
Le risorse utilizzate provengono da https://www.kenney.nl/.
Se interessa costruire giochi, queste sono alcune risorse davvero buone, molte sono gratuite e alcune sono a pagamento.

@ -19,11 +19,11 @@
- 理論
- [JavaScript を使ったゲーム構築入門](../1-introduction/translations/README.ja.md)
- 実践
- [キャンバスへの描画](../2-drawing-to-canvas/translations/README.ja.md)
- [Canvas への描画](../2-drawing-to-canvas/translations/README.ja.md)
- [画面の周りの要素の移動](../3-moving-elements-around/translations/README.ja.md)
- [衝突の検出](../4-collision-detection/translations/README.ja.md)
- [スコアの保持](../5-keeping-score/translations/README.ja.md)
- [ゲームの終了と再](../6-end-condition/translations/README.ja.md)
- [ゲームの終了と再起動](../6-end-condition/translations/README.ja.md)
## クレジット

@ -0,0 +1,306 @@
# Creazione di un'App Bancaria Parte 1: Modelli HTML e Rotte in un'app web
## Quiz Pre-Lezione
[Quiz Pre-Lezione](https://nice-beach-0fe9e9d0f.azurestaticapps.net/quiz/41?loc=it)
### Introduzione
Dall'avvento di JavaScript nei browser, i siti web stanno diventando più interattivi e complessi che mai. Le tecnologie web sono ora comunemente utilizzate per creare applicazioni completamente funzionali che vengono eseguite direttamente in un browser che vengono chiamate [applicazioni web](https://it.wikipedia.org/wiki/Applicazione_web). Poiché le app web sono altamente interattive, gli utenti non desiderano attendere il ricaricamento di una pagina intera ogni volta che viene eseguita un'azione. Ecco perché JavaScript viene utilizzato per aggiornare l'HTML direttamente utilizzando il DOM, per fornire un'esperienza utente più fluida.
In questa lezione, getteremo le basi per creare un'app web bancaria, utilizzando modelli HTML per creare più schermate che possono essere visualizzate e aggiornate senza dover ricaricare l'intera pagina HTML.
### Prerequisito
È necessario un server web locale per testare l'app web che verrà creata in questa lezione. Se non ne ha uno, si può installare [Node.js](https://nodejs.org) e utilizzare il comando `npx lite-server` dalla cartella del progetto. Creerà un server web locale e aprirà la propria app in un browser.
### Preparazione
Sul proprio computer, creare una cartella denominata `bank` con un file denominato `index.html` al suo interno. Si inizierà da questo codice [boilerplate](https://en.wikipedia.org/wiki/Boilerplate_code) HTML:
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Bank App</title>
</head>
<body>
<!-- Qui è dove si lavorerà -->
</body>
</html>
```
---
## Modelli HTML.
Se si desidera creare più schermate per una pagina Web, una soluzione potrebbe essere la creazione di un file HTML per ogni schermata che si desidera visualizzare. Tuttavia, questa soluzione presenta alcuni inconvenienti:
- È necessario ricaricare l'intero HTML quando si cambia schermata, il che può essere lento.
- È difficile condividere i dati tra le diverse schermate.
Un altro approccio consiste nell'avere un solo file HTML e definire più [modelli HTML](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/template) utilizzando l'elemento `<template>`. Un modello è un blocco HTML riutilizzabile che non viene visualizzato dal browser e deve essere istanziato in fase di esecuzione utilizzando JavaScript.
### Attività
Verrà creata un'app bancaria con due schermate: la pagina di accesso e il cruscotto. Innanzitutto, si aggiunge nel corpo dell'HTML un elemento segnaposto che si utilizzerà per istanziare le diverse schermate dell'app:
```html
<div id="app">Loading...</div>
```
Viene fornito un `ID` all'elemento per renderlo più facilmente individuabile con JavaScript in seguito.
> Suggerimento: poiché il contenuto di questo elemento verrà sostituito, si può inserire un messaggio di caricamento o un indicatore che verrà mostrato durante il caricamento dell'app.
Successivamente, si aggiunge il modello HTML seguente per la pagina di accesso. Per ora si inserirà solo un titolo e una sezione contenente un collegamento che si utilizzerà per eseguire la navigazione.
```html
<template id="login">
<h1>Bank App</h1>
<section>
<a href="/dashboard">Login</a>
</section>
</template>
```
Quindi si aggiungerà un altro modello HTML per la pagina del cruscotto. Questa pagina conterrà diverse sezioni:
- Un'intestazione con un titolo e un collegamento di disconnessione
- Saldo corrente del conto bancario
- Un elenco di transazioni, visualizzato in una tabella
```html
<template id="dashboard">
<header>
<h1>Bank App</h1>
<a href="/login">Logout</a>
</header>
<section>
Balance: 100$
</section>
<section>
<h2>Transactions</h2>
<table>
<thead>
<tr>
<th>Date</th>
<th>Object</th>
<th>Amount</th>
</tr>
</thead>
<tbody></tbody>
</table>
</section>
</template>
```
> Suggerimento: durante la creazione di modelli HTML, se si vuole vedere come apparirà, si possono commentare le righe tra `<template>` `</template>` racchiudendole `tra <!- ->-->`.
✅ Perché si pensa che vengano utilizzati gli attributi `id` sui modelli? Si potrebbe usare qualcos'altro come le classi?
## Visualizzazione di modelli con JavaScript
Se si prova il proprio file HTML corrente in un browser, si vedrà che si blocca visualizzando `Loading ...` Questo perché si deve aggiungere del codice JavaScript per istanziare e visualizzare i modelli HTML.
L'istanza di un modello viene solitamente eseguita in 3 passaggi:
1. Recuperare l'elemento del modello nel DOM, ad esempio utilizzando [`document.getElementById`](https://developer.mozilla.org/it/docs/Web/API/Document/getElementById).
2. Clonare l'elemento template, usando [`cloneNode`](https://developer.mozilla.org/en-US/docs/Web/API/Node/cloneNode).
3. Collegarlo al DOM sotto un elemento visibile, ad esempio utilizzando [`appendChild`](https://developer.mozilla.org/it/docs/Web/API/Node/appendChild).
✅ Perché è necessario clonare il modello prima di collegarlo al DOM? Cosa si pensa che accadrebbe se venisse saltato questo passaggio?
### Attività
Creare un nuovo file denominato `app.js` nella cartella del progetto e importare quel file nella sezione `<head>` del proprio HTML:
```html
<script src="app.js" defer></script>
```
Ora in `app.js`, verrà creata una nuova funzione `updateRoute`:
```js
function updateRoute(templateId) {
const template = document.getElementById(templateId);
const view = template.content.cloneNode(true);
const app = document.getElementById('app');
app.innerHTML = '';
app.appendChild(view);
}
```
Quello che si fa qui sono esattamente i 3 passaggi descritti sopra. Si istanza il modello con l'id `templateId` e si inserisce il suo contenuto clonato nel segnaposto dell'app. Notare che si deve usare `cloneNode (true)` per copiare l'intero sottoalbero del modello.
Ora chiamare questa funzione con uno dei template e guardare il risultato.
```js
updateRoute('login');
```
✅ Qual è lo scopo di questo codice `app.innerHTML = '';`? Cosa succede senza di essa?
## Creazione di rotte
Quando si parla di un'app web, si definisce *Routing* (instradamento) l'intento di mappare gli **URL** a schermate specifiche che dovrebbero essere visualizzate. Su un sito web con più file HTML, questa operazione viene eseguita automaticamente poiché i percorsi dei file si riflettono sull'URL. Ad esempio, con questi file nella cartella del progetto:
```
mywebsite/index.html
mywebsite/login.html
mywebsite/admin/index.html
```
Se si crea un server web con `mywebsite` come radice, la mappatura dell'URL sarà:
```
https://site.com --> mywebsite/index.html
https://site.com/login.html --> mywebsite/login.html
https://site.com/admin/ --> mywebsite/admin/index.html
```
Tuttavia, per l'app web in costruzione si utilizza un singolo file HTML contenente tutte le schermate, quindi questo comportamento predefinito non sarà di aiuto. Si deve creare questa mappa manualmente ed eseguire l'aggiornamento del modello visualizzato utilizzando JavaScript.
### Attività
Si userà un semplice oggetto per implementare una [mappa](https://it.wikipedia.org/wiki/Array_associativo) tra i percorsi degli URL e i propri modelli. Aggiungere questo oggetto all'inizio del file `app.js`.
```js
const routes = {
'/login': { templateId: 'login' },
'/dashboard': { templateId: 'dashboard' },
};
```
Ora modificare un po' la funzione `updateRoute`. Invece di passare direttamente il `templateId` come argomento, lo si vuole recuperare guardando prima l'URL corrente, quindi utilizzndo la mappa per ottenere il valore dell'ID del modello corrispondente. Si può usare [`window.location.pathname`](https://developer.mozilla.org/en-US/docs/Web/API/Location/pathname) per ottenere solo la sezione del percorso dall'URL.
```js
function updateRoute() {
const path = window.location.pathname;
const route = routes[path];
const template = document.getElementById(route.templateId);
const view = template.content.cloneNode(true);
const app = document.getElementById('app');
app.innerHTML = '';
app.appendChild(view);
}
```
Qui sono state mappato le rotte che sono state dichiarate al modello corrispondente. Si può provare se funziona correttamente modificando manualmente l'URL nel proprio browser.
✅ Cosa succede se si inserisce un percorso sconosciuto nell'URL? Come potrebbe essere risolto questo problema?
## Aggiungere navigazione
Il prossimo passo per la costruzione dell'app è aggiungere la possibilità di navigare tra le pagine senza dover modificare manualmente l'URL. Questo implica due cose:
1. Aggiornamento dell'URL corrente
2. Aggiornamento del modello visualizzato in base al nuovo URL
E' già stata trattata la seconda parte con la funzione `updateRoute`, quindi occorre capire come aggiornare l'URL corrente.
Si dovrà utilizzare JavaScript e più precisamente [history.pushState](https://developer.mozilla.org/en-US/docs/Web/API/History/pushState) che permette di aggiornare l'URL e creare una nuova voce nella cronologia di navigazione, senza ricaricare l'HTML.
> Nota: Sebbene l'elemento HTML ancora [`<a href>`](https://developer.mozilla.org/it/docs/Web/HTML/Element/a) possa essere usato da solo per creare collegamenti ipertestuali a diversi URL, è anche in grado di fare ricaricare al browser l'HTML nella modalità predefinita. È necessario prevenire questo comportamento quando si gestisce il routing con javascript personalizzato, utilizzando la funzione preventDefault() sull'evento click.
### Attività
Si crea una nuova funzione da utilizzare per navigare nell'app:
```js
function navigate(path) {
window.history.pushState({}, path, window.location.origin + path);
updateRoute();
}
```
Questo metodo aggiorna prima l'URL corrente in base al percorso fornito, quindi aggiorna il modello. La proprietà `window.location.origin` restituisce l'URL radice, permettendo di ricostruire un URL completo da un dato percorso.
Ora che si ha questa funzione, ci si può occupare del problema che si verifica se un percorso non corrisponde a nessuna rotta definita. Si modificherà la funzione `updateRoute` aggiungendo una soluzione di contingenza per indirizzare verso una delle rotte esistenti se non viene trovata una corrispondenza.
```js
function updateRoute() {
const path = window.location.pathname;
const route = routes[path];
if (!route) {
return navigate('/login');
}
...
```
Se non è possibile trovare una rotta, si esegue un reindirizzamento alla pagina `login`.
Ora si crea una funzione per ottenere l'URL quando si fa clic su un collegamento e per impedire il comportamento predefinito del browser per un collegamento:
```js
function onLinkClick(event) {
event.preventDefault();
navigate(event.target.href);
}
```
Si completa il sistema di navigazione aggiungendo collegamenti ai link di accesso (*Login*) e di disconnessione (*Logout*) nell'HTML.
```html
<a href="/dashboard" onclick="onLinkClick()">Login</a>
...
<a href="/login" onclick="onLinkClick()">Logout</a>
```
Utilizzando l 'attributo [`onclick`](https://developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers/onclick) associare l'evento `click` al codice JavaScript, in questo caso la chiamata alla funzione `navigate()` .
Provare a fare clic su questi collegamenti, ora si dovrebbe essere in grado di navigare tra le diverse schermate dell'app.
✅ Il metodo `history.pushState` fa parte dello standard HTML5 e implementato in [tutti i browser moderni](https://caniuse.com/?search=pushState). Se si sta creando un'app web per browser meno recenti, c'è un trucco che si può usare al posto di questa API: usando un [hash (`#`)](https://en.wikipedia.org/wiki/URI_fragment) prima del percorso si può implementare un instradatamento che funziona con la normale navigazione dell'elemento ancora e non ricarica la pagina, poiché il suo scopo era creare collegamenti all'interno di una pagina.
## Gestione dei pulsanti Avanti e Indietro del browser
L'utilizzo di `history.pushState` crea nuove voci nella cronologia di navigazione del browser. Si può verificare tenendo premuto il *pulsante Indietro* del proprio browser, dovrebbe visualizzare qualcosa del genere:
![Videata della cronologia di navigazione](../history.png)
Se si prova a fare clic sul pulsante Indietro alcune volte, si vedrà che l'URL corrente cambia e la cronologia viene aggiornata, ma lo stesso modello continua a essere visualizzato.
Questo perché il browser non sa che si deve chiamare `updateRoute()` ogni volta che cambia la cronologia. Se si dà un'occhiata alla documentazione di [`history.pushState`](https://developer.mozilla.org/en-US/docs/Web/API/History/pushState) si può vedere che se lo stato cambia, vale a dire che si è passati a un URL diverso, viene attivato l'[evento](https://developer.mozilla.org/en-US/docs/Web/API/Window/popstate_event) `popstate`. Verrà usato per risolvere quel problema.
### Attività
Per assicurarsi che il modello visualizzato venga aggiornato quando la cronologia del browser cambia, si aggiungerà una nuova funzione che chiama `updateRoute()`. Verrà fatto in fondo al file `app.js`:
```js
window.onpopstate = () => updateRoute();
updateRoute();
```
> Nota: è stata usata una [funzione freccia](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions) qui per dichiarare il gestore di eventi `popstate` per concisione, ma una funzione normale andrebbe bene allo stesso modo.
Ecco un video di aggiornamento sulle funzioni freccia:
[![Funzionifreccia](https://img.youtube.com/vi/OP6eEbOj2sc/0.jpg)](https://youtube.com/watch?v=OP6eEbOj2sc "")
> Fare clic sull'immagine sopra per un video sulle funzioni freccia
Ora provare a utilizzare i pulsanti Indietro e Avanti del proprio browser e controllare che il percorso visualizzato sia aggiornato correttamente questa volta.
---
## 🚀 Sfida
Aggiungere un nuovo modello e instradare per una terza pagina che mostra i crediti per questa app.
## Quiz Post-Lezione
[Quiz post-lezione](https://nice-beach-0fe9e9d0f.azurestaticapps.net/quiz/42?loc=it)
## Revisione e Auto Apprendimento
Il routing (instradamento) è una delle parti sorprendentemente complicate dello sviluppo web, soprattutto perché il web passa dai comportamenti di aggiornamento della pagina all'aggiornamento della pagina dell'applicazione a pagina singola. Leggere alcune informazioni su [come il servizio App Web Static di Azure](https://docs.microsoft.com/en-us/azure/static-web-apps/routes?WT.mc_id=academic-4621-cxa) gestisce il routing. Si può spiegare perché alcune delle decisioni descritte in quel documento sono necessarie?
## Compito
[Migliorare l'instradamento](assignment.it.md)

@ -202,7 +202,7 @@ function updateRoute() {
2番目の部分はすでに `updateRoute` 関数で処理したので、現在の URL を更新する方法を見つけなければなりません。
JavaScript、特に [history.pushState`](https://developer.mozilla.org/ja/docs/Web/API/History/pushState) を使う必要があります。これは HTML をリロードせずに URL を更新して閲覧履歴に新しいエントリを作成することができます。
JavaScript、特に [`history.pushState`](https://developer.mozilla.org/ja/docs/Web/API/History/pushState) を使う必要があります。これは HTML をリロードせずに URL を更新して閲覧履歴に新しいエントリを作成することができます。
> 注: HTML アンカー要素[`<a href>`](https://developer.mozilla.org/ja/docs/Web/HTML/Element/a)は単独で使用して異なる URL へのハイパーリンクを作成することができますが、ブラウザはデフォルトで HTML をリロードさせることになります。カスタム javascript でルーティングを扱う際には、クリックイベントの preventDefault() 関数を使用して、この動作を防ぐ必要があります。

@ -0,0 +1,14 @@
# Migliorare l'instradamento
## Istruzioni
La dichiarazione delle rotte contiene attualmente solo l'ID del modello da utilizzare. Quando si visualizza una nuova pagina, a volte è necessario un po 'di più. L'implementazione del routing viene migliorata con due funzionalità aggiuntive:
- Assegnare titoli a ciascun modello e aggiornare il titolo della finestra con questo nuovo titolo quando il modello cambia.
- Aggiungere un'opzione per eseguire del codice dopo le modifiche al modello. Si vuole stampare `"Il cruscotto è mostrato" nella` console per sviluppatori ogni volta che viene visualizzata la pagina del dashboard.
## Rubrica
| Criteri | Ottimo | Adeguato | Necessita miglioramento |
| -------- | ---------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------- |
| | Le due funzionalità sono implementate e funzionanti. L'aggiunta di titolo e codice funziona anche per una nuova rotta aggiunta nella dichiarazione di `routes` . | Sono funzionanti i due comportamenti aggiuntivi, ma il comportamento è fisso nel codice e non è configurabile tramite la dichiarazione di `routes`. L'aggiunta di una terza rotta con l'aggiunta di titolo e codice non funziona o funziona parzialmente. | Una delle funzionalità manca o non funziona correttamente. |

@ -0,0 +1,293 @@
# Creazione di un'App Bancaria Parte 2: Creazione di un form di accesso e registrazione
## Quiz Pre-Lezione
[Quiz Pre-Lezione](https://nice-beach-0fe9e9d0f.azurestaticapps.net/quiz/43?loc=it)
### Introduzione
In quasi tutte le moderne app web, si può creare un account per avere il proprio spazio privato. Poiché più utenti possono accedere a un'app web contemporaneamente, è necessario un meccanismo per archiviare i dati personali di ciascun utente separatamente e selezionare le informazioni da visualizzare. Non verrà trattato come gestire [l'identità dell'utente in modo sicuro](https://it.wikipedia.org/wiki/Autenticazione) poiché si tratta di un argomento ampio di per sé, ma ci si assicurerà che ogni utente sia in grado di creare uno (o più) conto bancario nella app.
In questa parte si utilizzeranno form HTML per aggiungere login e registrazione all'app. Si vedrà come inviare i dati a un'API del server a livello di programmazione e, infine, come definire le regole di convalida di base per gli input dell'utente.
### Prerequisito
È necessario aver completato i [modelli HTML e il routing](../../1-template-route/translations/README.it.md) dell'app web per questa lezione. È inoltre necessario installare [Node.js](https://nodejs.org) ed [eseguirel'API del server](../../api/README.md) in locale in modo da poter inviare dati per creare account.
Si può verificare che il server funzioni correttamente eseguendo questo comando in un terminale:
```sh
curl http://localhost:5000/api
# -> dovrebbe restituire "Bank API v1.0.0" come risultato
```
---
## Form e controlli
L'elemento `<form>` incapsula una sezione di un documento HTML in cui l'utente può inserire e inviare dati con controlli interattivi. Esistono tutti i tipi di controlli dell'interfaccia utente (UI) che possono essere utilizzati all'interno di un form, i più comuni sono gli elementi `<input>` e `<button>`.
Esistono molti [tipi](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input) diversi di `<input>`, ad esempio per creare un campo in cui l'utente può inserire il proprio nome utente si può utilizzare:
```html
<input id="username" name="username" type="text">
```
L'attributo `name` verrà utilizzato come nome della proprietà quando verranno inviati i dati del form. L'attributo `id` viene utilizzato per associare un'etichetta (`<label>`) al relativo controllo nel form.
> Si dia un'occhiata all'intero elenco di [tipi di `<input>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input) e [altri controlli del form](https://developer.mozilla.org/en-US/docs/Learn/Forms/Other_form_controls) per avere un'idea di tutti gli elementi nativi dell'interfaccia utente che si possono utilizzare durante la creazione della propria interfaccia utente.
✅ Si noti che `<input>` è un [elemento vuoto](https://developer.mozilla.org/en-US/docs/Glossary/Empty_element) su cui *non* si dovrebbe aggiungere un tag di chiusura corrispondente. È comunque possibile utilizzare la notazione a chiusura automatica `<input/>` , ma non è richiesta.
L'elemento `<button>` all'interno di un form è un po' speciale. Se non si specifica il suo attributo di tipo (`type`) , invierà automaticamente i dati del form al server quando viene premuto. Ecco i possibili valori di `type` :
- `submit`: l'impostazione predefinita all'interno di un `<form>`, il pulsante attiva l'azione di invio del form.
- `reset`: il pulsante ripristina tutti i controlli del form ai valori iniziali.
- `button`: non assegna un comportamento predefinito quando viene premuto il pulsante. È quindi possibile assegnargli azioni personalizzate utilizzando JavaScript.
### Attività
Si comincia aggiungendo un form al modello di accesso `login` . Servirà di un campo *username* per il nome utente e di un pulsante *Login* .
```html
<template id="login">
<h1>Bank App</h1>
<section>
<h2>Login</h2>
<form id="loginForm">
<label for="username">Username</label>
<input id="username" name="user" type="text">
<button>Login</button>
</form>
</section>
</template>
```
Se si guarda più attentamente, si può notare che è stato aggiunto anche un elemento `<label>` (etichetta). Gli elementi `<label>` vengono utilizzati per aggiungere un nome ai controlli dell'interfaccia utente, come il campo username. Le etichette sono importanti per la leggibilità dei form, ma offrono anche vantaggi aggiuntivi:
- Associaer un'etichetta a un controllo in un form, aiuta gli utenti che utilizzano tecnologie assistite (come unlettore di schremo) a capire quali dati dovrebbero fornire.
- È possibile fare clic sull'etichetta per portare direttamente il focus sull'input associato, rendendolo più facile da raggiungere su dispositivi basati su touch screen.
> [L'accessibilità](https://developer.mozilla.org/en-US/docs/Learn/Accessibility/What_is_accessibility) sul Web è un argomento molto importante che spesso viene trascurato. Grazie agli [elementi HTML semantici](https://developer.mozilla.org/en-US/docs/Learn/Accessibility/HTML) non è difficile creare contenuti accessibili se usati correttamente. Si può [leggere di più sull'accessibilità](https://developer.mozilla.org/en-US/docs/Web/Accessibility) per evitare errori comuni e diventare uno sviluppatore responsabile.
Ora si aggiungerà un secondo modulo per la registrazione, appena sotto il precedente:
```html
<hr/>
<h2>Register</h2>
<form id="registerForm">
<label for="user">Username</label>
<input id="user" name="user" type="text">
<label for="currency">Currency</label>
<input id="currency" name="currency" type="text" value="$">
<label for="description">Description</label>
<input id="description" name="description" type="text">
<label for="balance">Current balance</label>
<input id="balance" name="balance" type="number" value="0">
<button>Register</button>
</form>
```
Utilizzando l'attributo `value` si può impostare un valore predefinito per un dato input. Si noti inoltre che l'input per il saldo(`balance`) ha il tipo `number` (numero). Sembra diverso dagli altri input? Provare a interagire con esso.
✅ Si può navigare e interagire con i form utilizzando solo una tastiera? E come si pensa di fare?
## Invio dei dati al server
Ora che si ha un'interfaccia utente funzionale, il passaggio successivo è inviare i dati al server. Fare un rapido test utilizzando il codice attuale: cosa succede se si fa clic sul pulsante *Login* o *Register* ?
Si è notato il cambiamento nella sezione URL del proprio browser?
![Videata della modifica dell'URL del browser dopo aver fatto clic sul pulsante Registra](../images/click-register.png)
L'azione predefinita per un `<form>` consiste nell'inviare il form all'URL del server corrente utilizzando il [metodo GET](https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.3), aggiungendo i dati del modulo direttamente all'URL. Questo metodo presenta però alcuni difetti:
- I dati inviati sono di dimensioni molto limitate (circa 2000 caratteri)
- I dati sono direttamente visibili nell'URL (non eccezionale per le password)
- Non funziona con i caricamenti di file
Ecco perché si può cambiare e utilizzare il [metodo POST](https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.5) che invia i dati del modulo al server nel corpo della richiesta HTTP, senza nessuna delle limitazioni precedenti.
> Sebbene POST sia il metodo più comunemente utilizzato per inviare i dati, [in alcuni scenari specifici](https://www.w3.org/2001/tag/doc/whenToUseGet.html) è preferibile utilizzare il metodo GET, ad esempio quando si implementa un campo di ricerca.
### Attività
Aggiungere le proprietà `action` e `method` al form di registrazione:
```html
<form id="registerForm" action="//localhost:5000/api/accounts" method="POST">
```
Provare ora a registrare un nuovo account con il proprio nome. Dopo aver fatto clic sul pulsante *Register* si dovrebbe vedere qualcosa del genere:
![Una finestra del browser all'indirizzo localhost:5000/api/ accounts, che mostra una stringa JSON con i dati dell'utente](../images/form-post.png)
Se tutto va bene, il server dovrebbe rispondere alla richiesta con una risposta [JSON](https://www.json.org/json-en.html) contenente i dati dell'account che è stato creato.
✅ Provare una nuova registrazione con lo stesso nome. Che cosa accade?
## Invio di dati senza ricaricare la pagina
Come probabilmente si è notato, c'è un piccolo problema con l'approccio appena usato: quando si invia il modulo, si esce dalla propria app e il browser reindirizza all'URL del server. Si sta cercando qui di evitare tutti i ricaricamenti delle pagine con quest app web, poiché si sta creando un'[applicazione a pagina singola (SPA)](https://en.wikipedia.org/wiki/Single-page_application).
Per inviare i dati del modulo al server senza forzare il ricaricamento di una pagina, si deve utilizzare il codice JavaScript. Invece di inserire un URL nella proprietà `action` di un `<form>`, si può utilizzare qualsiasi codice JavaScript anteposto dalla stringa `javascript:` per eseguire un'azione personalizzata. Usarlo significa anche che si dovranno implementare alcune attività che erano state precedentemente eseguite automaticamente dal browser:
- Recuperare i dati del form
- Convertire e codificare i dati del form in un formato adatto
- Creare la richiesta HTTP e inviarla al server
### Attività
Sostituire `action` nel form di registrazione con:
```html
<form id="registerForm" action="javascript:register()">
```
Aprire `app.js` aggiungere una nuova funzione denominata `register`:
```js
function register() {
const registerForm = document.getElementById('registerForm');
const formData = new FormData(registerForm);
const data = Object.fromEntries(formData);
const jsonData = JSON.stringify(data);
}
```
Qui si recupera l'elemento form utilizzando `getElementById()` e si utilizza il [metodo di supporto FormData](https://developer.mozilla.org/en-US/docs/Web/API/FormData) per estrarre i valori dai controlli del forma come un insieme di coppie chiave/valore. Quindi si convertono i dati in un oggetto normale utilizzando [`Object.fromEntries()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/fromEntries) e infine si serializzano i dati in [JSON](https://www.json.org/json-en.html), un formato comunemente utilizzato per lo scambio di dati sul web.
I dati sono ora pronti per essere inviati al server. Creare una nuova funzione denominata `createAccount`:
```js
async function createAccount(account) {
try {
const response = await fetch('//localhost:5000/api/accounts', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: account
});
return await response.json();
} catch (error) {
return { error: error.message || 'Unknown error' };
}
}
```
Cosa fa questa funzione? Per prima cosa notare la parola chiave `async`. Ciò significa che la funzione contiene codice che verrà eseguito [**in modo asincrono**](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function). Quando viene utilizzato insieme alla parola chiave `await`, consente di attendere l'esecuzione del codice asincrono, come l'attesa della risposta del server qui, prima di continuare.
Ecco un breve video sull'utilizzo di `async/await`:
[![Async e Await per la](https://img.youtube.com/vi/YwmlRkrxvkk/0.jpg)](https://youtube.com/watch?v=YwmlRkrxvkk " gestione di promesse")
> Fare clic sull'immagine sopra per un video su async/await.
Si usa l'API `fetch()` per inviare dati JSON al server. Questo metodo richiede 2 parametri:
- L'URL del server, quindi rimettere `//localhost:5000/api/accounts` qui.
- Le impostazioni della richiesta. È qui che si imposta il metodo su `POST` e si fornisce il corpo (`body`) della richiesta. Dato che si inviano dati JSON al server, si deve anche impostare l'intestazione `Content-Type` su `application/json` in modo che il server sappia come interpretare il contenuto.
Poiché il server risponderà alla richiesta con JSON, si può utilizzare `await response.json()` per analizzare il contenuto JSON e restituire l'oggetto risultante. Notare che questo metodo è asincrono, quindi si usa la parola chiave `await` qui prima di tornare per assicurarsi che vengano rilevati anche eventuali errori durante l'analisi.
Ora aggiungere del codice alla funzione di `register` per chiamare `createAccount()`:
```js
const result = await createAccount(jsonData);
```
Poiché qui si usa la parola chiave `await`, si deve aggiungere la parola chiave `async` prima della funzione di registrazione
```js
async function register() {
```
Infine, aggiungere alcuni log per verificare il risultato. La funzione finale dovrebbe essere simile a questa:
```js
async function register() {
const registerForm = document.getElementById('registerForm');
const formData = new FormData(registerForm);
const jsonData = JSON.stringify(Object.fromEntries(formData));
const result = await createAccount(jsonData);
if (result.error) {
return console.log('An error occured:', result.error);
}
console.log('Account created!', result);
}
```
È stato un po' lungo ma si è arrivati! Se si apre [strumenti di sviluppo del browser](https://developer.mozilla.org/en-US/docs/Learn/Common_questions/What_are_browser_developer_tools) e si prava a registrare un nuovo account, non si dovrebbe vedere alcun cambiamento nella pagina web ma apparirà un messaggio nella console che conferma che tutto funziona.
![Videata che mostra il messaggio di registro nella console del browser](../images/browser-console.png)
✅ Si pensa che i dati vengano inviati al server in modo sicuro? E se qualcuno fosse in grado di intercettare la richiesta? Si possono leggere informazioni su [HTTPS](https://en.wikipedia.org/wiki/HTTPS) per saperne di più sulla comunicazione sicura dei dati.
## Convalida dati
Se si prova a registrare un nuovo account senza prima impostare un nome utente, si può vedere che il server restituisce un errore con codice di stato [400 Bad Request](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/400#:~:text=The%20HyperText%20Transfer%20Protocol%20(HTTP,%2C%20or%20deceptive%20request%20routing).), (richiesta non valida)
Prima di inviare i dati a un server è buona norma [convalidare i dati del modulo](https://developer.mozilla.org/en-US/docs/Learn/Forms/Form_validation) in anticipo quando possibile, per assicurarsi di inviare una richiesta valida. I controlli dei form HTML5 forniscono la convalida incorporata utilizzando vari attributi:
- `requested`: il campo deve essere compilato altrimenti il modulo non può essere inviato.
- `minlength` e `maxlength`: definisce il numero minimo e massimo di caratteri nei campi di testo.
- `min` e `max`: definisce il valore minimo e massimo di un campo numerico.
- `type`: definisce il tipo di dati attesi, come `number`, `email`, `file` o [altri tipi incorporati](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input). Questo attributo può anche modificare il rendering visivo del form.
- `pattern`: permette di definire un modello di [espressione regolare](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions) per verificare se i dati inseriti sono validi o meno.
> Suggerimento: si può personalizzare l'aspetto dei controlli del form a seconda che siano validi o meno utilizzando le pseudo-classi CSS `:valid` e `:invalid` .
### Attività
Ci sono 2 campi obbligatori per creare un nuovo account valido, nome utente (username) e la valuta (currency), gli altri campi sono opzionali. Aggiornare l'HTML del form, utilizzando sia l'attributo `required` che il testo nell'etichetta del campo in questo modo:
```html
<label for="user">Username (required)</label>
<input id="user" name="user" type="text" required>
...
<label for="currency">Currency (required)</label>
<input id="currency" name="currency" type="text" value="$" required>
```
Sebbene questa particolare implementazione del server non imponga limiti specifici sulla lunghezza massima dei campi, è sempre buona norma definire limiti ragionevoli per qualsiasi voce di testo inserita dell'utente.
Aggiungere un attributo `maxlength` ai campi di testo:
```html
<input id="user" name="user" type="text" maxlength="20" required>
...
<input id="currency" name="currency" type="text" value="$" maxlength="5" required>
...
<input id="description" name="description" type="text" maxlength="100">
```
Ora se si preme il pulsante *Register* e un campo non rispetta una regola di convalida che è stata definita, si dovrebbe vedere qualcosa del genere:
![Videata che mostra l'errore di convalida durante il tentativo di inviare il form](../images/validation-error.png)
La convalida come questa eseguita *prima* di inviare qualsiasi dato al server è chiamata convalida **lato client** . Tenere presente che non è sempre possibile eseguire tutti i controlli senza inviare i dati. Ad esempio, non si può verificare qui se esiste già un account con lo stesso nome utente senza inviare una richiesta al server. La convalida aggiuntiva eseguita sul server è denominata convalida **lato server** .
Di solito devono essere implementate entrambe; mentre l'utilizzo della convalida lato client migliora l'esperienza dell'utente fornendo un feedback immediato all'utente, la convalida lato server è fondamentale per assicurarsi che i dati utente da manipolare siano validi e sicuri.
---
## 🚀 Sfida
Mostrare un messaggio di errore nell'HTML se l'utente esiste già.
Ecco un esempio di come può apparire la pagina di accesso finale dopo l'applicazione di un po' di stile:
![Videata della pagina di accesso dopo l'aggiunta di stili CSS](../images/result.png)
## Quiz Post-Lezione
[Quiz post-lezione](https://nice-beach-0fe9e9d0f.azurestaticapps.net/quiz/44?loc=it)
## Revisione e Auto Apprendimento
Gli sviluppatori sono diventati molto creativi nei loro sforzi di costruzione di form, in particolare per quanto riguarda le strategie di convalida. Scoprire i diversi flussi di form cercando su [CodePen](https://codepen.com); si riescono a trovare dei form interessanti e stimolanti?
## Compito
[Dare uno stile alla propria app bancaria](assignment.it.md)

@ -12,7 +12,7 @@
### 前提条件
このレッスンでは、Web アプリの [HTML テンプレートとルーティング](../../1-template-route/README.md)が完了している必要があります。また、アカウントを作成するためのデータを送信できるように、ローカルに [Node.js](https://nodejs.org/ja) と[サーバー API を実行する](../../api/translations/README.ja.md)をインストールする必要があります。
このレッスンでは、Web アプリの [HTML テンプレートとルーティング](../../1-template-route/translations/README.ja.md)が完了している必要があります。また、アカウントを作成するためのデータを送信できるように、ローカルに [Node.js](https://nodejs.org/ja) と[サーバー API を実行する](../../api/translations/README.ja.md)をインストールする必要があります。
ターミナルでこのコマンドを実行することで、サーバーが正常に動作していることを確認することができます。

@ -0,0 +1,13 @@
# Dare uno stile alla propria app bancaria
## Istruzioni
Creare un nuovo file `styles.css` e aggiungere un collegamento nel file `index.html` corrente. Nel file CSS appena creato aggiungere uno stile per rendere la pagina di *Login* e *cruscotto* bella e ordinata. Provare a creare un tema cromatico per dare alla app il proprio marchio.
> Suggerimento: si può modificare l'HTML e aggiungere nuovi elementi e classi se necessario.
## Rubrica
| Criteri | Ottimo | Adeguato | Necessita miglioramento |
| -------- | ----------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------- |
| | Tutte le pagine sembrano pulite e leggibili, con un tema cromatico coerente e le diverse sezioni che risaltano correttamente. | Le pagine hanno uno stile ma senza un tema o con sezioni non chiaramente delimitate. | Le pagine mancano di stile, le sezioni sembrano disorganizzate e le informazioni sono difficili da leggere. |

@ -0,0 +1,334 @@
# Creazione di un'App Bancaria Parte 3: Metodi di Recupero e Utilizzo Dati
## Quiz Pre-Lezione
[Quiz Pre-Lezione](https://nice-beach-0fe9e9d0f.azurestaticapps.net/quiz/45?loc=it)
### Introduzione
Al centro di ogni applicazione web ci sono i *dati*. I dati possono assumere molte forme, ma il loro scopo principale è sempre quello di mostrare le informazioni all'utente. Con le app web che diventano sempre più interattive e complesse, il modo in cui l'utente accede e interagisce con le informazioni è ora una parte fondamentale dello sviluppo web.
In questa lezione si vedrà come recuperare i dati da un server in modo asincrono e utilizzare questi dati per visualizzare le informazioni su una pagina web senza ricaricare l'HTML.
### Prerequisito
Per questa lezinoe è necessario aver creato la parte [Form di Accesso e Registrazione](../../2-forms/translations/README.it.md) dell'app web. È inoltre necessario installare [Node.js](https://nodejs.org) ed eseguire [l'API del server](../../api/translations/README.it.md) in locale in modo da ottenere i dati dell'account.
Si può verificare che il server funzioni correttamente eseguendo questo comando in un terminale:
```sh
curl http://localhost:5000/api
# -> dovrebbe restituire "Bank API v1.0.0" come risultato
```
---
## AJAX e recupero dati
I siti Web tradizionali aggiornano il contenuto visualizzato quando l'utente seleziona un collegamento o invia dati utilizzando un form, ricaricando l'intera pagina HTML. Ogni volta che è necessario caricare nuovi dati, il server web restituisce una nuova pagina HTML che deve essere elaborata dal browser, interrompendo l'azione corrente dell'utente e limitando le interazioni durante il ricaricamento. Questo flusso di lavoro è anche chiamato *Applicazione MultiPagina* o *MPA* (Multi-Page Application) .
![Aggiornare il flusso di lavoro in un'applicazione multipagina](../images/mpa.png)
Quando le applicazioni web hanno iniziato a diventare più complesse e interattive, è emersa una nuova tecnica chiamata [AJAX (Asynchronous JavaScript and XML)](https://it.wikipedia.org/wiki/AJAX)). Questa tecnica consente alle app web di inviare e recuperare dati da un server in modo asincrono utilizzando JavaScript, senza dover ricaricare la pagina HTML, con conseguenti aggiornamenti più rapidi e interazioni utente più fluide. Quando vengono ricevuti nuovi dati dal server, la pagina HTML corrente può anche essere aggiornata con JavaScript utilizzando l'API [DOM](https://developer.mozilla.org/it/docs/Web/API/Document_Object_Model). Nel tempo, questo approccio si è evoluto in quella che ora viene chiamata [*Applicazione a Pagina Singola* o *SPA*](https://it.wikipedia.org/wiki/Single-page_application) (Single-Page Application).
![Aggiornare il flusso di lavoro in un'applicazione a pagina singola](../images/spa.png)
Quando è stato introdotto per la prima volta AJAX, l'unica API disponibile per recuperare i dati in modo asincrono era [`XMLHttpRequest`](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/Using_XMLHttpRequest). I browser moderni ora implementano anche l'[API Fetch](https://developer.mozilla.org/it/docs/Web/API/Fetch_API), più conveniente e potente, che utilizza le promesse ed è più adatta per manipolare i dati JSON.
> Sebbene tutti i browser moderni supportino l'`API Fetch`, se si desidera che la propria applicazione web funzioni su browser legacy o vecchi, è sempre una buona idea controllare prima la [tabella di compatibilità su caniuse.com](https://caniuse.com/fetch).
### Attività
[Nella lezione precedente](../../2-forms/translations/README.it.md) si è implementato il form di registrazione per creare un account. Ora verrà aggiunto il codice per accedere utilizzando un account esistente e recuperarne i dati. Aprire il file `app.js` e aggiungere una nuova funzione di accesso (`login`):
```js
async function login() {
const loginForm = document.getElementById('loginForm')
const user = loginForm.user.value;
}
```
Qui si inizia recuperando l'elemento form con `getElementById()`, quindi si ottiene il nome utente (user) dall'input con `loginForm.user.value`. È possibile accedere a ogni controllo del form tramite il suo nome (impostato nell'HTML utilizzando l'attributo `name`) come proprietà del form.
In modo simile a quanto fatto per la registrazione, verrà creata un'altra funzione per eseguire una richiesta al server, ma questa volta per recuperare i dati dell'account:
```js
async function getAccount(user) {
try {
const response = await fetch('//localhost:5000/api/accounts/' + encodeURIComponent(user));
return await response.json();
} catch (error) {
return { error: error.message || 'Unknown error' };
}
}
```
Si usa l'API `fetch` per richiedere i dati in modo asincrono dal server, ma questa volta non servono parametri aggiuntivi oltre all'URL da chiamare, poiché si sta solo interrogando i dati. Per impostazione predefinita, `fetch` crea una richiesta HTTP [`GET`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/GET), che è ciò che serve qui.
`encodeURIComponent()` è una funzione che evita i caratteri speciali per l'URL. Quali problemi si potrebbero avere se non viene chiamata questa funzione e si utilizza direttamente il valore del campo `user` nell'URL?
Aggiornare ora la funzione `login` per utilizzare `getAccount`:
```js
async function login() {
const loginForm = document.getElementById('loginForm')
const user = loginForm.user.value;
const data = await getAccount(user);
if (data.error) {
return console.log('loginError', data.error);
}
account = data;
navigate('/dashboard');
}
```
Innanzitutto, poiché `getAccount` è una funzione asincrona, occorre associarla alla parola chiave `await` per attendere il risultato dal server. Come con qualsiasi richiesta al server, occorre anche gestire i casi di errore. Per ora si aggiungerà solo un messaggio di log per visualizzare l'errore e verrà indirizzato più tardi.
Quindi si deve memorizzare i dati da qualche parte in modo da poterli utilizzare in seguito per visualizzare le informazioni nel cruscotto. Poiché la variabile `account` non esiste ancora, verrà creata come variabile globale all'inizio del file:
```js
let account = null;
```
Dopo che i dati dell'utente sono stati salvati in una variabile, si può navigare dalla pagina di accessi (*login*) al cruscotto (*dashboard*) utilizzando la funzione `navigate()` che già esiste.
Infine, si deve chiamare la funzione `login` quando viene inviato il form di accesso, modificando l'HTML:
```html
<form id="loginForm" action="javascript:login()">
```
Verificare che tutto funzioni correttamente registrando un nuovo account e provando ad accedere utilizzando lo stesso account.
Prima di passare alla parte successiva, si può anche completare la funzione di registrazione (`register`) aggiungendola in fondo alla funzione:
```js
account = result;
navigate('/dashboard');
```
✅ Si sa che per impostazione predefinita, si possono chiamare solo le API di un server che ha *stesso dominio e porta* della pagina web che si sta visualizzando? Questo è un meccanismo di sicurezza applicato dai browser. Un momento, la nostra app web è in esecuzione su `localhost:3000` mentre l'API del server è in esecuzione su `localhost:5000`, come mai funziona? Utilizzando una tecnica chiamata CORS [Cross-Origin Resource Sharing](https://developer.mozilla.org/it/docs/Web/HTTP/CORS), è possibile eseguire richieste HTTP con diverse origini (cross-origin) se il server aggiunge intestazioni speciali alla risposta, consentendo eccezioni per domini specifici.
> Ulteriori informazioni sulle API sono disponibili seguendo questa [lezione](https://docs.microsoft.com/en-us/learn/modules/use-apis-discover-museum-art?WT.mc_id=academic-4621-cxa)
## Aggiornare HTML per visualizzare i dati
Ora che si hanno i dati dell'utente, occorre aggiornare l'HTML esistente per visualizzarli. E' già noto come recuperare un elemento dal DOM utilizzando ad esempio `document.getElementById()`. Dopo aver ottenuto un elemento base, ecco alcune API che si possono utilizzare per modificarlo o aggiungervi elementi figlio:
- Usando la proprietà [`textContent`](https://developer.mozilla.org/en-US/docs/Web/API/Node/textContent) si può cambiare il testo di un elemento. Notare che la modifica di questo valore rimuove tutti i figli dell'elemento (se presenti) e li sostituisce con il testo fornito. In quanto tale, è anche un metodo efficiente per rimuovere tutti i figli di un dato elemento assegnandogli una stringa vuota `""` .
- Usando [`document.createElement()`](https://developer.mozilla.org/en-US/docs/Web/API/Document/createElement) insieme al metodo [`append()`](https://developer.mozilla.org/en-US/docs/Web/API/ParentNode/append) si può creare e allegare uno o più nuovi elementi figlio.
✅ Utilizzando la proprietà [`innerHTML`](https://developer.mozilla.org/en-US/docs/Web/API/Element/innerHTML) di un elemento è anche possibile modificare il suo contenuto HTML, ma questo dovrebbe essere evitato poiché è vulnerabile agli attacchi di [cross-site scripting (XSS)](https://developer.mozilla.org/en-US/docs/Glossary/Cross-site_scripting) .
### Attività
Prima di passare alla schermata del cruscotto, c'è ancora una cosa da fare nella pagina di accesso (*login*). Attualmente, se si prova ad accedere con un nome utente che non esiste, viene mostrato un messaggio nella console ma per un utente normale non cambia nulla e non sa cosa sta succedendo.
Si aggiunge ora un elemento segnaposto nel form di accesso dove si può visualizzare un messaggio di errore se necessario. Un buon posto sarebbe subito prima del pulsante (`<button>`) con testo Login:
```html
...
<div id="loginError"></div>
<button>Login</button>
...
```
Questo elemento `<div>` è vuoto, il che significa che non verrà visualizzato nulla sullo schermo fino a quando non verrà aggiunto del contenuto. Occorre anche assegnarli un `ID` in modo da poterlo recuperare facilmente con JavaScript.
Tornare al file `app.js` e creare una nuova funzione di supporto `updateElement`:
```js
function updateElement(id, text) {
const element = document.getElementById(id);
element.textContent = text;
}
```
Questo è abbastanza semplice: dato un *ID* di un elemento e un *testo*, si aggiornerà il contenuto del testo dell'elemento DOM con l'`id` corrispondente. Si usa questo metodo al posto del precedente messaggio di errore nella funzione `login` :
```js
if (data.error) {
return updateElement('loginError', data.error);
}
```
Ora se si prova ad accedere con un account non valido, si dovrebbe vedere qualcosa del genere:
![Videata che mostra il messaggio di errore visualizzato durante l'accesso](../images/login-error.png)
Ora si ha un testo di errore che viene visualizzato, ma se si prova con un lettore di schermo si noterà che non viene annunciato nulla. Affinché il testo che viene aggiunto dinamicamente a una pagina venga annunciato dai lettori di schermo, sarà necessario utilizzare qualcosa chiamato [Live Region](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Live_Regions). Qui verrà utilizzato un tipo specifico di live region chiamato alert:
```html
<div id="loginError" role="alert"></div>
```
Implementare lo stesso comportamento per gli errori della funzione `register` (non dimenticare di aggiornare l'HTML).
## Visualizzare le informazioni sulla dashboard
Utilizzando le stesse tecniche appena viste, ci si occuperà anche di visualizzare le informazioni dell'account nella pagina del cruscotto.
Ecco come appare un oggetto account ricevuto dal server:
```json
{
"user": "test",
"currency": "$",
"description": "Test account",
"balance": 75,
"transactions": [
{ "id": "1", "date": "2020-10-01", "object": "Pocket money", "amount": 50 },
{ "id": "2", "date": "2020-10-03", "object": "Book", "amount": -10 },
{ "id": "3", "date": "2020-10-04", "object": "Sandwich", "amount": -5 }
],
}
```
> Nota: per semplificare, si può utilizzare l'account `test` preesistente già popolato di dati.
### Attività
Iniziare sostituendo la sezione "Balance" (saldo) nell'HTML per aggiungere elementi segnaposto:
```html
<section>
Balance: <span id="balance"></span><span id="currency"></span>
</section>
```
Si aggiungerà anche una nuova sezione appena sotto per visualizzare la descrizione dell'account:
```html
<h2 id="description"></h2>
```
✅ Poiché la descrizione del'account funge da titolo per il contenuto sottostante, viene contrassegnata semanticamente come intestazione (heading). Scoprire di più su come [la struttura delle intestazioni](https://www.nomensa.com/blog/2017/how-structure-headings-web-accessibility) è importante per l'accessibilità ed esaminare la pagina per determinare cos'altro potrebbe essere un'intestazione.
Successivamente, verrà creata una nuova funzione in `app.js` per riempire il segnaposto:
```js
function updateDashboard() {
if (!account) {
return navigate('/login');
}
updateElement('description', account.description);
updateElement('balance', account.balance.toFixed(2));
updateElement('currency', account.currency);
}
```
Innanzitutto, controllare di avere i dati dell'account necessari prima di andare oltre. Quindi si usa la funzione `updateElement()` creata in precedenza per aggiornare l'HTML.
> Per rendere la visualizzazione del saldo più bella, si usa il metodo [`toFixed(2)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toFixed) per forzare la visualizzazione del valore con 2 cifre per la parte decimale.
Ora occorre chiamare la funzione `updateDashboard()` ogni volta che viene caricato il cruscotto. Se si è già terminato il [compito della lezione 1](../../1-template-route/translations/assignment.it.md) , questo dovrebbe essere immediato, altrimenti si può utilizzare la seguente implementazione.
Aggiungere questo codice alla fine della funzione `updateRoute()`:
```js
if (typeof route.init === 'function') {
route.init();
}
```
Aggiornare la definizione delle rotte con:
```js
const routes = {
'/login': { templateId: 'login' },
'/dashboard': { templateId: 'dashboard', init: updateDashboard }
};
```
Con questa modifica, ogni volta che viene visualizzata la pagina del cruscotto viene chiamata la funzione `updateDashboard()`. Dopo un accesso, si dovrebbe essere in grado di vedere il saldo del conto, la valuta e la descrizione.
## Creare righe di tabelle dinamicamente con modelli HTML
Nella [prima lezione](../../1-template-route/translations/README.it.md) sono stati utilizzati modelli HTML insieme al metodo [`appendChild()`](https://developer.mozilla.org/en-US/docs/Web/API/Node/appendChild) per implementare la navigazione nell'app. I modelli possono anche essere più piccoli e utilizzati per popolare dinamicamente parti ripetitive di una pagina.
Verrà usato un approccio simile per visualizzare l'elenco delle transazioni nella tabella HTML.
### Attività
Aggiungere un nuovo modello nell'elemento HTML `<body>`:
```html
<template id="transaction">
<tr>
<td></td>
<td></td>
<td></td>
</tr>
</template>
```
Questo modello rappresenta una singola riga di tabella, con le 3 colonne che si vuole popolare: *data*, *oggetto* e *importo* di una transazione.
Aggiungere quindi la proprietà `id` all'elemento `<tbody>` della tabella all'interno del modello del cruscotto per semplificare la ricerca dell'elemento utilizzando JavaScript:
```html
<tbody id="transactions"></tbody>
```
L'HTML è pronto, ora si passa al codice JavaScript e si crea una nuova funzione `createTransactionRow`:
```js
function createTransactionRow(transaction) {
const template = document.getElementById('transaction');
const transactionRow = template.content.cloneNode(true);
const tr = transactionRow.querySelector('tr');
tr.children[0].textContent = transaction.date;
tr.children[1].textContent = transaction.object;
tr.children[2].textContent = transaction.amount.toFixed(2);
return transactionRow;
}
```
Questa funzione fa esattamente ciò che implica il suo nome: utilizzando il modello creato in precedenza, crea una nuova riga di tabella e riempie il suo contenuto utilizzando i dati della transazione. Verrà usata nella funzione `updateDashboard()` per popolare la tabella:
```js
const transactionsRows = document.createDocumentFragment();
for (const transaction of account.transactions) {
const transactionRow = createTransactionRow(transaction);
transactionsRows.appendChild(transactionRow);
}
updateElement('transactions', transactionsRows);
```
Qui si utilizza il metodo [`document.createDocumentFragment()`](https://developer.mozilla.org/en-US/docs/Web/API/Document/createDocumentFragment) che crea un nuovo frammento DOM su cui si può lavorare, prima di collegarlo finalmente alla tabella HTML.
C'è ancora un'altra cosa da fare prima che questo codice possa funzionare, poiché la funzione `updateElement()` attualmente supporta solo contenuto di testo. Occorre cambiare un poco il suo codice:
```js
function updateElement(id, textOrNode) {
const element = document.getElementById(id);
element.textContent = ''; // Rimuove tutti i figli
element.append(textOrNode);
}
```
Si usa il metodo [`append()`](https://developer.mozilla.org/en-US/docs/Web/API/ParentNode/append) in quanto consente di allegare testo o [nodi DOM](https://developer.mozilla.org/en-US/docs/Web/API/Node) a un elemento genitore, che è perfetto per tutti i casi d'uso.
Se si prova a utilizzare l'account `test` per accedere, ora si dovrebbe vedere un elenco di transazioni sul cruscotto 🎉.
---
## 🚀 Sfida
Collaborare per far sembrare la pagina del cruscotto una vera app bancaria. Se è già stato definito lo stile della propria app, provare a utilizzare le [media query](https://developer.mozilla.org/en-US/docs/Web/CSS/Media_Queries) per creare una [disposizione reattiva](https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps/Responsive/responsive_design_building_blocks) che funzioni bene sia su dispositivi desktop che mobili.
Ecco un esempio di una pagina cruscotto con applicato uno stile:
![Videata di un risultato di esempio del cruscotto dopo l'applicazione dello stile](../../images/screen2.png)
## Quiz Post-Lezione
[Quiz post-lezione](https://nice-beach-0fe9e9d0f.azurestaticapps.net/quiz/46?loc=it)
## Compito
[Refattorizzare e commentare il codice](assignment.it.md)

@ -321,7 +321,7 @@ function updateElement(id, textOrNode) {
ダッシュボードページのスタイリング例です。
![スタイリング後のダッシュボードの結果例のスクリーンショット](../images/screen2.png)
![スタイリング後のダッシュボードの結果例のスクリーンショット](../../images/screen2.png)
## レッスン後の小テスト

@ -0,0 +1,15 @@
# Refattorizzare e commentare il codice
## Istruzioni
Man mano che la base del codice cresce, è importante rifattorizzare il codice frequentemente per mantenerlo leggibile e gestibile nel tempo. Aggiungere commenti e rifattorizzare la propria `app.js` per migliorare la qualità del codice:
- Estrarre costanti, come l'URL di base dell'API del server
- Fattorizzare codice simile: ad esempio si può creare una funzione `sendRequest()` per raggruppare il codice utilizzato sia in `createAccount()` che in `getAccount()`
- Riorganizzare il codice per renderlo più facile da leggere e aggiungere commenti
## Rubrica
| Criteri | Ottimo | Adeguato | Necessita miglioramento |
| -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------- |
| | Il codice è commentato, ben organizzato in diverse sezioni e di facile lettura. Le costanti vengono estratte ed è stata creata una funzione `sendRequest()` fattorizzata. | Il codice è pulito ma può ancora essere migliorato con più commenti, estrazione di costanti o fattorizzazione. | Il codice è disordinato, non commentato, le costanti non vengono estratte e il codice non è fattorizzato |

@ -31,7 +31,7 @@ There's 3 issues with the current code:
- The state is not persisted, as a browser refresh takes you back to the login page.
- There are multiple functions that modify the state. As the app grows, it can make it difficult to track the changes and it's easy to forget updating one.
- The state is not cleaned up, when you click on *Logout* the account data is still there even though you're on the login page.
- The state is not cleaned up, so when you click on *Logout* the account data is still there even though you're on the login page.
We could update our code to tackle these issues one by one, but it would create more code duplication and make the app more complex and difficult to maintain. Or we could pause for a few minutes and rethink our strategy.
@ -42,7 +42,7 @@ We could update our code to tackle these issues one by one, but it would create
- How to keep the data flows in an app understandable?
- How to keep the state data always in sync with the user interface (and vice versa)?
Once you've taken care of these, any other issues you might have may either be fixed already or have become easier to fix. There are many possible approaches for solving these problems, but we'll go with a common solution that consists in **centralizing the data and the ways to change it**. The data flows would go like this:
Once you've taken care of these, any other issues you might have may either be fixed already or have become easier to fix. There are many possible approaches for solving these problems, but we'll go with a common solution that consists of **centralizing the data and the ways to change it**. The data flows would go like this:
![Schema showing the data flows between the HTML, user actions and state](./images/data-flow.png)
@ -82,7 +82,7 @@ This refactoring by itself did not bring much improvements, but the idea was to
Now that we have put in place the `state` object to store our data, the next step is centralize the updates. The goal is to make it easier to keep track of any changes and when they happen.
To avoid having changes made to the `state` object it's also a good practice to consider it [*immutable*](https://en.wikipedia.org/wiki/Immutable_object), meaning that it cannot be modified at all. It also means that you have to create a new state object if you want to change anything in it. By doing this, you build a protection about potentially unwanted [side effects](https://en.wikipedia.org/wiki/Side_effect_(computer_science)), and open up possibilities for new features in your app like implementing undo/redo, while also making it easier to debug. For example, you could log every changes made to the state and keep an history of the changes to understand the source of a bug.
To avoid having changes made to the `state` object, it's also a good practice to consider it [*immutable*](https://en.wikipedia.org/wiki/Immutable_object), meaning that it cannot be modified at all. It also means that you have to create a new state object if you want to change anything in it. By doing this, you build a protection about potentially unwanted [side effects](https://en.wikipedia.org/wiki/Side_effect_(computer_science)), and open up possibilities for new features in your app like implementing undo/redo, while also making it easier to debug. For example, you could log every change made to the state and keep a history of the changes to understand the source of a bug.
In JavaScript, you can use [`Object.freeze()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze) to create an immutable version of an object. If you try to make changes to an immutable object, an exception will be raised.
@ -195,9 +195,9 @@ init();
Here we retrieve the saved data, and if there's any we update the state accordingly. It's important to do this *before* updating the route, as there might be code relying on the state during the page update.
We can also make the *Dashboard* page our application default page, as we are now persisting the account data. If no data is found, the dashboard takes care of redirecting to the *Login* page anyways. In `updateRoute()`, replace the fallback `return navigate('/login');` with `return navigate('dashboard');`.
We can also make the *Dashboard* page our application default page, as we are now persisting the account data. If no data is found, the dashboard takes care of redirecting to the *Login* page anyways. In `updateRoute()`, replace the fallback `return navigate('/login');` with `return navigate('/dashboard');`.
Now login in the app and try refreshing the page, you should stay on the dashboard. With that update we've taken care of all our initial issues...
Now login in the app and try refreshing the page. You should stay on the dashboard. With that update we've taken care of all our initial issues...
## Refresh the data

@ -0,0 +1,281 @@
# Creare un'App Bancaria Parte 4: Concetti di Gestione dello Stato
## Quiz Pre-Lezione
[Quiz Pre-Lezione](https://nice-beach-0fe9e9d0f.azurestaticapps.net/quiz/47?loc=it)
### Introduzione
Man mano che un'applicazione web cresce, diventa una sfida tenere traccia di tutti i flussi di dati. Quale codice riceve i dati, quale pagina li consuma, dove e quando deve essere aggiornata ... è facile ritrovarsi con codice disordinato e difficile da mantenere. Ciò è particolarmente vero quando è necessario condividere dati tra diverse pagine della propria app, ad esempio i dati dell'utente. Il concetto di *gestione dello stato* è sempre esistito in tutti i tipi di programmi, ma poiché le app web continuano a crescere in complessità, ora è un punto chiave su cui riflettere durante lo sviluppo.
In questa parte finale, si esaminerà l'app creata per ripensare a come viene gestito lo stato, consentendo il supporto per l'aggiornamento del browser in qualsiasi momento e persistendo i dati tra le sessioni utente.
### Prerequisito
È necessario aver completato la parte di [recupero dei dati](../../3-data/translations/README.it.md) dell'app web per questa lezione. È inoltre necessario installare [Node.js](https://nodejs.org) ed [eseguire l'API del server](../../api/translations/README.it.md) in locale in modo da ottenere i dati dell'account.
Si può verificare che il server funzioni correttamente eseguendo questo comando in un terminale:
```sh
curl http://localhost:5000/api
# -> dovrebbe restituire "Bank API v1.0.0" come risultato
```
---
## Ripensare la gestione dello stato
Nella [lezione precedente](../../3-data/translations/README.it.md), è stato introdotto un concetto basico di stato nell'app con la variabile globale `account` che contiene i dati bancari per l'utente attualmente connesso. Tuttavia, l'attuale implementazione presenta alcuni difetti. Si provi ad aggiornare la pagina quando ci si trova nella pagina del cruscotto. Che cosa accade?
Ci sono 3 problemi con il codice corrente:
- Lo stato non è persistente, poiché un aggiornamento del browser riporta alla pagina di accesso.
- Esistono più funzioni che modificano lo stato. Man mano che l'app cresce, può essere difficile tenere traccia delle modifiche ed è facile dimenticare di aggiornarne una.
- Lo stato non viene cancellato, quando si fa clic su *Logout* i dati dell'account sono ancora lì anche se si è nella pagina di accesso.
Si potrebbe aggiornare il codice per affrontare questi problemi uno per uno, ma creerebbe più duplicazioni del codice e renderebbe l'app più complessa e difficile da mantenere. Oppure ci si potrebbe fermare per qualche minuto e ripensare alla strategia.
> Quali problemi si stanno davvero cercando di risolvere qui?
La [gestione dello stato](https://en.wikipedia.org/wiki/State_management) consiste nel trovare un buon approccio per risolvere questi due problemi particolari:
- Come mantenere comprensibile il flusso di dati in un'app?
- Come mantenere i dati di stato sempre sincronizzati con l'interfaccia utente (e viceversa)?
Una volta che ci si è preso cura di questi, qualsiasi altro problema che si potrebbe avere potrebbe essere già stato risolto o essere diventato più facile da sistemare. Ci sono molti possibili approcci per risolvere questi problemi, ma si andrà con una soluzione comune che consiste nel **centralizzare i dati e le modalità per cambiarli**. Il flusso di dati andrebbe così:
![Schema che mostra i flussi di dati tra HTML, azioni dell'utente e stato](../images/data-flow.png)
> Non verrà trattata qui la parte in cui i dati attivano automaticamente l'aggiornamento della vista, poiché è legato a concetti più avanzati di [programmazione reattiva](https://en.wikipedia.org/wiki/Reactive_programming). È un buon argomento da sviluppare successivamente se si è pronti per un'immersione profonda.
✅ Esistono molte librerie con approcci diversi alla gestione dello stato, [Redux](https://redux.js.org) è un'opzione popolare. Dare un'occhiata ai concetti e ai modelli utilizzati spesso è un buon modo per apprendere quali potenziali problemi si potrebbe dover affrontare nelle grandi app web e come risolverli.
### Attività
Si inizierà con un po' di refattorizzazione. Sostituire la dichiarazione di `account` :
```js
let account = null;
```
Con:
```js
let state = {
account: null
};
```
L'idea è *centralizzare* tutti i dati dell'app in un unico oggetto di stato. Per ora c'è solo `account` nello stato, quindi non cambia molto, ma crea un percorso per le evoluzioni.
Si devono anche aggiornare le funzioni che lo utilizzano. Nelle funzioni `register()` e `login()` , sostituire `account = ...` con `state.account = ...`;
Nella parte superiore della funzione `updateDashboard()`, aggiungere questa riga:
```js
const account = state.account;
```
Questa refattorizzazione di per sé non ha portato molti miglioramenti, ma l'idea era di gettare le basi per i prossimi cambiamenti.
## Tenere traccia delle modifiche ai dati
Ora che si è impostato l'oggetto `state` per memorizzare i dati, il passaggio successivo è centralizzare gli aggiornamenti. L'obiettivo è rendere più facile tenere traccia di eventuali modifiche e quando si verificano.
Per evitare che vengano apportate modifiche all'oggetto `state` è anche una buona pratica considerarlo [*immutabile*](https://en.wikipedia.org/wiki/Immutable_object), nel senso che non può essere modificato affatto. Significa anche che si deve creare un nuovo oggetto di stato se si vuole cambiare qualcosa in esso. In questo modo, si crea una protezione [dagli effetti collaterali](https://it.wikipedia.org/wiki/Effetto_collaterale_(informatica)) potenzialmente indesiderati e si aprono possibilità per nuove funzionalità nella propria app come l'implementazione di funzioni di annulla/ripristina, semplificando anche il debug. Ad esempio, è possibile registrare tutte le modifiche apportate allo stato e conservare una cronologia delle modifiche per comprendere l'origine di un bug.
In JavaScript, si può utilizzare [`Object.freeze()`](https://developer.mozilla.org/it/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze) per creare una versione immutabile di un oggetto. Se si prova ad apportare modifiche a un oggetto immutabile, verrà sollevata un'eccezione.
✅ Si conosce la differenza tra un oggetto *shallow* e uno *deep* immutabile? Si può leggere [qui](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze#What_is_shallow_freeze) per saperne di più.
### Attività
Creare una nuova funzione `updateState()` :
```js
function updateState(property, newData) {
state = Object.freeze({
...state,
[property]: newData
});
}
```
In questa funzione, si crea un nuovo oggetto di stato e si copiano i dati dallo stato precedente utilizzando l' [*operatore spread (`...`)*](https://developer.mozilla.org/it/docs/Web/JavaScript/Reference/Operators/Spread_syntax). Quindi si sovrascrive una particolare proprietà dell'oggetto state con i nuovi dati usando la [notazione tra parentesi quadre](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Working_with_Objects#Objects_and_properties) `[property]` per l'assegnazione. Infine, si blocca l'oggetto per impedire modifiche utilizzando `Object.freeze()`. Per ora si ha solo la proprietà `account` memorizzata nello stato, ma con questo approccio si possono aggiungere tutte le proprietà che servono nello stato.
Si aggiornerà anche l'inizializzazione di `state` per assicurarsi che anche lo stato iniziale sia congelato:
```js
let state = Object.freeze({
account: null
});
```
Successivamente, aggiornare la funzione `register` sostituendo l'istruzione `state.account = result;` con:
```js
updateState('account', result);
```
Fare lo stesso con la funzione `login` , sostituendo `state.account = data;` con:
```js
updateState('account', data);
```
Si coglie ora l'occasione per risolvere il problema della mancata cancellazione dei dati dell'account quando l'utente fa clic su *Logout*.
Creare una nuova funzione `logout ()`:
```js
function logout() {
updateState('account', null);
navigate('/login');
}
```
In `updateDashboard()`, sostituire il reindirizzamento `return navigate('/ login');` con `return logout()`;
Provare a registrare un nuovo account, a disconnettersi e ad accedere nuovamente per verificare che tutto funzioni ancora correttamente.
> Suggerimento: si può dare un'occhiata a tutti i cambiamenti di stato aggiungendo `console.log (state)` nella parte inferiore di `updateState()` e aprendo la console negli strumenti di sviluppo del browser.
## Persistere lo stato
La maggior parte delle app web deve conservare i dati per poter funzionare correttamente. Tutti i dati critici vengono solitamente archiviati su un database e accessibili tramite un'API del server, come nel nostro caso i dati dell'account utente. Ma a volte è anche interessante mantenere alcuni dati sulla parte client dell'app in esecuzione nel browser, per una migliore esperienza utente o per migliorare le prestazioni di caricamento.
Quando si vuole mantenere i dati nel browser, ci sono alcune domande importanti da porsi:
- *I dati sono sensibili?* Si dovrebbe evitare di memorizzare dati sensibili sul client, come le password degli utenti.
- *Per quanto tempo si ha bisogno di conservare questi dati?* Si prevede di accedere a questi dati solo per la sessione corrente o si desidera che vengano memorizzati per sempre?
Esistono diversi modi per archiviare le informazioni all'interno di un'app web, a seconda di ciò che si desidera ottenere. Ad esempio, si possono utilizzare gli URL per memorizzare una interrogazione di ricerca e renderla condivisibile tra gli utenti. Si possono anche utilizzare i [cookie HTTP](https://developer.mozilla.org/it/docs/Web/HTTP/Cookies) se i dati devono essere condivisi con il server, come le informazioni di [autenticazione](https://it.wikipedia.org/wiki/Autenticazione) .
Un'altra opzione è utilizzare una delle tante API del browser per la memorizzazione dei dati. Due di loro sono particolarmente interessanti:
- [`localStorage`](https://developer.mozilla.org/it/docs/Web/API/Window/localStorage): un [archivio chiave/valore](https://en.wikipedia.org/wiki/Key%E2%80%93value_database) che consente di persistere i dati specifici del sito Web corrente in diverse sessioni. I dati salvati in esso non scadono mai.
- [`sessionStorage`](https://developer.mozilla.org/it/docs/Web/API/Window/sessionStorage): funziona come `localStorage` tranne per il fatto che i dati in esso memorizzati vengono cancellati al termine della sessione (alla chiusura del browser).
Notare che entrambe queste API consentono solo di memorizzare [stringhe](https://developer.mozilla.org/it/docs/Web/JavaScript/Reference/Global_Objects/String). Se si desidera archiviare oggetti complessi, si dovranno serializzare nel formato [JSON](https://developer.mozilla.org/it/docs/Web/JavaScript/Reference/Global_Objects/JSON) utilizzando [`JSON.stringify()`](https://developer.mozilla.org/it/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify).
✅ Se si desidera creare un'app web che non funziona con un server, è anche possibile creare un database sul client utilizzando l' [`API` IndexedDB](https://developer.mozilla.org/it/docs/Web/API/IndexedDB_API). Questo è riservato a casi d'uso avanzati o se è necessario archiviare una quantità significativa di dati, poiché è più complesso da usare.
### Attività
Si vuole che gli utenti rimangano collegati fino a quando non fanno clic esplicitamente sul pulsante *Logout* , quindi si utilizzerà `localStorage` per memorizzare i dati dell'account. Per prima cosa, si definisce una chiave che verrà usata per memorizzare i dati.
```js
const storageKey = 'savedAccount';
```
Aggiungere quindi questa riga alla fine della funzione `updateState()`:
```js
localStorage.setItem(storageKey, JSON.stringify(state.account));
```
Con questo, i dati dell'account utente verranno mantenuti e sempre aggiornati poiché si sono centralizzati in precedenza tutti gli aggiornamenti di stato. È qui che si inizia a trarre vantaggio da tutte le rifattorizzazioni precedenti 🙂.
Poiché i dati vengono salvati, ci si deve anche occupare di ripristinarli quando l'app viene caricata. Dato che si inizierà ad avere più codice di inizializzazione, potrebbe essere una buona idea creare una nuova funzione di inizializzazione `init` , che includa anche il codice precedente nella parte inferiore di `app.js`:
```js
function init() {
const savedAccount = localStorage.getItem(storageKey);
if (savedAccount) {
updateState('account', JSON.parse(savedAccount));
}
// Il codice di inizializzazione precedente
window.onpopstate = () => updateRoute();
updateRoute();
}
init();
```
Qui si recuperano i dati salvati e, se ce ne sono, si aggiorna lo stato di conseguenza. È importante farlo *prima* di aggiornare la rotta, poiché potrebbe esserci del codice che si basa sullo stato durante l'aggiornamento della pagina.
Si può anche rendere la pagina del *cruscotto* la pagina predefinita dell'applicazione, poiché ora si sta persistendo i dati dell'account. Se non vengono trovati dati, il cruscotto si occupa comunque di reindirizzare alla pagina di *Login* . In `updateRoute()`, sostituire le istruzioni di contingenza `return navigate ('/login');` con `return navigate ('/dashboard') ;`.
Ora accedere all'app e provare ad aggiornare la pagina, si dovrebbe rimanere sul cruscotto. Con quell'aggiornamento ci si è presi cura di tutti i problemi iniziali...
## Aggiornare i dati
...Si potrebbe uttavia anche averne creato uno nuovo. Oops!
Andare al cruscotto utilizzando l'account `test`, quindi eseguire questo comando su un terminale per creare una nuova transazione:
```sh
curl --request POST \
--header "Content-Type: application/json" \
--data "{ \"date\": \"2020-07-24\", \"object\": \"Bought book\", \"amount\": -20 }" \
http://localhost:5000/api/accounts/test/transactions
```
Provare subito ad aggiornare la pagina del cruscotto nel browser. Che cosa accade? Si vede la nuova transazione?
Lo stato viene mantenuto indefinitamente grazie a `localStorage`, ma ciò significa anche che non viene aggiornato fino a quando non si esce dall'app e si accede di nuovo!
Una possibile strategia per risolvere questo problema è ricaricare i dati dell'account ogni volta che viene caricato il cruscotto, per evitare lo stallo dei dati.
### Attività
Creare una nuova funzione `updateAccountData`:
```js
async function updateAccountData() {
const account = state.account;
if (!account) {
return logout();
}
const data = await getAccount(account.user);
if (data.error) {
return logout();
}
updateState('account', data);
}
```
Questo metodo controlla che si sia attualmente collegati, quindi ricarica i dati dell'account dal server.
Creare un'altra funzione chiamata `refresh`:
```js
async function refresh() {
await updateAccountData();
updateDashboard();
}
```
Questa aggiorna i dati dell'account, quindi si occupa dell'aggiornamento dell'HTML della pagina del cruscotto. È ciò che si deve chiamare quando viene caricata la rotta del cruscotto (dashboard). Aggiornare la definizione del percorso con:
```js
const routes = {
'/login': { templateId: 'login' },
'/dashboard': { templateId: 'dashboard', init: refresh }
};
```
Provare a ricaricare il cruscotto ora, dovrebbe visualizzare i dati dell'account aggiornati.
---
## 🚀 Sfida
Ora che i dati dell'account vengono ricaricati ogni volta che viene caricato il cruscotto, si pensa che sia ancora necessario persistere *tutti i dati dell'account* ?
Provare a lavorare insieme per cambiare ciò che viene salvato e caricato da `localStorage` per includere solo ciò che è assolutamente necessario per il funzionamento dell'app.
## Quiz Post-Lezione
[Quiz post-lezione](https://nice-beach-0fe9e9d0f.azurestaticapps.net/quiz/48?loc=it)
## Compito
[Implementare la finestra di dialogo "Aggiungi transazione"](assignment.it.md)
Ecco un esempio di risultato dopo aver completato il compito:
![Videata che mostra un esempio di dialogo "Aggiungi transazione"](../images/dialog.png)

@ -12,7 +12,7 @@ Web アプリケーションが成長するにつれて、すべてのデータ
### 前提条件
このレッスンでは、Web アプリの[データ取得](./././3-data/translations/README.ja.md)の部分が完了している必要があります。また、アカウントデータを管理するためには、ローカルに [Node.js](https://nodejs.org/ja) をインストールし、[サーバー API を実行する](.../../api/translations/README.ja.md)をインストールする必要があります。
このレッスンでは、Web アプリの[データ取得](../../3-data/translations/README.ja.md)の部分が完了している必要があります。また、アカウントデータを管理するためには、ローカルに [Node.js](https://nodejs.org/ja) をインストールし、[サーバー API を実行する](../../api/translations/README.ja.md)をインストールする必要があります。
ターミナルでこのコマンドを実行することで、サーバーが正常に動作しているかどうかをテストすることができます。
@ -82,7 +82,7 @@ const account = state.account;
データを保存するために `state` オブジェクトを配置したので、次のステップは更新を一元化することです。目的は、いつ変更があったのか、いつ変更が発生したのかを簡単に把握できるようにすることです。
`state` オブジェクトに変更が加えられないようにするためには、`state` オブジェクトを [*immutable*](https://en.wikipedia.org/wiki/Immutable_object) と考えるのも良い方法です。これはまた、何かを変更したい場合には新しいステートオブジェクトを作成しなければならないことを意味します。このようにすることで、潜在的に望ましくない[副作用](https://en.wikipedia.org/wiki/Side_effect_(computer_science)についての保護を構築し、アンドゥ/リドゥの実装のようなアプリの新機能の可能性を開くと同時に、デバッグを容易にします。例えば、ステートに加えられたすべての変更をログに記録し、バグの原因を理解するために変更の履歴を保持することができます。
`state` オブジェクトに変更が加えられないようにするためには、`state` オブジェクトを [*immutable*](https://en.wikipedia.org/wiki/Immutable_object) と考えるのも良い方法です。これはまた、何かを変更したい場合には新しいステートオブジェクトを作成しなければならないことを意味します。このようにすることで、潜在的に望ましくない[副作用](https://ja.wikipedia.org/wiki/%E5%89%AF%E4%BD%9C%E7%94%A8_(%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%A0))についての保護を構築し、アンドゥ/リドゥの実装のようなアプリの新機能の可能性を開くと同時に、デバッグを容易にします。例えば、ステートに加えられたすべての変更をログに記録し、バグの原因を理解するために変更の履歴を保持することができます。
JavaScript では、[`Object.freeze()`](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze) を使って、オブジェクトの不変バージョンを作成することができます。不変オブジェクトに変更を加えようとすると例外が発生します。

@ -0,0 +1,25 @@
# Implementare la finestra di dialogo "Aggiungi transazione"
## Istruzioni
All'app bancaria manca ancora una caratteristica importante: la possibilità di inserire nuove transazioni.
Utilizzando tutto quanto appreso nelle quattro lezioni precedenti, implementare una finestra di dialogo "Aggiungi transazione":
- Aggiungere un pulsante "Aggiungi transazione" nella pagina del cruscotto
- Creare una nuova pagina con un modello HTML o usare JavaScript per mostrare/nascondere l'HTML della finestra di dialogo senza lasciare la pagina del cruscotto (si può usare la proprietà [`hidden`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/hidden) per quello o le classi CSS)
- Assicurarsi di gestire l' [accessibilità dalla tastiera e dal lettore di schermo](https://developer.paciellogroup.com/blog/2018/06/the-current-state-of-modal-dialog-accessibility/) per la finestra di dialogo
- Implementare un form HTML per ricevere i dati di input
- Creare dati JSON dai dati del form e inviarli all'API
- Aggiorna la pagina del cruscotto con i nuovi dati
Guardare [le specifiche dell'API del server](../api/README.md) per vedere quale API si devono chiamare e qual è il formato JSON previsto.
Ecco un esempio di risultato dopo aver completato il compito:
![Videata che mostra un esempio di dialogo "Aggiungi transizione"](../images/dialog.png)
## Rubrica
| Criteri | Ottimo | Adeguato | Necessita miglioramento |
| -------- | ------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------- | --------------------------------------------|
| | L'aggiunta di una transazione viene implementata seguendo completamente tutte le migliori pratiche viste nelle lezioni. | L'aggiunta di una transazione è implementata, ma non seguendo le migliori pratiche viste nelle lezioni o funzionante solo parzialmente. | L'aggiunta di una transazione non funziona affatto. |

@ -16,7 +16,7 @@
以下は、課題を完了した後の結果の例です。
![「トランジションの追加」ダイアログの例を示すスクリーンショット](../../images/dialog.png)
![「トランジションの追加」ダイアログの例を示すスクリーンショット](../images/dialog.png)
## ルーブリック

@ -0,0 +1,33 @@
# API Bank
> Bank API è costruita con [Node.js](https://nodejs.org) + [Express](https://expressjs.com/).
L'API è già costruita e non fa parte dell'esercizio.
Tuttavia, se interessa imparare come creare un'API come questa, si può seguire questa serie di video: https://aka.ms/NodeBeginner (i video da 17 a 21 riguardano questa specifica API).
Si può anche dare un'occhiata a questo tutorial interattivo: https://aka.ms/learn/express-api
## Mettere in esecuzione il server
Assicurarsi di aver [installato](https://nodejs.org) Node.js.
1. Eseguire il comando `git clone` per questo repository.
2. Aprire un terminale nella cartella `api` , quindi eseguire `npm install`.
3. Eseguire `npm start`.
Il server dovrebbe mettersi in ascolto sulla porta `5000`.
> Nota: tutte le voci vengono archiviate in memoria e non sono persistenti, quindi quando il server viene arrestato tutti i dati vengono persi.
## Dettagli API
| Rotta | Descrizione |
---------------------------------------------|------------------------------------
| GET /api/ | Ottiene informazioni sul server |
| POST /api/accounts/ | Crea un account, ad es.: `{user: "Giovanni", description: "Il mio budget", currency: "EUR", balance: 100}` |
| GET /api/accounts/:user | Ottiene tutti i dati per l'account specificato |
| DELETE /api/accounts/: user | Rimuove l'account specificato |
| POST /api/account/:user/transactions | Aggiunge una transazione, ad es .: `{date: '2020-07-23T18:25:43.511Z', object: "Acquistato un libro", amount: -20}` |
| DELETE /api/accounts/:user/transactions/:id | Rimuove la transazione specificata |

@ -0,0 +1,13 @@
# App Bancaria
> Soluzione di esempio per il progetto app bancaria, costruito con semplice HTML5, CSS e JavaScript (non è stata usato alcun framework o libreria).
## Eseguire l'app
Per prima cosa assicurarsi di avere in esecuzione il [server API](../../api/translations/README.it.md).
Può essere usato un qualsiasi server web per eseguire l'app, ma visto che si dovrebbe avere installato comunque [Node.js](https://nodejs.org) per eseguire l'API, è possibile:
1. Utilizzare il comando `git clone` con questo repository.
2. Aprire un terminale, poi eseguire `npx lite-server solution`. Verrà fatto partire un server web di sviluppo sulla porta `3000`
3. Aprire `http://localhost:3000` in un browser per eseguire l'app.

@ -0,0 +1,21 @@
# :dollar: Costruire una Banca
In questo progetto si imparerà come costruire una banca immaginaria. Queste lezioni includono istruzioni su come disporre un'app web e fornire rotte, creare form, gestire lo stato e recuperare dati da un'API da cui è possibile ottenere i dati della banca.
| ![Videata1](../images/screen1.png) | ![Videata2](../images/screen2.png) |
|--------------------------------|--------------------------------|
## Lezioni
1. [Modelli HTML e Rotte in un'app Web](../1-template-route/translations/README.it.md)
2. [Creare un Form di Accesso e Registrazione](../2-forms/translations/README.it.md)
3. [Metodi di Recupero e Utilizzo Dati](../3-data/translations/README.it.md)
4. [Concetti di Gestione dello Stato](../4-state-management/translations/README.it.md)
### Crediti
Queste lezioni sono state scritte con il :hearts: da [Yohan Lasorsa](https://twitter.com/sinedied).
Se interessati a imparare come costruire l' [API del server](../../api/tranlations/README.it.md) utilizzata in queste lezioni, è possibile seguire [questa serie di video](https://aka.ms/NodeBeginner) (in particolare i video da 17 a 21).
Si può anche dare un'occhiata a [questo tutorial interattivo di apprendimento](https://aka.ms/learn/express-api).

@ -6,12 +6,18 @@ We welcome translations for the lessons in this curriculum!
There are [**translations**](https://github.com/microsoft/Web-Dev-For-Beginners/tree/main/1-getting-started-lessons/1-intro-to-programming-languages/translations) folders which contain the translated markdown files.
Translated lessons should follow this naming convention:
> Note, please do not translate any code in the code sample files; the only things to translate are README, assignments, and the quizzes. Thanks!
Translated files should follow this naming convention:
**README._[language]_.md**
where _[language]_ is a two letter language abbreviation following the ISO 639-1 standard (e.g. `README.es.md` for Spanish and `README.nl.md` for Dutch).
**assignment._[language]_.md**
Similar to Readme's, please translate the assignments as well.
**Quizzes**
1. Add your translation to the quiz-app by adding a file here: https://github.com/microsoft/Web-Dev-For-Beginners/tree/main/quiz-app/src/assets/translations, with proper naming convention (en.json, fr.json). **Please don't localize the words 'true' or 'false' however. thanks!**

@ -1,24 +0,0 @@
_प्रति प्रश्न के एक उत्तर की जाँच करके अपने प्रस्तुतिकरण के साथ इस प्रश्नोत्तरी को पूरा करें._
क्विज़ को पूरा करने के लिए आपको निम्नलिखित लर्निंग मॉड्यूल(एस) को पूरा करना होगा:
[लर्न लिंक १]()
[लर्न लिंक २]()
1. [Q1]
- [ ] [विकल्प १]
- [ ] [विकल्प २]
- [ ] [विकल्प ३]
2. [Q2]
- [ ] [विकल्प १]
- [ ] [विकल्प २]
- [ ] [विकल्प ३]
3. [Q3]
- [ ] [विकल्प १]
- [ ] [विकल्प २]
- [ ] [विकल्प ३]

@ -1,19 +0,0 @@
_इस क्विज को कक्षा में पूरा करें_
1. [Q1]
- [ ] [विकल्प १]
- [ ] [विकल्प २]
- [ ] [विकल्प ३]
2. [Q2]
- [ ] [विकल्प १]
- [ ] [विकल्प २]
- [ ] [विकल्प ३]
3. [Q3]
- [ ] [विकल्प १]
- [ ] [विकल्प २]
- [ ] [विकल्प ३]

@ -8,7 +8,7 @@
> **学生の皆さん**、このカリキュラムを自分で使うためには、リポジトリ全体をフォークして、講義前の小テストから始めて、講義を読んで、残りのアクティビティを自分で完成させてください。解答コードは各レッスンの /solutions フォルダにありますが、解答コードをコピーするのではなく、授業を理解した上でプロジェクトを作成するようにしてください。また、友達と勉強会を作って、一緒に勉強するのもいいでしょう。さらに勉強するには、[Microsoft Learn](https://docs.microsoft.com/users/jenlooper-2911/collections/jg2gax8pzd6o81?WT.mc_id=academic-13441-cxa) や以下のビデオを見ることをお勧めします。
[![Promo video](screenshot.png)](https://youtube.com/watch?v=R1wrdtmBSII "Promo video")
[![Promo video](../screenshot.png)](https://youtube.com/watch?v=R1wrdtmBSII "Promo video")
> 上の画像をクリックすると、プロジェクトとそれを作成した人々についてのビデオを見ることができます。

Loading…
Cancel
Save