-
-
Notifications
You must be signed in to change notification settings - Fork 224
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* feat(todoapp): Todoとイベント * feat(todoapp): 入力内容をコンソールに表示する * feat(todoapp): Todoアイテムの追加 * add summary * chore: add title * test(todoapp): add test for event-driven * fix(todoapp): form eventに経こう * fix: test name * fix title * bold * fix * fix link * fix link * fix link * add <!-- doctest:disable --> * fix typo * fix lint
- Loading branch information
Showing
41 changed files
with
676 additions
and
25 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,2 @@ | ||
import { App } from "./src/App"; | ||
import { App } from "./src/App.js"; | ||
const app = new App(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
// MIT © 2018 azu | ||
"use strict"; | ||
/** | ||
* TODOアイテムを追加 | ||
* @param {string} title | ||
* @returns {Cypress.Chainable<JQuery<HTMLElement>>} | ||
*/ | ||
const addNewTodo = (title) => { | ||
cy.get("#js-form-input").type(title); | ||
return cy.get("#js-form").submit(); | ||
}; | ||
|
||
module.exports.addNewTodo = addNewTodo; |
9 changes: 9 additions & 0 deletions
9
source/use-case/todoapp/cypress/integration/app-structure/todo-html/todo-html-spec.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,18 @@ | ||
const URL = "/app-structure/todo-html"; | ||
const visitWithConsole = require("../../../helper/visit-with-console").visitWithConsole; | ||
describe(URL, function() { | ||
it(".todoappにスタイルが適応されている", function() { | ||
cy.visit(URL).then((win) => { | ||
const position = win.getComputedStyle(win.document.querySelector(".todoapp")).position; | ||
expect(position).to.equal("relative"); | ||
}); | ||
}); | ||
it("ロードするとApp.jsのログが表示される", function() { | ||
visitWithConsole(URL).then(({ logSpy }) => { | ||
const log0 = logSpy.getCall(0).args[0]; | ||
const log1 = logSpy.getCall(1).args[0]; | ||
expect(log0).to.equal("App.js: loaded"); | ||
expect(log1).to.equal("App initialized"); | ||
}); | ||
}); | ||
}); |
9 changes: 1 addition & 8 deletions
9
source/use-case/todoapp/cypress/integration/final/final/final-spec.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
24 changes: 24 additions & 0 deletions
24
source/use-case/todoapp/cypress/integration/form-event/add-todo-item/add-todo-item-spec.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
const addNewTodo = require("../../../helper/todo-helper").addNewTodo; | ||
const URL = "/form-event/add-todo-item"; | ||
describe(URL, function() { | ||
it("入力欄を埋めて送信するとTodoアイテム(li)のみが追加される", function() { | ||
cy.visit(URL); | ||
const inputText = "test"; | ||
addNewTodo(inputText).then(() => { | ||
// ulはない | ||
cy.get("#js-todo-list ul").should(items => { | ||
expect(items).to.have.length(0); | ||
}); | ||
// liはある | ||
cy.get("#js-todo-list li").should(items => { | ||
expect(items).to.have.length(1); | ||
}); | ||
}); | ||
addNewTodo(inputText).then(() => { | ||
// liが増える | ||
cy.get("#js-todo-list li").should(items => { | ||
expect(items).to.have.length(2); | ||
}); | ||
}); | ||
}); | ||
}); |
15 changes: 15 additions & 0 deletions
15
source/use-case/todoapp/cypress/integration/form-event/prevent-event/prevent-event-spec.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
const addNewTodo = require("../../../helper/todo-helper").addNewTodo; | ||
const URL = "/form-event/prevent-event"; | ||
const visitWithConsole = require("../../../helper/visit-with-console").visitWithConsole; | ||
describe(URL, function() { | ||
it("入力欄を埋めて送信するとコンソールログに表示される", function() { | ||
visitWithConsole(URL).then(({ logSpy }) => { | ||
const inputText = "test"; | ||
addNewTodo(inputText).then(() => { | ||
const logCalls = logSpy.getCalls(); | ||
const lastLog = logCalls[logCalls.length - 1].args[0]; | ||
expect(lastLog).to.equal(`入力欄の値: ${inputText}`); | ||
}); | ||
}); | ||
}); | ||
}); |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,190 @@ | ||
--- | ||
author: azu | ||
--- | ||
|
||
# フォームとイベント {#form-event} | ||
|
||
ここからはJavaScriptでTodoアプリの動作を実際に作っていきます。 | ||
|
||
このセクションでは、前のセクションでHTMLに目印を付けたTodoリスト(`#js-todo-list`)に対してTodoアイテムを追加する処理を作っていきます。 | ||
|
||
## Todoアイテムの追加 {#add-todo-item} | ||
|
||
ユーザーが次のような操作を行い、Todoアイテムを追加します。 | ||
|
||
1. 入力欄にTodoアイテムのタイトルを入力する | ||
2. 入力欄でEnterを押し送信する | ||
3. TodoリストにTodoアイテムが追加される | ||
|
||
これをJavaScriptで実現するには次のことが必要です。 | ||
|
||
- form要素から送信(`submit`)されたことをイベントで受け取る | ||
- input要素(入力欄)に入力された内容を取得する | ||
- 入力内容をタイトルにしたTodoアイテムを作成し、Todoリスト(`#js-todo-list`)にTodoアイテム要素を追加する | ||
|
||
まずは、form要素から送信されたイベントを受け取り、入力内容をコンソールログに表示してみることから始めてみましょう。 | ||
|
||
## 入力内容をコンソールに表示 {#input-to-console} | ||
|
||
form要素でEnterを押し送信すると`submit`イベントが発火されます。 | ||
この`submit`イベントは`addEventListener`メソッドを利用することで受け取れます。 | ||
|
||
<!-- doctest:disable --> | ||
```js | ||
// id="js-form`の要素を取得 | ||
const formElement = document.querySelector("#js-form"); | ||
// form要素から発火されたsubmitイベントを受け取る | ||
formElement.addEventListener("submit", (event) => { | ||
// イベントが発火された時に呼ばれるコールバック関数 | ||
}); | ||
``` | ||
|
||
フォームが送信されたときに入力内容をコンソールに表示するには、 | ||
`addEventListener`コールバック関数内で入力内容をConsole APIで出力すればよいことになります。 | ||
|
||
入力内容はinput要素の`value`プロパティから取得できます。 | ||
|
||
<!-- doctest:disable --> | ||
```js | ||
const inputElement = document.querySelector("#js-form-input"); | ||
console.log(inputElement.value); // => "input要素の入力内容" | ||
``` | ||
|
||
これらを組み合わせて`App.js`に「入力内容をコンソールに表示」する機能を実装してみましょう。 | ||
`App`クラスに`mount`というメソッドを定義して、その中に処理を書いていきましょう。 | ||
|
||
次のようにフォーム(`#js-form`)をEnteで送信すると、input要素(`#js-form-input`)に書かれた内容が開発者ツールのコンソールに表示するという実装を行います。 | ||
|
||
[import, title:"src/App.js"](./prevent-event/src/App.js) | ||
|
||
このままでは、`App#mount`は呼び出されないため何も行われません。 | ||
そんのため、`index.js`も変更して、`App`クラスの`mount`メソッドを呼び出すようにします。 | ||
|
||
[import, title:"index.js"](./prevent-event/index.js) | ||
|
||
これらの変更後にブラウザでページをリロードすると、`App#mount`が実行されるようになります。 | ||
`submit`イベントが監視されているので、入力欄に何か入力してEnterで送信してみるとその内容がコンソールに表示されます。 | ||
|
||
![入力内容がコンソールに表示される](./img/event-driven.png) | ||
|
||
先ほどの`App#mount`では、発火された`submit`イベントのコールバック関数内で`event.preventDefault();`を呼び出しています。 | ||
`preventDefault`メソッドは`submit`イベントが発火されたフォーム本来の動作をキャンセルするメソッドです。 | ||
フォーム本来の処理とは、フォームの内容を指定したURLへ送信するという動作です。 | ||
ここでは`form`要素に送信先が指定されていないため、現在のURLに対してフォームを送信が行われるのをキャンセルしています。 | ||
|
||
<!-- doctest:disable --> | ||
```js | ||
formElement.addEventListener("submit", (event) => { | ||
// submitイベントの本来の動作を止める | ||
event.preventDefault(); | ||
console.log(`入力欄の値: ${inputElement.value}`); | ||
}); | ||
``` | ||
|
||
<!-- textlint-disable no-js-function-paren --> | ||
|
||
現在のURLに対してフォームを送信が行われると、結果的にページがリロードされてしまうため、`event.preventDefault()`を呼び出していました。 | ||
これは`event.preventDefault()`をコメントアウトすると、ページがリロードされてしまうことが確認できます。 | ||
|
||
<!-- textlint-enable no-js-function-paren --> | ||
|
||
<!-- doctest:disable --> | ||
```js | ||
formElement.addEventListener("submit", (event) => { | ||
// preventDefaultしないとページがリロードされてしまう | ||
// event.preventDefault(); | ||
console.log(`入力欄の値: ${inputElement.value}`); | ||
}); | ||
``` | ||
|
||
ここまでで`todoapp`ディレクトリは次のような変更を加えました。 | ||
|
||
``` | ||
todoapp | ||
├── index.html | ||
├── index.js (App#mountの呼び出し) | ||
├── package.json | ||
└── src | ||
└── App.js (App#mountの実装) | ||
``` | ||
|
||
|
||
ここまでのTodoアプリは次のURLで実際に確認できます。 | ||
|
||
<a href="./prevent-event//" target="_blank">https://asciidwango.github.io/js-primer/use-case/todoapp/form-event/prevent-event/</a> | ||
|
||
## 入力内容をTodoリストに表示 {#input-to-todolist} | ||
|
||
フォーム送信時に入力内容を取得する方法が分かったので、次はその入力内容をTodoリスト(`#js-todo-list`)に表示します。 | ||
|
||
HTMLではリストのアイテムを記述する際には`<li>`タグを使います。 | ||
また後ほどTodoリストに表示するTodoアイテムの要素には、完了状態を表すチェックボックスや削除ボタンなども含めたいです。 | ||
これらの要素を含むものを手続き的にDOM APIで作成すると見通しが悪くなるため、HTML文字列からHTML要素を生成するユーティリティモジュールを作成しましょう。 | ||
|
||
次の`html-util.js`を`src/view/html-util.js`というパスに作成します。 | ||
|
||
この`html-util.js`は「[ajaxapp: HTML文字列をDOMに追加する][]」でも利用した`escapeSpecialChars`をベースにしています。 | ||
ajaxappでの`escapeHTML`タグ関数では出力は**HTML文字列**でしたが、今回作成する`element`タグ関数の出力は**HTML要素**(Element)です。 | ||
|
||
これはTodoリスト(`#js-todo-list`)というすでに存在する要素に対して要素を**追加**するには、HTML文字列ではなく要素が必要になります。 | ||
また、HTML文字列に対しては`addEventListener`でイベントを監視するということはできません。 | ||
そのため、チェックボックスの状態が変わったことや削除ボタンが押されたことを知る必要があるTodoアプリでは要素が必要になります。 | ||
|
||
[import, title:"src/view/html-util.js"](./add-todo-item/src/view/html-util.js) | ||
|
||
`element`タグ関数では、同じファイルに定義した`htmlToElement`関数を使ってHTML文字列からHTML要素を作成しています。 | ||
`htmlToElement`関数の中で利用している[template要素][]はHTML5で追加された、HTML文字列の断片からHTML要素を作成できる要素です。 | ||
|
||
この`element`タグ関数を使うことで、次のようにHTML文字列からHTML要素を作成できます。 | ||
作成した要素は、`appendChild`メソッドなどで既存の要素に子要素として追加できます。 | ||
|
||
<!-- doctest:disable --> | ||
```js | ||
// HTML文字列からHTML要素を作成 | ||
const newElement = element`<ul> | ||
<li>新しい要素</li> | ||
</ul>`; | ||
// 作成した要素を既存の要素に追加(appendChild)する | ||
document.body.appendChild(newElement); | ||
``` | ||
|
||
次に、この`element`タグ関数を使い、フォームから送信された入力内容をTodoリストに要素として追加してみます。 | ||
|
||
`App.js`から先ほど作成した`html-util.js`の`element`タグ関数を`import`します。 | ||
そして`submit`イベントのハンドラで、Todoアイテムを表現する要素を作成し、Todoリスト(`#js-todo-list`)の子要素として追加(`appendChild`)します。 | ||
|
||
[import, title:"src/App.js"](./add-todo-item/src/App.js) | ||
|
||
これらの変更後にブラウザでページをリロードすると、入力内容を送信するたびにTodoリスト下へTodoアイテムが追加されます。 | ||
|
||
このセクションでの変更点は次のとおりです。 | ||
|
||
``` | ||
todoapp | ||
├── index.html | ||
├── index.js | ||
├── package.json | ||
└── src | ||
├── App.js(Todoアイテムの表示の実装) | ||
└── view | ||
└── html-util.js(追加) | ||
``` | ||
|
||
|
||
現在のTodoアプリは次のURLで実際に確認できます。 | ||
|
||
<a href="./add-todo-item/" target="_blank">https://asciidwango.github.io/js-primer/use-case/todoapp/form-event/add-todo-item/</a> | ||
|
||
## まとめ {#conclusion} | ||
|
||
このセクションではform要素の`submit`イベントを監視し、入力内容を元にTodoアイテムをTodoリストの追加を実装しました。 | ||
今回のTodoアイテムの追加のように多くのウェブアプリは、何らかのイベントが発生うぃ、そのイベントを監視してJavaScriptで処理し表示を更新します。 | ||
このようなイベントが発生したことを元に処理を進める方法を**イベント駆動**(イベントドリブン)と呼びます。 | ||
|
||
今回のTodoアイテムの追加では、`submit`イベントを入力にして、**直接**Todoリスト要素の内容を更新という出力をしていました。 | ||
このように直接DOMを更新するという方法はコードが短くなりますが、柔軟性がなくなるという問題があります。 | ||
|
||
次のセクションではこの問題点を解消するために、今回扱ったイベントの仕組みをより深く見ていきます。 | ||
|
||
[ajaxapp: HTML文字列をDOMに追加する]: ../../ajaxapp/display/README.md#html-to-dom | ||
[template要素]: https://developer.mozilla.org/ja/docs/Web/HTML/Element/template |
18 changes: 18 additions & 0 deletions
18
source/use-case/todoapp/form-event/add-todo-item/index.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
<html lang="en"> | ||
<head> | ||
<meta charset="UTF-8"> | ||
<title>Todo App</title> | ||
<link href="https://asciidwango.github.io/js-primer/use-case/todoapp/final/final/index.css" rel="stylesheet" /> | ||
</head> | ||
<body> | ||
<div class="todoapp"> | ||
<form id="js-form"> | ||
<input id="js-form-input" class="new-todo" type="text" placeholder="What need to be done?" autocomplete="off"> | ||
</form> | ||
<div id="js-todo-list" class="todo-list"> | ||
<!-- 動的に更新されるTodoリスト --> | ||
</div> | ||
</div> | ||
<script src="./index.js" type="module"></script> | ||
</body> | ||
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
import { App } from "./src/App.js"; | ||
const app = new App(); | ||
app.mount(); |
Oops, something went wrong.