Skip to content

Fetch #22

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 16 commits into from
Oct 20, 2019
2 changes: 1 addition & 1 deletion 5-network/01-fetch/01-fetch-users/_js.view/source.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@

async function getUsers(names) {
/* your code */
/* twój kod */
}
4 changes: 2 additions & 2 deletions 5-network/01-fetch/01-fetch-users/_js.view/test.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
describe("getUsers", function() {

it("gets users from GitHub", async function() {
let users = await getUsers(['iliakan', 'remy', 'no.such.users']);
it("pobiera użytkowników GitHuba", async function() {
let users = await getUsers(['iliakan', 'remy', 'nieistniejący.użytkownik']);
assert.equal(users[0].login, 'iliakan');
assert.equal(users[1].login, 'remy');
assert.equal(users[2], null);
Expand Down
14 changes: 7 additions & 7 deletions 5-network/01-fetch/01-fetch-users/solution.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@

To fetch a user we need: `fetch('https://api.github.com/users/USERNAME')`.
Do pobrania użytkownika wykorzystamy: `fetch('https://api.github.com/users/USERNAME')`.

If the response has status `200`, call `.json()` to read the JS object.
Jeżeli odpowiedź zostanie zwrócona ze statusem `200`, wywołamy metodę `.json()`, aby móc odczytać javascriptowy obiekt.

Otherwise, if a `fetch` fails, or the response has non-200 status, we just return `null` in the resulting arrray.
Jeżeli natomiast `fetch` się nie powiedzie lub status odpowiedzi będzie inny niz 200, wówczas w tablicy wynikowej zwracamy po prostu `null`.

So here's the code:
Kod wygląda następująco:

```js demo
async function getUsers(names) {
Expand Down Expand Up @@ -33,8 +33,8 @@ async function getUsers(names) {
}
```

Please note: `.then` call is attached directly to `fetch`, so that when we have the response, it doesn't wait for other fetches, but starts to read `.json()` immediately.
Zauważ, że metoda `.then` jest dołączona bezpośrednio do `fetch`, więc nie czeka ona na kolejne żądania, lecz jak tylko otrzyma odpowiedź, natychmiast odczytuje ją przy użyciu metody `.json()`.

If we used `await Promise.all(names.map(name => fetch(...)))`, and call `.json()` on the results, then it would wait for all fetches to respond. By adding `.json()` directly to each `fetch`, we ensure that individual fetches start reading data as JSON without waiting for each other.
Gdybyśmy jednak użyli `await Promise.all(names.map(name => fetch(...)))` i wywołali metodę `.json()` dopiero na rezultacie, wówczas musiałaby ona czekać, aż wszystkie żądania zwrócą swoje odpowiedzi. Dołączając `.json()` bezpośrednio do każdego zapytania `fetch` możemy być pewni, że pojedyncze zapytania zaczną odczytywać dane jako JSON, bez czekania nawzajem na siebie.

That's an example of how low-level Promise API can still be useful even if we mainly use `async/await`.
Jest to przykład tego, jak przydatne może być niskopoziomowe Promise API, nawet jeżeli głównie korzystamy z `async/await`.
16 changes: 8 additions & 8 deletions 5-network/01-fetch/01-fetch-users/task.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
# Fetch users from GitHub
# Pobierz użytkowników z GitHuba

Create an async function `getUsers(names)`, that gets an array of GitHub logins, fetches the users from GitHub and returns an array of GitHub users.
Stwórz asynchroniczną funkcję `getUsers(names)`, która otrzymuje tablicę z loginami do GitHuba, a następnie zwraca tablicę z odpowiadającymi im użytkownikami.

The GitHub url with user information for the given `USERNAME` is: `https://api.github.com/users/USERNAME`.
Informacje o użytkowniku przypisanym do `USERNAME`, znajdują się pod adresem: `https://api.github.com/users/USERNAME`.

There's a test example in the sandbox.
W naszym środowisku izolowanym znajduje się przykład testowy.

Important details:
Ważne informacje:

1. There should be one `fetch` request per user.
2. Requests shouldn't wait for each other. So that the data arrives as soon as possible.
3. If any request fails, or if there's no such user, the function should return `null` in the resulting array.
1. Można wykonać tylko jedno żądanie `fetch` o dane użytkownika.
2. Żądania nie powinny na siebie oczekiwać. Chodzi o to, aby dane dotarły jak najszybciej.
3. Jeżeli żądanie się nie powiedzie lub nie będzie użytkownika o podanej nazwie, funkcja powinna zwrócić `null` w tablicy wynikowej.
198 changes: 99 additions & 99 deletions 5-network/01-fetch/article.md

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions 5-network/01-fetch/post.view/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ let router = new Router();

router.post('/user', async (ctx) => {
ctx.body = {
message: "User saved."
message: "Zapisano użytkownika."
};
});

Expand All @@ -17,7 +17,7 @@ router.post('/image', async (ctx) => {
limit: '1mb'
});
ctx.body = {
message: `Image saved, size:${body.length}.`
message: `Zapisano obraz, rozmiar:${body.length}.`
};
});

Expand Down
96 changes: 48 additions & 48 deletions 5-network/02-formdata/article.md
Original file line number Diff line number Diff line change
@@ -1,31 +1,31 @@

# FormData

This chapter is about sending HTML forms: with or without files, with additional fields and so on.
W niniejszym rozdziale omówimy wysyłkę formularzy HTML: z plikami lub bez, z dodatkowymi polami i tak dalej.

[FormData](https://xhr.spec.whatwg.org/#interface-formdata) objects can help with that. As you might have guessed, it's the object to represent HTML form data.
Pomoże nam w tym obiekt [FormData](https://xhr.spec.whatwg.org/#interface-formdata). Jak zapewne się domyślasz, jest to obiekt reprezentujący dane formularza HTML.

The constructor is:
Konstruktor wygląda następująco:
```js
let formData = new FormData([form]);
```

If HTML `form` element is provided, it automatically captures its fields.
Przechwyci on automatycznie wszystkie pola formularza HTML na stronie.

The special thing about `FormData` is that network methods, such as `fetch`, can accept a `FormData` object as a body. It's encoded and sent out with `Content-Type: form/multipart`.
`FormData` posiada tę szczególną cechę, że metody sieciowe takie jak `fetch` mogą przyjmować obiekt `FormData` jako ciało. Jest on wówczas kodowany i wysyłany jako `Content-Type: form/multipart`.

From the server point of view, that looks like a usual form submission.
Z perspektywy serwera wygląda to jak zwykłe przesłanie formularza.

## Sending a simple form
## Wysyłanie prostego formularza

Let's send a simple form first.
Na początek wyślijmy prosty formularz.

As you can see, that's almost one-liner:
Jak widać, to niemal jedna linijka:

```html run autorun
<form id="formElem">
<input type="text" name="name" value="John">
<input type="text" name="surname" value="Smith">
<input type="text" name="name" value="Jan">
<input type="text" name="surname" value="Kowalski">
<input type="submit">
</form>

Expand All @@ -47,48 +47,48 @@ As you can see, that's almost one-liner:
</script>
```

In this example, the server code is not presented, as it's beyound our scope. The server accepts the POST request and replies "User saved".
Kod serwera jest poza naszym zakresem zainteresowania, zatem nie pokazujemy go w tym przykładzie. W każdym razie serwer akceptuje żądanie POST i odpowiada komunikatem: "Użytkownik zapisany".

## FormData Methods
## Metody FormData

We can modify fields in `FormData` with methods:
Pola w `FormData` możemy zmieniać następującymi metodami:

- `formData.append(name, value)` - add a form field with the given `name` and `value`,
- `formData.append(name, blob, fileName)` - add a field as if it were `<input type="file">`, the third argument `fileName` sets file name (not form field name), as it it were a name of the file in user's filesystem,
- `formData.delete(name)` - remove the field with the given `name`,
- `formData.get(name)` - get the value of the field with the given `name`,
- `formData.has(name)` - if there exists a field with the given `name`, returns `true`, otherwise `false`
- `formData.append(name, value)` - dodaj pole formularza o nazwie `name` i wartości `value`,
- `formData.append(name, blob, fileName)` - dodaj pole tak jakby było znacznikiem `<input type="file">`; trzeci argument `fileName` ustawia nazwę pliku (nie nazwę formularza), tak jakby była nazwą pliku w systemie plików użytkownika,
- `formData.delete(name)` - usuń pole `name`,
- `formData.get(name)` - pobierz wartość pola `name`,
- `formData.has(name)` - jeżeli istnieje pole `name`, zwróć `true`; w innym przypadku zwróć `false`

A form is technically allowed to have many fields with the same `name`, so multiple calls to `append` add more same-named fields.
Formularz, z technicznego punktu widzenia, może mieć pól o nazwie `name`, tak więc wiele wywołań metody `append` doda wiele pól o tej samej nazwie.

There's also method `set`, with the same syntax as `append`. The difference is that `.set` removes all fields with the given `name`, and then appends a new field. So it makes sure there's only field with such `name`, the rest is just like `append`:
Istnieje również metoda `set`, która ma taką samą składnię jak `append`. Różnica polega na tym, że `.set` usuwa wszystkie pola o nazwie `name`, a następnie dodaje nowe pole. Dzięki temu zapewnia, że istnieje tylko jedno pole o nazwie `name`. Pozostała część wygląda jak w metodzie `append`:

- `formData.set(name, value)`,
- `formData.set(name, blob, fileName)`.

Also we can iterate over formData fields using `for..of` loop:
Możemy również iterować po polach `formData`, używając pętli `for..of`:

```js run
let formData = new FormData();
formData.append('key1', 'value1');
formData.append('key2', 'value2');

// List key/value pairs
// Wylicz pary klucz/wartość
for(let [name, value] of formData) {
alert(`${name} = ${value}`); // key1=value1, then key2=value2
alert(`${name}=${value}`); // key1=value1 oraz key2=value2
}
```

## Sending a form with a file
## Wysyłanie formularza z plikiem

The form is always sent as `Content-Type: form/multipart`, this encoding allows to send files. So, `<input type="file">` fields are sent also, similar to a usual form submission.
Formularz jest zawsze wysyłany jako `Content-Type: form/multipart`, gdyż takie kodowanie pozwala na wysyłkę plików. Tak więc pola `<input type="file">` są również wysyłane, podobnie jak ma to miejsce w zwykłym przesłaniu formularza.

Here's an example with such form:
Oto przykład takiego formularza:

```html run autorun
<form id="formElem">
<input type="text" name="firstName" value="John">
Picture: <input type="file" name="picture" accept="image/*">
<input type="text" name="firstName" value="Jan">
Obraz: <input type="file" name="picture" accept="image/*">
<input type="submit">
</form>

Expand All @@ -110,21 +110,21 @@ Here's an example with such form:
</script>
```

## Sending a form with Blob data
## Wysyłanie formularza z danymi typu Blob

As we've seen in the chapter <info:fetch>, it's easy to send dynamically generated binary data e.g. an image, as `Blob`. We can supply it directly as `fetch` parameter `body`.
W rozdziale pt. "<info:fetch>" widzieliśmy, że wysyłka dynamicznie generowanych danych binarnych, np. obrazu jako `Blob`, jest dość prosta. Możemy go umieścić jako parametr `body` funkcji `fetch`.

In practice though, it's often convenient to send an image not separately, but as a part of the form, with additional fields, such as "name" and other metadata.
W praktyce jednak często wygodniej jest wysłać obraz nie osobno, ale jako część formularza, z dodatkowymi polami, takimi jak "nazwa” i inne metadane.

Also, servers are usually more suited to accept multipart-encoded forms, rather than raw binary data.
Ponadto serwery są zwykle lepiej przystosowane do akceptowania formularzy zakodowanych w postaci wieloczęściowej (ang. *multipart*) niż surowych danych binarnych.

This example submits an image from `<canvas>`, along with some other fields, as a form, using `FormData`:
W tym przykładzie wysyłamy w formularzu obraz ze znacznika `<canvas>` wraz z innymi polami, używając do tego `FormData`:

```html run autorun height="90"
<body style="margin:0">
<canvas id="canvasElem" width="100" height="80" style="border:1px solid"></canvas>

<input type="button" value="Submit" onclick="submit()">
<input type="button" value="Prześlij" onclick="submit()">

<script>
canvasElem.onmousemove = function(e) {
Expand All @@ -138,7 +138,7 @@ This example submits an image from `<canvas>`, along with some other fields, as

*!*
let formData = new FormData();
formData.append("firstName", "John");
formData.append("firstName", "Jan");
formData.append("image", imageBlob, "image.png");
*/!*

Expand All @@ -154,36 +154,36 @@ This example submits an image from `<canvas>`, along with some other fields, as
</body>
```

Please note how the image `Blob` is added:
Zwróć uwagę, w jaki sposób dodawany jest obraz jako `Blob`:

```js
formData.append("image", imageBlob, "image.png");
formData.append("image", imageBlob, "obraz.png");
```

That's same as if there were `<input type="file" name="image">` in the form, and the visitor submitted a file named `"image.png"` (3rd argument) with the data `imageBlob` (2nd argument) from their filesystem.
To tak, jakby w formularzu był znacznik `<input type="file" name="image">`, a użytkownik załadował z systemu plików plik o nazwie `"obraz.png"` (trzeci argument) jako `imageBlob` (drugi argument).

The server reads form data and the file, as if it were a regular form submission.
Serwer odczytuje dane formularza i plik, tak jakby było to zwykłe przesyłanie formularza.

## Summary
## Podsumowanie

[FormData](https://xhr.spec.whatwg.org/#interface-formdata) objects are used to capture HTML form and submit it using `fetch` or another network method.
Obiekty klasy [FormData](https://xhr.spec.whatwg.org/#interface-formdata) służą do przechwycenia formularza HTML i przesłania go za pomocą `fetch` lub innej funkcji sieciowej.

We can either create `new FormData(form)` from an HTML form, or create a object without a form at all, and then append fields with methods:
Możemy albo utworzyć `new FormData(form)` na podstawie formularza HTML, albo stworzyć obiekt bez formularza, a następnie dołączyć do niego pola metodami:

- `formData.append(name, value)`
- `formData.append(name, blob, fileName)`
- `formData.set(name, value)`
- `formData.set(name, blob, fileName)`

Let's note two peculiarities here:
Zwróćmy uwagę na dwie osobliwości:

1. The `set` method removes fields with the same name, `append` doesn't. That's the only difference between them.
2. To send a file, 3-argument syntax is needed, the last argument is a file name, that normally is taken from user filesystem for `<input type="file">`.
1. Metoda `set` usuwa zduplikowane pola o tej samej nazwie, a `append` nie. To jedynia różnica między nimi.
2. Aby wysłać plik, potrzebna jest trójargumentowa składnia, gdzie ostatnim argumentem jest nazwa pliku, zwykle pobierana z systemu plików na potrzeby `<input type="file">`.

Other methods are:
Inne metody to:

- `formData.delete(name)`
- `formData.get(name)`
- `formData.has(name)`

That's it!
I to by było na tyle!
6 changes: 3 additions & 3 deletions 5-network/02-formdata/post.view/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ let router = new Router();

router.post('/user', async (ctx) => {
ctx.body = {
message: "User saved"
message: "Użytkownik zapisany"
};
});

Expand All @@ -34,7 +34,7 @@ router.post('/image-form', async (ctx) => {
});

ctx.body = {
message: `Image saved, firstName: ${fields.firstName}, Image size:${files[0].length}, fileName: ${files[0].filename}`
message: `Obraz zapisany, imię: ${fields.firstName}, rozmiar obrazu:${files[0].length}, nazwa pliku: ${files[0].filename}.`
};
});

Expand All @@ -61,7 +61,7 @@ router.post('/user-avatar', async (ctx) => {
});

ctx.body = {
message: `User with picture, firstName: ${fields.firstName}, picture size:${files[0].length}`
message: `Użytkownik ze zdjęciem, imię: ${fields.firstName}, rozmiar obrazu:${files[0].length}.`
};
});

Expand Down