準備
npm create rescript-app@latest
src/SousekiForm.res
module Document = {
@send external getElementById: (Dom.document, string) => Dom.element = "getElementById"
@send external getElementsByName: (Dom.document, string) => Dom.nodeList = "getElementsByName"
@get external getBody: Dom.document => Dom.element = "body"
}
module Element = {
@send external addEventListener: (Dom.element, string, () => unit, bool) => unit = "addEventListener"
@get external getDataset: Dom.element => Dom.domStringMap = "dataset"
@get external getValue: Dom.element => string = "value"
@get external getChecked: Dom.element => bool = "checked"
@set external setInnerHTML: (Dom.element, string) => unit = "innerHTML"
}
module NodeList = {
@get external getLength: Dom.nodeList => int = "length"
@send external item: (Dom.nodeList, int) => Dom.element = "item"
}
module DomStringMap = {
@get external getCorrectChoice: Dom.domStringMap => string = "correctChoice"
}
// 目的:設問部のinputのNodeをReScriptのlistに格納する
// getListedNodeList : Dom.nodeList -> Dom.element list
let getListedNodeList = nodeList => {
let length = nodeList->NodeList.getLength
// 目的: nodeList->item(0)からnodeList->item(i)までのDom.elementをReScriptのlistに格納する
// loop : int -> Dom.element list
let rec loop = i =>
if i < 0 {list{}}
else {list{nodeList->NodeList.item(i), ...loop(i - 1)}}
loop(length - 1)
}
// 目的: 指定された選択肢のvalueを返す
// getAnswer : (Dom.element -> bool) -> Dom.element list -> string
let rec getAnswer = (lst, f) => switch lst {
| list{} => ""
| list{first, ...rest} => if f(first) {first->Element.getValue} else {getAnswer(rest, f)}
}
// 目的: ボタンがクリックされたときの処理
// onBtnClick : unit -> unit
let onBtnClick = () => {
let listedNodeList = document->Document.getElementsByName("Q1")->getListedNodeList
let correctAnswer = listedNodeList->getAnswer(e => e->Element.getDataset->DomStringMap.getCorrectChoice == "true")
let selectedAnswer = listedNodeList->getAnswer(Element.getChecked)
let judgement = correctAnswer == selectedAnswer ? "正解です!" : "不正解です。"
let message = "正解は" ++ correctAnswer ++ "。あなたの回答は" ++ selectedAnswer ++ "でした。"
document->Document.getBody->Element.setInnerHTML(judgement ++ "<br>" ++ message)
}
document->Document.getElementById("btn")->Element.addEventListener("click", onBtnClick, false)
src/souseki_form.html
<!doctype HTML>
<html lang="ja">
<head>
<title>漱石先生クイズ</title>
</head>
<body>
<form>
<dl>
<dt>Q1. 漱石先生のデビュー作は?</dt>
<dd><p>漱石先生のデビュー作は次のうちどれでしょう?</p>
<p><input type="radio" name="Q1" value="坊っちゃん" checked>坊っちゃん<br>
<input type="radio" name="Q1" value="吾輩は猫である" data-correct-choice="true">吾輩は猫である<br>
<input type="radio" name="Q1" value="明暗">明暗</p>
</dd>
</dl>
<button id="btn" type="button">答え合わせ</button>
</form>
<script type="module" src="SousekiForm.res.js"></script>
</body>
</html>
parcel-test/
├── rescript.json
├── package.json
└── src/
├── SousekiForm.res
└── souseki_form.html
npm run res:build
Live Serverなどのローカルのサーバで試すと、以下のWebページが表示されます。
See the Pen 漱石先生クイズ #3 by sgigagaeru (@sgigagaeru) on CodePen.
改修して動作確認
SousekiForm.res
のgetAnswer
を再帰を使って書いてます。
let rec getAnswer = (lst, f) => switch lst {
| list{} => ""
| list{first, ...rest} => if f(first) {first->Element.getValue} else {getAnswer(rest, f)}
}
find
を使う方が良いかと思ったので修正します。
let getAnswer = (lst, f) => switch List.find(lst, f) {
| None => ""
| Some(e) => e->Element.getValue
}
ローカルサーバにアクセスしてページの動作を確認すると、動作しません。
Uncaught TypeError: Failed to resolve module specifier "@rescript/core/src/Core__List.res.js". Relative references must start with either "/", "./", or "../".
モジュールが見つけられないようです。
Parcel
でバンドル
バンドルして一つのファイルにしてしまえば良いのではと思ったので、parcel
でバインドします。
以下のコマンドをプロジェクトのトップディレクトリ(rescript.json
、package.json
等が置かれているディレクトリ)で実行します。
npm install --save-dev parcel
npx parcel build src/SousekiForm.res.js
dist
ディレクトリが作成され、SousekiForm.res.js
、SousekiForm.res.js.map
が置かれました。
parcel-test/
├── rescript.json
├── package.json
├── src/
│ ├── SousekiForm.res
│ └── souseki_form.html
└── dist/
├── SousekiForm.res.js
└── SousekiForm.res.js.map
確認
dist
配下のSousekiForm.res.js
を読み込むようにsouseki_form.html
のscript
タグを修正します。
<script type="module" src="../dist/SousekiForm.res.js"></script>
ブラウザで確認すると動きます。
バンドラが作成したJavaScriptファイルはモジュール形式ではないようなので、読み込みからtype="module"
を外してみます。
<script src="../dist/SousekiForm.res.js"></script>
これでも動きました。1
-
type="module"
があってもなくても動くのは不思議です。 ↩