mirror of
https://github.com/microsoft/Web-Dev-For-Beginners.git
synced 2025-08-30 10:00:18 +02:00
Merge branch 'main' into nl
This commit is contained in:
@@ -27,7 +27,7 @@ curl http://localhost:5000/api
|
||||
|
||||
The `<form>` element encapsulates a section of an HTML document where the user can input and submit data with interactive controls. There are all sorts of user interface (UI) controls that can be used within a form, the most common one being the `<input>` and the `<button>` elements.
|
||||
|
||||
There are a lot of different [types](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input) of `<input>`, for example to create a field where the user can enter its username you can use:
|
||||
There are a lot of different [types](https://developer.mozilla.org/docs/Web/HTML/Element/input) of `<input>`, for example to create a field where the user can enter its username you can use:
|
||||
|
||||
```html
|
||||
<input id="username" name="username" type="text">
|
||||
@@ -35,9 +35,9 @@ There are a lot of different [types](https://developer.mozilla.org/en-US/docs/We
|
||||
|
||||
The `name` attribute will be used as the property name when the form data will be sent over. The `id` attribute is used to associate a `<label>` with the form control.
|
||||
|
||||
> Take a look at the whole list of [`<input>` types](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input) and [other form controls](https://developer.mozilla.org/en-US/docs/Learn/Forms/Other_form_controls) to get an idea of all the native UI elements you can use when building your UI.
|
||||
> Take a look at the whole list of [`<input>` types](https://developer.mozilla.org/docs/Web/HTML/Element/input) and [other form controls](https://developer.mozilla.org/docs/Learn/Forms/Other_form_controls) to get an idea of all the native UI elements you can use when building your UI.
|
||||
|
||||
✅ Note that `<input>` is an [empty element](https://developer.mozilla.org/en-US/docs/Glossary/Empty_element) on which you should *not* add a matching closing tag. You can however use the self-closing `<input/>` notation, but it's not required.
|
||||
✅ Note that `<input>` is an [empty element](https://developer.mozilla.org/docs/Glossary/Empty_element) on which you should *not* add a matching closing tag. You can however use the self-closing `<input/>` notation, but it's not required.
|
||||
|
||||
The `<button>` element within a form is a bit special. If you do not specify its `type` attribute, it will automatically submit the form data to the server when pressed. Here are the possible `type` values:
|
||||
|
||||
@@ -68,7 +68,7 @@ If you take a closer look, you can notice that we also added a `<label>` element
|
||||
- By associating a label to a form control, it helps users using assistive technologies (like a screen reader) to understand what data they're expected to provide.
|
||||
- You can click on the label to directly put focus on the associated input, making it easier to reach on touch-screen based devices.
|
||||
|
||||
> [Accessibility](https://developer.mozilla.org/en-US/docs/Learn/Accessibility/What_is_accessibility) on the web is a very important topic that's often overlooked. Thanks to [semantic HTML elements](https://developer.mozilla.org/en-US/docs/Learn/Accessibility/HTML) it's not difficult to create accessible content if you use them properly. You can [read more about accessibility](https://developer.mozilla.org/en-US/docs/Web/Accessibility) to avoid common mistakes and become a responsible developer.
|
||||
> [Accessibility](https://developer.mozilla.org/docs/Learn/Accessibility/What_is_accessibility) on the web is a very important topic that's often overlooked. Thanks to [semantic HTML elements](https://developer.mozilla.org/docs/Learn/Accessibility/HTML) it's not difficult to create accessible content if you use them properly. You can [read more about accessibility](https://developer.mozilla.org/docs/Web/Accessibility) to avoid common mistakes and become a responsible developer.
|
||||
|
||||
Now we'll add a second form for the registration, just below the previous one:
|
||||
|
||||
@@ -156,7 +156,7 @@ function register() {
|
||||
}
|
||||
```
|
||||
|
||||
Here we retrieve the form element using `getElementById()` and use the [`FormData`](https://developer.mozilla.org/en-US/docs/Web/API/FormData) helper to extract the values from form controls as a set of key/value pairs. Then we convert the data to a regular object using [`Object.fromEntries()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/fromEntries) and finally serialize the data to [JSON](https://www.json.org/json-en.html), a format commonly used for exchanging data on the web.
|
||||
Here we retrieve the form element using `getElementById()` and use the [`FormData`](https://developer.mozilla.org/docs/Web/API/FormData) helper to extract the values from form controls as a set of key/value pairs. Then we convert the data to a regular object using [`Object.fromEntries()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/fromEntries) and finally serialize the data to [JSON](https://www.json.org/json-en.html), a format commonly used for exchanging data on the web.
|
||||
|
||||
The data is now ready to be sent to the server. Create a new function named `createAccount`:
|
||||
|
||||
@@ -175,7 +175,7 @@ async function createAccount(account) {
|
||||
}
|
||||
```
|
||||
|
||||
What's this function doing? First, notice the `async` keyword here. This means that the function contains code that will execute [**asynchronously**](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function). When used along the `await` keyword, it allows waiting for asynchronous code to execute - like waiting for the server response here - before continuing.
|
||||
What's this function doing? First, notice the `async` keyword here. This means that the function contains code that will execute [**asynchronously**](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/async_function). When used along the `await` keyword, it allows waiting for asynchronous code to execute - like waiting for the server response here - before continuing.
|
||||
|
||||
Here's a quick video about `async/await` usage:
|
||||
|
||||
@@ -219,7 +219,7 @@ async function register() {
|
||||
}
|
||||
```
|
||||
|
||||
That was a bit long but we got there! If you open your [browser developer tools](https://developer.mozilla.org/en-US/docs/Learn/Common_questions/What_are_browser_developer_tools), and try registering a new account, you should not see any change on the web page but a message will appear in the console confirming that everything works.
|
||||
That was a bit long but we got there! If you open your [browser developer tools](https://developer.mozilla.org/docs/Learn/Common_questions/What_are_browser_developer_tools), and try registering a new account, you should not see any change on the web page but a message will appear in the console confirming that everything works.
|
||||
|
||||

|
||||
|
||||
@@ -227,15 +227,15 @@ That was a bit long but we got there! If you open your [browser developer tools]
|
||||
|
||||
## Data validation
|
||||
|
||||
If you try to register a new account without setting an username first, you can see that the server returns an error with status code [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).).
|
||||
If you try to register a new account without setting an username first, you can see that the server returns an error with status code [400 (Bad Request)](https://developer.mozilla.org/docs/Web/HTTP/Status/400#:~:text=The%20HyperText%20Transfer%20Protocol%20(HTTP,%2C%20or%20deceptive%20request%20routing).).
|
||||
|
||||
Before sending data to a server it's a good practice to [validate the form data](https://developer.mozilla.org/en-US/docs/Learn/Forms/Form_validation) beforehand when possible, to make sure you send a valid request. HTML5 forms controls provides built-in validation using various attributes:
|
||||
Before sending data to a server it's a good practice to [validate the form data](https://developer.mozilla.org/docs/Learn/Forms/Form_validation) beforehand when possible, to make sure you send a valid request. HTML5 forms controls provides built-in validation using various attributes:
|
||||
|
||||
- `required`: the field needs to be filled otherwise the form cannot be submitted.
|
||||
- `minlength` and `maxlength`: defines the minimum and maximum number of characters in text fields.
|
||||
- `min` and `max`: defines the minimum and maximum value of a numerical field.
|
||||
- `type`: defines the kind of data expected, like `number`, `email`, `file` or [other built-in types](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input). This attribute may also change the visual rendering of the form control.
|
||||
- `pattern`: allows to define a [regular expression](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions) pattern to test if the entered data is valid or not.
|
||||
- `type`: defines the kind of data expected, like `number`, `email`, `file` or [other built-in types](https://developer.mozilla.org/docs/Web/HTML/Element/input). This attribute may also change the visual rendering of the form control.
|
||||
- `pattern`: allows to define a [regular expression](https://developer.mozilla.org/docs/Web/JavaScript/Guide/Regular_Expressions) pattern to test if the entered data is valid or not.
|
||||
|
||||
> Tip: you can customize the look of your form controls depending if they're valid or not using the `:valid` and `:invalid` CSS pseudo-classes.
|
||||
|
||||
|
@@ -25,7 +25,7 @@ curl http://localhost:5000/api
|
||||
|
||||
El elemento `<form>` encapsula una sección de un documento HTML donde el usuario puede ingresar y enviar datos con controles interactivos. Hay todo tipo de controles de interfaz de usuario (UI) que se pueden usar dentro de un formulario, siendo el más común los elementos `<input>` y `<button>`.
|
||||
|
||||
Hay muchos [types diferentes](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input) de `<input>`, por ejemplo, para crear un campo donde el usuario puede ingresar su nombre de usuario que puede usar:
|
||||
Hay muchos [types diferentes](https://developer.mozilla.org/docs/Web/HTML/Element/input) de `<input>`, por ejemplo, para crear un campo donde el usuario puede ingresar su nombre de usuario que puede usar:
|
||||
|
||||
|
||||
```html
|
||||
@@ -34,9 +34,9 @@ Hay muchos [types diferentes](https://developer.mozilla.org/en-US/docs/Web/HTML/
|
||||
|
||||
El atributo `name` se usa para identificar el control y se usará como el nombre de la propiedad cuando se envíen los datos del formulario.
|
||||
|
||||
> Eche un vistazo a la lista completa de [`<input>` tipos](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input) y [otros controles de formulario](https://developer.mozilla.org/en-US/docs/Learn/Forms/Other_form_controls) para tener una idea de todos los elementos nativos de la interfaz de usuario que puede utilizar al crear su interfaz de usuario.
|
||||
> Eche un vistazo a la lista completa de [`<input>` tipos](https://developer.mozilla.org/docs/Web/HTML/Element/input) y [otros controles de formulario](https://developer.mozilla.org/docs/Learn/Forms/Other_form_controls) para tener una idea de todos los elementos nativos de la interfaz de usuario que puede utilizar al crear su interfaz de usuario.
|
||||
|
||||
✅ Tenga en cuenta que `<input>` es un [elemento vacío](https://developer.mozilla.org/en-US/docs/Glossary/Empty_element) en el que *no* debe agregar una etiqueta de cierre coincidente. Sin embargo, puede usar la notación de cierre automático `<input/>`, pero no es necesaria.
|
||||
✅ Tenga en cuenta que `<input>` es un [elemento vacío](https://developer.mozilla.org/docs/Glossary/Empty_element) en el que *no* debe agregar una etiqueta de cierre coincidente. Sin embargo, puede usar la notación de cierre automático `<input/>`, pero no es necesaria.
|
||||
|
||||
El elemento `<button>` dentro de un formulario es un poco especial. Si no especifica su atributo `type`, automáticamente enviará los datos del formulario al servidor cuando se presione. Estos son los posibles valores de tipo:
|
||||
|
||||
@@ -68,7 +68,7 @@ Si observa más de cerca, puede notar que también agregamos un elemento `<label
|
||||
- Al asociar una etiqueta a un control de formulario, ayuda a los usuarios que utilizan tecnologías de asistencia (como un lector de pantalla) a comprender qué datos se espera que proporcionen.
|
||||
- Puede hacer clic en la etiqueta para centrarse directamente en la entrada asociada, lo que facilita el acceso a los dispositivos con pantalla táctil.
|
||||
|
||||
> [Accesibilidad](https://developer.mozilla.org/en-US/docs/Learn/Accessibility/What_is_accessibility) en la web es un tema muy importante que a menudo se pasa por alto. Gracias a los [elementos semánticos HTML5](https://developer.mozilla.org/en-US/docs/Learn/Accessibility/HTML) no es difícil crear contenido accesible si los usas correctamente. Puede [leer más sobre accesibilidad](https://developer.mozilla.org/en-US/docs/Web/Accessibility) para evitar errores comunes y convertirse en un desarrollador responsable.
|
||||
> [Accesibilidad](https://developer.mozilla.org/docs/Learn/Accessibility/What_is_accessibility) en la web es un tema muy importante que a menudo se pasa por alto. Gracias a los [elementos semánticos HTML5](https://developer.mozilla.org/docs/Learn/Accessibility/HTML) no es difícil crear contenido accesible si los usas correctamente. Puede [leer más sobre accesibilidad](https://developer.mozilla.org/docs/Web/Accessibility) para evitar errores comunes y convertirse en un desarrollador responsable.
|
||||
|
||||
Ahora agregaremos un segundo formulario para el registro, justo debajo del anterior:
|
||||
|
||||
@@ -157,7 +157,7 @@ function register() {
|
||||
}
|
||||
```
|
||||
|
||||
Aquí recuperamos el elemento del formulario usando `getElementById()` y usamos el ayudante [`FormData`](https://developer.mozilla.org/en-US/docs/Web/API/FormData) para extraer los valores del formulario controles como un conjunto de pares clave/valor. Luego convertimos los datos a un objeto regular usando [`Object.fromEntries()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/fromEntries) y finalmente serialice los datos en [JSON](https://www.json.org/json-en.html), un formato que se utiliza comúnmente para intercambiar datos en la web.
|
||||
Aquí recuperamos el elemento del formulario usando `getElementById()` y usamos el ayudante [`FormData`](https://developer.mozilla.org/docs/Web/API/FormData) para extraer los valores del formulario controles como un conjunto de pares clave/valor. Luego convertimos los datos a un objeto regular usando [`Object.fromEntries()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/fromEntries) y finalmente serialice los datos en [JSON](https://www.json.org/json-en.html), un formato que se utiliza comúnmente para intercambiar datos en la web.
|
||||
|
||||
Los datos ahora están listos para enviarse al servidor. Cree una nueva función llamada `createAccount`:
|
||||
|
||||
@@ -177,7 +177,7 @@ async function createAccount(account) {
|
||||
}
|
||||
```
|
||||
|
||||
¿Qué hace esta función? Primero, observe la palabra clave `async` aquí. Esto significa que la función contiene código que se ejecutará [** asincrónicamente**](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function). Cuando se usa junto con la palabra clave `await`, permite esperar a que se ejecute el código asincrónico, como esperar la respuesta del servidor aquí, antes de continuar.
|
||||
¿Qué hace esta función? Primero, observe la palabra clave `async` aquí. Esto significa que la función contiene código que se ejecutará [** asincrónicamente**](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/async_function). Cuando se usa junto con la palabra clave `await`, permite esperar a que se ejecute el código asincrónico, como esperar la respuesta del servidor aquí, antes de continuar.
|
||||
|
||||
Aquí hay un video rápido sobre el uso de `async/await`:
|
||||
|
||||
@@ -219,7 +219,7 @@ async function register() {
|
||||
}
|
||||
```
|
||||
|
||||
¡Eso fue un poco largo pero llegamos allí! Si abre sus [herramientas de desarrollo del navegador](https://developer.mozilla.org/en-US/docs/Learn/Common_questions/What_are_browser_developer_tools) e intenta registrar una nueva cuenta, no debería ver ningún cambio en la página web pero aparecerá un mensaje en la consola confirmando que todo funciona.
|
||||
¡Eso fue un poco largo pero llegamos allí! Si abre sus [herramientas de desarrollo del navegador](https://developer.mozilla.org/docs/Learn/Common_questions/What_are_browser_developer_tools) e intenta registrar una nueva cuenta, no debería ver ningún cambio en la página web pero aparecerá un mensaje en la consola confirmando que todo funciona.
|
||||
|
||||

|
||||
|
||||
@@ -227,15 +227,15 @@ async function register() {
|
||||
|
||||
## Validación de datos
|
||||
|
||||
Si intenta registrar una nueva cuenta sin establecer un nombre de usuario primero, puede ver que el servidor devuelve un error con el código de estado [400 (Solicitud incorrecta)](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/400#:~:text=The%20HyperText%20Transfer%20Protocol%20(HTTP,%2C%20or%20deceptive%20request%20routing).).
|
||||
Si intenta registrar una nueva cuenta sin establecer un nombre de usuario primero, puede ver que el servidor devuelve un error con el código de estado [400 (Solicitud incorrecta)](https://developer.mozilla.org/docs/Web/HTTP/Status/400#:~:text=The%20HyperText%20Transfer%20Protocol%20(HTTP,%2C%20or%20deceptive%20request%20routing).).
|
||||
|
||||
Antes de enviar datos a un servidor, es una buena práctica [validar los datos del formulario](https://developer.mozilla.org/en-US/docs/Learn/Forms/Form_validation) de antemano cuando sea posible, para asegurarse de enviar un solicitud válida. Los controles de formularios HTML5 proporcionan una validación incorporada utilizando varios atributos:
|
||||
Antes de enviar datos a un servidor, es una buena práctica [validar los datos del formulario](https://developer.mozilla.org/docs/Learn/Forms/Form_validation) de antemano cuando sea posible, para asegurarse de enviar un solicitud válida. Los controles de formularios HTML5 proporcionan una validación incorporada utilizando varios atributos:
|
||||
|
||||
- `required`: el campo debe completarse; de lo contrario, el formulario no se podrá enviar
|
||||
- `minlength` y `maxlength`: define el número mínimo y máximo de caracteres en los campos de texto.
|
||||
- `min` y `max`: define el valor mínimo y máximo de un campo numérico.
|
||||
- `type`: define el tipo de datos esperados, como `number`, `email`, `file` u [otros tipos integrados](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input). Este atributo también puede cambiar la representación visual del control de formulario.
|
||||
- `patrón`: permite definir un patrón [expresión regular](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions) para probar si los datos ingresados son válidos o no.
|
||||
- `type`: define el tipo de datos esperados, como `number`, `email`, `file` u [otros tipos integrados](https://developer.mozilla.org/docs/Web/HTML/Element/input). Este atributo también puede cambiar la representación visual del control de formulario.
|
||||
- `patrón`: permite definir un patrón [expresión regular](https://developer.mozilla.org/docs/Web/JavaScript/Guide/Regular_Expressions) para probar si los datos ingresados son válidos o no.
|
||||
|
||||
> Consejo: puedes personalizar el aspecto de los controles de tu formulario dependiendo de si son válidos o no usando las pseudoclases CSS `:valid` y `:invalid`.
|
||||
|
||||
|
@@ -27,7 +27,7 @@ curl http://localhost:5000/api
|
||||
|
||||
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:
|
||||
Esistono molti [tipi](https://developer.mozilla.org/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">
|
||||
@@ -35,9 +35,9 @@ Esistono molti [tipi](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/
|
||||
|
||||
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 dia un'occhiata all'intero elenco di [tipi di `<input>`](https://developer.mozilla.org/docs/Web/HTML/Element/input) e [altri controlli del form](https://developer.mozilla.org/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.
|
||||
✅ Si noti che `<input>` è un [elemento vuoto](https://developer.mozilla.org/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` :
|
||||
|
||||
@@ -68,7 +68,7 @@ Se si guarda più attentamente, si può notare che è stato aggiunto anche un el
|
||||
- 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.
|
||||
> [L'accessibilità](https://developer.mozilla.org/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/docs/Learn/Accessibility/HTML) non è difficile creare contenuti accessibili se usati correttamente. Si può [leggere di più sull'accessibilità](https://developer.mozilla.org/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:
|
||||
|
||||
@@ -155,7 +155,7 @@ function register() {
|
||||
}
|
||||
```
|
||||
|
||||
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.
|
||||
Qui si recupera l'elemento form utilizzando `getElementById()` e si utilizza il [metodo di supporto FormData](https://developer.mozilla.org/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/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`:
|
||||
|
||||
@@ -174,7 +174,7 @@ async function createAccount(account) {
|
||||
}
|
||||
```
|
||||
|
||||
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.
|
||||
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/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`:
|
||||
|
||||
@@ -218,7 +218,7 @@ async function register() {
|
||||
}
|
||||
```
|
||||
|
||||
È 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.
|
||||
È stato un po' lungo ma si è arrivati! Se si apre [strumenti di sviluppo del browser](https://developer.mozilla.org/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.
|
||||
|
||||

|
||||
|
||||
@@ -226,15 +226,15 @@ async function register() {
|
||||
|
||||
## 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)
|
||||
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/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:
|
||||
Prima di inviare i dati a un server è buona norma [convalidare i dati del modulo](https://developer.mozilla.org/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.
|
||||
- `type`: definisce il tipo di dati attesi, come `number`, `email`, `file` o [altri tipi incorporati](https://developer.mozilla.org/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/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` .
|
||||
|
||||
|
@@ -27,7 +27,7 @@ curl http://localhost:5000/api
|
||||
|
||||
`<form>` 요소는 사용자가 대화형 컨트롤을 사용하여 데이터를 입력하고 제출할 수 있는 HTML 문서의 섹션을 캡슐화합니다. 폼 내에서 쓸 수 있는 모든 종류의 사용자 인터페이스(UI) 컨트롤이 있으며, 가장 일반적인 컨트롤은 `<input>`과 `<button>` 요소입니다.
|
||||
|
||||
`<input>`에는 다양한 [types](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input)이 많이 있습니다, 예를 들어 사용자 이름으로 입력 가능한 필드를 만들려면 다음과 같이 사용할 수 있습니다:
|
||||
`<input>`에는 다양한 [types](https://developer.mozilla.org/docs/Web/HTML/Element/input)이 많이 있습니다, 예를 들어 사용자 이름으로 입력 가능한 필드를 만들려면 다음과 같이 사용할 수 있습니다:
|
||||
|
||||
```html
|
||||
<input name="username" type="text">
|
||||
@@ -35,9 +35,9 @@ curl http://localhost:5000/api
|
||||
|
||||
`name` 속성은 컨트롤을 식별하는 데 사용되고 폼 데이터를 전송할 때 속성 이름으로 사용됩니다.
|
||||
|
||||
> UI를 작성할 때 쓸 수 있는 모든 네이티브 UI 요소에 대한 아이디어를 얻으려면 [`<input>` types](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input)과 [other form controls](https://developer.mozilla.org/en-US/docs/Learn/Forms/Other_form_controls)의 전체 목록을 찾아봅시다.
|
||||
> UI를 작성할 때 쓸 수 있는 모든 네이티브 UI 요소에 대한 아이디어를 얻으려면 [`<input>` types](https://developer.mozilla.org/docs/Web/HTML/Element/input)과 [other form controls](https://developer.mozilla.org/docs/Learn/Forms/Other_form_controls)의 전체 목록을 찾아봅시다.
|
||||
|
||||
✅ `<input>`은 닫는 태그를 맞추지 *않는* [empty element](https://developer.mozilla.org/en-US/docs/Glossary/Empty_element)입니다. 자동으로-닫는 `<input/>` 표기법을 사용할 수 있지만, 필수는 아닙니다.
|
||||
✅ `<input>`은 닫는 태그를 맞추지 *않는* [empty element](https://developer.mozilla.org/docs/Glossary/Empty_element)입니다. 자동으로-닫는 `<input/>` 표기법을 사용할 수 있지만, 필수는 아닙니다.
|
||||
|
||||
폼 내의 `<button>` 요소는 약간 특별합니다. `type` 속성을 지정하지 않으면, 눌렀을 때 폼 데이터가 자동으로 서버에 제출됩니다. 가능한 `type` 값은 다음과 같습니다:
|
||||
|
||||
@@ -68,7 +68,7 @@ curl http://localhost:5000/api
|
||||
- 라벨을 폼 컨트롤에 연결하면, (화면 판독기와 같은) 보조 기술을 사용하는 사용자가 받는 것으로 예상되는 데이터를 이해하는 데 도움이 됩니다.
|
||||
- 라벨을 클릭하여 연결된 입력에 직접 맞출 수 있으므로, 터치-스크린 기반 장치에서 더 쉽게 접근할 수 있습니다.
|
||||
|
||||
> 웹에서의 [Accessibility](https://developer.mozilla.org/en-US/docs/Learn/Accessibility/What_is_accessibility)은 종종 간과되는 매우 중요한 주제입니다. [HTML5 semantic elements](https://developer.mozilla.org/en-US/docs/Learn/Accessibility/HTML) 덕분에 이를 적절하게 사용한다면 접근성 콘텐츠로 만드는 것은 어렵지 않습니다. 일반적인 실수를 피하고 책임있는 개발자가 되기 위해 [accessibility에 대하여 읽을 수](https://developer.mozilla.org/en-US/docs/Web/Accessibility) 있습니다.
|
||||
> 웹에서의 [Accessibility](https://developer.mozilla.org/docs/Learn/Accessibility/What_is_accessibility)은 종종 간과되는 매우 중요한 주제입니다. [HTML5 semantic elements](https://developer.mozilla.org/docs/Learn/Accessibility/HTML) 덕분에 이를 적절하게 사용한다면 접근성 콘텐츠로 만드는 것은 어렵지 않습니다. 일반적인 실수를 피하고 책임있는 개발자가 되기 위해 [accessibility에 대하여 읽을 수](https://developer.mozilla.org/docs/Web/Accessibility) 있습니다.
|
||||
|
||||
이제 이전 항목의 바로 아래에, 가입을 위한 두번째 폼을 추가합니다:
|
||||
|
||||
@@ -156,7 +156,7 @@ function register() {
|
||||
}
|
||||
```
|
||||
|
||||
여기서는 `getElementById()`를 사용하여 폼 요소를 검색하고, [`FormData`](https://developer.mozilla.org/en-US/docs/Web/API/FormData) 헬퍼를 사용하여 키/값 쌍 집합으로 폼 컨트롤에서 값을 추출합니다. 그러고 [`Object.fromEntries()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/fromEntries)를 사용하여 데이터를 일반 객체로 변환하여 최종적으로 웹에서 데이터를 교환할 때, 일반적으로 사용되는 포맷인 [JSON](https://www.json.org/json-en.html)으로 데이터를 직렬화합니다.
|
||||
여기서는 `getElementById()`를 사용하여 폼 요소를 검색하고, [`FormData`](https://developer.mozilla.org/docs/Web/API/FormData) 헬퍼를 사용하여 키/값 쌍 집합으로 폼 컨트롤에서 값을 추출합니다. 그러고 [`Object.fromEntries()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/fromEntries)를 사용하여 데이터를 일반 객체로 변환하여 최종적으로 웹에서 데이터를 교환할 때, 일반적으로 사용되는 포맷인 [JSON](https://www.json.org/json-en.html)으로 데이터를 직렬화합니다.
|
||||
|
||||
데이터는 이제 서버에 보낼 준비가 되었습니다. `createAccount`라고 지은 새로운 함수를 만듭니다:
|
||||
|
||||
@@ -175,7 +175,7 @@ async function createAccount(account) {
|
||||
}
|
||||
```
|
||||
|
||||
이 함수는 어떤 일을 할까요? 먼저, 여기있는 `async` 키워드를 확인하세요. 이 함수는 [**asynchronously**](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function)하게 실행하는 코드가 포함되어 있다는 것을 의미합니다. `await` 키워드와 함께 사용하면, 비동기 코드가 실행될 때까지 기다릴 수 있습니다 - 여기에서 서버의 응답을 기다리는 것과 같습니다 - 계속하기 전에.
|
||||
이 함수는 어떤 일을 할까요? 먼저, 여기있는 `async` 키워드를 확인하세요. 이 함수는 [**asynchronously**](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/async_function)하게 실행하는 코드가 포함되어 있다는 것을 의미합니다. `await` 키워드와 함께 사용하면, 비동기 코드가 실행될 때까지 기다릴 수 있습니다 - 여기에서 서버의 응답을 기다리는 것과 같습니다 - 계속하기 전에.
|
||||
|
||||
여기는 ``async/await` 사용 방식에 대한 간단한 영상입니다:
|
||||
|
||||
@@ -217,7 +217,7 @@ async function register() {
|
||||
}
|
||||
```
|
||||
|
||||
조금 길지만 도착했습니다! [browser developer tools](https://developer.mozilla.org/en-US/docs/Learn/Common_questions/What_are_browser_developer_tools)를 열고, 새 계정을 가입하면, 웹 페이지에 변경 사항이 표시되지 않으면서 콘솔에 작동을 확인할 메시지가 나타납니다.
|
||||
조금 길지만 도착했습니다! [browser developer tools](https://developer.mozilla.org/docs/Learn/Common_questions/What_are_browser_developer_tools)를 열고, 새 계정을 가입하면, 웹 페이지에 변경 사항이 표시되지 않으면서 콘솔에 작동을 확인할 메시지가 나타납니다.
|
||||
|
||||

|
||||
|
||||
@@ -225,15 +225,15 @@ async function register() {
|
||||
|
||||
## Data 검증하기
|
||||
|
||||
사용자 이름을 먼저 설정하지 않고 새 계정을 가입하려하면, 서버에서 상태 코드 [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).) 오류를 반환하는 것으로 볼 수 있습니다.
|
||||
사용자 이름을 먼저 설정하지 않고 새 계정을 가입하려하면, 서버에서 상태 코드 [400 (Bad Request)](https://developer.mozilla.org/docs/Web/HTTP/Status/400#:~:text=The%20HyperText%20Transfer%20Protocol%20(HTTP,%2C%20or%20deceptive%20request%20routing).) 오류를 반환하는 것으로 볼 수 있습니다.
|
||||
|
||||
데이터를 서버로 보내기 전에 할 수 있다면, 유요한 요청을 보낼 수 있도록, 미리 [validate the form data](https://developer.mozilla.org/en-US/docs/Learn/Forms/Form_validation)를 실습하는 것이 좋습니다. HTML5 포맷 컨트롤은 다양한 속성을 사용하여 빌트인 유효성 검사를 제공합니다:
|
||||
데이터를 서버로 보내기 전에 할 수 있다면, 유요한 요청을 보낼 수 있도록, 미리 [validate the form data](https://developer.mozilla.org/docs/Learn/Forms/Form_validation)를 실습하는 것이 좋습니다. HTML5 포맷 컨트롤은 다양한 속성을 사용하여 빌트인 유효성 검사를 제공합니다:
|
||||
|
||||
- `required`: 필드를 채워야하며 안 채운다면 폼을 제출할 수 없습니다.
|
||||
- `minlength`와 `maxlength`: 텍스트 입력의 최소 및 최대 문자 수를 정의합니다.
|
||||
- `min`과 `max`: 숫자 필드의 최소값과 최대값을 정의합니다.
|
||||
- `type`: `number`, `email`, `file` 또는 [other built-in types](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input)처럼, 예상되는 데이터의 종류를 정의합니다. 이 속성은 폼 컨트롤의 비주얼 렌더링을 바꿀 수도 있습니다.
|
||||
- `pattern`: 입력된 데이터가 유효한지 테스트하기 위해 [regular expression](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions) 패턴을 정의할 수 있습니다.
|
||||
- `type`: `number`, `email`, `file` 또는 [other built-in types](https://developer.mozilla.org/docs/Web/HTML/Element/input)처럼, 예상되는 데이터의 종류를 정의합니다. 이 속성은 폼 컨트롤의 비주얼 렌더링을 바꿀 수도 있습니다.
|
||||
- `pattern`: 입력된 데이터가 유효한지 테스트하기 위해 [regular expression](https://developer.mozilla.org/docs/Web/JavaScript/Guide/Regular_Expressions) 패턴을 정의할 수 있습니다.
|
||||
|
||||
> Tip: 유효하거나 `:valid`와 `:invalid` CSS pseudo-classes를 사용하지 않는 여부에 따라 폼 컨트롤의 모양을 커스텀할 수 있습니다.
|
||||
|
||||
|
@@ -27,7 +27,7 @@ curl http://localhost:5000/api
|
||||
|
||||
Elemen `<form>` merangkumi bahagian dokumen HTML di mana pengguna dapat memasukkan dan menyerahkan data dengan kawalan interaktif. Terdapat pelbagai jenis kawalan antara muka pengguna (UI) yang dapat digunakan dalam bentuk, yang paling umum adalah elemen `<input>` dan elemen `<button>`.
|
||||
|
||||
Terdapat banyak [jenis yang berbeza](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input) dari `<input>`, misalnya untuk membuat bidang di mana pengguna boleh memasukkan nama penggunanya yang boleh anda gunakan:
|
||||
Terdapat banyak [jenis yang berbeza](https://developer.mozilla.org/docs/Web/HTML/Element/input) dari `<input>`, misalnya untuk membuat bidang di mana pengguna boleh memasukkan nama penggunanya yang boleh anda gunakan:
|
||||
|
||||
```html
|
||||
<input id="username" name="username" type="text">
|
||||
@@ -35,9 +35,9 @@ Terdapat banyak [jenis yang berbeza](https://developer.mozilla.org/en-US/docs/We
|
||||
|
||||
Atribut `name` akan digunakan sebagai nama harta ketika data borang akan dihantar. Atribut `id` digunakan untuk mengaitkan` <label>` dengan kawalan bentuk.
|
||||
|
||||
> Lihat keseluruhan senarai [`<input>` types](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input) dan [kawalan bentuk lain](https://developer.mozilla.org/en-US/docs/Learn/Forms/Other_form_controls) untuk mendapatkan idea tentang semua elemen UI asli yang boleh anda gunakan semasa membina UI anda.
|
||||
> Lihat keseluruhan senarai [`<input>` types](https://developer.mozilla.org/docs/Web/HTML/Element/input) dan [kawalan bentuk lain](https://developer.mozilla.org/docs/Learn/Forms/Other_form_controls) untuk mendapatkan idea tentang semua elemen UI asli yang boleh anda gunakan semasa membina UI anda.
|
||||
|
||||
✅ Perhatikan bahawa `<input>` adalah [elemen kosong](https://developer.mozilla.org/en-US/docs/Glossary/Empty_element) di mana anda seharusnya *tidak* menambahkan tag penutup yang sepadan. Anda bagaimanapun boleh menggunakan notasi `<input/>` tutup sendiri, tetapi tidak diperlukan.
|
||||
✅ Perhatikan bahawa `<input>` adalah [elemen kosong](https://developer.mozilla.org/docs/Glossary/Empty_element) di mana anda seharusnya *tidak* menambahkan tag penutup yang sepadan. Anda bagaimanapun boleh menggunakan notasi `<input/>` tutup sendiri, tetapi tidak diperlukan.
|
||||
|
||||
Elemen `<button>` dalam bentuk agak istimewa. Sekiranya anda tidak menentukan atribut `type`, ia akan secara automatik mengirimkan data borang ke pelayan ketika ditekan. Berikut adalah nilai `type` yang mungkin:
|
||||
|
||||
@@ -68,7 +68,7 @@ Sekiranya anda melihat lebih dekat, anda dapat melihat bahawa kami juga menambah
|
||||
- Dengan mengaitkan label dengan kawalan bentuk, ia membantu pengguna menggunakan teknologi bantu (seperti pembaca skrin) untuk memahami data apa yang diharapkan dapat mereka berikan.
|
||||
- Anda dapat mengklik label untuk memberi fokus langsung pada input yang berkaitan, sehingga lebih mudah dijangkau pada peranti berdasarkan layar sentuh.
|
||||
|
||||
> [Kebolehcapaian](https://developer.mozilla.org/en-US/docs/Learn/Accessibility/What_is_accessibility) di web adalah topik yang sangat penting yang sering diabaikan. Terima kasih kepada [elemen HTML semantik](https://developer.mozilla.org/en-US/docs/Learn/Accessibility/HTML) tidak sukar untuk membuat kandungan yang boleh diakses jika anda menggunakannya dengan betul. Anda boleh [baca lebih lanjut mengenai kebolehaksesan](https://developer.mozilla.org/en-US/docs/Web/Accessibility) untuk mengelakkan kesilapan biasa dan menjadi pembangun yang bertanggungjawab.
|
||||
> [Kebolehcapaian](https://developer.mozilla.org/docs/Learn/Accessibility/What_is_accessibility) di web adalah topik yang sangat penting yang sering diabaikan. Terima kasih kepada [elemen HTML semantik](https://developer.mozilla.org/docs/Learn/Accessibility/HTML) tidak sukar untuk membuat kandungan yang boleh diakses jika anda menggunakannya dengan betul. Anda boleh [baca lebih lanjut mengenai kebolehaksesan](https://developer.mozilla.org/docs/Web/Accessibility) untuk mengelakkan kesilapan biasa dan menjadi pembangun yang bertanggungjawab.
|
||||
|
||||
Sekarang kita akan menambah borang kedua untuk pendaftaran, tepat di bawah yang sebelumnya:
|
||||
|
||||
@@ -156,7 +156,7 @@ function register() {
|
||||
}
|
||||
```
|
||||
|
||||
Di sini kita mengambil elemen borang menggunakan `getElementById()` dan menggunakan pembantu [`FormData`](https://developer.mozilla.org/en-US/docs/Web/API/FormData) untuk mengekstrak nilai dari borang kawalan sebagai satu set pasangan kunci / nilai. Kemudian kami menukar data ke objek biasa menggunakan [`Object.fromEntries()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/fromEntries) dan akhirnya bersiri data ke [JSON](https://www.json.org/json-en.html), format yang biasa digunakan untuk pertukaran data di web.
|
||||
Di sini kita mengambil elemen borang menggunakan `getElementById()` dan menggunakan pembantu [`FormData`](https://developer.mozilla.org/docs/Web/API/FormData) untuk mengekstrak nilai dari borang kawalan sebagai satu set pasangan kunci / nilai. Kemudian kami menukar data ke objek biasa menggunakan [`Object.fromEntries()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/fromEntries) dan akhirnya bersiri data ke [JSON](https://www.json.org/json-en.html), format yang biasa digunakan untuk pertukaran data di web.
|
||||
|
||||
Data kini siap dihantar ke pelayan. Buat fungsi baru bernama `createAccount`:
|
||||
|
||||
@@ -175,7 +175,7 @@ async function createAccount(account) {
|
||||
}
|
||||
```
|
||||
|
||||
Apakah fungsi ini? Pertama, perhatikan kata kunci `async` di sini. Ini bermaksud bahawa fungsi tersebut mengandungi kod yang akan menjalankan [**asynchronously**](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function). Apabila digunakan di sepanjang kata kunci `await`, ia membolehkan menunggu kod tak segerak untuk dilaksanakan - seperti menunggu tindak balas pelayan di sini - sebelum meneruskan.
|
||||
Apakah fungsi ini? Pertama, perhatikan kata kunci `async` di sini. Ini bermaksud bahawa fungsi tersebut mengandungi kod yang akan menjalankan [**asynchronously**](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/async_function). Apabila digunakan di sepanjang kata kunci `await`, ia membolehkan menunggu kod tak segerak untuk dilaksanakan - seperti menunggu tindak balas pelayan di sini - sebelum meneruskan.
|
||||
|
||||
Berikut adalah video ringkas mengenai penggunaan `async/await`:
|
||||
|
||||
@@ -219,7 +219,7 @@ async function register() {
|
||||
}
|
||||
```
|
||||
|
||||
Itu agak lama tetapi kami sampai di sana! Sekiranya anda membuka [alat pembangun penyemak imbas](https://developer.mozilla.org/en-US/docs/Learn/Common_questions/What_are_browser_developer_tools), dan cuba mendaftarkan akaun baru, anda tidak akan melihat perubahan di laman web tetapi mesej akan muncul di konsol yang mengesahkan bahawa semuanya berfungsi.
|
||||
Itu agak lama tetapi kami sampai di sana! Sekiranya anda membuka [alat pembangun penyemak imbas](https://developer.mozilla.org/docs/Learn/Common_questions/What_are_browser_developer_tools), dan cuba mendaftarkan akaun baru, anda tidak akan melihat perubahan di laman web tetapi mesej akan muncul di konsol yang mengesahkan bahawa semuanya berfungsi.
|
||||
|
||||

|
||||
|
||||
@@ -227,15 +227,15 @@ Itu agak lama tetapi kami sampai di sana! Sekiranya anda membuka [alat pembangun
|
||||
|
||||
## Pengesahan data
|
||||
|
||||
Sekiranya anda cuba mendaftarkan akaun baru tanpa menetapkan nama pengguna terlebih dahulu, anda dapat melihat bahawa pelayan mengembalikan ralat dengan kod status [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).).
|
||||
Sekiranya anda cuba mendaftarkan akaun baru tanpa menetapkan nama pengguna terlebih dahulu, anda dapat melihat bahawa pelayan mengembalikan ralat dengan kod status [400 (Bad Request)](https://developer.mozilla.org/docs/Web/HTTP/Status/400#:~:text=The%20HyperText%20Transfer%20Protocol%20(HTTP,%2C%20or%20deceptive%20request%20routing).).
|
||||
|
||||
Sebelum menghantar data ke pelayan, adalah amalan yang baik untuk [mengesahkan data borang](https://developer.mozilla.org/en-US/docs/Learn/Forms/Form_validation) terlebih dahulu apabila mungkin, untuk memastikan anda menghantar permintaan yang sah. Kawalan borang HTML5 memberikan pengesahan terbina dalam menggunakan pelbagai atribut:
|
||||
Sebelum menghantar data ke pelayan, adalah amalan yang baik untuk [mengesahkan data borang](https://developer.mozilla.org/docs/Learn/Forms/Form_validation) terlebih dahulu apabila mungkin, untuk memastikan anda menghantar permintaan yang sah. Kawalan borang HTML5 memberikan pengesahan terbina dalam menggunakan pelbagai atribut:
|
||||
|
||||
- `diperlukan`: bidang perlu diisi jika tidak, borang tidak dapat dihantar.
|
||||
- `minlength` dan` maxlength`: menentukan bilangan aksara minimum dan maksimum dalam bidang teks.
|
||||
- `min` dan `max`: menentukan nilai minimum dan maksimum medan angka.
|
||||
- `type`: mentakrifkan jenis data yang diharapkan, seperti `number`, `email`, `file` atau [other built-in types](https://developer.mozilla.org/en-US/docs/Web/HTML/Elemen/input). Atribut ini juga dapat mengubah rendering visual kawalan bentuk.
|
||||
- `pattern`: memungkinkan untuk menentukan [ungkapan biasa](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions) untuk menguji sama ada data yang dimasukkan sah atau tidak.
|
||||
- `type`: mentakrifkan jenis data yang diharapkan, seperti `number`, `email`, `file` atau [other built-in types](https://developer.mozilla.org/docs/Web/HTML/Elemen/input). Atribut ini juga dapat mengubah rendering visual kawalan bentuk.
|
||||
- `pattern`: memungkinkan untuk menentukan [ungkapan biasa](https://developer.mozilla.org/docs/Web/JavaScript/Guide/Regular_Expressions) untuk menguji sama ada data yang dimasukkan sah atau tidak.
|
||||
|
||||
> Petua: anda boleh menyesuaikan rupa kawalan borang anda bergantung pada apakah itu sah atau tidak menggunakan kelas pseudo CSS `:invalid` dan`:valid`.
|
||||
|
||||
|
297
7-bank-project/2-forms/translations/README.zh-tw.md
Normal file
297
7-bank-project/2-forms/translations/README.zh-tw.md
Normal file
@@ -0,0 +1,297 @@
|
||||
# 建立銀行網頁應用程式 Part 2:登入與註冊表單
|
||||
|
||||
## 課前測驗
|
||||
|
||||
[課前測驗](https://nice-beach-0fe9e9d0f.azurestaticapps.net/quiz/43?loc=zh_tw)
|
||||
|
||||
### 大綱
|
||||
|
||||
在大多數當代網頁應用程式中,你可以建立自己的帳戶來擁有自己的私人空間。許多用戶在同一時間可以存取同一個網頁應用程式,你就必須有一套機制分開儲存不同用戶的資料並顯示適當的資訊。我們不會涉及到如何管理[用戶個資的安全](https://zh.wikipedia.org/wiki/%E8%BA%AB%E4%BB%BD%E9%AA%8C%E8%AF%81),它是個相當廣泛的主題,我們僅會確保每個用戶能在這款銀行應用上建立一到多個數位帳戶。
|
||||
|
||||
在這單元中,我們會使用 HTML 表單來新增登入與註冊的功能。我們會看到如何使用伺服器 API 傳遞資料,定義基本的用戶字串輸入之檢查機制。
|
||||
|
||||
### 開始之前
|
||||
|
||||
你需要完成第一單元 [HTML 模板與網頁路由](../../1-template-route/translations/README.zh-tw.md)的應用程式。你還需要安裝 [Node.js](https://nodejs.org) 與在本地端[運行伺服器 API](../../api/translations/README.zh-tw.md)以傳輸建立帳戶所需的資料。
|
||||
|
||||
你可以測試伺服器是否運作正常,在終端機內輸入指令:
|
||||
|
||||
```sh
|
||||
curl http://localhost:5000/api
|
||||
# -> 會回傳結果 "Bank API v1.0.0"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 表單與其控制
|
||||
|
||||
`<form>` 元素打包了 HTML 文件中使用者輸入與提交資料的地方。有許多種使用者介面(UI)以表單的方式呈現,最常見的內容會包含 `<input>` 與 `<button>` 元素。
|
||||
|
||||
有許多種 `<input>` 的[種類](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input),舉例來說,若要建立使用者輸入使用者名稱的地方,你可以:
|
||||
|
||||
```html
|
||||
<input id="username" name="username" type="text">
|
||||
```
|
||||
|
||||
`name` 屬性同時亦是表單傳輸資料的名稱。`id` 屬性是用來與 `<label>` 做表單控制(form control)的連接。
|
||||
|
||||
> 花點時間看看 [`<input>` 種類](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input)的清單與[其他表單控制](https://developer.mozilla.org/en-US/docs/Learn/Forms/Other_form_controls),讓你在建立使用者介面時,有全部供你使用的原生 UI 元素可以參考。
|
||||
|
||||
✅ 紀錄一下 `<input>` 是種[空元素](https://developer.mozilla.org/en-US/docs/Glossary/Empty_element),你*不應該*在它後面加上對應的結束標籤。然而,你仍然可以在它的後面使用 `<input/>`,這沒有強制規定。
|
||||
|
||||
表單中的 `<button>` 元素是有些特別。如果你沒有指定它的 `type` 屬性,它會在你輸入文字時,自動地提交表單內容給伺服器。這邊有一些你可以設定的 `type` 內容:
|
||||
|
||||
- `submit`: `<form>` 內的預設型態,按鈕會觸發表單提交這項行為。
|
||||
- `reset`: 按鈕會重置所有表單控制回初始狀態。
|
||||
- `button`: 在按鈕按下時不執行預設行為。你可以藉由 JavaScript 自由定義之後的動作。
|
||||
|
||||
### 課題
|
||||
|
||||
在 `login` 模板內加入表單。我們需要*使用者名稱(username)*的輸入框與*登入(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>
|
||||
```
|
||||
|
||||
如果你仔細地看,你會注意到我們在這裡還加了 `<label>` 元素。`<label>` 元素被用來新增文字到 UI 上,譬如說我們的使用者名稱。為了讓表單得以被閱讀,標籤是很重要的,此外它還有額外的優點:
|
||||
|
||||
- 連結標籤到表單控制上,它能幫助使用者的額外工具,好比說螢幕報讀器,理解接下來該提供何種資料。
|
||||
- 你可以點擊標籤,它會跳轉到相對應的輸入框,讓使用觸控型裝置的用戶更容易操作。
|
||||
|
||||
> [網頁親和力](https://developer.mozilla.org/en-US/docs/Learn/Accessibility/What_is_accessibility)是非常重要但常被忽視的主題。感謝[語義化 HTML 元素](https://developer.mozilla.org/en-US/docs/Learn/Accessibility/HTML)的幫助,建立無障礙的網頁內容變得更加容易。你可以[閱讀更多有關網頁親和力的文章](https://developer.mozilla.org/en-US/docs/Web/Accessibility),避免觸犯到常見的錯誤並成為負責任的開發者。
|
||||
|
||||
現在,我們加入第二張表單給用戶註冊使用,就像前一張一樣:
|
||||
|
||||
```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>
|
||||
```
|
||||
|
||||
藉由 `value` 屬性,我們可以定義輸入框內的預設值。
|
||||
注意一下 `balance` 的輸入類型為 `number`。它看起來與其他輸入框不一樣嗎?試著與它互動看看。
|
||||
|
||||
✅ 你能只利用鍵盤造訪表格,與表格互動嗎?你是如何做到的?
|
||||
|
||||
## 提交資料給伺服器
|
||||
|
||||
現在我們有可以使用的 UI 了,下一個步驟要將資料送給我們的伺服器。讓我們來快速地測試一下程式:在點擊 *Login* 或 *Register* 按鈕後,發生了什麼事?
|
||||
|
||||
你有注意到瀏覽器的網址列改變了嗎?
|
||||
|
||||

|
||||
|
||||
`<form>` 預設的行為:使用 [GET 方法](https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.3)提交表格,將表格資料接在網址後面,傳送給目前網址的伺服器。然而這個方法有一些缺點:
|
||||
|
||||
- 資料大小有上限限制(大約 2000 字元)
|
||||
- 可以直接在網址內看到資料(對密碼而言,這並不恰當)
|
||||
- 它不能做檔案的上傳
|
||||
|
||||
這也是為什麼你需要將它轉換為 [POST 方法](https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.5),將表單資料存在 HTTP 請求的內容中。這樣就不會遇到上述的限制。
|
||||
|
||||
> POST 是常見的資料傳輸方法,[在一些特別的情況下](https://www.w3.org/2001/tag/doc/whenToUseGet.html),使用 GET 方法相對起來比較恰當。例如進行搜尋的時候。
|
||||
|
||||
### 課題
|
||||
|
||||
加入 `action` 與 `method` 屬性到註冊表單之中:
|
||||
|
||||
```html
|
||||
<form id="registerForm" action="//localhost:5000/api/accounts" method="POST">
|
||||
```
|
||||
|
||||
現在,試著以你的名字申請新的帳戶。在點擊 *Register* 按鈕後,你應該能看到像這樣的畫面:
|
||||
|
||||

|
||||
|
||||
若所有事情都運作正常,伺服器應該會回應你的請求,附帶 [JSON](https://www.json.org/json-en.html) 包含著你剛建立的帳戶資料。
|
||||
|
||||
✅ 試著以相同名字再註冊一次。發生了什麼事?
|
||||
|
||||
## 不重新載入地提交資料
|
||||
|
||||
你可能會注意到,這些行動間出現了一個小問題:在提交表單時,我們離開了網頁應用,瀏覽器又重新導回到伺服器的網址。我們試著避免網頁應用重新載入所有的頁面,做出[單一頁面應用程式 (SPA)](https://zh.wikipedia.org/zh-tw/%E5%8D%95%E9%A1%B5%E5%BA%94%E7%94%A8)。
|
||||
|
||||
為了讓傳遞資料給伺服器時,不發生頁面重新載入的情況,我們需要使用 JavaScript。
|
||||
|
||||
比起直接在 `<form>` 元素的 `action` 屬性加入網址,你可以使用 `javascript:` 字串接在程式語句前頭來執行自訂的行為。使用這方法也意味著你需要額外修改一些原本瀏覽器會做的行為。
|
||||
|
||||
- 接收表單資料
|
||||
- 轉換並編碼表單資料成合適的格式
|
||||
- 建立 HTTP 請求並傳遞給伺服器
|
||||
|
||||
### 課題
|
||||
|
||||
將註冊表單的 `action` 替換為:
|
||||
|
||||
```html
|
||||
<form id="registerForm" action="javascript:register()">
|
||||
```
|
||||
|
||||
開啟 `app.js`,加入新的函式 `register`:
|
||||
|
||||
```js
|
||||
function register() {
|
||||
const registerForm = document.getElementById('registerForm');
|
||||
const formData = new FormData(registerForm);
|
||||
const data = Object.fromEntries(formData);
|
||||
const jsonData = JSON.stringify(data);
|
||||
}
|
||||
```
|
||||
|
||||
我們使用 `getElementById()` 蒐集表單的元素,使用 [`FormData`](https://developer.mozilla.org/en-US/docs/Web/API/FormData) 協助從表單控制中取出 key/value 的數據對。
|
||||
之後,利用 [`Object.fromEntries()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/fromEntries) 轉換資料成正規物件,最後再將檔案轉成 [JSON](https://www.json.org/json-en.html) ── 一個在網路上常見的資料交換格式。
|
||||
|
||||
現在資料已經準備提交給伺服器了。建立新函式 `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' };
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
這個函式做了什麼?首先,注意關鍵字 `async`,代表著函式包含了[**非同步化程式**](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function)。在與關鍵字 `await` 一起使用時,它會在繼續運行程式前,等待非同步的程式被執行,就像等待伺服器回應一樣。
|
||||
|
||||
這邊有關於使用 `async/await` 的影片:
|
||||
|
||||
[](https://youtube.com/watch?v=YwmlRkrxvkk "Async 與 Await 管理 promises")
|
||||
|
||||
> 點擊上方圖片以觀看關於 async/await 的影片。
|
||||
|
||||
我們使用 API `fetch()` 來傳送 JSON 資料給伺服器。這個方法需要使用兩個參數:
|
||||
|
||||
- 伺服器的網址,在此使用 `//localhost:5000/api/accounts`。
|
||||
- 網頁請求的設定,就是我們定義 `POST` 方法與提供請求的 `body`。當我們傳輸 JSON 資料給伺服器,我們還需要在標頭的 `Content-Type` 定為 `application/json`,伺服器才知道該如何解讀裡面的內容。
|
||||
|
||||
當伺服器以 JSON 回應請求後,我們可以使用 `await response.json()` 來取得 JSON 的內容並回傳結果。注意在此為非同步程式的方法,我們使用關鍵字 `await` 回傳任何在解讀封包時產生的錯誤訊息。
|
||||
|
||||
現在,在函式 `register` 中呼叫 `createAccount()`:
|
||||
|
||||
```js
|
||||
const result = await createAccount(jsonData);
|
||||
```
|
||||
|
||||
因為我們在這此使用了關鍵字 `await`,我們需要在註冊函式前新增關鍵字 `async`:
|
||||
|
||||
```js
|
||||
async function register() {
|
||||
```
|
||||
|
||||
最後,我們儲存一些紀錄來檢查結果。最後的函式應該會如下方格式:
|
||||
|
||||
```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);
|
||||
}
|
||||
```
|
||||
|
||||
過程有些冗長,但我們達成了!當你開啟[瀏覽器開發者工具](https://developer.mozilla.org/en-US/docs/Learn/Common_questions/What_are_browser_developer_tools),試著註冊新的帳戶,你應該能看到網頁並沒有改變,但命令欄中會顯示帳戶成功註冊的訊息。
|
||||
|
||||

|
||||
|
||||
✅ 你覺得傳給伺服器的資料是安全的嗎?其他人有辦法攔截網頁請求嗎?你可以閱讀 [HTTPS](https://en.wikipedia.org/wiki/HTTPS),了解更多關於安全的資料傳輸。
|
||||
|
||||
## 資料驗證
|
||||
|
||||
試著在註冊新帳戶時,不輸入你的使用者名稱,你會發現伺服器回傳了錯誤狀態訊息:[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).)。
|
||||
|
||||
在傳輸資料給伺服器之前,最好先[驗證表單資料](https://developer.mozilla.org/en-US/docs/Learn/Forms/Form_validation),以確保我們傳送合法的網頁請求。 HTML5 表單控制內建包含了驗證方法,使用了多樣的屬性: controls provides built-in validation using various attributes:
|
||||
|
||||
- `required`: 輸入框必須被填寫,否則表單不能被提交。
|
||||
- `minlength` 和 `maxlength`: 定義輸入框的文字下限與文字上限。
|
||||
- `min` 和 `max`: 定義輸入框的數字下限與數字上限。
|
||||
- `type`: 定義輸入框內的資料格式,例如`數字`、`email`、`檔案`或是[其他內建的格式](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input)。這個屬性可能會改變表單控制的表現方法。
|
||||
- `pattern`: 允許定義[正規表示法](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions)的字串,測試輸入的內容是否滿足它。
|
||||
|
||||
> 提示:你可以自定義表單控制的呈現方法,利用 CSS pseudo-classes `:valid` 和 `:invalid` 判斷內容是否合理。
|
||||
|
||||
### 課題
|
||||
|
||||
在建立新的合法帳戶時,有兩個必須被填寫的輸入框:使用者名稱與資產狀態,而其他選項則是可有可無。現在更新表單的 HTML 語法,使用 `required` 屬性並標記提示在標籤中:
|
||||
|
||||
```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>
|
||||
```
|
||||
|
||||
伺服器並沒設定輸入框的文字上限,定義合理的文字輸入上限是必要的。
|
||||
|
||||
在文字框內加入 `maxlength` 屬性:
|
||||
|
||||
```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">
|
||||
```
|
||||
|
||||
現在,如果文字框並沒有滿足我們所定義的規則時,在點擊了 *Register* 按鈕後,你會看到:
|
||||
|
||||

|
||||
|
||||
這類在傳輸資料給伺服器*之前*的驗證系統稱之為**用戶端(client-side)**驗證。但注意有些資料是沒有辦法在傳輸前被驗證的。舉例來說,我們沒辦法在發出請求前,確認是否已經存在著一組相同姓名的帳戶。伺服器上額外的驗證措施就稱之為**伺服器端(server-side)**驗證。
|
||||
|
||||
通常這兩個驗證都需要去編寫,用戶端驗證能及時回饋給用戶,提升使用者體驗;伺服器端驗證確保你要處理的用戶資料是合理且安全的。
|
||||
|
||||
---
|
||||
|
||||
## 🚀 挑戰
|
||||
|
||||
當相同使用者名稱的帳戶已經存在時,在 HTML 上顯示錯誤訊息。
|
||||
|
||||
這邊有做過一些造型的最終登入頁面範本。
|
||||
|
||||

|
||||
|
||||
## 課後測驗
|
||||
|
||||
[課後測驗](https://nice-beach-0fe9e9d0f.azurestaticapps.net/quiz/44?loc=zh_tw)
|
||||
|
||||
## 複習與自學
|
||||
|
||||
開發者在建立表單時需要發揮他們的創意,尤其是策畫資料驗證的規則。在 [CodePen](https://codepen.com) 上學習不同表單流程,你能發現什麼有趣且令人發想的表單嗎?
|
||||
|
||||
## 作業
|
||||
|
||||
[造型化你的銀行程式](assignment.zh-tw.md)
|
13
7-bank-project/2-forms/translations/assignment.zh-tw.md
Normal file
13
7-bank-project/2-forms/translations/assignment.zh-tw.md
Normal file
@@ -0,0 +1,13 @@
|
||||
# 造型化你的銀行程式
|
||||
|
||||
## 簡介
|
||||
|
||||
建立新的檔案 `styles.css`,匯入到你的 `index.html` 檔案中。藉由 CSS 檔,你能讓*登入*與*儀表板*頁面看起來更漂亮且整潔。試著為你的程式加入主題色彩,對應到你的品牌。
|
||||
|
||||
> 提示:你可以修改 HTML 檔,在必要時新增元素與 classes。
|
||||
|
||||
## 學習評量
|
||||
|
||||
| 作業內容 | 優良 | 普通 | 待改進 |
|
||||
| -------- | -------------------------------------------------- | ------------------------------------ | ---------------------------------------- |
|
||||
| | 所有的頁面整潔且易讀:統一的主題色彩且排版顯示正常 | 頁面有調整過,但缺乏主題且排版有瑕疵 | 頁面缺乏造型,排版凌亂且頁面資訊難以理解 |
|
Reference in New Issue
Block a user