チュートリアル: 天気予報アプリケーションの作成 (Part I)
こんにちは、ゼノフィnakamuraです。
3回にわたるSencha Touchチュートリアルで、worldweatheronline.comのWebサービスから天気情報をロードする簡単なユーティリティアプリケーション “Do I need my Umbrella”(傘は必要ですか)アプリケーションを作成します。天気コードに基づいて、このアプリケーションは傘が必要か必要ではないか予想できます。
このチュートリアルで、アプリケーションのコーディングを初めます。次のチュートリアルはかっこいいSencha Touchテーマの生成とPhoneGapを利用して、アプリケーションをネイティブアプリケーションにポートする方法に集中します。
このチュートリアルに必要なもの:
- Sencha Touch 2.3 以降
- Sencha Cmd 4.x
- モダンブラウザ
- エディタ
ここに追加のリソースがあります:
動作しているアプリケーションは
ここで
ご覧になれます。
最終的なコードは
ここ
からダウンロードできます。
いくつかの美味しいチュートリアルが欲しいでしょ —
コントローラー
と
関数。
デモアプリケーションを生成して、動作する
まずはハードディスクのどこかにsenchaフォルダを生成しましょう。 Sencha Touch 2.3フレームワークをダウンロードして、新しく作成したsenchaフォルダに展開しましょう。さて、アプリケーション用のフォルダを生成しましょう。”DoINeedMyUmbrella”と書くより短いからdinmuという名前をつけましたが、自分が好きなように名付けして下さい。
次にコマンドラインを開いて下さい(MS-Dos PromptまたはMac OS X Terminal)。Senchaフレームワークフォルダに移動して(cd sencha/touch-2.3.x
)、次のコマンドを実行して、Sencha Touch MVCフォルダ構造を生成して下さい:
1 | sencha fs web -p 80 start -map /path/to/sencha/folder/ |
このコマンドはアプリケーション (Do I need my Umbrella) の完全なMVC構造を生成します。Dinmu名前空間を取り込んで、全てのクラスをプレフィックスします。生成されたフォルダ構成を確認して下さい。
さて次は、コマンドラインからWebサーバーを起動しましょう — senchaフォルダへのパスを使用して下さい。(もし自分のApache Webサーバーを優先で利用したかったら、ご自由に起動して、この手順をスキップしても大丈夫です。)Mac OS Xでは、次のコマンドを実行するにはアクセス許可が必要になるかもしれません:もしアクセス許可のエラーが発生したら、上のコマンドをsudoでプレフィックスして下さい。
1 | sencha fs web -p 80 start -map /path/to/sencha/folder/ |
これはビルトインのJetty Webサーバーを起動します。サーバーの動作を続ける為にはCLIウィンドウが開いている必要がありますので、次のコマンドには新しいCLIウィンドウを開くのがいいでしょう。
生成されたSencha Touchのアプリケーションをテストしましょう。モダンブラウザ(例えばGoogle ChromeまたはSafari)を開いて、 http://localhost/dinmu を実行して下さい。下部タブパネルと二つのデモスライド付きのSenchaデモアプリケーションインターフェイスが表示されるはずです:
データパッケージ
次の手順ではデータを定義するモデルを生成します。アプリケーションに保存するべき設定がいくつかあります: id, city, country, units, geolocation このデータ設定をモデルフィールドとして定義します。Sencha Cmdはこのモデルをスキャフォールディングできます。 dinmuフォルダのコマンドラインから次のコマンドを実行して下さい:
1 | sencha generate model Setting id,city,country,units,geo:boolean |
このコマンドがアプリケーションのモデルを生成しました。Settingというクラス名をにして、それぞれのフィールドを定義する為に、全てのフィールド名が含まれている一つの文字列を指定しています。再びフォルダ構造を確認しましょう。
app/model/Setting.jsをエディタで開いて下さい。Dinmu.model.Setting名前空間はapp/model/Setting.jsと同じである事が解りますね。これがSettingモデルの実装となります:Sencha CmdがSettingモデルクラスを定義しました。それはSencha Touchフレームワークのモデル実装 (Ext.data.Model) を拡張し、全てのフィールドとフィールド類が含まれています。
フィールドidはアプリケーション内にあるモデルレコードのidを全て定義します。ユニークなidとして動作するには設定する必要があります。fields配列の前にidPropertyとidentifierを設定して下さい。
1 2 | idProperty: 'id', identifier: 'uuid', |
このユニークIDのロジックはアプリケーションに “import” する必要があるSenchaクラスに含まれています。それにはrequiresを利用できます。ここではExt.data.identifier.Uuidクラスをrequiresします。
1 | requires: ['Ext.data.identifier.Uuid'], |
次の段階はモデルのvalidationsを作成することです。fields配列の後にvalidations配列を生成して下さい。この配列はこのフィールドのデータがあるかどうか検証する為のバリデーションオブジェクトが含まれています:
1 2 3 4 5 6 7 8 9 10 | validations: [{ type: 'presence', field: 'city', message: "Please provide a city." }, { type: 'presence', field: 'country', message: "Please provide a country." } ], |
デバイスにローカルな設定を保存したいので、最後の手順はクライアントプロキシを追加することです。
localstorageを利用します。localstorageプロキシはブラウザのlocalstorageに全てのデータを保存します。validations配列の直後にproxyオブジェクトを定義して下さい:
1 2 3 4 | proxy: { type: 'localstorage', id: 'weathersettings' } |
ビューコンポーネント
Sencha Cmdが生成する標準のtabpanelインターフェイスもいい感じですが、ユーティリティアプリケーションにはふさわしくありません。Do I need my Umbrellaアプリケーションではカルーセルのインターフェイスが必要となります。
IDEまたはテキストエディタでapp/view/Main.jsのコードを開いて下さい。
現在のDinmu.view.Main実装はSencha Touch Ext.tab.Panelクラスを拡張しています。画面の下部にタブを追加する為にtabBarPositionプロパティがあります。
これらは不要なので、tabBarPosition:bottomを削除して、Sencha Touch Carouselクラスから拡張するように、extendをExt.Carouselに変更して下さい。ここからブラウザを開いて、http://localhost/dinmu を実行できます。 生成されたSenchaデモアプリケーションを見えるはずです。tabpanelインターフェイスはカルーセルインターフェイスに変わります。ビューを横にスライドできます。
デフォルトのコンポーネントをさらに削除しましょう。デモビデオは必要ないから、requires配列からExt.Videoを外しましょう。items配列の中身を空にして、そこに別なアイテムを2つ入れましょう。
items配列の最初オブジェクト(デフォルトではコンテナ)にはただhtmlプロパティしかありません。これはSettings Formのプレースホルダーテキストにセットされますので、後でコーディングできます。2つ目のオブジェクトはitemId: ‘mainview’ というプロパティを持っていて、textviewの値にセットされた(スタイリングの為の)clsプロパティがあります。さらに、カルーセルの方向を縦にするために、directionプロパティを追加し、‘vertical’にして下さい。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | Ext.define('Dinmu.view.Main', { extend: 'Ext.Carousel', xtype: 'main', requires: [ 'Ext.TitleBar' ], config: { direction: 'vertical', items: [{ html: 'Settings Form' },{ itemId: 'mainview', cls: 'textview' }] } }); |
ブラウザで見てみますと、このアプリケーションはとても素朴に見えます。上部にtitlebarを下部にtoolbarを追加しましょう。 Settings Formオブジェクトの前に新しいオブジェクトを生成して下さい。 Viewport(画面)にExt.TitleBarの新しいインスタンスを追加するために、このオブジェクトはxtype:’titlebar’を取得します。CSSクラスプロパティ:cls: ‘title’ でタイトルバーにクラス名をセットして下さい。このタイトルバーを画面の上までくっつけるdocked: ‘top’ プロパティをセットして下さい。titleプロパティを使って、タイトルを:Do I need my Umbrella” に変更して下さい:
1 2 3 4 5 6 | { xtype: 'titlebar', cls: 'title', docked: 'top', title: 'Do I need my Umbrella?' }, |
下部のツールバーにも同じことをします。今度はxtypeはtitlebarではなく、toolbarです。clsはfooterにセットします。画面の下にドッキングして下さい。ツールバーはtitleプロパティの代わりに、htmlプロパティが必要です。これをどれかのコピーライト文字列にセットして下さい。より明るいルックアンドフィールを生成する為にuiプロパティにlightをセットします。正しいフレームワーククラスがメモリーにロードされるように、ファイルの頭のrequires配列にExt.Toolbarを追加することを忘れずに。
1 2 3 4 5 6 7 | { xtype: 'toolbar', cls: 'footer', ui: 'light', docked: 'bottom', html: '<span>Powered by &copy; Sencha Touch</span>' }, |
次の手順は上部のタイトルバーにいくつかのボタンを生成することです。
titlebarには2つのボタンが含まれているitems配列が必要です。backボタンは設定画面で表示され、デフォルト画面で表示されるのはsettingsボタンです。xtypeをbuttonにセットする必要はありません、Ext.TitleBarのデフォルト項目はボタンです。backクボタンはデフォルトで非表示で、タイトルバーの中で左揃えにします。settingsボタンには設定の歯車のアイコンを表示します。
次の様なコードになっている事を確認して下さい:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | Ext.define('Dinmu.view.Main', { extend: 'Ext.Carousel', xtype: 'main', requires: [ 'Ext.TitleBar', 'Ext.Toolbar' ], config: { direction: 'vertical', items: [ { xtype: 'titlebar', cls: 'title', docked: 'top', title: 'Do I need my Umbrella?', items: [{ cls: 'back', hidden: true, ui: 'back', action: 'back', align: 'left', text: 'back' }, { iconCls: 'settings', action: 'settings', ui: 'plain', align: 'right' }] }, { html: 'Settings Form' },{ itemId: 'mainview', cls: 'textview' }, { xtype: 'toolbar', cls: 'footer', ui: 'light', docked: 'bottom', html: '<span>Powered by &copy; Sencha Touch</span>' }] } }); |
ブラウザを開いて http://localhost/dinmu を開いて下さい。Ext.TitleBarの右角に歯車のボタンが表示されるはずです。
フォーム Forms
さて、フォームを作り始めます。これも生成しましょう。コマンドラインのdinmuフォルダに移動して、次を実行してSencha Touchのフォームを生成して下さい:
1 | sencha generate form SettingsView geo:toggle,units:select,city,country |
スキャフォールディングされたフォームクラスをレビューしましょう。app/view/SettingsViewを開いて下さい。Dinmu.view.SettingsViewクラスのxtypeはsettingsviewにセットされています。xtypeプロパティを利用してカスタムメイドされたクラスをitems配列に割り当てられます。
ではそれをやりましょう。Dinmu.view.Main (app/view/Main.js)を開いて、コードに設定項目を見つけて下さい。もしxtypeを指定しなかったら、デフォルトで、containerにセットされます。新しいxtype: settingsviewを参照する必要があるので、Main viewコードに xtype: ‘settingsview’ を追加して下さい。プレースホルダーのhtml文字列はもう必要ありません。html: settingsプロパティを削除して下さい。そして、このクラスがメモリーに確実にロードされるようにDinmu.view.SettingsViewをrequires配列に忘れずに追加して下さい。
見た目をよりよくするように、SettingsViewでExt.form.Panelにfieldsetを追加して下さい。このfieldsetは四つの新しいフィールドと送信ボタンが含まれています。fieldsetはformpanelの子アイテムで、自分の子としてはフォームfieldsとbuttonがあります。
configオブジェクトに2つ目のitems配列を生成して下さい(titleプロパティの後で)。2つ目のitems配列を子供として入れ子にして下さい。親のitems配列にはxtypeがfieldsetでtitle: ‘Your location’ とinstructionsの行がある一つのアイテムがあります。
子供のitems配列に全てのフィールドとボタンが含まれている事を確認して下さい。あなたのコードが次のようになっていることを確認して下さい:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | Ext.define('Dinmu.view.SettingsView', { extend: 'Ext.form.Panel', xtype: 'settingsview', config: { items:[{ xtype: 'fieldset', title: 'SettingsView', instructions: 'In case you do not want the app to detect your location you can enter the city and country.', items: [ { name: 'geo', xtype: 'togglefield', label: 'Geo' }, { name: 'units', xtype: 'selectfield', label: 'Units' }, { name: 'city', xtype: 'textfield', label: 'City' }, { name: 'country', xtype: 'textfield', label: 'Country' }, { xtype: 'button', text: 'Submit', ui: 'confirm' } ] }] } }); |
ブラウザを開いて http://localhost/dinmu を実行して下さい。titleやinstructionsがある設定フォームが見えるはずです。 しかしunits selectフィールドに何か奇妙な部分があります。値がありません。
selectfieldにいくつかの値を追加しましょう。
二つのオブジェクトがあるoptions配列を作ります。1つは text: ‘Fahrenheit’ とvalue: ‘f’で、もう一つはtext: ‘Celsius’とvalue: ‘c’です。
togglefieldのラベルGEOは余りよくありません。これをlabel: ‘Auto detect?’ に変更して下さい。このラベルのテキストはよりスペースを使うため、labelWidthを55%にセットします。デフォルトでgeolocationを有効にさせる為にGeoフィールドの値をvalue: ‘1’ にセットして下さい。
units、city、countryフィールドに disabled: trueを追加して、それらを無効にして下さい。
ボタンのテキストをSubmitではなくRefreshに変更しましょう。これはボタンコンポーネントで変更して下さい。marginの値を’10 5′に設定して追加して下さい。さらに、ボタンにactionプロパティを追加して、値を‘refresh’にセットして下さい。こうすれば、後でボタンを参照できます:
1 2 3 4 5 6 7 | { xtype: 'button', text: 'Refresh', action: 'refresh', margin: '10 5' ui: 'confirm' } |
コンソールが警告を出力していることにお気づきかもしれません。Ext.LoaderはすべてのSencha Touchフレームワーククラスをメモリーに正確な順番でロードするメカニズムで、フォームフィールドで利用されているクラスをロードする必要があります。configオブジェクトの上にrequires配列を生成して、次の文字列を割り当てて下さい:
1 2 3 4 5 6 7 | requires: [ 'Ext.form.FieldSet', 'Ext.field.Toggle', 'Ext.field.Select', 'Ext.field.Text', 'Ext.Button' ], |
インターフェイスが完成しました。
次が完成したsettingsviewのコードです:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 | Ext.define('Dinmu.view.SettingsView', { extend: 'Ext.form.Panel', xtype: 'settingsview', requires: [ 'Ext.form.FieldSet', 'Ext.field.Toggle', 'Ext.field.Select', 'Ext.field.Text', 'Ext.Button' ], config: { items:[{ xtype: 'fieldset', title: 'SettingsView', instructions: 'In case you do not want the app to detect your location you can enter the city and country.', items: [ { name: 'geo', xtype: 'togglefield', label: 'Auto Detect?', labelWidth: '55%', value: '1' }, { name: 'units', xtype: 'selectfield', options: [ { text: 'Fahrenheit', value: 'f' }, { text: 'Celsius', value: 'c' }], label: 'Units', disabled: true }, { name: 'city', xtype: 'textfield', label: 'City', disabled: true }, { name: 'country', xtype: 'textfield', label: 'Country', disabled: true }, { xtype: 'button', text: 'Refresh', action: 'refresh', margin: '10 5', ui: 'confirm' } ] }] } }); |
でもちょっとまって、メインビューはどうなったの? これからメインビューにデータとページを動的に挿入します。そのため、これをできるようにするには少しロジックが必要となります。まずはコントローラーの生成から始めましょう。
コントローラーの構築
controllerは設定model(アプリケーションのデータ)と設定viewを結ぶものとなります。すべてのビューコンポーネントへの参照が含まれていて、そのイベントを発送します。dinmuフォルダに移動して、コマンドラインから次のコマンドを実行して下さい:
1 | sencha generate controller Main |
このコマンドはMainコントローラーを生成します。エディタでapp/controller/Main.jsを開いて下さい。空の参照オブジェクト(refs)と空のcontrolオブジェクトを持つコントローラーがあります。
次に、すべてのビューコンポーネント (main, settingsview)、titlebarの設定 と backボタン、form fields and refreshボタンにrefsを生成しましょう。セレクターはCSSに似ています。あなたのコードは次のようになっているでしょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 | refs: { mainView: 'main', settingsView: 'settingsview', btnSettings: 'main button[action=settings]', btnRefresh: 'settingsview button[action=refresh]', btnBack: 'main button[action=back]', toggleGeo: 'settingsview togglefield', fieldCity: 'settingsview textfield[name=city]', fieldCountry: 'settingsview textfield[name=country]', fieldUnits: 'settingsview selectfield' }, |
ここまでで、イベントが発生するビューコンポーネントすべてに参照を設定したので、controlを追加できます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | control: { 'btnRefresh': { tap: 'onRefresh' }, 'btnSettings': { tap: 'onSettingsBtnTap' }, 'btnBack': { tap: 'onBackBtnTap' }, 'toggleGeo': { change: 'onToggle' }, 'mainView': { activeitemchange: 'onCarouselChange' } } |
ブラウザでイベントをテストする前に、まずコントローラーをapp.jsと繋げます。これはMVCのエントリーポイントになります。app.jsを開いて、requires配列の真下にcontrollers配列を生成し、文字列 “Main” を入れます。これで、Mainコントローラーをapp/controller/Main.jsファイルにマッピングします。
1 2 3 | controllers: [ 'Main' ], |
次は少しロジックを追加します。Dinmu.controller.Mainに戻って、goodies-tutorialのディレクトリにある関数を追加して下さい。それはcontroller.txtにあります。
launch関数を提供された関数に置き換えてください。
ストアとシングルトンを設定する
ストアはモデルオブジェクトのクライアント側のキャッシュをカプセル化します。ストアはプロキシがあることもありますし、中に含まれているモデルインスタンス(レコード)のソーティング、フィルタリング、グループ化、問い合わせする関数を提供することもあります。
我々のアプリケーションではすべてのユーザー設定を保存するためのストアが必要です。
残念ながら、Sencha Cmdでストアを生成することはできません。その代わりに、Settings.jsという新しいファイルをapp/storeに作りましょう。Dinmu.store.Settingsという新しいクラスを定義しましょう。このクラスはExt.data.Storeクラスから全てのメソッドとプロパティを拡張します。configオブジェクトでは、modelというプロパティを生成して下さい。Settingモデルに接続するはずです。また、Settingsストアは自動的にロードさせます。:
1 2 3 4 5 6 7 8 | Ext.define('Dinmu.store.Settings', { extend: 'Ext.data.Store', requires: ['Dinmu.model.Setting'], config: { model: 'Dinmu.model.Setting', autoLoad: true } }); |
controller/Main.jsを開いて、configオブジェクトにstores配列を生成して、そこにDinmu.store.Settingsを追加して下さい:
1 | stores: 'Dinmu.store.Settings', |
MVCフォルダを利用して、外でビジネスロジックを分離した方がよい場合があります。
appフォルダの中にutilsという新しいフォルダを生成して下さい。その中にFunctions.jsというファイルを生成して、Dinmu.utils.Functionsというクラスを定義して下さい。このクラスではsingletonプロパティをtrueに設定します。これでこのクラスはシングルトンになっていますので、そのインスタンスは一つ以上生成することは出来ませんが、コードのどの部分からでもシングルトンメソッドを実行できます:
1 2 3 4 | Ext.define('Dinmu.utils.Functions', { singleton: true, //singleton methods here }); |
Dinmu.utils.Functionsをapp.jsファイルのrequires配列に追加して下さい。
goodies-tutorialフォルダからfunctions.txtのスニペットを開いて、utils/Functions.jsファイルに全ての関数をコピーして下さい。
このアプリケーションは http://api.worldweatheronline.com/ に天気情報をリクエストするのに必要な関数が含まれています。もし自分のAPI_KEYを要求して利用したかったら、API_KEYプロパティの文字列を変更して、Functions.jsの上で編集できます。これはデバイスから位置情報を要求するやmainviewのテンプレートにデータを挿入するロジックも含まれています。もしこのロジックに興味がありましたら、functions.txtファイルに完全なコメントを書きましたので、そこに説明されています。
ロジックをテストするにはGoogle Chrome Developer Toolsを開いて、Consoleタブに切り替えて、次の行を入力してください:Dinmu.utils.Functions.getWeather(‘London’)。これでロンドンの天気オブジェクトが返ってきて、mainviewに少しの文章を表示するはずです。
やっとDo I need my Umbrellaアプリケーションが完成しました!ブラウザを開いて http://localhost/dinmu を実行して下さい。アプリケーションを改善する為の次の段階はテーマを適用と、製品ビルドを作ることです。これは次のSencha Blogチュートリアルで説明します。
Sencha Certification もしこのチュートリアルが役に立って、Sencha TouchまたはExt JSの技術的なトレーニングを受けたかったら、世界中で行っている オープン形式トレーニング やオンライントレーニングに参加して下さい。トレーニングを受けてSenchaアプリケーションを開発した後、 Sencha Certification であなたのスキルを証明できます。Fast Track Trainingコースに申し込むとSencha Certificationが$200割引になります。