【Fuse】EachでObservableなButtonをつくる

今回は[こちら](https://www.fusetools.com/docs/tutorial/multiple-hikes)のページに沿って進めていきます。

###イントロ

前回の記事では、静的なテキストの表示など基礎的なところから、入力フィールドを追加して、変更を伝達させるなどを行いました。今回はいくつかのハイキング情報を作成、選択、編集できるようにしてみたいと思います。

このチュートリアルでは、管理が楽になるよう、MainView.uxに全ての情報を保存します。ビューの上部にセレクターを追加して、編集したいハイキングを選択し、前回のビューにこのデータを入力しましょう。

※この段階ではまだ変更は保持されません。モデルのデータをビューに適用するだけですので、起動時にリセットされます。しかし、後の章でそれを解決する予定です。

### ハイキングのリストのデータを作る

ハイキングのリストを表示するには、そのデータが必要なので、アプリのモデルのようなものを構築します。まずは単純な配列でつくってみます。JSに次のデータを追加します。

```

var _hikes = [
    {
        id: 0,
        name: "Tricky Trails",
        location: "Lakebed, Utah",
        distance: 10.4,
        rating: 4,
        comments: "This hike was nice and hike-like. Glad I didn't bring a bike."
    },
    {
        id: 1,
        name: "Mondo Mountains",
        location: "Black Hills, South Dakota",
        distance: 20.86,
        rating: 3,
        comments: "Not the best, but would probably do again. Note to self: don't forget the sandwiches next time."
    },
    {
        id: 2,
        name: "Pesky Peaks",
        location: "Bergenhagen, Norway",
        distance: 8.2,
        rating: 5,
        comments: "Short but SO sweet!!"
    },
    {
        id: 3,
        name: "Rad Rivers",
        location: "Moriyama, Japan",
        distance: 12.3,
        rating: 4,
        comments: "Took my time with this one. Great view!"
    },
    {
        id: 4,
        name: "Dangerous Dirt",
        location: "Cactus, Arizona",
        distance: 19.34,
        rating: 2,
        comments: "Too long, too hot. Also that snakebite wasn't very fun."
    }
];

```

各アイテムが同じフィールドを持っています。IDは一意の識別子です。

※各変数のスコープですが、基本的には同一の`<JavaScript>`でないとデータにはアクセスできないようです。なので、必要なデータは同じ`<JavaScript>`内に含めるように配置してください。

### ハイキングリストを表示しよう

データはできたので、それを表示するビューを作りましょう。まずは作ったデータを`module.exports`に含めるところからです。

```
module.exports = {
	hikes: _hikes,
	name: _name,
	location: _location,
	distance: _distance,
	rating: _rating,
	comments: _comments
}
```

ハイキングを選択できるようにするため、それぞれのボタンを表示します。押されたらそのハイキングの編集ビューが表示されるようにします。データの数に応じてボタンが作られるようにしたいので、下記のようにします。

```MainView.ux
<Each Items="{hikes}">
</Each>
```

`<Each>`はとても強力な機能です。`<Each>`は`items`プロパティで指定されたコレクションを取得し、各アイテムを各タグの中のサブツリーのコピーに渡します。`<Each>`の中に、各アイテムをコピペするような感じです。

ここでは、ハイキングの名前がセットされたボタンを`<Each>`を使って作成します。

```MainView.ux
<Each Items="{hikes}">
    <Button Text="{name}" />
</Each>
```

これを`<StackPanel>`の一番上にセットすれば、各アイテムの`name`がテキストとしてセットされたボタンが完成します。当然、`<Each>`を複数配置すればその分ボタンは生成されます。`<JavaScript>`側では、データを丸ごと公開だけである（nameという属性は公開していない）ことに注目してください。`export`された要素の中に`name`というプロパティが既にありますが、`<Each>`は、`hikes`に自動的にコンテキストを絞り込んでくれます。なので、意図した`name`をバインドしてくれます。

![01.png](https://qiita-image-store.s3.amazonaws.com/0/100937/558739d6-e18c-409d-a146-5799abd9bda1.png)

###ハイキングを選択する

さて、ボタンを作ることができたので、クリックして選択しましょう。現状のビューは編集可能な複数のObservableで構成されています。そこに、現在編集しているハイキングを表すObservableを追加します。デフォルトの値は空にしておきます。

```
var hike = Observable();
```

今度は、今作ったObservableから、他のObservableにデータを渡す必要があります。片方は、ハイキングが行われるたびにObservableなフィールドを追加しないといけないので、もうちょっと綺麗に書きたいところです。

そこで、`map`メソッドを使ってObservableを複製します。

```var name = hike.map(function(x) { return x.name; });```

リアクティブプログラミングに慣れていないときは、少し気になるかもしれませんが、心配しないでください。深く考えず、直感的に見てもらえば大丈夫です。

このコードを分解してみると、まず、`hike`に`map`を実行しています。`map`は、新しいObservableを返します。そのObservableの中に入る各値は、`hike`の中の各値が伝搬されています。つまり、新しく作られた`Observable`は、`hike`の中にある値からマップされ、`hike`の値に基づいています。

また、渡している関数はマッピング関数とよばれ、引数を一つとり、その引数に基づいた値を返します。場合によっては、他のコードに影響を与えてしまう場合もありますが、基本的にはpure関数なので、値が変わることはありません。

簡単にいえば、ハイキングに新しい値が追加されても、逐一Observableを加えなくていいということです！Modelに追加しておけば、自動的に引数として渡されます。map関数は、その渡された引数に基づいて新しい値を返します。この値がObservableの値となります。

Observableについては、[Observable Crash Course](https://www.youtube.com/watch?v=bB9P4mTGtVU)と[リファレンス](https://www.fusetools.com/docs/fusejs/observable)で詳細を調べることができます。

さて、それでは、`name`と同じように、ほかのものもリファクタリングしましょう。

```MainView.ux
var name = hike.map(function(x) { return x.name; });
var location = hike.map(function(x) { return x.location; });
var distance = hike.map(function(x) { return x.distance; });
var rating = hike.map(function(x) { return x.rating; });
var comments = hike.map(function(x) { return x.comments; });
```

最後に、各ボタンと、クリックしたときに、実行する関数を繋げる必要があります。まず、空の関数を作り、`export`に追加します。

```
function chooseHike() {
}

module.exports = {
	hikes: _hikes,
	name: _name,
	location: _location,
	distance: _distance,
	rating: _rating,
	comments: _comments,
	chooseHike: chooseHike
};
```

ボタンとこの関数を紐付けます。

```
<Button Text="{name}" Clicked="{chooseHike}" />
```

これであとは関数の中身を作っていくだけです。Observableの値を指定したいのですが、どうすればよいでしょう？ButtonのClickedに関数をバインドしたので、引数として、`<Each>`でまわしているデータのコンテキストを表すデータフィールドが渡されます。そのデータをとって、渡しましょう。

```
function chooseHike(_item) {
	hike.value = _item.data;
}
```

保存すれば、期待通り動作していると思います。最初は空だった入力フィールドが、ハイキングを選択するとそれぞれのデータで埋められます。

今日の成果はこんな感じです！

![02.gif](https://qiita-image-store.s3.amazonaws.com/0/100937/672a4d7f-f3d9-6d5a-290d-252d7740d56d.gif)

コード全体はこんな感じ

```Mainview.ux
<App>
	<ClientPanel>
		<JavaScript>
			var _hikes = [
			    {
			        id: 0,
			        name: "Tricky Trails",
			        location: "Lakebed, Utah",
			        distance: 10.4,
			        rating: 4,
			        comments: "This hike was nice and hike-like. Glad I didn't bring a bike."
			    },
			    {
			        id: 1,
			        name: "Mondo Mountains",
			        location: "Black Hills, South Dakota",
			        distance: 20.86,
			        rating: 3,
			        comments: "Not the best, but would probably do again. Note to self: don't forget the sandwiches next time."
			    },
			    {
			        id: 2,
			        name: "Pesky Peaks",
			        location: "Bergenhagen, Norway",
			        distance: 8.2,
			        rating: 5,
			        comments: "Short but SO sweet!!"
			    },
			    {
			        id: 3,
			        name: "Rad Rivers",
			        location: "Moriyama, Japan",
			        distance: 12.3,
			        rating: 4,
			        comments: "Took my time with this one. Great view!"
			    },
			    {
			        id: 4,
			        name: "Dangerous Dirt",
			        location: "Cactus, Arizona",
			        distance: 19.34,
			        rating: 2,
			        comments: "Too long, too hot. Also that snakebite wasn't very fun."
			    }
			];

			var Observable = require("FuseJS/Observable")

			var hike = Observable();
			var _name = hike.map(function(x) { return x.name; });
			var _location = hike.map(function(x) { return x.location; });
			var _distance = hike.map(function(x) { return x.distance; });
			var _rating = hike.map(function(x) { return x.rating; });
			var _comments = hike.map(function(x) { return x.comments; });

			function chooseHike(_item) {
				hike.value = _item.data;
			}

			module.exports = {
				hikes: _hikes,
				name: _name,
				location: _location,
				distance: _distance,
				rating: _rating,
				comments: _comments,
				chooseHike: chooseHike
			};
		</JavaScript>
		<ScrollView>
			<StackPanel>
				<Each Items="{hikes}">
				    <Button Text="{name}" Clicked="{chooseHike}" />
				</Each>
				<Text>Name:</Text>
				<TextBox Value="{name}" />

				<Text>Location:</Text>
				<TextBox Value="{location}" />

				<Text>Distance (km):</Text>
				<TextBox Value="{distance}" InputHint="Decimal" />

				<Text>Rating:</Text>
				<TextBox Value="{rating}" InputHint="Integer" />

				<Text>Comments:</Text>
				<TextView Value="{comments}" TextWrapping="Wrap" />
			</StackPanel>
		</ScrollView>
	</ClientPanel>
</App>
```

###今回登場したFuseの機能たち
- [Each](https://www.fusetools.com/docs/fuse/reactive/each)
- [Button](https://www.fusetools.com/docs/fuse/controls/button)

###明日は

各ビューを完全に違う画面に分離して、コンポーネントっぽくしたいと思います！