【Fuse】Observableとデータバインディング

さて、今回から、実際にUXマークアップを書きながら、それがどんなアプリになるのかを書いていきたいと思います。とはいっても、自分もまだ初心者なので、まずは公式サイトのチュートリアルに沿って進めていきます。

今回のページは[こちら](https://www.fusetools.com/docs/tutorial/edit-hike-view)

###イントロ

複数回のチュートリアルで作るアプリはハイキング情報を表示したり、編集したりするアプリです。情報を表示するビジュアルを表すビューと編集可能なビジュアルを表すビューのふたつから構成されます。

チュートリアルの最終形は[こちら](https://github.com/fusetools/hikr/tree/chapter-1)にアップされています。

今回は[こちら](https://www.fusetools.com/docs/tutorial/edit-hike-view)のページを参考に進めていきます。

```アジェンダ
・プロジェクトを作成して、プレビューする

・Observerを追加してみる

・viewとview modelを分離してみる
```

では早速始めましょう！

###プロジェクトを作る
前にも一度説明した気がしますが、まずはプロジェクトをつくります。

```
$ fuse create app tutorial01
```

実行すると下記のような構成になります。

```
$ ls
MainView.ux		tutorial01.unoproj
```

このふたつのファイルが最低限必要なファイルになります。
.unoprojにはいろいろなプロジェクトの設定を、MainView.uxには、アプリのビューを記述していきます。今回はこのMainView.uxをいじっていきます。


###最初のハイキングを表示しよう

プロジェクトを作ったら、データの表示をやってみましょう。まず、MainView.uxを開きます。

```MainView.ux
<App>
</App>
```

空の`<App>`がすでに挿入されているはずです。注意したいのが、UXマークアップでは、大文字と小文字を区別しますので、`<app>`と書かないようにしましょう。

さて、単純なテキストを追加してみます。

```MainView.ux
<App>
	<Text>Tricky Trails</Text>
</App>
```

保存すると、すぐにプレビューにその変更が反映されていると思います。

![01.png](https://qiita-image-store.s3.amazonaws.com/0/100937/a1982b3a-d13f-714f-171f-28daa55980d6.png)

Fuseではタイポや誤植があると、プレビューが実行されません。なので、こまめに保存して、間違っている箇所がどこなのかを細かく確認するとよいでしょう。

先ほどのテキストはローカルのシミュレータで確認すると問題ないですが、デバイスのステータスバーに少しかぶることがあります。これを防ぐために、`ClientPanel`を追加します。

```MainView.ux
<App>
	<ClientPanel>
		<Text>Tricky Trails</Text>
	</ClientPanel>
</App>
```

`ClientPanel`は画面の上部と下部に、OS特有の領域を確保するためのコンテナです。デフォルトで入れておくといいと思います。

続いて、`StackPanel`を追加します。`StackPanel`は、含まれる子要素を垂直、または水平に並べるためのコンテナです。含まれる子要素が一つの場合はとくに変化がありませんが、これから要素を追加していくので、あらかじめ入れておきます。

```MainView.ux
<App>
	<ClientPanel>
		<StackPanel>
			<Text>Tricky Trails</Text>
		</StackPanel>
	</ClientPanel>
</App>
```

これで準備はバッチリです！

###ビューモデルとデータバインディング

さて、テキストが表示されたところまではいいですが、完全にハードコーディングになってしまっています。ハイキングの情報は編集したいので、表示するテキストの内容は分離させたいところです。そこでJavaScriptの登場です！

ハイキングの名前をエクスポートするJSを追加してみましょう。

```MainView.ux
<App>
	<ClientPanel>
		<JavaScript>
			var _name = "Tricky Trails";

			module.exports = {
				name: _name
			};
		</JavaScript>
		<StackPanel>
			<Text Value="{name}" />
		</StackPanel>
	</ClientPanel>
</App>
```

非常に簡単ですね！`<JavaScript>`を追加して、UXマークアップで使いたい値を`module.exports`するだけで、あとは`{key}`で参照できます。ただ、まだJS -> UXマークアップの一方通行なので、今度は編集できるようにしてみましょう。

###Observablesを追加して、Two-Wayバインディングを実現する

まずは入力用のTextBoxを追加する必要があります。Textboxは基本的なスタイリングがされている１行の入力フィールドです。今回は名前を編集するのに使います。しかし、追加する前に、このTextboxが編集されたらその内容を他のUIに通知する仕組みを先に作りましょう。具体的にはFuseが用意しているObservableを追加して、名前の変数の状態を監視します。Observableを使用するにはまずインポートする必要があります。

```
var Observable = require("FuseJS/Observable");
```

そして、このObservableに名前の変数を監視させるよう設定します。

```
var name = Observable("Tricky Trails");
```

変数nameに、テキストをそのまま入れる代わりに、Observableを代入します。これだけで、変更を監視し、その変更をUIに伝えるところまで、必要な処理を組んでくれます。

では実際にTextboxを追加して、変更してみましょう！

```MainView.ux
<App>
	<ClientPanel>
		<JavaScript>
			var Observable = require("FuseJS/Observable")
			var _name = Observable("Tricky Trails");

			module.exports = {
				name: _name
			};
		</JavaScript>
		<StackPanel>
			<Text Value="{name}" />

            <TextBox Value="{name}" />
		</StackPanel>
	</ClientPanel>
</App>
```

プレビューを見てみると、Observableに設定されたテキストがただのテキストと、入力用のテキストボックスに指定されているのがわかります。そして、下のテキストボックスを編集すると、上のテキストの方にも反映されます。これはTextboxのValueプロパティが双方向データバインディングを提供しているからです。

上のテキストはキャプションに使いたいと思います。また、名前の他にも、フィールドも追加しておきます。

```
var _name = Observable("Tricky Trails");
var _location = Observable("Lakebed, Utah");
var _distance = Observable(10.4);
var _rating = Observable(4);
var _comments = Observable("This hike was nice and hike-like. Glad I didn't bring a bike.");

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

UIの方も同じく追加します。

```
<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>
<TextBox Value="{comments}" />
```

よりユーザビリティをあげるために、`InputHint`を追加しています。これはHTMLのinputでいうtypeみたいなもので、それぞれのOSで適切なキーボードを表示するように設定できます。

![スクリーンショット 2016-12-11 1.56.12.png](https://qiita-image-store.s3.amazonaws.com/0/100937/5e5759d2-817a-4d59-ccff-c84fdd50676e.png)

###仕上げ

いくつか最後の仕上げをします。上の画像のコメントの欄を見てみてください。途中で文章が見えなくなっているのがわかります。これではわかりづらいので、複数行になるようにしてみましょう。

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

`TextBox`ではなく、`TextView`を使い、`TextWrapping="Wrap"`を指定します。そうすると、テキストを全て表示するように折り返してくれます。

しかし、今度はコメントがめちゃくちゃ長いと、画面外に出てしまって、見えなくなってしまいます。そこで今度は`ScrollView`を使います。

```
<ScrollView>
	<StackPanel>
		<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>
```

これで、画面全体を上下にスクロールできるようになりました。このチュートリアルの成果物は下記です。

![スクリーンショット 2016-12-11 2.05.15.png](https://qiita-image-store.s3.amazonaws.com/0/100937/aa29c380-9c1e-129b-dc76-fe0afe509595.png)

コード全体は下記です。

```MainView.ux
<App>
	<ClientPanel>
		<JavaScript>
			var Observable = require("FuseJS/Observable")
			var _name = Observable("Tricky Trails");
			var _location = Observable("Lakebed, Utah");
			var _distance = Observable(10.4);
			var _rating = Observable(4);
			var _comments = Observable("This hike was nice and hike-like. Glad I didn't bring a bike.");

			module.exports = {
				name: _name,
				location: _location,
				distance: _distance,
				rating: _rating,
				comments: _comments
			};
		</JavaScript>
		<ScrollView>
			<StackPanel>
				<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の機能たち
- [App](https://www.fusetools.com/docs/fuse/app)
- [Text](https://www.fusetools.com/docs/fuse/controls/text)
- [StackPanel](https://www.fusetools.com/docs/fuse/controls/stackpanel)
- [Textbox](https://www.fusetools.com/docs/fuse/controls/textbox)
- [Observable](https://www.fusetools.com/docs/fusejs/observable)
- [TextView](https://www.fusetools.com/docs/fuse/controls/textview)
- [ScrollView](https://www.fusetools.com/docs/fuse/controls/scrollview)

###明日の内容
明日は[こちら](https://www.fusetools.com/docs/tutorial/multiple-hikes)の内容に沿って進めます。今回は一つのハイキングに関する情報でしたが、複数のハイキングを作ってみましょう。