Meteorで非公開ツール用のログインフォームを作る(その2)
前回に引き続いて非公開Webアプリ用のログインフォームの作成をば。ログイン中の画面が表示されるところまで作ったので、この画面を折りたためるようにして追加のユーザーを作成できるようにします。
折りたたみ、つまり要素の一時的な表示/非表示は、HTMLを書き換えるMeteorのテンプレートでやると無駄が多そうなので、jQueryのshow/hideを使ったほうがよさげです。 meteorでは、次のコマンドで簡単にjQueryが組み込めます(どうも最新バージョンではなさげですが)。
meteor add jquery
jQueryを使うときの注意もろもろ
jQueryを組み込むのは楽なんですが、実際に使おうとすると色々と勝手が違うことに気付きます。
まず、初期状態ではフォームが折りたたまれた状態にしたいのですが、MeteorではHTMLを動的に生成するので、jQueryで操作する対象の要素が確実に存在するタイミングでhide()などを実行しないといけません。テンプレートがレンダリングされたときに呼び出されるrenderedというコールバック関数があるので、これの中に処理を書いておけばよさそうです。
2つめはjQueryのイベント処理が利用できないらしいということ。試しにやってみましたがダメでした(追記:どうもこれは勘違いっぽいです。イベントが使えなかったらjQueryプラグインの大半が動かないし、そんははずはない)。
それっぽい英語の記事(Using Jquery $(this) in Meteor)を見つけたので、どうやらイベント処理はMeteor側で書いたほうがいいようです。
client/logging.html(ファイル名を変えました)
<template name="logincontrol"> <ul> <li><a id="logout">ログアウト</a></li> {{#if superuser}} <li class="expandable"><a>新規ユーザー作成</a></li> {{> createuserform}} <li class="expandable"><a>ユーザー管理</a></li> <div>管理画面</div> {{/if}} </ul> </template>
client/logging.js(ファイル名を変えました)
//ログイン後のコントロール Template.logincontrol.events({ 'click #logout': function (event) { Meteor.logout(); } }); Template.logincontrol.superuser = function(){ return Meteor.user().profile.superuser; }; //隠しコントロールの表示/非表示 Template.logincontrol.rendered = function(){ $('.expandable').next().hide(); }; Template.logincontrol.events({ 'click .expandable': function (event) { $(event.currentTarget).next().toggle(); } });
テンプレートの再レンダリングに注意
ユーザー作成のテンプレートもそのまま流用できたので楽勝……かと思いきや、試しにフォームに文字を入力するとなぜか勝手にフォームが折りたたまれてしまう現象が。
しばらく意味がわからなかったのですが、実はフォームのバリデーション(ユーザー名の重複チェックとパスワードの一致チェック)の結果をMeteorのテンプレートの機能で表示させていたのがいかんようです。つまり、バリデーションの結果を変更するためにHTMLが書き換わると、それが親テンプレートのrenderdを呼び出してしまい、jQueryのhide()が呼び出されてしまうと。公式リファレンスのrenderedの項をよく読んだら、「〜each time any part of the template is re-rendered.」って書いてあります。
というわけで、バリデーションの結果表示などもjQueryのshow()/hide()を使う形に書き換えます。 crient/logging.htmlのcreateuserformテンプレートに警告用のテキストを追加。
<template name="createuserform"> <div class="form-signin" role="form"> <dl> <dt>UserName(半角英数)</dt> <dd><input type="text" id="newusername"> <span class="alert" id="alert_username">すでに使われています(not unique)</span> </dd> <dt>UserFullName(日本語可)</dt> <dd><input type="text" id="newuserfullname"></dd> <dt>Password</dt> <dd><input type="password" id="newuserpassword1"></dd> <dt>Password(確認)</dt> <dd><input type="password" id="newuserpassword2"></dd> <span class="alert" id="alert_password">一致していません(not the same)</span> <dt><input type="checkbox" id="chk_superuser"></dt> <dd>スーパーユーザー</dd> </dl> <button id="btn_createnewuser" class="btn btn-lg btn-primary btn-block">Create</button> </div> </template>
crient/logging.jsでテンプレートのレンダリング時に警告を隠すようにして、
//ユーザー作成フォームの警告を隠す Template.createuserform.rendered = function () { $('.alert').hide(); };
keyup時にバリデーションしてだめだったらjQueryのshow()で警告を表示するように……。
//createuserformのイベント処理 Template.createuserform.events({ //ユニークネーム検出 'keyup #newusername':function(event){ var val = event.currentTarget.value; Meteor.call('isUniqueName', val, function (error, result) { if(result == true) $('#alert_username').hide(); else $('#alert_username').show(); }); }, //パスワード一致検出 'keyup #newuserpassword1, keyup #newuserpassword2':function(event){ var pass1 = $('#newuserpassword1').val(); var pass2 = $('#newuserpassword2').val(); // console.log(val1 + ':' + val2); if(pass1 != pass2) $('#alert_password').show(); else $('#alert_password').hide();; }, ……後略……
サーバ側とクライアント側のどちらで実行するかで関数の動作が違う
ついでに、Accounts.createUser()がサーバ側で実行されるように変更。クライアントで実行するとユーザー作成後に新しいユーザーで再ログインする仕様なのですが、スーパーユーザーの人が普通ユーザーをまとめて作成する使い方を想定しているので。これはMeteor.methods()とMeteor.call()をかませばいいはず。
//createuserformのイベント処理 Template.createuserform.events({ ……中略…… //ユーザー作成 'click #btn_createnewuser':function(event){ var user = $('#newusername').val(); var fullname = $('#newuserfullname').val(); var pass1 = $('#newuserpassword1').val(); var supermode = $('#chk_superuser').prop('checked'); if(Session.get('uniquename')==false || Session.get('notvalidpass')==true){ return; } if(user=='' || pass1=='' || fullname =='') return; Meteor.call('createUserOnServer', user, pass1, fullname, supermode, function (error, result) { if(!error){ $('#newusername').val(''); $('#newuserfullname').val(''); $('#newuserpassword1').val(''); $('#newuserpassword2').val(''); $('#chk_superuser').prop('checked', false); showLoginFormStatus('ユーザー'+result+'を作成しました'); Meteor.call('isFirstUser', 1, function (error, result) { Session.set('firstuser', result); }); } else { showLoginFormStatus('ユーザー作成に失敗しました'); } }); } });
server/bootstrap.js
Meteor.methods({ ……中略…… //ユーザー作成 createUserOnServer:function(user, pass, fullname, supermode){ console.log('createUser '+user); return Accounts.createUser({ username: user, password: pass, profile: { name: fullname, superuser: supermode } }); } });
公式ドキュメントを読むと、パスワードなしメールアドレスありでサーバ側で実行すると、パスワード設定用のメールをユーザーに送るとか何かすごい色々やってくれるようです。