Merge branch 'main' into nl

This commit is contained in:
Jen Looper
2021-03-21 14:07:35 -04:00
committed by GitHub
190 changed files with 9464 additions and 618 deletions

View File

@@ -84,9 +84,9 @@ Now that we have put in place the `state` object to store our data, the next ste
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.
In JavaScript, you can use [`Object.freeze()`](https://developer.mozilla.org/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.
✅ Do you know the difference between a *shallow* and a *deep* immutable object? You can read about it [here](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze#What_is_shallow_freeze).
✅ Do you know the difference between a *shallow* and a *deep* immutable object? You can read about it [here](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze#What_is_shallow_freeze).
### Task
@@ -101,7 +101,7 @@ function updateState(property, newData) {
}
```
In this function, we're creating a new state object and copy data from the previous state using the [*spread (`...`) operator*](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax#Spread_in_object_literals). Then we override a particular property of the state object with the new data using the [bracket notation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Working_with_Objects#Objects_and_properties) `[property]` for assignment. Finally, we lock the object to prevent modifications using `Object.freeze()`. We only have the `account` property stored in the state for now, but with this approach you can add as many properties as you need in the state.
In this function, we're creating a new state object and copy data from the previous state using the [*spread (`...`) operator*](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/Spread_syntax#Spread_in_object_literals). Then we override a particular property of the state object with the new data using the [bracket notation](https://developer.mozilla.org/docs/Web/JavaScript/Guide/Working_with_Objects#Objects_and_properties) `[property]` for assignment. Finally, we lock the object to prevent modifications using `Object.freeze()`. We only have the `account` property stored in the state for now, but with this approach you can add as many properties as you need in the state.
We'll also update the `state` initialization to make sure the initial state is frozen too:
@@ -149,16 +149,16 @@ When you want to persist data in your browser, there are a few important questio
- *Is the data sensitive?* You should avoid storing any sensitive data on client, such as user passwords.
- *For how long do you need to keep this data?* Do you plan to access this data only for the current session or do you want it to be stored forever?
There are multiple ways of storing information inside a web app, depending on what you want to achieve. For example, you can use the URLs to store a search query, and make it shareable between users. You can also use [HTTP cookies](https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies) if the data needs to be shared with the server, like [authentication](https://en.wikipedia.org/wiki/Authentication) information.
There are multiple ways of storing information inside a web app, depending on what you want to achieve. For example, you can use the URLs to store a search query, and make it shareable between users. You can also use [HTTP cookies](https://developer.mozilla.org/docs/Web/HTTP/Cookies) if the data needs to be shared with the server, like [authentication](https://en.wikipedia.org/wiki/Authentication) information.
Another option is to use one of the many browser APIs for storing data. Two of them are particularly interesting:
- [`localStorage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage): a [Key/Value store](https://en.wikipedia.org/wiki/Key%E2%80%93value_database) allowing to persist data specific to the current web site across different sessions. The data saved in it never expires.
- [`sessionStorage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/sessionStorage): this one is works the same as `localStorage` except that the data stored in it is cleared when the session ends (when the browser is closed).
- [`localStorage`](https://developer.mozilla.org/docs/Web/API/Window/localStorage): a [Key/Value store](https://en.wikipedia.org/wiki/Key%E2%80%93value_database) allowing to persist data specific to the current web site across different sessions. The data saved in it never expires.
- [`sessionStorage`](https://developer.mozilla.org/docs/Web/API/Window/sessionStorage): this one is works the same as `localStorage` except that the data stored in it is cleared when the session ends (when the browser is closed).
Note that both these APIs only allow to store [strings](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String). If you want to store complex objects, you will need to serialize it to the [JSON](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON) format using [`JSON.stringify()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify).
Note that both these APIs only allow to store [strings](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String). If you want to store complex objects, you will need to serialize it to the [JSON](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON) format using [`JSON.stringify()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify).
✅ If you want to create a web app that does not work with a server, it's also possible to create a database on the client using the [`IndexedDB` API](https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API). This one is reserved for advanced use cases or if you need to store significant amount of data, as it's more complex to use.
✅ If you want to create a web app that does not work with a server, it's also possible to create a database on the client using the [`IndexedDB` API](https://developer.mozilla.org/docs/Web/API/IndexedDB_API). This one is reserved for advanced use cases or if you need to store significant amount of data, as it's more complex to use.
### Task

View File

@@ -6,7 +6,7 @@ Our bank app is still missing one important feature: the possibility to enter ne
Using everything that you've learnt in the four previous lessons, implement an "Add transaction" dialog:
- Add an "Add transaction" button in the dashboard page
- Either create a new page with an HTML template, or use JavaScript to show/hide the dialog HTML without leaving the dashboard page (you can use [`hidden`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/hidden) property for that, or CSS classes)
- Either create a new page with an HTML template, or use JavaScript to show/hide the dialog HTML without leaving the dashboard page (you can use [`hidden`](https://developer.mozilla.org/docs/Web/HTML/Global_attributes/hidden) property for that, or CSS classes)
- Make sure you handle [keyboard and screen reader accessibility](https://developer.paciellogroup.com/blog/2018/06/the-current-state-of-modal-dialog-accessibility/) for the dialog
- Implement an HTML form to receive input data
- Create JSON data from the form data and send it to the API

View File

@@ -83,9 +83,9 @@ Ahora que hemos implementado el objeto `state` para almacenar nuestros datos, el
Para evitar que se realicen cambios en el objeto `state`, también es una buena práctica considerarlo [*inmutable*](https://en.wikipedia.org/wiki/Immutable_object), lo que significa que no se puede modificar en absoluto. También significa que debe crear un nuevo objeto de estado si desea cambiar algo en él. Al hacer esto, crea una protección contra [efectos secundarios](https://en.wikipedia.org/wiki/Side_effect_(computer_science)) potencialmente no deseados y abre posibilidades para nuevas funciones en su aplicación, como implementar deshacer / rehacer, al mismo tiempo que facilita la depuración. Por ejemplo, puede registrar todos los cambios realizados en el estado y mantener un historial de los cambios para comprender el origen de un error.
En JavaScript, puede usar [`Object.freeze()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze) para crear una versión inmutable de un objeto. Si intenta realizar cambios en un objeto inmutable, se generará una excepción.
En JavaScript, puede usar [`Object.freeze()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze) para crear una versión inmutable de un objeto. Si intenta realizar cambios en un objeto inmutable, se generará una excepción.
✅ ¿Conoce la diferencia entre un objeto *superficial* y un objeto inmutable *profundo*? Puede leer sobre esto [aquí](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze#What_is_shallow_freeze).
✅ ¿Conoce la diferencia entre un objeto *superficial* y un objeto inmutable *profundo*? Puede leer sobre esto [aquí](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze#What_is_shallow_freeze).
### Tarea
@@ -100,7 +100,7 @@ function updateState(property, newData) {
}
```
En esta función, estamos creando un nuevo objeto de estado y copiamos datos del estado anterior usando el operador [*spread (`...`)*](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax#Spread_in_object_literals). Luego anulamos una propiedad particular del objeto de estado con los nuevos datos usando la [notación de corchetes](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Working_with_Objects#Objects_and_properties) `[property]` para asignación. Finalmente, bloqueamos el objeto para evitar modificaciones usando `Object.freeze()`. Solo tenemos la propiedad `account` almacenada en el estado por ahora, pero con este enfoque puede agregar tantas propiedades como necesite en el estado.
En esta función, estamos creando un nuevo objeto de estado y copiamos datos del estado anterior usando el operador [*spread (`...`)*](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/Spread_syntax#Spread_in_object_literals). Luego anulamos una propiedad particular del objeto de estado con los nuevos datos usando la [notación de corchetes](https://developer.mozilla.org/docs/Web/JavaScript/Guide/Working_with_Objects#Objects_and_properties) `[property]` para asignación. Finalmente, bloqueamos el objeto para evitar modificaciones usando `Object.freeze()`. Solo tenemos la propiedad `account` almacenada en el estado por ahora, pero con este enfoque puede agregar tantas propiedades como necesite en el estado.
También actualizaremos la inicialización del `estado` para asegurarnos de que el estado inicial también esté congelado:
@@ -148,16 +148,16 @@ Cuando desee conservar los datos en su navegador, hay algunas preguntas importan
- *¿Son los datos confidenciales?* Debe evitar almacenar datos confidenciales en el cliente, como contraseñas de usuario.
- *¿Por cuánto tiempo necesita conservar estos datos?* ¿Planea acceder a estos datos solo para la sesión actual o desea que se almacenen para siempre?
Hay varias formas de almacenar información dentro de una aplicación web, dependiendo de lo que desee lograr. Por ejemplo, puede utilizar las URL para almacenar una consulta de búsqueda y hacer que se pueda compartir entre los usuarios. También puede utilizar [cookies HTTP](https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies) si los datos deben compartirse con el servidor, como [autenticación](https://en.wikipedia.org/wiki/Authentication) información.
Hay varias formas de almacenar información dentro de una aplicación web, dependiendo de lo que desee lograr. Por ejemplo, puede utilizar las URL para almacenar una consulta de búsqueda y hacer que se pueda compartir entre los usuarios. También puede utilizar [cookies HTTP](https://developer.mozilla.org/docs/Web/HTTP/Cookies) si los datos deben compartirse con el servidor, como [autenticación](https://en.wikipedia.org/wiki/Authentication) información.
Otra opción es utilizar una de las muchas API del navegador para almacenar datos. Dos de ellos son particularmente interesantes:
- [`localStorage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage): un [almacén de claves / valores](https://en.wikipedia.org/wiki/Key%E2%80%93value_database) que permite conservar datos específicos del sitio web actual en diferentes sesiones. Los datos guardados en él nunca caducan.
- [`sessionStorage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/sessionStorage): este funciona igual que `localStorage` excepto que los datos almacenados en él son se borra cuando finaliza la sesión (cuando se cierra el navegador).
- [`localStorage`](https://developer.mozilla.org/docs/Web/API/Window/localStorage): un [almacén de claves / valores](https://en.wikipedia.org/wiki/Key%E2%80%93value_database) que permite conservar datos específicos del sitio web actual en diferentes sesiones. Los datos guardados en él nunca caducan.
- [`sessionStorage`](https://developer.mozilla.org/docs/Web/API/Window/sessionStorage): este funciona igual que `localStorage` excepto que los datos almacenados en él son se borra cuando finaliza la sesión (cuando se cierra el navegador).
Tenga en cuenta que estas dos API solo permiten almacenar [cadenas](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String). Si desea almacenar objetos complejos, deberá serializarlos al formato [JSON](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON) usando [`JSON.stringify()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify).
Tenga en cuenta que estas dos API solo permiten almacenar [cadenas](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String). Si desea almacenar objetos complejos, deberá serializarlos al formato [JSON](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON) usando [`JSON.stringify()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify).
✅ Si desea crear una aplicación web que no funcione con un servidor, también es posible crear una base de datos en el cliente usando la [API de IndexedDB](https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API). Este está reservado para casos de uso avanzados o si necesita almacenar una cantidad significativa de datos, ya que es más complejo de usar.
✅ Si desea crear una aplicación web que no funcione con un servidor, también es posible crear una base de datos en el cliente usando la [API de IndexedDB](https://developer.mozilla.org/docs/Web/API/IndexedDB_API). Este está reservado para casos de uso avanzados o si necesita almacenar una cantidad significativa de datos, ya que es más complejo de usar.
### Tarea

View File

@@ -86,7 +86,7 @@ Per evitare che vengano apportate modifiche all'oggetto `state` è anche una buo
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ù.
✅ Si conosce la differenza tra un oggetto *shallow* e uno *deep* immutabile? Si può leggere [qui](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze#What_is_shallow_freeze) per saperne di più.
### Attività
@@ -101,7 +101,7 @@ function updateState(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.
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/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:

View File

@@ -84,9 +84,9 @@ const account = state.account;
`state` 객체가 변경되지 않으려면, [*immutable*](https://en.wikipedia.org/wiki/Immutable_object)한 것으로 간주하는 것이 좋습니다. 즉, 전혀 수정할 수 없다는 점을 의미합니다. 또한 변경하려는 경우에는 새로운 상태 객체를 만들어야 된다는 점을 의미합니다. 이렇게 하면, 잠재적으로 원하지 않는 [side effects](https://en.wikipedia.org/wiki/Side_effect_(computer_science))에 보호하도록 만들고, undo/redo를 구현하는 것 처럼 앱의 새로운 기능에 대한 가능성을 열어 디버깅을 더 쉽게 만듭니다. 예를 들자면, 상태에 대한 모든 변경점을 남기고 유지하여 버그의 원인을 파악할 수 있습니다.
JavaScript에서, [`Object.freeze()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze)를 사용하여 변경할 수 없는 버전의 객체를 만들 수 있습니다. 변경 불가능한 객체를 바꾸려고 하면 예외가 발생합니다.
JavaScript에서, [`Object.freeze()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze)를 사용하여 변경할 수 없는 버전의 객체를 만들 수 있습니다. 변경 불가능한 객체를 바꾸려고 하면 예외가 발생합니다.
✅ *shallow*와 *deep* 불변 객체의 차이점을 알고 계시나요? [here](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze#What_is_shallow_freeze)에서 읽을 수 있습니다.
✅ *shallow*와 *deep* 불변 객체의 차이점을 알고 계시나요? [here](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze#What_is_shallow_freeze)에서 읽을 수 있습니다.
### 작업
@@ -101,7 +101,7 @@ function updateState(property, newData) {
}
```
이 함수에서는, 새로운 상태 객체를 만들고 [*spread (`...`) operator*](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax#Spread_in_object_literals)로 이전 상태의 데이터를 복사합니다. 그러고 할당을 위해 [bracket notation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Working_with_Objects#Objects_and_properties) `[property]`를 사용하여 상태 객체의 특정한 속성을 새로운 데이터로 다시 정의합니다. 최종적으로, 변경되는 것을 막기 위해 `Object.freeze()`를 사용하여 객체를 잠급니다. 지금 상태에는 `account` 속성만 저장되어 있지만, 이 접근 방식으로 상태에 필요한 순간마다 많은 속성들을 추가할 수 있습니다.
이 함수에서는, 새로운 상태 객체를 만들고 [*spread (`...`) operator*](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/Spread_syntax#Spread_in_object_literals)로 이전 상태의 데이터를 복사합니다. 그러고 할당을 위해 [bracket notation](https://developer.mozilla.org/docs/Web/JavaScript/Guide/Working_with_Objects#Objects_and_properties) `[property]`를 사용하여 상태 객체의 특정한 속성을 새로운 데이터로 다시 정의합니다. 최종적으로, 변경되는 것을 막기 위해 `Object.freeze()`를 사용하여 객체를 잠급니다. 지금 상태에는 `account` 속성만 저장되어 있지만, 이 접근 방식으로 상태에 필요한 순간마다 많은 속성들을 추가할 수 있습니다.
또한 초기 상태가 동결되도록 `state` 초기화 작업도 갱신합니다:
@@ -149,16 +149,16 @@ function logout() {
- *민감한 데이터인가요?* 사용자 암호와 같은, 민감한 데이터는 클라이언트에 저장하지 않아야 합니다.
- *데이터를 얼마나 오래 보관해야 하나요?* 현재 세션에서만 데이터에 접근하거나 계속 저장할 계획인가요?
달성하려는 목표에 따라, 웹 앱 안에서 정보를 저장하는 방법에는 여러 가지가 있습니다. 예를 들면, URL을 사용하여 검색 쿼리를 저장하고, 사용자끼리 공유할 수 있습니다. [authentication](https://en.wikipedia.org/wiki/Authentication) 정보처럼, 데이터를 서버와 공유해야하는 경우에도 [HTTP cookies](https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies)를 사용할 수 있습니다.
달성하려는 목표에 따라, 웹 앱 안에서 정보를 저장하는 방법에는 여러 가지가 있습니다. 예를 들면, URL을 사용하여 검색 쿼리를 저장하고, 사용자끼리 공유할 수 있습니다. [authentication](https://en.wikipedia.org/wiki/Authentication) 정보처럼, 데이터를 서버와 공유해야하는 경우에도 [HTTP cookies](https://developer.mozilla.org/docs/Web/HTTP/Cookies)를 사용할 수 있습니다.
다른 옵션으로는 데이터 저장을 위해 여러 브라우저 API 중 하나를 사용하는 것입니다. 그 중 2가지가 특히 흥미롭습니다:
- [`localStorage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage): [Key/Value store](https://en.wikipedia.org/wiki/Key%E2%80%93value_database)는 다른 세션에서 현재 웹 사이트에 대한 특정 데이터를 유지할 수 있습니다. 저장된 데이터는 만료되지 않습니다.
- [`sessionStorage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/sessionStorage): 이는 세션이 끝날 때(브라우저가 닫힐 때)에 저장된 데이터가 지워진다는 점을 제외하면 `localStorage`와 동일하게 작동합니다.
- [`localStorage`](https://developer.mozilla.org/docs/Web/API/Window/localStorage): [Key/Value store](https://en.wikipedia.org/wiki/Key%E2%80%93value_database)는 다른 세션에서 현재 웹 사이트에 대한 특정 데이터를 유지할 수 있습니다. 저장된 데이터는 만료되지 않습니다.
- [`sessionStorage`](https://developer.mozilla.org/docs/Web/API/Window/sessionStorage): 이는 세션이 끝날 때(브라우저가 닫힐 때)에 저장된 데이터가 지워진다는 점을 제외하면 `localStorage`와 동일하게 작동합니다.
이 두 API는 모두 [strings](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)만 저장할 수 있습니다. 복잡한 객체를 저장하려면, [`JSON.stringify()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify)를 사용하여 [JSON](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON) 포맷으로 직렬화해야 합니다.
이 두 API는 모두 [strings](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)만 저장할 수 있습니다. 복잡한 객체를 저장하려면, [`JSON.stringify()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify)를 사용하여 [JSON](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON) 포맷으로 직렬화해야 합니다.
✅ 서버에서 동작하지 않는 웹 앱을 만드려면, [`IndexedDB` API](https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API)로 클라이언트에 데이터베이스를 만들 수도 있습니다. 이는 고급 사용 케이스이거나, 사용하기 복잡한 많은 양의 데이터를 저장해야 할 때에 사용하도록 되어있습니다.
✅ 서버에서 동작하지 않는 웹 앱을 만드려면, [`IndexedDB` API](https://developer.mozilla.org/docs/Web/API/IndexedDB_API)로 클라이언트에 데이터베이스를 만들 수도 있습니다. 이는 고급 사용 케이스이거나, 사용하기 복잡한 많은 양의 데이터를 저장해야 할 때에 사용하도록 되어있습니다.
### 작업

View File

@@ -84,9 +84,9 @@ Sekarang kita telah meletakkan objek `state` untuk menyimpan data kita, langkah
Untuk mengelakkan berlakunya perubahan pada objek `state`, adalah praktik yang baik untuk mempertimbangkannya [*tidak berubah*](https://en.wikipedia.org/wiki/Immutable_object), yang bermaksud bahawa ia tidak dapat diubah sama sekali. Ini juga bermaksud bahawa anda harus membuat objek keadaan baru jika anda ingin mengubah apa-apa di dalamnya. Dengan melakukan ini, anda membina perlindungan mengenai [kesan sampingan](https://en.wikipedia.org/wiki/Side_effect_ (computer_science)), dan membuka kemungkinan untuk ciri baru dalam aplikasi anda seperti melaksanakan undo / redo, sambil mempermudah debug. Sebagai contoh, anda boleh mencatat setiap perubahan yang dibuat ke negeri dan menyimpan sejarah perubahan untuk memahami sumber pepijat.
Dalam JavaScript, anda boleh menggunakan [`Object.freeze()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze) untuk membuat versi yang tidak berubah dari sebuah objek. Sekiranya anda cuba membuat perubahan pada objek yang tidak dapat diubah, pengecualian akan ditimbulkan.
Dalam JavaScript, anda boleh menggunakan [`Object.freeze()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze) untuk membuat versi yang tidak berubah dari sebuah objek. Sekiranya anda cuba membuat perubahan pada objek yang tidak dapat diubah, pengecualian akan ditimbulkan.
✅ Adakah anda tahu perbezaan antara objek *cetek* dan *dalam* tidak berubah? Anda boleh membacanya [di sini](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze#What_is_shallow_freeze).
✅ Adakah anda tahu perbezaan antara objek *cetek* dan *dalam* tidak berubah? Anda boleh membacanya [di sini](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze#What_is_shallow_freeze).
### Tugas
@@ -101,7 +101,7 @@ function updateState(property, newData) {
}
```
Dalam fungsi ini, kami membuat objek keadaan baru dan menyalin data dari keadaan sebelumnya menggunakan operator [*spread (`...`) operator*](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax#Spread_in_object_literals). Kemudian kami menimpa harta benda objek tertentu dengan data baru menggunakan [notasi kurungan](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Working_with_Objects#Objects_and_properties) `[property]` untuk tugasan. Akhirnya, kita mengunci objek untuk mengelakkan pengubahsuaian menggunakan `Object.freeze()`. Kami hanya menyimpan harta `akaun` di negeri ini buat masa ini, tetapi dengan pendekatan ini anda dapat menambahkan seberapa banyak harta tanah yang anda perlukan di negeri ini.
Dalam fungsi ini, kami membuat objek keadaan baru dan menyalin data dari keadaan sebelumnya menggunakan operator [*spread (`...`) operator*](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/Spread_syntax#Spread_in_object_literals). Kemudian kami menimpa harta benda objek tertentu dengan data baru menggunakan [notasi kurungan](https://developer.mozilla.org/docs/Web/JavaScript/Guide/Working_with_Objects#Objects_and_properties) `[property]` untuk tugasan. Akhirnya, kita mengunci objek untuk mengelakkan pengubahsuaian menggunakan `Object.freeze()`. Kami hanya menyimpan harta `akaun` di negeri ini buat masa ini, tetapi dengan pendekatan ini anda dapat menambahkan seberapa banyak harta tanah yang anda perlukan di negeri ini.
Kami juga akan mengemas kini inisialisasi `state` untuk memastikan keadaan awal juga dibekukan:
@@ -149,16 +149,16 @@ Apabila anda ingin menyimpan data dalam penyemak imbas anda, terdapat beberapa s
- *Adakah data sensitif?* Anda harus mengelakkan menyimpan data sensitif pada pelanggan, seperti kata laluan pengguna.
- *Berapa lama anda perlu menyimpan data ini?* Adakah anda merancang untuk mengakses data ini hanya untuk sesi semasa atau adakah anda ingin menyimpannya selamanya?
Terdapat banyak cara untuk menyimpan maklumat di dalam aplikasi web, bergantung pada apa yang ingin anda capai. Sebagai contoh, anda boleh menggunakan URL untuk menyimpan pertanyaan carian, dan menjadikannya boleh dibagikan antara pengguna. Anda juga boleh menggunakan [kuki HTTP](https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies) jika data perlu dikongsi dengan pelayan, seperti [pengesahan](https://en.wikipedia.org/wiki/Authentication) maklumat.
Terdapat banyak cara untuk menyimpan maklumat di dalam aplikasi web, bergantung pada apa yang ingin anda capai. Sebagai contoh, anda boleh menggunakan URL untuk menyimpan pertanyaan carian, dan menjadikannya boleh dibagikan antara pengguna. Anda juga boleh menggunakan [kuki HTTP](https://developer.mozilla.org/docs/Web/HTTP/Cookies) jika data perlu dikongsi dengan pelayan, seperti [pengesahan](https://en.wikipedia.org/wiki/Authentication) maklumat.
Pilihan lain adalah menggunakan salah satu dari banyak API penyemak imbas untuk menyimpan data. Dua daripadanya sangat menarik:
- [`localStorage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage): a [Key/Value store](https://en.wikipedia.org/wiki/Key%E2%80%93value_database) yang memungkinkan untuk mengekalkan data khusus untuk laman web semasa di pelbagai sesi. Data yang disimpan di dalamnya tidak akan luput.
- [`sessionStorage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/sessionStorage): ini berfungsi sama seperti `localStorage` kecuali data yang disimpan di dalamnya dibersihkan semasa sesi berakhir (semasa penyemak imbas ditutup).
- [`localStorage`](https://developer.mozilla.org/docs/Web/API/Window/localStorage): a [Key/Value store](https://en.wikipedia.org/wiki/Key%E2%80%93value_database) yang memungkinkan untuk mengekalkan data khusus untuk laman web semasa di pelbagai sesi. Data yang disimpan di dalamnya tidak akan luput.
- [`sessionStorage`](https://developer.mozilla.org/docs/Web/API/Window/sessionStorage): ini berfungsi sama seperti `localStorage` kecuali data yang disimpan di dalamnya dibersihkan semasa sesi berakhir (semasa penyemak imbas ditutup).
Perhatikan bahawa kedua-dua API ini hanya membenarkan menyimpan [string](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String). Sekiranya anda ingin menyimpan objek yang kompleks, anda perlu membuat siri ke siri [JSON](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON) menggunakan [`JSON.stringify()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify).
Perhatikan bahawa kedua-dua API ini hanya membenarkan menyimpan [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String). Sekiranya anda ingin menyimpan objek yang kompleks, anda perlu membuat siri ke siri [JSON](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON) menggunakan [`JSON.stringify()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify).
✅ Sekiranya anda ingin membuat aplikasi web yang tidak berfungsi dengan pelayan, anda juga boleh membuat pangkalan data pada klien menggunakan [`IndexedDB` API](https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API). Yang ini dikhaskan untuk kes penggunaan lanjutan atau jika anda perlu menyimpan sejumlah besar data, kerana lebih kompleks untuk digunakan.
✅ Sekiranya anda ingin membuat aplikasi web yang tidak berfungsi dengan pelayan, anda juga boleh membuat pangkalan data pada klien menggunakan [`IndexedDB` API](https://developer.mozilla.org/docs/Web/API/IndexedDB_API). Yang ini dikhaskan untuk kes penggunaan lanjutan atau jika anda perlu menyimpan sejumlah besar data, kerana lebih kompleks untuk digunakan.
### Tugas

View File

@@ -0,0 +1,284 @@
# 建立銀行網頁應用程式 Part 4 狀態控管的概念
## 課前測驗
[課前測驗](https://nice-beach-0fe9e9d0f.azurestaticapps.net/quiz/47?loc=zh_tw)
### 大綱
隨著網頁應用越來越龐大,追蹤資料流的動向也是一種挑戰。程式取得了何種資料、網頁如何處理它、何時何處被更新上去……這些很容易地導致程式碼凌亂而難以維護。尤其是當你需要在不同頁面上做資料共享時,好比說使用者的資料。*狀態控管(state management)* 的觀念已經存在於所有程式中,我們也開始需要在開發複雜的網頁應用程式時,注意這個關鍵點。
在這個最終章內,我們會總覽整個程式並重新思考該如何管理程式狀態,讓瀏覽器能在任何時刻做重新整理,在不同的使用者階段維持資料的狀態。
### 開始之前
你需要先完成[取得資料](../../3-data/translations/README.zh-tw.md)的網頁開發章節。你還需要安裝 [Node.js](https://nodejs.org) 並於本地端[執行伺服器 API](../../api/translations/README.zh-tw.md)以管理使用者資料。
你可以測試伺服器是否運作正常,在終端機中輸入指令:
```sh
curl http://localhost:5000/api
# -> should return "Bank API v1.0.0" as a result
```
---
## 思考狀態控管
在[前一堂課](../../3-data/translations/README.zh-tw.md)中,我們介紹了應用程式基本的狀態,全域變數 `account` 提供登入帳戶的相關銀行資料。然而,現在的專案存在著一些瑕疵。試著在儀表板介面中重新整理。發生了什麼事?
目前我們的程式碼有三個問題:
- 網頁狀態並沒有被儲存,當瀏覽器重新整理時,會被導回登入頁面。
- 有許多函式會修改網頁狀態。隨著應用程式變大,我們很難去追蹤之後的改變,時刻地去更新相關的網頁狀態。
- 網頁狀態並不完整,當你*登出*帳戶時,帳戶資訊仍然顯示在登入頁面上。
我們是可以逐一的解決這些問題,但這樣會創造出許多獨立的程式碼,讓應用程式更複雜而難以去管理。或者是我們停下來思考一下我們的策略。
> 我們究竟要解決什麼問題?
[狀態控管(State management)](https://en.wikipedia.org/wiki/State_management)可以為兩項問題提供良好的解決方案:
- 如何讓應用程式中的資料流容易理解?
- 如何讓網頁狀態一直與使用者介面,或是相關物件進行同步?
一旦你處理好這些問題,其他問題可以被簡化,甚至被一併解決。有許多可能的方法能解決這些問題,但我們使用一種常見的解法:**中心化資料與更新方式**。資料流會呈現下列模式:
![HTML、使用者行為與網頁狀態的架構圖](../images/data-flow.png)
> 我們不會處理如何讓資料同步觸發頁面的更新,這比較像是關於[回應式程式設計](https://zh.wikipedia.org/wiki/%E5%93%8D%E5%BA%94%E5%BC%8F%E7%BC%96%E7%A8%8B)的更進階知識。當你更深入網頁開發領域時,這是個很好的發展方向。
✅ 有許多函式庫提供狀態管理的方式,[Redux](https://redux.js.org) 就是常見的選擇。閱讀它的概念與運作模式,這是種有效的的學習方式,讓你在大型的網頁開發中預測潛在的風險,並預想解決方案。
### 課題
我們會先做一些程式重構。替換掉 `account` 的定義:
```js
let account = null;
```
變成:
```js
let state = {
account: null
};
```
這個構想是要*中心化*應用程式資料到一個狀態物件中。目前我們只有 `account` 在狀態中,但這能提供未來新增新功能的基礎。
我們還需要更新與它相關的函式。在函式 `register()``login()` ,將 `account = ...` 替換為 `state.account = ...`
在函式 `updateDashboard()` 的上方,加入此行:
```js
const account = state.account;
```
這個重構並不會帶來任何提升,但這是之後改變上的基礎。
This refactoring by itself did not bring much improvements, but the idea was to lay out the foundation for the next changes.
## 追蹤資料改變
現在我們有 `state` 物件儲存資料了,接下來要來中心化這些更新。目標是能輕易地追蹤任何被觸發的改變。
為了避免改動 `state` 物件,我們考慮使它[*不可變*](https://zh.wikipedia.org/wiki/%E4%B8%8D%E5%8F%AF%E8%AE%8A%E7%89%A9%E4%BB%B6),意味著它不能被做任何的修改。
這也代表你必須建立新的狀態物件來替換它。藉由這個方式,你就有一套保護措施阻絕潛在非預期[風險](https://zh.wikipedia.org/wiki/%E5%89%AF%E4%BD%9C%E7%94%A8_(%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%A7%91%E5%AD%A6)),也開創出應用程式內還原與重做的功能,讓程式偵錯更加的容易。舉例來說,你可以紀錄狀態的改變,儲存狀態的歷史紀錄來了解錯誤的來源。
在 JavaScript 中,你可以使用 [`Object.freeze()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze) 來建立不可變物件。若你想在不可變物件上做更動,例外處理(exception)就會發生。
✅ 你知道*淺複製(shallow)*和*深複製(deep)*這兩種不可變物件的差別嗎?你可以從[這裡](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze#What_is_shallow_freeze)閱讀相關資訊。
### 課題
我們來建立新的函式 `updateState()`
```js
function updateState(property, newData) {
state = Object.freeze({
...state,
[property]: newData
});
}
```
在這個函式中,我們會建立新的狀態物件,並利用[*展開運算子(`...`)(Spread Operator)*](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax#Spread_in_object_literals)複製前一個資料狀態。接著,我們使用[括弧記法(Bracket Notation)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Working_with_Objects#Objects_and_properties) `[property]` 賦予並覆蓋特定的狀態物件。最後,我們為物件上鎖,`Object.freeze()` 避免任何的改動。目前我們只有 `account` 資料存在狀態中,利用此方法可以讓你新增任何你想要的資料。
我們會更新 `state` 初始化設定,確保初始狀態也被上鎖:
```js
let state = Object.freeze({
account: null
});
```
接著,更新函式 `register`,將 `state.account = result;` 替換為:
```js
updateState('account', result);
```
在函式 `login` 上做一樣的事,將 `state.account = data;` 替換為:
```js
updateState('account', data);
```
藉由這個機會,我們能解決帳戶資料在*登出*時,不會被清除的問題。
建立新的函式 `logout()`
```js
function logout() {
updateState('account', null);
navigate('/login');
}
```
`updateDashboard()` 中,替換重新導向 `return navigate('/login');``return logout()`
試著註冊新的帳戶,登入登出以確保功能都運作正常。
> 提示:你可以觀察所有的狀態改變,在 `updateState()` 裡的最下方加入 `console.log(state)`,開啟瀏覽器開發工具,命令欄就會顯示狀態的紀錄。
## 紀錄狀態
多數的網頁應用程式需要儲存資料以確保運作正常。所有重要的資料都會存在資料庫中,並藉由伺服器 API 來存取,就像我們專案中的帳戶資料。但有時候,瀏覽器用戶端的應用程式也需要儲存一些資料,提供更好的使用者體驗與增進負載效能。
當你想在瀏覽器內儲存資料,你必須思考幾項重要的問題:
- *這項資料很危險嗎?* 你應該要避免在用戶端儲存敏感的資料,例如帳戶密碼。
- *你需要儲存資料多久?* 你打算短時間內做存取,還是永久地保存?
網頁應用程式中有許多儲存資訊的方法,一切都取決於你想達成的目標。舉例來說,你可以利用網址來儲存搜尋資訊,讓使用者間能共享資訊。若資料需要與伺服器共享,好比說[認證](https://zh.wikipedia.org/wiki/%E8%BA%AB%E4%BB%BD%E9%AA%8C%E8%AF%81)資訊,你可以使用 [HTTP cookies](https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies)。
另一個選擇是使用其中一個廣大的瀏覽器 API 來儲存資料。下列這兩項就特別有趣:
- [`localStorage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage)[Key/Value 儲存法](https://zh.wikipedia.org/wiki/%E9%94%AE-%E5%80%BC%E5%AD%98%E5%82%A8)可以保存不同時刻的網頁資料。這些資料不會有期限的限制。
- [`sessionStorage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/sessionStorage):它的運作模式與 `localStorage` 相同,只差在資料會在網頁段落結束時被清除,如瀏覽器關閉時。
紀錄一下這兩個 API 只能儲存[字串](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)格式。
如果你想儲存更複雜的物件,你需要利用 [`JSON.stringify()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify) 將資料整理成 [JSON](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON) 格式。
✅ 如果你想要建立不仰賴伺服器的網頁應用程式,你有辦法在用戶端建立資料庫。[`IndexedDB` API](https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API) 可以應用在更進階的案例上,儲存更大量的資料,當然使用上也相對複雜。
### 課題
我們想讓使用者在登出之前,保持登入狀態。所以我們使用 `localStorage` 來儲存帳戶資料。首先,定義一組 key 來紀錄我們的資料內容。
```js
const storageKey = 'savedAccount';
```
在函式 `updateState()` 末端加入此行:
```js
localStorage.setItem(storageKey, JSON.stringify(state.account));
```
藉由此方式,帳戶資料就能保存下來,並隨著之前中心化後的狀態而更新。我們開始從之前的重構獲取效益了 🙂。
當資料被儲存後,我們還需要在程式讀取時載入資料。在 `app.js` 下方編寫更多的初始化程式,建立新的函式 `init` 並收入之前的程式碼:
```js
function init() {
const savedAccount = localStorage.getItem(storageKey);
if (savedAccount) {
updateState('account', JSON.parse(savedAccount));
}
// 之前的初始化程式
window.onpopstate = () => updateRoute();
updateRoute();
}
init();
```
我們在此接收了儲存資料,並同步地更新狀態資訊。這必須在更新路由*之前*完成,否則有些程式碼會在頁面更新時,依據狀態來決定其行為。
當儲存完帳戶資料後,我們也定義了*儀表板*頁面為我們的預設首頁。若程式沒有找到資料,儀表板頁面也能重新導向回*登入*頁面。在 `updateRoute()` 中,替換回傳值 `return navigate('/login');``return navigate('/dashboard');`
登入應用程式並重新整理頁面。你應該能維持在儀表板那頁。這個改變也解決了我們最初面臨的問題......
## 重整資料
......但我們可能也產生了新問題。啊呀!
使用 `test` 帳戶進入儀表板頁面,在終端機內執行下列指令以建立新的交易項目:
```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
```
試著重新整理瀏覽器內儀表板頁面。發生了什麼事?你有看到新的交易項目嗎?
感謝 `localStorage` 的幫助,狀態成功的儲存下來,但也代表我們在登出登入之前,不能再改變它的內容了!
一個可能的修復策略是在儀表板載入時,重新載入帳戶資訊以避免資料不同步。
### 課題
建立新的函式 `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);
}
```
這個方法能檢查我們是否已經登入,重新從伺服器載入用戶資料。
建立另一個函式 `refresh`
```js
async function refresh() {
await updateAccountData();
updateDashboard();
}
```
這能更新帳戶資料,更新 HTML 中的儀表板頁面。這是在儀表板路由被載入時,我們所需要呼叫的函式。更新路由定義為:
```js
const routes = {
'/login': { templateId: 'login' },
'/dashboard': { templateId: 'dashboard', init: refresh }
};
```
試著重新載入儀表板,它現在應該能顯示更新後的帳戶資料。
---
## 🚀 挑戰
每一次儀表板載入時,我們都會重新載入帳戶資料,你認為我們還需要在用戶端儲存*所有的帳戶*資料嗎?
試著改變 `localStorage` 內的儲存內容,只包含我們能運行程式的必要資料。
## 課後測驗
[課後測驗](https://nice-beach-0fe9e9d0f.azurestaticapps.net/quiz/48?loc=zh_tw)
## 作業
[編寫"加入交易明細"視窗](assignment.zh-tw.md)
這邊有完成之後的結果:
!["加入交易明細"視窗的例子截圖](../images/dialog.png)

View File

@@ -6,7 +6,7 @@ A nuestra aplicación bancaria todavía le falta una característica importante:
Utilizando todo lo que ha aprendido en las cuatro lecciones anteriores, implemente un cuadro de diálogo "Agregar transacción":
- Agregue un botón "Agregar transacción" en la página del panel
- Cree una nueva página con una plantilla HTML o use JavaScript para mostrar / ocultar el cuadro de diálogo HTML sin salir de la página del tablero (puede usar [`hidden`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/hidden) para eso, o clases CSS)
- Cree una nueva página con una plantilla HTML o use JavaScript para mostrar / ocultar el cuadro de diálogo HTML sin salir de la página del tablero (puede usar [`hidden`](https://developer.mozilla.org/docs/Web/HTML/Global_attributes/hidden) para eso, o clases CSS)
- Implementar un formulario HTML para recibir datos de entrada.
- Cree datos JSON a partir de los datos del formulario y envíelos a la API
- Actualiza la página del tablero con los nuevos datos.

View File

@@ -6,7 +6,7 @@
पिछले चार पाठों में आपने जो कुछ भी सीखा है, उसका उपयोग करके "लेनदेन जोड़ें" डियलॉग को लागू करें:
- डैशबोर्ड पृष्ठ में "लेनदेन जोड़ें" बटन जोड़ें
- या तो HTML टेम्पलेट के साथ एक नया पृष्ठ बनाएँ, या डैशबोर्ड पृष्ठ को छोड़े बिना संवाद HTML दिखाने / छिपाने के लिए जावास्क्रिप्ट का उपयोग करें (आप उसके लिए, या CSS कक्षाओं के लिए [`hidden`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/hidden) हुई संपत्ति का उपयोग कर सकते हैं)
- या तो HTML टेम्पलेट के साथ एक नया पृष्ठ बनाएँ, या डैशबोर्ड पृष्ठ को छोड़े बिना संवाद HTML दिखाने / छिपाने के लिए जावास्क्रिप्ट का उपयोग करें (आप उसके लिए, या CSS कक्षाओं के लिए [`hidden`](https://developer.mozilla.org/docs/Web/HTML/Global_attributes/hidden) हुई संपत्ति का उपयोग कर सकते हैं)
- सुनिश्चित करें कि आप [कीबोर्ड और स्क्रीन रीडर एक्सेसिबिलिटी](https://developer.paciellogroup.com/blog/2018/06/the-current-state-of-modal-dialog-accessibility/) डियलॉग संभालते हैं
- इनपुट डेटा प्राप्त करने के लिए एक HTML फॉर्म को लागू करें
- फॉर्म डेटा से JSON डेटा बनाएं और इसे API पर भेजें

View File

@@ -6,7 +6,7 @@ All'app bancaria manca ancora una caratteristica importante: la possibilità di
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)
- 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/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

View File

@@ -5,7 +5,7 @@
우리 은행 앱에는 여전히 새로운 거래를 입력 할 수 있는 한 가지 중요한 기능이 없습니다. 이전 네 가지 수업에서 배운 모든 것을 사용하여 "트랜잭션 추가" 대화 상자를 구현해봅시다.
- 대시 보드 페이지에 "거래 추가" 버튼 추가
- HTML 템플릿으로 새 페이지를 만들거나 JavaScript를 사용하여 대시 보드 페이지를 벗어나지 않고 대화 상자 HTML을 표시하거나 숨깁니다(해당 항목에 대해 [`hidden`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/hidden) 속성 또는 CSS 클래스를 사용할 수 있음).
- HTML 템플릿으로 새 페이지를 만들거나 JavaScript를 사용하여 대시 보드 페이지를 벗어나지 않고 대화 상자 HTML을 표시하거나 숨깁니다(해당 항목에 대해 [`hidden`](https://developer.mozilla.org/docs/Web/HTML/Global_attributes/hidden) 속성 또는 CSS 클래스를 사용할 수 있음).
- 대화 상자에 대한 [키보드 및 화면 판독기 접근성](https://developer.paciellogroup.com/blog/2018/06/the-current-state-of-modal-dialog-accessibility/)을 처리해야합니다.
- 입력 데이터 수신을 위한 HTML 양식 구현
- 양식 데이터에서 JSON 데이터를 만들어 API로 보냅니다.

View File

@@ -6,7 +6,7 @@ Aplikasi bank kami masih belum mempunyai satu ciri penting: kemungkinan untuk me
Menggunakan semua yang telah anda pelajari dalam empat pelajaran sebelumnya, laksanakan dialog "Tambah transaksi":
- Tambahkan butang "Tambah transaksi" di halaman papan pemuka
- Buat halaman baru dengan templat HTML, atau gunakan JavaScript untuk menunjukkan / menyembunyikan HTML dialog tanpa meninggalkan halaman papan pemuka (anda boleh menggunakan [`hidden`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/hidden) property for that, or CSS classes) untuk itu, atau kelas CSS)
- Buat halaman baru dengan templat HTML, atau gunakan JavaScript untuk menunjukkan / menyembunyikan HTML dialog tanpa meninggalkan halaman papan pemuka (anda boleh menggunakan [`hidden`](https://developer.mozilla.org/docs/Web/HTML/Global_attributes/hidden) property for that, or CSS classes) untuk itu, atau kelas CSS)
- Pastikan anda mengendalikan [kebolehcapaian pembaca papan kekunci dan skrin](https://developer.paciellogroup.com/blog/2018/06/the-current-state-of-modal-dialog-accessibility/) untuk dialog
- Terapkan borang HTML untuk menerima data input
- Buat data JSON dari data formulir dan kirimkan ke API

View File

@@ -0,0 +1,25 @@
# 編寫"加入交易明細"視窗
## 簡介
我們的銀行應用程式還缺乏一項重要的功能:輸入新的交易明細。
使用你在這四堂課中學到的知識,編寫"加入交易明細"視窗:
- 在儀表板頁面新增"加入交易明細"按鈕
- 加入新的 HTML 模板建立新頁面,或是在同一頁面中使用 JavaScript 顯示 HTML 窗格(可以使用 [`hidden`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/hidden) 屬性,或是 CSS classes)
- 確保視窗能滿足[鍵盤與螢幕報讀器的相容性](https://developer.paciellogroup.com/blog/2018/06/the-current-state-of-modal-dialog-accessibility/)
- 編寫 HTML 表單來接收輸入資料
- 建立 JSON 表單資料並傳送到 API 上
- 使用新資料更新到儀表板頁面上
看看[伺服器 API 規格](../../api/README.zh-tw.md)來查詢你需要呼叫的 API 和所需的 JSON 格式。
這邊有完成作業後的成果:
!["加入交易明細"視窗的例子截圖](../images/dialog.png)
## 學習評量
| 作業內容 | 優良 | 普通 | 待改進 |
| -------- | ------------------------------------ | -------------------------------------------------- | ---------------------- |
| | 利用課程內容完美的製作出交易明細功能 | 有製作出交易明細功能,但有缺少部分要點且功能不完全 | 新的交易明細功能不正常 |