javascriptおよびxpages vote技術解説
TRANSCRIPT
Notes Consortium 九州地区研究会
2014年8月22日
リコーITソリューションズ株式会社 海老原賢次
JavaScriptおよび
XPages Vote技術解説
2014/9/4 1
自己紹介
海老原 賢次– リコーITソリューションズ株式会社 鹿児島開発部
– システム開発 主にWeb系
– 得意なもの
• Domino, XPages, C#, ASP.NET, JavaScript, HTML, CSS, jQuery・・・
– 不得意なもの
• Dojo(w)
– 嫌いなもの
• 冷やしトマト
2014/9/4 2
本日の内容
高度な JavaScript 解説– 私のブログ“Xpagesで行こう”で紹介している内容からピックアップして、詳しく解説します。
• JavaScriptの禁忌技
• オブジェクトリテラル
• コードの再利用(オブジェクト指向言語としての側面とその利用)
• コードの再利用(関数型言語としての側面とその利用)
海老原が作ったXpagesアプリの中を見てみよう– お恥ずかしながら、実際のXPagesアプリの中をお見せして解説します!
2014/9/4 4
ブログの紹介
XPagesで行こう!– http://goo.gl/TKOnf0
• 最近更新がサボり気味ですが・・・
• XPagesとJavaScriptの情報をあげています。
2014/9/4 6
JavaScriptの禁忌技
JavaScriptは変数の宣言が必須でないなど、非常に柔らかい言語です。そのため、コーディングするときに気をつけないといけない点がいくつか有ります。
オンラインで HTML, CSS, JavaScript の動作確認ができるサイトを利用します。PCをお持ちで、インターネットに接続できる方は、下記のサイトを利用してください。
http://jsbin.com/
2014/9/4 7
準備
2014/9/4 8
var output = [];
//--ここから--------------
//--ここまで--------------
$.each(output, function(i,o){var p=$('<p></p>');p.text(o);$('#output').append(p);
});
禁忌技その1-変数はvarで必ず宣言する
varで変数宣言した場合と、しない場合では、振る舞いが異なります。
2014/9/4 9
var val1 = "hoge1";function func1(){
var val1 = "hoge1-1";val2 = "hoge2-1";output.push("val1 - 1: " + val1);output.push("val2 - 1: " + val2);
}
func1();output.push("val1: " + val1);output.push("val2: " + val2);
Run it!
val1 - 1: hoge1-1val2 - 1: hoge2-1val1: hoge1val2: hoge2-1
Result
var で変数宣言していない場合、グローバル変数として扱われます。
禁忌技その2 –等価比較演算子に”==“は使わない
等価比較演算子には、2種類あります。”==“と”===“です。
”==“は左右の項目の型が違う場合、右辺の型を左辺の型に自動変換します。
“===“は左右の項目の型が違う場合、falseを返します。
2014/9/4 10
var val1 = 1;var val2 = "1";output.push(val1 == val2);output.push(val1 === val2);
Run it!
true
false
Result
“==“とした場合、それが型変換を意図したものなのかそうでないのかがわかりづらいので、”===“を使用するのをおすすめします。
否定も同様に”!=“ではなく”!==“を使用します。
禁忌技その3 -型変換で気をつけたい事
parseInt()関数の罠– parseInt(“07”) -> 7
– parseInt(“08”) -> 0
• ※IE8以下のバージョン。Firefox,IE9以上,Chromeでは “08” -> 8 となります。
parseIntは文字列を数値に変換しますが、0で始まる文字列は8進数と判断してしまいます。
第2引数でn進数を指定できますが、よく忘れます。
文字 -> 数値の変換は [Number]を使用します– Number(“07”) -> 7
– Number(“08”) -> 8
2014/9/4 11
型変換について
文字 -> 数値– Number(“08”)
– +“08”
数値 -> 文字– String(8)
– 8 + “”
など
ブログでまとめています。参考にしてください。– http://goo.gl/ApOjkj
2014/9/4 12
オブジェクトリテラル
JavaScriptは、オブジェクト指向ですが、JavaやC#などと違い、クラスは存在しません。
なので、JavaScriptではクラス無しにオブジェクトが作成できます。
2014/9/4 13
var user = {f_name: "kenji", //プロパティl_name: "ebihara",getFullName: function(title){ //メソッド
return this.f_name + ' ' +this.l_name + ' ' + title;
}}output.push(user.getFullName("san"));
Run it!
kenji ebihara san
Result
コードの再利用(オブジェクト指向による)
繰り返しになりますが、JavaScriptは、クラスは存在しません。
そもそもクラスは、オブジェクト指向プログラムにおいて、コードの再利用を効率的に行うための手法の1つです。
JavaScriptでは、「プロトタイプ・チェーン」を採用しています。
プロトタイプ・チェーンの前にコンストラクタ関数の説明をします。
2014/9/4 14
コンストラクタ関数
クラスがないJavaScriptでも、new を使ったオブジェクト化の仕組みが有ります。これがコンストラクタ関数です。
2014/9/4 15
var ProtoUser = function(){this.f_name = "gonbe";this.l_name = "nanashi";this.title = "san";this.getFullName = function(){
return this.f_name + ' ' +this.l_name + ' ' + this.title;
};};var user = new ProtoUser();user.f_name = 'ichiro';user.l_name = 'suzuki';output.push(user.getFullName());
Run it!
ichiro suzuki san
Result
コンストラクタ関数説明
コンストラクタ関数は、通常の関数と同じ書き方。コンストラクタ関数であることを示す宣言はありません。
new をつけて関数を呼び出すと、コンストラクタ関数として動作します。
コンストラクタ関数として呼ばれた場合、コンストラクタ関数内の “this”は、新しく生成されるオブジェクトを示します。– newを付けない場合、通常の関数として実行されるので、関数内の”this“は呼び出し側の”this”となる。
暗黙的に、新しいオブジェクト”this”を返します。
コンストラクタ関数がprototypeを持っている場合、新しいオブジェクトからそのプロトタイプ・オブジェクトに関連されます。
2014/9/4 16
プロトタイプとは
クラスは継承されるものですが、JavaScriptのプロトタイプは移譲されるものです。
クラスとプロトタイプの違い
2014/9/4 17
クラス オブジェクト(実態)
オブジェクト(実態)
オブジェクト(実態)
継承
継承
継承
プロトタイプ(実態)
オブジェクト(実態)
オブジェクト(実態)
オブジェクト(実態)
移譲
移譲
移譲
オブジェクトはクラスの特性を”受け継ぐ” オブジェクトは他のオブジェクト(プロトタイプ)に処理を”移譲する”※プロトタイプはオブジェクトです
プロトタイプの実例
2014/9/4 18
var ProtoUser = function(){this.f_name = 'gonbe';this.l_name = 'nanashi';this.title = 'san';this.getFullName = function(){
return this.f_name + ' ' +this.l_name + ' ' + this.title;
};};var ProtoStaff = function(){
this.dep = 'kagosima';this.getNameAndDepartment = function(title){
return this.getFullName(title) + ' @' +this.dep;
};};ProtoStaff.prototype = new ProtoUser();var ebihara = new ProtoStaff();ebihara.f_name = 'kenji';ebihara.l_name = 'ebihara';output.push(ebihara.getFullName('san'));output.push(ebihara.getNameAndDepartment('san'));
Run it!
kenji ebihara san
kenji ebihara san kagosima
Result
オブジェクトの関係
2014/9/4 19
ProtoUser
f_name=‘gonbe’
l_name=‘nanashi’
title = ‘san’
getFullName()
ProtoStaff()
Prototype
ebihara
f_name = ‘kenji’
l_name = ‘ebihara’
dep = ‘kagosima’
getNameAndDepartment()
(__prototype__)
ebiharaは、プロトタイプとしてProtoStaffのオブジェクトを持ちます。ebihara.getFullName() と呼び出すことができますが、ebiharaオブジェクトには持っていません。プロトタイプ・チェーンによってProtoUserのgetFullName()がコールされます。
newでのオブジェクト生成引き継ぐ
プロトタイプチェーン
2014/9/4 20
ProtoUser
title = ‘san’
(__prototype__)
ebihara
f_name = ‘kenji’
l_name = ‘ebihara’
dep = ‘kagosima’
getNameAndDepartment()
(__prototype__)
もし、プロトタイプのオブジェクトがプロトタイプを持っている場合、Ebihara.getFullName()とした場合、JavaScriptエンジンは、ebiharaにそのメソッドがないので、ProtoUserを探しに行きます。そこにもない場合は、さらに上のParentProtoUser を探しに行きます。仮にProtoUserに getFullName() がある場合は、それが採用され、ParentProtoUserの同名のメソッドは無視されます。
ParentProtoUser
f_name=‘gonbe’
l_name=‘nanashi’
getFullName()
コードの再利用(関数型言語による)
JavaScriptは関数型言語としての側面も持っています。– 関数型言語とは・・・
→ http://goo.gl/wamZU
JavaScriptの関数の特徴– 関数もオブジェクトである。つまり変数のように「持ち運べる」
– 関数は他のオブジェクトの依存性が低い。オブジェクトのメンバーであっても、外に出せる。
– クロージャ
2014/9/4 21
メソッド拝借
2014/9/4 22
var obj1 = {a: 1,func1: function(){return this.a + 1;
}};var obj2 = {a: 2,func2: obj1.func1
};output.push(obj1.func1());output.push(obj2.func2());
Run it!
2
3
Result
obj2ではobj1のfunc1をfunc2として参照しています。
func1のの中の”this”は、obj1を示しているのではなく、実行したオブジェクトとなります。
つまり、func1はobj1に依存しない、ということがわかります。
関数の共通化
2014/9/4 23
var func = function(){return this.a + 1;
};var obj1 = {a: 1,func1: func
};var obj2 = {a: 2,func2: func
};
output.push(obj1.func1());output.push(obj2.func2());
Run it!
2
3
Result
このように、外部で関数を変数に置き、オブジェクトでそれらを参照することができます。
コールバック関数
関数の引数に関数を渡す事ができます。
呼び出される関数側で、処理の途中で引数に渡された関数を実行できます。– 共通の処理だけど、その途中で一部の処理が呼び出し側でいろいろ異なる場合に便利。
– →依存性注入
2014/9/4 24
コールバック関数の例
2014/9/4 25
var each = function(arr,func){for(var i = 0,max = arr.length; i<
max; i++){func(i, arr[i]);
}};var array = ["a","b","c","d"];each(array, function(i, item){output.push(item + i);
});each(array, function(i, item){output.push(i + item);
});
Run it!
a0
b1
c2
d3
0a
1b
2c
3d
Result
each関数の仕様
動作概要– 引数に指定した配列をループする。その際に配列内の値ごとに引数に指定された関数を実施する。
引数– 第1引数に配列を指定する
– 第2引数に関数を指定する。この関数は第1引数にインデックスとなる数値、第2引数に配列内の値が渡されることを想定すること。
メリット– JavaScriptの for ステートメントは使いかたを注意する必要がある。
– この関数を使用することで、一定の for の使いかたを強制できる。また、タイプする量が少ない。
• 配列をループして順次処理するばあい、for-inは使用しないほうが良い。→ http://goo.gl/g9krt
• また、for(var i=0; i < arr.length; i++) とするとパフォーマンスに影響する
2014/9/4 26
クロージャとは
Wikipediaによると・・・
[引数以外の変数を実行時の環境ではなく、自身が定義された環境(静的スコープ)において解決することを特徴とする。]
・・・意味不明。ということで実践で確認しましょう。
2014/9/4 27
クロージャ実践
2014/9/4 28
var createCounter = function(){var count = 0;var obj = {countUp: function(){return ++count;
},getCurrent: function(){return count;
}};return obj;
};var counter = createCounter();output.push(counter.getCurrent());output.push(counter.countUp());output.push(counter.getCurrent());var counter2 = createCounter();output.push(counter2.getCurrent());output.push(counter2.countUp());output.push(counter2.getCurrent());
Run it!
0
1
1
0
1
1
Result
クロージャ解説
createCounter()関数でオブジェクトを返しています。
このオブジェクト(counter)のメソッド(関数)は、 createCounter()関数内の変数を参照しています。
通常の関数の変数であれば、変数のスコープは関数内なので、終了と同時に破棄されるはずですが、 counterのメソッド実行時にそれが参照され、且つ状態を保持しています。
これがクロージャです。
JavaScriptのオブジェクトにはプライベート変数等は定義できませんが、クロージャを使用することで、同様のことができます。
コンストラクタ関数で使用されることが多いです。
2014/9/4 29
アンケートWebアプリの仕様
複数の選択肢を持った質問を作成できます。
選択肢は択一です。
すべてのユーザーは質問の一覧を見ることができます。
回答者は質問毎に1つだけ回答を投票できます。(複数の回答は不可)
回答者は回答を修正することができます。
すべてのユーザーは回答の集計結果を見ることができます。
集計結果は、棒ブラフと円グラフで表示されます。
集計結果は、リアルタイムで表示されます。(画面のリフレッシュは手動)
質問を修正することができます。
2014/9/4 31
アンケートWebアプリ 今後搭載予定の機能
一覧で、投票数が表示されます。
質問を投稿した人しか編集できません。
回答の開始日-終了日を指定できます。
選択肢の選択を択一か複数可かを指定できるようにします。
質問画面のURLを他のDominoユーザーにメールで送る事ができます。
回答者を指定したユーザーのみに制限できます。
one uiとは別にbootstrap版を用意して、レスポンシブデザインとして、モバイル端末でも表示できるようにします。
2014/9/4 32
ファイル構成
コードビハインド モデル– 1つのXPagesに対して、1つのSSJS,CSJSが存在する(CSJSは必要な場合の
み)
– 対になるSSJS,CSJSの名前は、XPagesの名前に“_JSS”,“_JS”のサフィックスをつける。
– XPagesの各イベントの処理は、SSJSに書くこととし、XPages内ではSSJSの関数を呼び出すだけとする。
– 各SSJSでは、先頭でXPages名と同じ名前のオブジェクトを作成し、その中にメソッドなどを記載することで、名前の衝突を避ける。
• JavaScriptにおける名前空間パターン→ http://goo.gl/9mLFUu
2014/9/4 37
Themeを利用した共通リソースの読み込み
Themeを作成し、そこに各ページで共通で読み込むライブラリ(CSS,JavaScriptなど)を登録しておくことで、一元化できる。
2014/9/4 38
外部ライブラリの配置
jQueryなどは、スクリプトライブラリに登録すると、エラーが出て配置できない。
またこのような外部のライブラリは編集する必要が無いため、設計要素として登録する必要はない。
WebContentに置くことで、回避できる。
パッケージ・エクスプローラで配置が可能。
2014/9/4 39
文書とXPagesの関係
1つの文書とXPagesは1:nの関係にある。
こうすることで、1つのデータを様々な表現・利用方法がシンプルに実現できる– Notesフォームのように1:1である必要はない。(不可能ではないが・・・)
2014/9/4 40
XSP
XSP
XSP
Question.xsp新規作成・編集用
Vote.xsp回答用
Result.xsp結果表示用
Question文書
Vote文書
質問文書と回答文書の関係
1つの質問文書に対して、0個以上の回答文書が有ります。
回答文書は、返答文書ではありません。
回答文書には、質問文書の独自ID(QuestionId)が入っていて関連性を持たせています。
2014/9/4 41
Question文書
Vote文書
選択肢はJSONデータで保持
Notes文書の欠点として、複数データを持ちづらいことがあげられます。
テキストや数値の複数値は保持できるが、いくつかの値をセットとしたオブジェクトのようなものをリストとして保持する機能はありません。
そこで、そのような値をJSONとしてテキストで保持しています。
2014/9/4 42
Choicesフィールドに、テキストでJSONデータを保持している。オブジェクトは、id, sortkey(並び順), name(表示値) のプロパティを持っており、それを配列としている。
値の例:[{"id":"e10974c4-2bbc-4f85-9d3f-43bcdf6ce784","sortkey":1,"name":"IE"},{"id":"f73d3e6e-1413-1f30-60c5-b76139086a87","sortkey":2,"name":"Chrome"},{"id":"555ac5c9-7143-85df-6c34-c42c0a4cc9a1","sortkey":3,"name":"FireFox"},{"id":"f8d0f5c8-1773-c48a-dcdf-a219e7e5b881","sortkey":4,"name":"Safari"},{"id":"efc1b927-a347-4d47-08df-fe9a55d12b86","sortkey":5,"name":"Other"}]
質問画面での選択肢の表示
JSONで定義された選択肢データはクライアント・スクリプトで解析され、画面に表示します。
画面内に非表示のテキストボックスを配置し、Choicesフィールドをバインドします。
ページの読み込み時にクライアント・スクリプトでこのテキストボックスからJSONデータを取り出し、オブジェクト配列化して、画面に表示します。
選択肢追加のボタンを押すと、選択肢オブジェクト配列にプッシュし、Choicesフィールドをバインドした隠しテキストボックスに格納します。
JSON->オブジェクト,オブジェクト->JSONの変換は、XSP.fromJson()を利用しています。
2014/9/4 43
回答・結果画面での選択肢の表示
回答・結果画面では動的に変わることがないので、サーバー側でオブジェクト化して、それを Repeat コントロールのデータソースとして渡して描画しています。
2014/9/4 44
<ul id="ChoicesList"><xp:repeat id=“Coices” rows=“99" disableOutputTag="true"value="#{javascript:return fromJson(TargetDoc.getItemValueString('Choices'));}"var="item">
<xp:panel tagName="li"><label>
<input type="radio" name="checked”value="#{item.id}" checked="#{javascript:Vote.voteDocument.getItemValueString('ChoiceId') === item.id? 'checked' : '';}" />
<xp:text escape="true" value="#{item.name}"></xp:text></label>
</xp:panel></xp:repeat>
</ul>
文書のIDをUUIDで持つ
文書や選択肢の一意なキーを独自に持っています。これは、回答文書との関連などに使用されます。
連番だと、番号をコントロールする仕組みを作らなくてはなりませんが、UUIDだと手軽に一意な文字列を発行できます。
2014/9/4 45
[Question_JSS]import JSON_JSS;var Question = {
save: function(){TargetDoc.setValue('QuestionId', java.util.UUID.randomUUID().toString());TargetDoc.save();var SavedInformation:com.ibm.xsp.component.xp.XspDiv =
getComponent("SavedInformation");SavedInformation.setRendered(true);
}};
結果画面-棒グラフの表示
選択肢ごとのカウントは、「Result」ビューでカウントします。
選択肢のオブジェクト毎にカウント数と全体のパーセンテージをセットします。– 「Result_JSS」の「getResults()」関数を参照してください。
選択肢オブジェクトの配列を Repeat コントロールのデータソースとします。
描画するときに、ボックスのスタイルの”Width”に上で算出したパーセンテージをセットします。
2014/9/4 46
<div class="bar" style="width:#{item.per}%">
結果画面-グラフの表示
グラフの表示には、オープンソースの jQuery Visualize を利用しています。
これは、HTMLのテーブルをデータソースとしてグラフを表示します。
そのため、棒グラフで使用したオブジェクトでテーブルを作成し、非表示としています。
グラフを表示するコードは下記だけで実現できます。
2014/9/4 47
$(function(){$('.resultTable').visualize({type: 'pie', height: '300px', width: '420px'});
});