Help us understand the problem. What is going on with this article?

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

More than 3 years have passed since last update.

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

今回のページはこちら

イントロ

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

チュートリアルの最終形はこちらにアップされています。

今回はこちらのページを参考に進めていきます。

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

・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

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は基本的なスタイリングがされている1行の入力フィールドです。今回は名前を編集するのに使います。しかし、追加する前に、この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

仕上げ

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

<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

コード全体は下記です。

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の機能たち

明日の内容

明日はこちらの内容に沿って進めます。今回は一つのハイキングに関する情報でしたが、複数のハイキングを作ってみましょう。

tkskto
インタラクションエンジニア
https://tkskto.me
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした