ログインシステムのビルド

最も頻繁に質問に上がるチュートリアルは、ログインシステムの作り方に関するものです。このチュートリアルは、それを作成することから始めます。これは、ログインシステムを作成する方法の 1 つに過ぎないということに注意してください。方法は、他にもたくさんあります。ここでは、ログイン/ログアウトの機能を搭載したアプリケーションの作成に対するアプローチのひとつを紹介することを目的としています。

免責事項:このチュートリアルでは、「ログイン」および「ログアウト」ビューを備えたアプリケーションを設計するひとつの方法をユーザーに示すことを目的としています。このチュートリアルでは安全なデータ接続、セッション管理、正確な認証などは行いません。純粋に、学習目的で作成されています。

ステップ 1 - アプリケーションの生成

最初に行うのは、Sencha Cmd を使用したアプリケーションの作成です。コマンドラインインターフェイスまたは CLI で次のコマンドを入力すると、これを行えます。

sencha -sdk extjs/5.0.1 generate app TutorialApp ./TutorialApp

生成される TutorialApp は完全に機能する Cmd 生成アプリケーションであり、これをログイン/ログアウトアプリケーションの基礎として使用します。このアプリケーションは、使用しているウェブサーバーにあるアプリケーションの置き場所に移動すると、見ることができます。次はその例です。

`http://localhost/TutorialApp`

または、新しく生成した TutorialApp フォルダから「sencha app watch」を起動します。これにより Jetty サーバーが動作し、従来のウェブサーバーを使用する必要がなくなります。「sencha app watch」を使用するのであれば、このアプリケーションは次の場所にあります。

`http://localhost:1841/`

注意:Sencha Cmd をお持ちでないか、使用方法がよくわからない場合は、実行前に Cmd Introduction guide をお読みください。

ステップ 2 - ログインビューコンポーネントの作成

次に、新たに生成した TutorialApp に移動します。生成したアプリケーションの「app/view」フォルダにドリルダウンします。デフォルトで作成された「main」フォルダがあるはずです。このフォルダには Main.jsMainController.jsMainModel.js があります。

では、ログイン機能の作成プロジェクトを進めましょう。「login」という名前に新しい app/view フォルダを作成します。「login」フォルダを作成したら、次のファイルをこのフォルダに追加します。

- Login.js
- LoginController.js

ファイル構造は次のようになります。

ステップ 3 - autoCreateViewport の無効化

Ext.applicationautoCreateViewport コンフィグは、 Viewport Plugin を利用することによって「TutorialApp.view.main.Main」を自動的にロードしてインスタンス化する非常に便利な方法です。ただし、初期画面を選択する前に Ext.application の起動関数で行うことが必要な評価がいくつかあります。たとえば、ユーザーがログインしていない場合には、メインビューを作成したくありません。

この目的を達成するため、autoCreateViewport コンフィグを app.jsExt.application コンフィグから削除しましょう。app.js はプロジェクトのルートにあります。

注意:autoCreateviewport を削除したので、インスタンス化されているクラスがない状態となり、アプリケーションを更新すると空白のページが表示されます。

ステップ 4 - ログインウィンドウの作成

次に、作成したログインビューを作成してみましょう。では、空の Login.js ファイルを開き、ログインウィンドウの定義を開始します。

まず、クラスを定義し、基底の Window クラスを拡張します。次のようになります。

Ext.define("TutorialApp.view.login.Login",{
    extend: 'Ext.window.Window'
    xtype: 'login'
});

ログインクラスを Ext.Window の拡張として定義しました。これには、login. という名前でアクセスできます。では、クラスに一意のプロパティを設定していきましょう。最初に、ウィンドウ自体に設定をいくつか追加します。

Ext.define("TutorialApp.view.login.Login",{
    extend: 'Ext.window.Window'
    xtype: 'login'

    requires: [
        'TutorialApp.view.login.LoginController',
        'Ext.form.Panel'
    ],

    controller: 'login',
    bodyPadding: 10,
    title: 'Login Window',
    closable: false,
    autoShow: true
});

その設定について、順を追って説明していきます。

requires

requires ブロックは、このクラスをインスタンス化する前に依存する可能性のあるクラスを確実に含めるようにするものです。ここでは、次の行でコントローラに指定する LoginController.js を含めることが必要です。また、ビューにはフォームパネルが含まれているため、Ext.form.Panel を含めることも必要です。

controller

controller コンフィグ では、ビューの特定のインスタンスに添付する viewController を指定します。このコントローラでは、このビューまたはその子コンポーネントに関連するすべてのロジックを含める場所を提供します。ここでは、コントローラを login として指定し、これが使用するコントローラのエイリアスとなります。

bodyPadding

bodyPadding コンフィグは純粋に見た目を整えるためのものです。このコンフィグでは、ウィンドウ本体のコンテンツの外周に「10px」のパディングを適用します。

title

内容を正確に表す値を title コンフィグに指定することで、ヘッダが作成され、値がタイトルとして追加されます。

closable

Closable では、ウィンドウを閉じることができるかどうか決定します。ウィンドウにはデフォルトで閉じるボタンが付けられます。しかし、これはログインウィンドウであるため、ユーザーに閉じられては困ります。閉じられてしまうと、空のページが表示されるだけとなってしまいます。

autoShow

ウィンドウは、作ればデフォルトで表示されるというものではありません。autoShow を true と設定することが、これ以上なにもしなくてもウィンドウを表示できる便利な方法です。

ウィンドウの設定について説明したところで、次に子コンポーネントを付与してみましょう。ログインフォームなので、フォームアイテムをウィンドウの子として作成します。続いて、フォームフィールドを 2 つ、テキストフィールド、送信ボタンを追加します。

このファイルの最後のコードは次のようになります。

Ext.define("TutorialApp.view.login.Login",{
    extend: 'Ext.window.Window',
        xtype: 'login',

    requires: [
        'TutorialApp.view.login.LoginController',
        'Ext.form.Panel'
    ],

    controller: 'login',
    bodyPadding: 10,
    title: 'Login Window',
    closable: false,
    autoShow: true,

    items: {
        xtype: 'form',
        reference: 'form',
        items: [{
            xtype: 'textfield',
            name: 'username',
            fieldLabel: 'Username',
            allowBlank: false
        }, {
            xtype: 'textfield',
            name: 'password',
            inputType: 'password',
            fieldLabel: 'Password',
            allowBlank: false
        }, {
            xtype: 'displayfield',
            hideEmptyLabel: false,
            value: 'Enter any non-blank password'
        }],
        buttons: [{
            text: 'Login',
            formBind: true,
            listeners: {
                click: 'onLoginClick'
            }
        }]
    }
});

では、この子について説明していきます。

items

まず目に留まる設定は、items 配列です。コンテナでは、フォームパネル同様、この配列にコンポーネントまたはコンポーネントの設定オブジェクトが格納されます。これらのコンポーネントはコンテナレイアウトを使用してコンテナの本体に表示されます。

xtype

それぞれのコンポーネントクラスには独自の xtype があります。xtype は、コンポーネントのインスタンスを簡単に作成するためのショートカットであると考えて差し支えありません。この場合、‘form’ という xtype、正確に言うと Ext.form.Panel を設定しています。FormPanel は特殊なタイプのパネルであり、フィールドに対する設定や作業に対する利便性を備えています。

フォームアイテム

次に、馴染み深い別のアイテム配列があります。ここでは、アイテム配列を使用して追加のアイテムを 1 レベル深くネストします。親コンポーネントである FormPanel の内部にはより多くのコンポーネントを配置します。ここでは、ネストしたコンポーネントがログインフォームを形成するフォームフィールドです。

このコンポーネントの配列はとてもわかりやすいものであるため、簡単に説明するに留めます。最初のアイテムには Ext.form.field.Text textfield の xtype、username名前usernameフィールドラベルfalse と設定された allowBlank があります。これが、空白にしておくことができない名前の値とフィールドラベルを持つテキストフィールドに要約されます。

次のフィールドは、パスワードを設定するタイプを除き、ほぼ同じです。これにより、入力値がセキュリティ上の理由から「*」に置き換えられます。

このアイテム配列の最後のメンバーは 表示フィールド です。表示フィールドはテキストフィールドですが、フォームと共に送信されることはありません。これは、ユーザーの関与なしで対象のデータを移送できるため、便利です。ここでは、空白以外のパスワードは送信される可能性があることをユーザーに警告します。

ボタン

最後にボタンの配列について触れます。これは、パネルにボタンを追加するのに役立つコンフィグです。この特定のボタンには「Login」というテキストが含まれます。

formBind

使用するボタンには formBind というコンフィグがあり、true と設定されています。true と設定されている formBind があるコンポーネントは、フォームの有効性に応じて有効/無効が切り替わります。つまり、このボタンは 2 つの入力フィールドに値が入らない限り、クリックできる状態になりません。

リスナ

リスナオブジェクト は、初期化時にコンポーネントに追加されるイベントハンドラを格納するためのものです。ここでは、誰かがボタンをクリックするのを待機します。クリックされると、onLoginClick() という関数にイベントを「転送」します。onLoginClick() はあとでログインコントローラで定義します。

注意:まだログインビューを呼び出していないため、アプリケーションを更新しても変化は見られません。

ステップ 5 - ログインロジックの追加

次に、ログインコントローラを作成しましょう。これは、ログインビューに対するユーザーの操作を扱うロジックを格納しているクラスです。では、空の LoginController.js ファイルを開き、ログインウィンドウのロジック定義を開始します。

LoginController.js の全容は次のとおりです。

Ext.define('TutorialApp.view.login.LoginController', {
    extend: 'Ext.app.ViewController',
    alias: 'controller.login',

    onLoginClick: function(){        

        // This would be the ideal location to verify the user's credentials via 
        // a server-side lookup. We'll just move forward for the sake of this example.

        // Set the localStorage value to true
        localStorage.setItem("TutorialLoggedIn", true);

        // Remove Login Window
        this.getView().destroy();

        // Add the main view to the viewport
        Ext.widget('app-main');
    }
});

上記のコードはややコンテキスト合っていないように思えますが、次のセクションで起動関数に関する説明を読むと、腑に落ちると思います。このクラスには、ログインボタンがクリックされると呼び出される onLoginClick() 関数が含まれています。

コードのこの部分には各ステートメントの目的が注記されていますが、さらに詳細に知るために、順を追って見ていきましょう。

onLoginClick()

まず、onLoginClick() という関数を作成します。これは、ログインビューにおけるログインボタンのクリックイベントを転送する関数です。

コメントにもあるように、ここでサーバーを呼び出してユーザーの認証情報が有効かどうかを確認します。通常は、AJAX または REST リクエストの形で入ってきます。ただしこのチュートリアルでは、空白以外ならすべての入力値を受け入れます。成功したら、残りのコード実行します。失敗したら、ユーザーに認証情報の再入力を許可します。ここでは失敗する可能性はないため、先に進みます。

localStorage

このチュートリアルでは localStorage を利用してユーザーのログイン状態を維持します。認証確認に成功すると、ユーザーがメインビューに対する適切なアクセス権を持っているかどうかを決定できます。この使用可否決定のため、localStorage にキーと値のペアを設定してアプリケーションにユーザーの有効性を伝達できます。次に、TutorialLoggedIn という localStorage キーが true と設定されているかどうかを、初期起動する関数で確認します(次のセクション)。

getView()

ViewControllers では、getView() という非常に便利なメソッドを導入しました。getView() は、呼び出し元である viewController に関連付けられている現在のビューを返します。この場合、このビューがログインウィンドウです。もうログインウィンドウは表示しませんので、this.getView().destroy() を使用してログインウィンドウの参照を取得し、破棄します。

Ext.widget(‘app-main’)

ログインウィンドウを破棄したら、メインビューを表示するようにビューを変更します。ここでは、Main.js のインスタンス化に Ext.Widget(‘app-main’) を使用します。

注意:‘app-main’ は Sencha Cmd で生成した Main.js に設定されている xtype を参照します。

ステップ 6 - Application.js に対する起動ロジックの追加

次に、Application.js および起動関数について説明します。

Application.js はアプリケーションの中核をなすものです。Application.js はビュー、ストア、モデルフォルダと同じレベルにあります。launch() という便利な関数を提供しており、アプリケーションに必要なクラスがすべてロードされるとこれが発火されます。このチュートリアルの Application.js ファイルの全コードは次のとおりです。

/**
 * 主要なアプリケーションクラス。このクラスのインスタンスは `app.js` が呼び出されるとこれによって作成されます
 * Ext.application()。アプリケーションの起動と初期化に最適な場所です。
 * details.
 */
Ext.define('TutorialApp.Application', {
    extend: 'Ext.app.Application',

    name: 'TutorialApp',

    stores: [
        // TODO: add global / shared stores here
    ],
    views: [
        'TutorialApp.view.login.Login',
        'TutorialApp.view.main.Main'
    ],
    launch: function () {

        // Check whether the browser supports LocalStorage
        // It's important to note that this type of application could use
        // any type of storage, i.e., Cookies, LocalStorage, etc.
        var supportsLocalStorage = Ext.supports.LocalStorage, 
            loggedIn;

        if (!supportsLocalStorage) {

            // Alert the user if the browser does not support localStorage
            Ext.Msg.alert('Your Browser Does Not Support Local Storage');
            return;
        }

        // Check to see the current value of the localStorage key
        loggedIn = localStorage.getItem("TutorialLoggedIn");

        // This ternary operator determines the value of the TutorialLoggedIn key.
        // If TutorialLoggedIn isn't true, we display the login window,
        // otherwise, we display the main view        
        Ext.widget(loggedIn ? 'app-main' : 'login');

    }
});

では、これらの動作について説明します。

requires

requires の役割については既に説明しましたが、この特定の配列について触れることにします。Application.js のために、ログインの検証結果に応じてログインまたはメインのビューをロードするようにアプリケーションの準備をすることが必要です。そのため、いずれの結果にも対応できるように TutorialApp.view.main.Main および TutorialApp.view.login.Login が必要です。

launch

先に述べたとおり、起動関数はアプリケーションに必要なものがすべてロードされると発火される関数です。これは、ログイン/ログアウトを行うアプリケーションのユーザーの状態に関するロジックを実行するのに適した場所です。

Ext.supports.LocalStorage

Ext.supports.LocalStorage は、使用するブラウザで localStorage をサポートしているかどうかについて、true または false を返す便利な Ext シングルトン関数です。最新のブラウザでは localStorage をサポートしていますが、古いバージョンの IE ではサポートしていません。localStorage を製品版のアプリケーションに使用することは望ましくないかもしれませんが、クッキーやセッションといったサーバーサイドのソリューションを使用せずに情報をストアするには大変便利な方法です。このチュートリアルでは、localStorage がサポートされているかを確認します。サポートされていない場合、使用しているブラウザではこのアプリケーションを使用できない旨のエラーメッセージを表示します。続いて return を実行し、後続のロジックの実行を停止します。

localstorage.getItem()

ブラウザで localStorage がサポートされている場合、次のステップは前回セットされた TutorialLoggedIn というキーを確認することです。ここでは、loggedIn 変数にそのキーの値の結果をセットします。存在しない場合、loggedIn は null となります。存在する場合、loggedIn には LoginController のロジックにおいて前回 true とセットしました。

Widget Ternary

ほとんどのプログラミング言語には三項演算子と呼ばれる省略表現があります。三項演算子を使用すると、従来の if/else 文に必要なコードの量を最小化できます。ここでは、三項演算子を使用して「loggedIn がある場合(null ではない場合)はメインビューをロードし、その他の場合はログインビューをロードする」と記述します。続いて、カプセル化する Ext.widget 関数を使用して三項演算子の結果を生成します。

ステップ 7 - ビューポートプラグインの追加

覚えていると思いますが、このチュートリアルのはじめのほうで autoCreateViewport コンフィグを app.js から削除しました。ビューポートが定義されていないため、メインビューの描画先がわかりません。ここでは、ビューポートプラグインをミキシングすることでこれを変更し、Main.js がアプリケーションのビューポートとして機能するようにします。これにより、メインビューがブラウザ内で使用できる高さと幅のすべてを占有します。次の行を Main.js に追加するだけで、これを行えます。

plugins: 'viewport',

その結果、Main.js ファイルは次のようになります。

/**
 * このクラスがアプリケーションのメインビューです。これは、`app.js` において
 * 「autoCreateViewport」プロパティと指定されています。この設定により自動的に「viewport」
 * プラグインが適用され、このクラスのインスタンスをボディ要素に格上げします。
 *
 * TODO - このビューのこのコンテンツをアプリケーションのニーズに応じて入れ替えてください。
 */
Ext.define('TutorialApp.view.main.Main', {
    extend: 'Ext.container.Container',
    plugins: 'viewport',
    requires: [
        'TutorialApp.view.main.MainController',
        'TutorialApp.view.main.MainModel'
    ],

    xtype: 'app-main',

    controller: 'main',
    viewModel: {
        type: 'main'
    },

    layout: {
        type: 'border'
    },

    items: [{
        xtype: 'panel',
        bind: {
            title: '{name}'
        },
        region: 'west',
        html: '<ul><li>This area is commonly used for navigation, for example, using a "tree" component.</li></ul>',
        width: 250,
        split: true,
        tbar: [{
            text: 'Button',
            handler: 'onClickButton'
        }]
    },{
        region: 'center',
        xtype: 'tabpanel',
        items:[{
            title: 'Tab 1',
            html: '<h2>Content appropriate for the current navigation.</h2>'
        }]
    }]
});

クリックハンドラにはデフォルトで生成された ‘onClickButton’ を使用するため、その他の Main.js の部分を変更する必要はありません。

ステップ 8 - メインロジックの追加

もう一息です。残っているのは、ユーザーにアプリケーションからログアウトする方法を提供することだけです。これは、localStorage から TutorialLoggedIn キーを破棄することを意味します。このロジックは MainController.js ファイルに記述します。生成されたその他のコードは削除して構いません。これが、このチュートリアルで行う MainController.js の最後の定義です。

Ext.define('TutorialApp.view.main.MainController', {
    extend: 'Ext.app.ViewController',
    alias: 'controller.main',

    onClickButton: function () {

        // Remove the localStorage key/value
        localStorage.removeItem('TutorialLoggedIn');

        // Remove Main View
        this.getView().destroy();

        // Add the Login Window
        Ext.widget('login');
    }
});

基本的には作成した LoginController.js コードの逆に過ぎないため、ここでは簡単に述べるに留めます。

この機能を簡単に説明すると、onClickButton は作成した Main.js ビューのボタンハンドラによって呼び出される関数です。クリックイベントが検出されると、次のステップを実行します。 + ユーザーのログイン状態を維持する localStorage キーを削除します。

  • 現在のビューである Main.js を破棄します。

  • ログインビューを再作成します。

これで、作成したアプリケーションをブラウザにロードして、完全に動作するログイン/ログアウトアプリケーションを表示できます。

まとめ

チュートリアルはお楽しみいただけたでしょうか。これはアプリケーションの基礎に過ぎませんが、今後のプロジェクトを容易にするためのコンセプトをいくつかご紹介できたのであれば幸いです。今後のチュートリアルの関するご提案などがあれば、フォーラムでお気軽にお知らせください。また、ご質問等は サポートポータルまたはフォーラムでお気軽にお寄せください。

Last updated