Meteorで非公開ツール用のログインフォームを作る(その4)
さて、(たぶん)最後のステップとして、ユーザーの削除や情報変更を行うユーザー管理画面を作成してみます。
これまでとだいぶ見た目が変わっていますが、見よう見まねでBootstrapなるものを入れ、CSSを少しいじって、ログインコントロール部分が固定ヘッダになるよう調整してみました。 また、テンプレートを利用してアプリケーション名を手軽に変更できるようにもしてみています。
ユーザー一覧へのアクセスを許可する
全ユーザーの情報はデフォルトでは非公開になっているので、サーバ側でパブリッシュ設定を行います(正確にはautopublishパッケージをリムーブした状態のデフォルト)。
参考:体感!JavaScriptで超速アプリケーション開発 -Meteor完全解説 第11回 データ同期とリアクティブ・プログラミング
だだ漏れにならないよう一応アクセスするユーザーがスーパーユーザーであることを確認してから 返すようにしましょうか。 publish内ではthisのuserIdに「データを求めてきたユーザーの_id」が入っているので、 それを元にそのユーザーのprofile.superuserがtrueかどうかを確認します。
server/bootstrap.js
//ユーザー管理のためにpublish Meteor.publish("users", function(){ //スーパーユーザー以外が尋ねてきたときは離脱 var curuser = Meteor.users.findOne({_id: this.userId}); if(curuser==null || curuser.profile.superuser == false) return null; return Meteor.users.find(); });
クライアント側では対になるサブスクライブ(購読)という処理をどこかで行う必要があります。 ログインが成功したタイミングあたりが妥当でしょうか?
//ログインボタンでログイン Template.loginform.events({ 'click #btn_login': function () { var user = document.querySelector('#loginusername').value; var pass = document.querySelector('#loginuserpassword').value; Meteor.loginWithPassword({username:user}, pass, function(err){ if(err){ showLoginFormStatus('ログインできません。ユーザー名とパスワードを確認してください'); } else { //スーパーユーザーならサブスクライブ if(Meteor.user().profile.superuser){ g_usershandle = Meteor.subscribe('allusers'); } } }); }, 'click #loginname': function (event) { $(event.currentTarget).next().toggle('fast'); } }); var g_usershandle = null;
Meteor.subscribeはサブスクライブハンドルというものを返すので、それをグローバル変数g_usershandleに代入しておきます。 このサブスクライブハンドルは、購読を停止するstop()と購読可能な状態になっているかを確認するready()というメソッドを持っています。
ログオフ時に購読を停止する処理も書いておきます。
Template.logincontrol.events({ 'click #logout': function (event) { if(g_usershandle){ g_usershandle.stop(); g_usershandle = null; } Meteor.logout(); }, ……後略……
ユーザー情報の一覧を表示する
ユーザーの一覧を表として表示するので、{{#each 配列を返すテンプレート関数名}}
を使ったテンプレートを書きます。
#each
の間では、配列の各要素のプロパティに{{プロパティ名}}
でアクセスできます。
<template name="manageuserform"> <div class="form-horizontal" role="form" id="dialog_manageuser"> <table class="table table-bordered"> <tr><th>username</th><th>fullname</th><th>supermode</th></tr> {{#each users}} <tr> <td>{{username}}</td> <td>{{profile.name}}</td> <td>{{profile.superuser}}</td> </tr> {{/each}} </table> </div> </template>
そしてダイアログを表示するためのコード(前回参照)と、ユーザー一覧を返すテンプレート関数usersを定義します。
/** ユーザー管理テンプレート **/ //レンダリング時 Template.manageuserform.rendered = function () { $('#dialog_manageuser').dialog({ title: 'Manage User', autoOpen: true, modal: true, width: '80%', close: function( event, ui ) {Session.set('usermanage', false);} }); }; //ユーザー一覧 Template.manageuserform.users = function(){ return Meteor.users.find(); }
一覧いただきました。
publishとsubscribeを使ってアクセス許可したら、後はサーバサイドと同じようにDBアクセスするだけでいいみたいです。 ずいぶんシンプルですね。 どうもこのへん、わかったようなわからないような……。
リロードすると購読範囲が変わってしまう
ここで問題発生。リロードしたら現在ログインしているユーザーの分しか取得できなくなってしまいました。
ログイン時にサブスクライブしているので、リロードで自動ログインした場合は維持されないということのようです。
なので、サブスクライブ処理を書く場所を、ログイン時からユーザー一覧のテンプレート関数実行時に移動してみました。
//ユーザー一覧 Template.manageuserform.users = function(){ //スーパーユーザーならサブスクライブ if(Meteor.user().profile.superuser){ g_usershandle = Meteor.subscribe('allusers'); } return Meteor.users.find(); }
うまく行ったかと思いきや、なんだか妙なことになってしまう。
スーパーユーザーのみにユーザー管理ボタンを表示させるために定義しておいたテンプレート関数があったので、 そちらに移動してみます。
Template.logincontrol.superuser = function(){ //スーパーユーザーならサブスクライブ if(Meteor.user().profile.superuser){ g_usershandle = Meteor.subscribe('allusers'); } return Meteor.user().profile.superuser; };
今度は大丈夫みたいです。よくわからんのですが、表示のタイミングでサブスクライブしてはいかんようです。 (その後もユーザー管理ダイアログを出した状態でソースコードをいじるとダイアログが増えることがあったので、そもそも前回の記事に書いたレンダリング時にjQuery UIのdialogを呼び出すというやり方があまりよくないのかも)
(追記:調べたところdialogによって作られた要素には「aria-describedby="ダイアログ化した要素のID"」が付くので、それをjQueryで調べてdialogの複数作成を防ぐ処理を追加しました。あまり奇麗ではないけど)
リアクティブなんとかがすごい
ここでウィンドウを2つ開いてリアクティブなんちゃらのテストをしてみます。
一方で新規ユーザーを追加すると……
ひゅー、これはすごい。
次はユーザー管理画面でフルネームの変更やsupermodeのオン/オフ、ユーザーの削除などができるようにしてみます。 Excelみたいにセルを直接編集できるようにしてみたいところですが……。
その後デプロイを試してから(Appfogというところにデプロイできるらしい)、ようやく本番のアプリに手を付けてみようかな……的な。