Quantcast
Channel: パークのソフトウエア開発者ブログ|ICT技術(Java・Android・iPhone・C・Ruby)なら株式会社パークにお任せください
Viewing all 138 articles
Browse latest View live

環境構築 第五回(まとめ)

$
0
0


<<<第五回 まとめ>>>

  業務の中で端末構築を経験して苦労したのは、
   ・がちがちに端末の操作を固めてしまわないといけない
   ・認証に関連する動作確認
   ・プラットフォームを移植したアプリの検証
   ・動きがおかしいときの調査
   ・サーバに引きずられる仕様の確認
  といったようなものだった。
  MSに聞かないと分からないもの(広報されていないバグ的な動き)が幾つか発生した。
  MSに聞ける窓口を持たないと、構築時の不具合をきれいに解消することは難しいと感じた。
  Ex:starup関連など

 以上。

Backbone.js入門その5「静的HTMLでBackbone.Viewを作る」

$
0
0

こんばんは。ゆんぼうです。

今回は、Backbone.jsで、
静的HTMLでViewを作成します。

前回までは、htmlのbodyにBackbone.Viewのelをappendして、Webページを表示しておりましたが、
今回は、htmlのbodyに要素を直接記述して、要素のidをBackbone.Viewのelに紐付けて表示します。

今回使用する環境は下記の通りです。

Webブラウザ
・Mozilla Firefox (v34.05) https://www.mozilla.org/ja/firefox/new/
・FireBug (v2.0.7) https://addons.mozilla.org/ja/firefox/addon/firebug/

JavaScriptライブラリ
・Backbone.js (v1.1.2) http://backbonejs.org/
・Underscore.js (v1.7.0) http://underscorejs.org/
・jQuery (v2.1.1) http://jquery.com/


index.html のbody に下記の内容を記述します。


<div id="id_buttons">
<input id="id_button_update" type="button" value="更新">
<input id="id_button_delete" type="button" value="削除">
</div>




■ViewにIDを定義する

【デモはこちら】
【ソースファイルはこちら】

Viewのelに要素のIDを定義して、Webページを表示します。
main.js に下記の内容を記述します。


$(function() {
var BaseView = Backbone.View.extend({
el : '#id_buttons', // (1)
events : {
'click #id_button_update' : 'onClickUpdateButton',
'click #id_button_delete' : 'onClickDeleteeButton'
},
onClickUpdateButton : function(event) {
console.log('onClickUpdateButton');
},
onClickDeleteeButton : function(event) {
console.log('onClickDeleteeButton');
}
});

var baseView = new BaseView();
});


(1)BaseViewのelプロパティに要素ID「id_buttons」を定義します。
そのBaseViewを new すると、要素IDとBaseView.Viewが紐付けられます。
(※ボタンを押下すると、ログが出力されることで、紐付けが確認できます。)



■IDをViewに渡す

【デモはこちら】
【ソースファイルはこちら】

Viewのオブジェクトに、要素IDを渡します。
main.js に下記の内容を記述します。


$(function() {
var ButtonView = Backbone.View.extend({
events : {
'click' : 'onClick'
},
onClick : function(event) {
console.log(event.target.id + '#onClick');
}
});

var buttonView1 = new ButtonView({ // (1)
'el' : '#id_button_update'
});

var buttonView2 = new ButtonView({ // (2)
'el' : '#id_button_delete'
});
});


(1)更新ボタンの要素ID「id_button_update」を渡して、ButtonViewのオブジェクト生成します。
更新ボタンとButtonViewが紐付けられます。
(2)削除ボタンも同様です。

更新ボタンと削除ボタンはそれぞれ別々のViewオブジェクトとなります。

以上です。



□過去の記事

Backbone.js入門その4「SuperViewとSubViewのアクセスを作成する」
Backbone.js入門その3「Backbone.ViewでSubViewを作る」
Backbone.js入門その2「Backbone.Viewで複数の要素を作る」
Backbone.js入門その1「Backbone.Viewで1つの要素を作る」

Backbone.js入門その6「ModelとCollection」

$
0
0

こんばんは。ゆんぼうです。

今回は、Backbone.jsのBackbone.ModelとBackbone.Collectionを使ったViewを作成します。
Modelは、データを表します。
Backbone.jsでは、データの更新や検証、Ajax通信などの機能を提供します。
Collectionは、Modelの配列を表します。
Backbone.jsでは、配列の追加、削除、並び替えなどの機能を提供します。

今回使用する環境は下記の通りです。

Webブラウザ
・Mozilla Firefox (v34.05) https://www.mozilla.org/ja/firefox/new/
・FireBug (v2.0.7) https://addons.mozilla.org/ja/firefox/addon/firebug/

JavaScriptライブラリ
・Backbone.js (v1.1.2) http://backbonejs.org/
・Underscore.js (v1.7.0) http://underscorejs.org/
・jQuery (v2.1.1) http://jquery.com/



■プルダウン

【デモはこちら】
【ソースファイルはこちら】

以前の記事で作成したプルダウンを
今回は、ModelとCollectionを使用して作成します。

index.html のhead に下記の内容を記述します。
こちらは前回と同じです。


<script type="text/template" id="template-programing-subview">
<label for="language"><%= title %></label>
<select id="language"></select>
</script>


main.js に下記の内容を記述します。


$(function() {

var ProgramingLanguageModel = Backbone.Model.extend({ // (1)
defaults : { // (2)
'name' : '',
'value' : 0
}
});

var ProgramingLanguageCollection = Backbone.Collection.extend({ // (3)
model : ProgramingLanguageModel // (4)
});

var SampleView = Backbone.View.extend({

events : {
'change #language' : 'onChange'
},
initialize : function() {
this.compiledTemplate = _.template($('#template-programing-subview').text());
this.collection = new ProgramingLanguageCollection([ { // (5)
'name' : 'JavaScript',
'value' : 1
}, {
'name' : 'Java',
'value' : 2
}, {
'name' : 'C言語',
'value' : 3
}, {
'name' : 'Ruby',
'value' : 4
}, {
'name' : 'Python',
'value' : 5
} ]);
},
render : function() {
var data = {
'title' : 'プログラミング言語を選択してください'
};
this.$el.append(this.compiledTemplate(data));
_.each(this.collection.models, function(model) { // (6)
this.$el.find('#language').append((new OptionView(model)).render().el);
}, this);
return this;
},
onChange : function(event) {
console.log('onChange:' + event.target.value);
}
});

var OptionView = Backbone.View.extend({
tagName : 'option',
initialize : function(model) {
this.label = model.get('name'); // (7)
this.attributes = {
'value' : model.get('value') // (8)
};
},
render : function() {
this.$el.html(this.label);
return this;
}
});

var sampleView = new SampleView();
$('body').append(sampleView.render().el);
});


(1)Modelを定義します。Modelは、Backbone.Model.extend()関数を使用します。
(2)defaultsプロパティで、デフォルト値の設定を行います。
(3)Collectionを定義します。Collectionは、Backbone.Collection.extend()関数を使用します。
(4)CollectionにModelを設定します。ProgramingLanguageCollection は、ProgramingLanguageModel の配列を持つということを表します。
(5)Viewのinitialize関数で、ProgramingLanguageCollectionのオブジェクトを生成しています。その引数に、5つ分のデータを設定しています。
このとき、ProgramingLanguageCollection には、5つ分のProgramingLanguageModelを保持しています。
(6)Viewのrender関数で、Collectionの配列のサイズ分をSubViewに追加しています。this.collection.models は、ProgramingLanguageModelの配列を指します。
(7)1つ分のProgramingLanguageModelから、nameプロパティを取得しています。
(8)1つ分のProgramingLanguageModelから、valueプロパティを取得しています。

以上です。



□過去の記事

Backbone.js入門その5「静的HTMLでBackbone.Viewを作る」
Backbone.js入門その4「SuperViewとSubViewのアクセスを作成する」
Backbone.js入門その3「Backbone.ViewでSubViewを作る」
Backbone.js入門その2「Backbone.Viewで複数の要素を作る」
Backbone.js入門その1「Backbone.Viewで1つの要素を作る」

Backbone.js入門その7「リストと登録ボタンを作る」

$
0
0

こんばんは。ゆんぼうです。

今回は、Backbone.jsを使用して、リストと登録ボタンを作成します。

今回使用する環境は下記の通りです。

Webブラウザ
・Mozilla Firefox (v34.05) https://www.mozilla.org/ja/firefox/new/
・FireBug (v2.0.7) https://addons.mozilla.org/ja/firefox/addon/firebug/

JavaScriptライブラリ
・Backbone.js (v1.1.2) http://backbonejs.org/
・Underscore.js (v1.7.0) http://underscorejs.org/
・jQuery (v2.1.1) http://jquery.com/



■リストと登録ボタン

【デモはこちら】
【ソースファイルはこちら】

リストと登録ボタンを作成します。
登録ボタンを押下すると、リストに追加されます。

index.html の head に下記の内容を記述します。


<script type="text/template" id="template-user-list-item">
<%= label.name %>
</script>
<script type="text/template" id="template-user-register">
<label for="id_input_name"><%= label.name %></label>
<input id="id_input_name" type="text">
<br>
<input id="id_button_regist" type="button" value="<%= label.registButton %>">
<hr>
</script>


main.js に下記の内容を記述します。


$(function() {

var UserModel = Backbone.Model.extend({ // (1)
defaults : {
'name' : ''
}
});

var UserCollection = Backbone.Collection.extend({ // (2)
model : UserModel
});

var UserListView = Backbone.View.extend({
name : 'UserListView',
tagName : 'ul',
initialize : function(args) {
this.collection = args.collection;
this.listenTo(this.collection, 'add', this.onAdd);
},
render : function() {
this.$el.html('');
_.each(this.collection.models, function(model) {
this.$el.append((new UserListItemView({
'model' : model
})).render().el);
}, this);
return this;
},
onAdd : function() { // (5)
console.log(this.name + '#onAdd');
this.render();
}
});

var UserListItemView = Backbone.View.extend({
name : 'UserListItemView',
tagName : 'li',
initialize : function(args) {
this.model = args.model;
this.compiledTemplate = _.template($('#template-user-list-item').text());
},
render : function() {
var templateData = {
'label' : {
'name' : this.model.get('name')
}
};
this.$el.append(this.compiledTemplate(templateData));
return this;
}
});

var UserRegisterView = Backbone.View.extend({
name : 'UserRegisterView',
events : {
'click #id_button_regist' : 'onClickRegistButton'
},
initialize : function(args) {
this.collection = args.collection;
this.compiledTemplate = _.template($('#template-user-register').text());
},
render : function() {
var templateData = {
'label' : {
'name' : '名前 : ',
'registButton' : '登録'
}
};
this.$el.append(this.compiledTemplate(templateData))
return this;
},
onClickRegistButton : function() { // (4)
console.log(this.name + '#onClickRegistButton');
this.collection.add({
'name' : this.$el.find('#id_input_name').val()
});
}
});

var BaseView = Backbone.View.extend({
name : 'BaseView',
initialize : function() { // (3)
this.collection = new UserCollection();
this.userListView = new UserListView({
'collection' : this.collection
});
this.userRegisterView = new UserRegisterView({
'collection' : this.collection
});
},
render : function() {
this.$el.append(this.userRegisterView.render().el).append(this.userListView.render().el);
return this;
}
});

var baseView = new BaseView();
$('body').append(baseView.render().el);
});


(1)ユーザーの情報を表す UserModel を定義します。
(2) UserModel の配列を表す UserCollection を定義します。
(3) BaseView がページ全体のViewになります。BaseView の initialize 関数で、UserCollection のオブジェクトを生成して、それぞれのSubViewに渡しています。
(4) UserRegisterView の onClickRegistButton 関数で、登録ボタン押下時に UserCollection にデータを追加しています。
(5) UserCollection にデータが追加された場合、add イベントを発火されます。その後、 UserListView の onAdd が実行され、UserCollection のデータを元にリストが描画されます。



■バリデーション

【デモはこちら】
【ソースファイルはこちら】

リストと登録ボタンを拡張します。
Modelの validate 関数を用いて、何も入力されていない状態で
登録ボタンを押下すると、アラートが表示されるように修正します。

main.js に下記の内容を記述します。


var UserModel = Backbone.Model.extend({
defaults : {
'name' : ''
},
validate : function(attribuite) { // (1)
if (_.isEmpty(attribuite.name)) {
return "名前が入力されていません";
}
}
});


(1) UserModel に validate 関数を追加します。ここでは、name 属性が空の場合、メッセージを返します。


var UserRegisterView = Backbone.View.extend({
name : 'UserRegisterView',
events : {
'click #id_button_regist' : 'onClickRegistButton'
},
initialize : function(args) {
this.collection = args.collection;
this.listenTo(this.collection, 'invalid', this.onInvalid);
this.compiledTemplate = _.template($('#template-user-register').text());
},
render : function() {
var templateData = {
'label' : {
'name' : '名前 : ',
'registButton' : '登録'
}
};
this.$el.append(this.compiledTemplate(templateData))
return this;
},
onClickRegistButton : function() {
console.log(this.name + '#onClickRegistButton');
this.collection.add({
'name' : this.$el.find('#id_input_name').val(),
}, {
validate : true // (1)
});
},
onInvalid : function(collection, message) { // (2)
console.log(this.name + '#onInvalid');
alert(message);
}
});


(1) UserCollection にデータを追加する引数に、validate : true を追記します。この追記で、 UserCollection の add 関数を実行すると、 UserCollection の validate 関数も実行されます。
(2) UserCollection の validate 関数が実行され、検証に失敗すると、invalid イベントが発火されます。その後、UserRegisterView の onInvalid 関数が実行され、アラートが表示されます。

以上です。



□過去の記事

Backbone.js入門その6「ModelとCollection」
Backbone.js入門その5「静的HTMLでBackbone.Viewを作る」
Backbone.js入門その4「SuperViewとSubViewのアクセスを作成する」
Backbone.js入門その3「Backbone.ViewでSubViewを作る」
Backbone.js入門その2「Backbone.Viewで複数の要素を作る」
Backbone.js入門その1「Backbone.Viewで1つの要素を作る」

Backbone.js入門その8「リストと削除ボタンを作る」

$
0
0

こんばんは。ゆんぼうです。

今回は、Backbone.jsを使用して、リストと削除ボタンを作成します。

今回使用する環境は下記の通りです。

Webブラウザ
・Mozilla Firefox (v34.05) https://www.mozilla.org/ja/firefox/new/
・FireBug (v2.0.7) https://addons.mozilla.org/ja/firefox/addon/firebug/

JavaScriptライブラリ
・Backbone.js (v1.1.2) http://backbonejs.org/
・Underscore.js (v1.7.0) http://underscorejs.org/
・jQuery (v2.1.1) http://jquery.com/



■リスト毎の削除ボタン

【デモはこちら】
【ソースファイルはこちら】

ここで作成する内容は下記の通りです。
リスト毎に、削除ボタンを表示します。
削除ボタンを押下すると、対象のリストが削除されます。

index.html の head に下記の内容を記述します。


<script type="text/template" id="template-user-list-item">
<%= label.name %>
<input type="button" class="cls_button_delete" value="<%= label.deleteButton %>">
</script>
<script type="text/template" id="template-user-register">
<label for="id_input_name"><%= label.name %></label>
<input id="id_input_name" type="text">
<br>
<input id="id_button_regist" type="button" value="<%= label.registButton %>">
<hr>
</script>


テンプレートID template-user-list-item はリスト1件分のテンプレート、
template-user-register が登録画面のテンプレートです。

main.js に下記の内容を記述します。


var UserListView = Backbone.View.extend({
name : 'UserListView',
tagName : 'ul',
initialize : function(args) {
this.collection = args.collection;
this.listenTo(this.collection, 'add', this.onAdd);
this.listenTo(this.collection, 'remove', this.onRemove);
},
render : function() {
this.$el.html('');
_.each(this.collection.models, function(model) {
this.$el.append((new UserListItemView({
'model' : model,
'collection' : this.collection
})).render().el);
}, this);
return this;
},
onAdd : function() {
console.log(this.name + '#onAdd');
this.render();
},
onRemove : function() { // (3)
console.log(this.name + '#onRemove');
this.render();
}
});

var UserListItemView = Backbone.View.extend({
name : 'UserListItemView',
tagName : 'li',
events : {
'click .cls_button_delete' : 'onClickDeleteButton'
},
initialize : function(args) {
this.model = args.model;
this.collection = args.collection;
this.compiledTemplate = _.template($('#template-user-list-item').text());
},
render : function() {
var templateData = {
'label' : {
'name' : this.model.get('name'),
'deleteButton' : '削除'
}
};
this.$el.append(this.compiledTemplate(templateData)); // (1)
return this;
},
onClickDeleteButton : function() {
console.log(this.name + '#onClickDeleteButton');
this.collection.remove(this.model); // (2)
}
});


(1) リスト1件分を表す UserListItemView に削除ボタンのテンプレートを追加します。
(2) 削除ボタンを押下したときに、collection.remove で collection から該当の model を削除します。
(3) collection には既に model が削除されています。remove イベントが発火されたときに、render 関数を呼び出しリストを再描画します。その結果、削除された後のリストが表示されます。



■チェックボックスリストと削除ボタン

【デモはこちら】
【ソースファイルはこちら】

先ほどは、リスト毎に削除しておりましたが、
ここでは、チェックボックスにチェックがあるリストを纏めて削除するものを作成します。

index.html のhead に下記の内容を記述します。


<script type="text/template" id="template-user-list">
<input id="id_button_delete" type="button" value="<%= label.deleteButton %>">
<hr>
<ul id="id_ul_user_list"></ul>
</script>
<script type="text/template" id="template-user-list-item">
<input class="cls_checkbox" type="checkbox"> <%= label.name %>
</script>
<script type="text/template" id="template-user-register">
<label for="id_input_name"><%= label.name %></label>
<input id="id_input_name" type="text">
<br>
<input id="id_button_regist" type="button" value="<%= label.registButton %>">
<hr>
</script>


テンプレートID template-user-list はリスト全体のテンプレートです。

main.js に下記の内容を記述します。


var UserModel = Backbone.Model.extend({
defaults : {
'name' : ''
},
isSelected : false, // (2)
validate : function(attribuite) {
if (_.isEmpty(attribuite.name)) {
return "名前が入力されていません";
}
}
});

var UserCollection = Backbone.Collection.extend({
model : UserModel,
removeSelectedModel : function() { // (5)
var selectedCollection = this.models.filter(function(model) {
return model.isSelected;
});
this.remove(selectedCollection);
}
});

var UserListView = Backbone.View.extend({
name : 'UserListView',
events : {
'click #id_button_delete' : 'onClickDeleteButton'
},
initialize : function(args) {
this.collection = args.collection;
this.listenTo(this.collection, 'add', this.onAdd);
this.listenTo(this.collection, 'remove', this.onRemove);
this.compiledTemplate = _.template($('#template-user-list').text());
},
render : function() {
var templateData = {
'label' : {
'deleteButton' : '削除'
}
};
this.$el.append(this.compiledTemplate(templateData)) // (1)
return this;
},
renderItem : function() {
this.$el.find('#id_ul_user_list').html('');
_.each(this.collection.models, function(model) {
this.$el.find('#id_ul_user_list').append((new UserListItemView({
'model' : model,
'collection' : this.collection
})).render().el);
}, this);
},
onAdd : function() {
console.log(this.name + '#onAdd');
this.renderItem();
},
onRemove : function() {
console.log(this.name + '#onRemove');
this.renderItem(); // (6)
},
onClickDeleteButton : function() {
console.log(this.name + '#onClickDeleteButton');
this.collection.removeSelectedModel(); // (4)
}
});

var UserListItemView = Backbone.View.extend({
name : 'UserListItemView',
tagName : 'li',
events : {
'click .cls_checkbox' : 'onClickCheckBox'
},
initialize : function(args) {
this.model = args.model;
this.collection = args.collection;
this.compiledTemplate = _.template($('#template-user-list-item').text());
},
render : function() {
var templateData = {
'label' : {
'name' : this.model.get('name')
}
};
this.$el.append(this.compiledTemplate(templateData));
return this;
},
onClickCheckBox : function(event) {
console.log(this.name + '#onClickCheckBox');
this.model.isSelected = event.target.checked; // (3)
}
});


(1) リスト全体を表す UserListView に削除ボタンのテンプレートを追加します。
(2) model に isSelected プロパティを追加します。
(3) チェックボックスをON/OFFすると、model の isSelected にチェック状態を代入します。
(4) 削除ボタンを押下すると、collection の removeSelectedModel 関数を実行します。
(5) isSelected=true である model を削除します。
(6) remove イベントが発火されたときに、render 関数を呼び出しリストを再描画します。

以上です。



□過去の記事

Backbone.js入門その7「リストと登録ボタンを作る」
Backbone.js入門その6「ModelとCollection」
Backbone.js入門その5「静的HTMLでBackbone.Viewを作る」
Backbone.js入門その4「SuperViewとSubViewのアクセスを作成する」
Backbone.js入門その3「Backbone.ViewでSubViewを作る」
Backbone.js入門その2「Backbone.Viewで複数の要素を作る」
Backbone.js入門その1「Backbone.Viewで1つの要素を作る」

Backbone.js入門その9「リストと詳細ボタンを作る」

$
0
0

こんばんは。ゆんぼうです。

今回は、Backbone.jsを使用して、リストと詳細ボタンを作成します。

今回使用する環境は下記の通りです。

Webブラウザ
・Mozilla Firefox (v34.05) https://www.mozilla.org/ja/firefox/new/
・FireBug (v2.0.7) https://addons.mozilla.org/ja/firefox/addon/firebug/

JavaScriptライブラリ
・Backbone.js (v1.1.2) http://backbonejs.org/
・Underscore.js (v1.7.0) http://underscorejs.org/
・jQuery (v2.1.1) http://jquery.com/



■リストと詳細ボタン

【デモはこちら】
【ソースファイルはこちら】

今回は、登録する項目を増やします。
リスト毎の詳細ボタンを押下すると、アラートが表示され、
登録した項目が表示されるものを作成します。


index.html の head に下記の内容を記述します。


<script type="text/template" id="template-user-list-item">
<%= label.name %>
<input type="button" class="cls_button_detail" value="<%= label.detailButton %>">
<input type="button" class="cls_button_delete" value="<%= label.deleteButton %>">
</script>
<script type="text/template" id="template-user-register">
<label for="id_input_name"><%= label.name %></label>
<input id="id_input_name" type="text">
<br>
<label for="id_input_age"><%= label.age %></label>
<input id="id_input_age" type="number" min="0" max="99">
<br>
<label for="id_select_gender"><%= label.gender %></label>
<select id="id_select_gender">
<option><%= label.male %></option>
<option><%= label.female %></option>
</select>
<br>
<label for="id_select_blood"><%= label.blood %></label>
<select id="id_select_blood">
<option>A</option>
<option>B</option>
<option>O</option>
<option>AB</option>
</select>
<br>
<label for="id_input_mail"><%= label.mail %></label>
<input id="id_input_mail" type="text">
<br>
<input id="id_button_regist" type="button" value="<%= label.registButton %>">
<hr>
</script>


テンプレートID template-user-list-item はリスト1件分のテンプレートです。
削除ボタンと詳細ボタンを表示します。

template-user-register が登録画面のテンプレートです。
名前、年齢、性別、血液型、メールアドレスの入力項目を表示します。

main.js に下記の内容を記述します。


var UserListItemView = Backbone.View.extend({
name : 'UserListItemView',
tagName : 'li',
events : {
'click .cls_button_detail' : 'onClickDetailButton',
'click .cls_button_delete' : 'onClickDeleteButton'
},
initialize : function(args) {
this.model = args.model;
this.collection = args.collection
this.compiledTemplate = _.template($('#template-user-list-item').text());
},
render : function() {
var templateData = {
'label' : {
'name' : this.model.get('name'),
'detailButton' : '詳細',
'deleteButton' : '削除'
}
};
this.$el.append(this.compiledTemplate(templateData));
return this;
},
onClickDetailButton : function() {
console.log(this.name + '#onClickDetailButton');
var message = '';
message += '\n名前 : ' + this.model.get('name'); // (1)
message += '\n年齢 : ' + this.model.get('age');
message += '\n性別 : ' + this.model.get('gender');
message += '\n血液型 : ' + this.model.get('blood');
message += '\nメールアドレス : ' + this.model.get('mailAddress');
alert(message); // (2)
},
onClickDeleteButton : function() {
console.log(this.name + '#onClickDeleteButton');
this.collection.remove(this.model);
}
});


(1) model.get で指定したプロパティを取得することができます。
(2) アラートに生成した文字列を渡しています。

以上です。



□過去の記事

Backbone.js入門その8「リストと削除ボタンを作る」
Backbone.js入門その7「リストと登録ボタンを作る」
Backbone.js入門その6「ModelとCollection」
Backbone.js入門その5「静的HTMLでBackbone.Viewを作る」
Backbone.js入門その4「SuperViewとSubViewのアクセスを作成する」
Backbone.js入門その3「Backbone.ViewでSubViewを作る」
Backbone.js入門その2「Backbone.Viewで複数の要素を作る」
Backbone.js入門その1「Backbone.Viewで1つの要素を作る」

Backbone.js入門その10「テーブルと登録ボタンを作る」

$
0
0

こんばんは。ゆんぼうです。

前回までは、登録ボタンを押すと、リストが追加されていきました。
今回は、Backbone.jsでテーブルを作成します。

今回使用する環境は下記の通りです。

Webブラウザ
・Mozilla Firefox (v34.05) https://www.mozilla.org/ja/firefox/new/
・FireBug (v2.0.7) https://addons.mozilla.org/ja/firefox/addon/firebug/

JavaScriptライブラリ
・Backbone.js (v1.1.2) http://backbonejs.org/
・Underscore.js (v1.7.0) http://underscorejs.org/
・jQuery (v2.1.1) http://jquery.com/



■ヘッダーが1つのテーブル

【デモはこちら】
【ソースファイルはこちら】

「名前」ヘッダーを持つテーブルと登録ボタンを作成します。
名前のテキストボックスに入力して、このボタンを押下すると、
テーブルに名前が追加されます。

index.html のhead に下記の内容を記述します。


<script type="text/template" id="template-user-table">
<table class="cls_table">
<thead>
<tr>
<th><%= header.name %></th>
</tr>
</thead>
<tbody id="id_tbody">
</tbody>
</table>
</script>
<script type="text/template" id="template-user-table-item">
<td><%= data.name %></td>
</script>


テーブル用のテンプレートを用意します。
テンプレートID template-user-table がテーブルのヘッダー、
template-user-table-item がテーブルのデータとなります。

main.js に下記の内容を記述します。


var UserTableView = Backbone.View.extend({
name : 'UserTableView',
initialize : function(args) {
this.collection = args.collection;
this.listenTo(this.collection, 'add', this.onAdd);
this.compiledTemplate = _.template($('#template-user-table').text());
},
render : function() {
var tempData = {
'header' : {
'name' : '名前'
}
};
this.$el.append(this.compiledTemplate(tempData)); // (1)
return this;
},
renderItem : function() {
this.$el.find('#id_tbody').html('');
_.each(this.collection.models, function(model) { // (3)
this.$el.find('#id_tbody').append((new UserTableItemView({
'model' : model
})).render().el);
}, this);
},
onAdd : function() {
console.log(this.name + '#onAdd');
this.renderItem(); // (2)
}
});


UserRegisterView は前回と同じ内容です。
今回は、テーブル全体を現す UserTableView とテーブルの1件分のデータを表す UserTableItemView を新たに作成します。

(1) UserTableView でテーブルのヘッダーを描画します。
(2)(3) 登録ボタンを押下すると、UserCollection にデータが追加され、add イベントが発火されます。onAdd 関数が呼び出され、renderItem 関数でテーブルデータを描画します。



■ヘッダーが複数のテーブル

【デモはこちら】
【ソースファイルはこちら】

先ほど作成したテーブルに
「名前」以外に、「年齢」「性別「血液型」「メールアドレス」のヘッダーを追加します。

index.html のhead に下記の内容を記述します。


<script type="text/template" id="template-user-table">
<table class="cls_table">
<thead>
<tr>
<th><%= header.name %></th>
<th><%= header.age %></th>
<th><%= header.gender %></th>
<th><%= header.blood %></th>
<th><%= header.mailAddress %></th>
</tr>
</thead>
<tbody id="id_tbody">
</tbody>
</table>
</script>
<script type="text/template" id="template-user-table-item">
<td><%= data.name %></td>
<td><%= data.age %></td>
<td><%= data.gender %></td>
<td><%= data.blood %></td>
<td><%= data.mailAddress %></td>
</script>


テンプレートにそれぞれ「年齢」「性別「血液型」「メールアドレス」を追加します。
また、UserModel にも同様に追加します。

以上です。



□過去の記事

Backbone.js入門その9「リストと詳細ボタンを作る」
Backbone.js入門その8「リストと削除ボタンを作る」
Backbone.js入門その7「リストと登録ボタンを作る」
Backbone.js入門その6「ModelとCollection」
Backbone.js入門その5「静的HTMLでBackbone.Viewを作る」
Backbone.js入門その4「SuperViewとSubViewのアクセスを作成する」
Backbone.js入門その3「Backbone.ViewでSubViewを作る」
Backbone.js入門その2「Backbone.Viewで複数の要素を作る」
Backbone.js入門その1「Backbone.Viewで1つの要素を作る」

Backbone.js入門その11「ソートテーブルを作る」

$
0
0

こんばんは。ゆんぼうです。

今回は、Backbone.jsを使用して、ヘッダーを押す度にソートを行うテーブルを作成します。

今回使用する環境は下記の通りです。

Webブラウザ
・Mozilla Firefox (v34.05) https://www.mozilla.org/ja/firefox/new/
・FireBug (v2.0.7) https://addons.mozilla.org/ja/firefox/addon/firebug/

JavaScriptライブラリ
・Backbone.js (v1.1.2) http://backbonejs.org/
・Underscore.js (v1.7.0) http://underscorejs.org/
・jQuery (v2.1.1) http://jquery.com/



■数値のソート

【デモはこちら】
【ソースファイルはこちら】

「年齢」ヘッダーを持つテーブルと登録ボタンを作成します。
年齢のテキストボックスに入力して、このボタンを押下すると、
テーブルに年齢が追加されます。
さらに、テーブルのヘッダーを押下するたびに、ソートが行われます。


index.html のhead に下記の内容を記述します。


<script type="text/template" id="template-user-table">
<table class="cls_table">
<thead>
<tr>
<th><a href="javascript:void(0)" class="cls_header" name="age"><%= header.age %><span class="cls_sort_icon"></span></a></th>
</tr>
</thead>
<tbody id="id_tbody">
</tbody>
</table>
</script>
<script type="text/template" id="template-user-table-item">
<td><%= data.age %></td>
</script>


テーブルのヘッダーとデータのテンプレートを作成します。
前回と異なる点は、ヘッダーにリンクを追加しています。

main.js に下記の内容を記述します。


var UserCollection = Backbone.Collection.extend({
model : UserModel,
sortAttribute : '',
sortDirection : 1,
sortToggle : function(attribute) {
this.sortDirection *= -1; // (3)
this.sortAttribute = attribute;
this.sort(); // (4)
},
comparator : function(model) {
return model.get(this.sortAttribute) * this.sortDirection; // (5)
}
});

var UserTableView = Backbone.View.extend({
name : 'UserTableView',
events : {
'click .cls_header' : 'onClickHeader'
},
initialize : function(args) {
this.collection = args.collection;
this.listenTo(this.collection, 'add', this.onAdd);
this.listenTo(this.collection, 'sort', this.onSort);
this.compiledTemplate = _.template($('#template-user-table').text());
this.currentHeader = null;
},
render : function() {
var tempData = {
'header' : {
'age' : '年齢'
}
};
this.$el.append(this.compiledTemplate(tempData));
return this;
},
renderItem : function() {
this.$el.find('#id_tbody').html('');
_.each(this.collection.models, function(model) {
this.$el.find('#id_tbody').append((new UserTableItemView({
'model' : model
})).render().el);
}, this);
},
renderClearSortIcon : function() {
_.each([ 'cls_sort_up', 'cls_sort_down' ], function(sortClass) {
this.$el.find("a[name='" + this.collection.sortAttribute + "'] span").removeClass(sortClass);
}, this);
},
renderSortIcon : function() {
var sortClass = null;
if (this.collection.sortDirection == 1) {
sortClass = 'cls_sort_up';
} else {
sortClass = 'cls_sort_down';
}
this.$el.find("a[name='" + this.collection.sortAttribute + "'] span").addClass(sortClass);
},
onAdd : function() {
console.log(this.name + '#onAdd');
this.renderItem();
},
onSort : function() {
console.log(this.name + '#onSort');
this.renderItem(); // (6)
this.renderSortIcon(); // (7)
},
onClickHeader : function(event) {
console.log(this.name + '#onClickHeader');
this.renderClearSortIcon(); // (1)
this.collection.sortToggle(event.target.name); // (2)
}
});


(1) テーブルのヘッダーをクリックすると、ソートの向きを表すソートアイコンを消去します。
(2) event.target.name は、aタグのname属性値「age」を指します。これを引数にして、UserCollection の sortToggle 関数を実行します。
(3) ソートの向きを指定します。関数が実行する度にソートの向きが逆になります。
(4) sort 関数で UserCollection のソートを実行します。
(5) sort 関数の内部で使用する比較関数 comparator を作成します。comparator の引数は、UserModel になります。UserModel から取り出したデータがソートされます。[+]は昇順、[-]は降順になります。
(6) ソートを実行すると、「sort」 イベントが発火されます。sort イベントが発火されると、テーブルのデータを再描画します。
(7) ソートアイコンを描画します。



■文字列のソート

【デモはこちら】
【ソースファイルはこちら】

さきほど作成したテーブルを
「年齢」から「名前」に変更します。

main.js に下記の内容を記述します。


var UserCollection = Backbone.Collection.extend({
model : UserModel,
sortAttribute : '',
sortDirection : 1,
sortToggle : function(attribute) {
this.sortDirection *= -1;
this.sortAttribute = attribute;
this.sort();
},
comparator : function(a, b) { // (1)
var a = a.get(this.sortAttribute), b = b.get(this.sortAttribute);
if (a == b) {
return 0;
}
if (this.sortDirection == 1) {
return b < a ? 1 : -1;
} else {
return a < b ? 1 : -1;
}
}
})


(1) テーブルのデータが数値から文字列に変更することで、comparator の比較方法も文字列の比較に変更します。文字列は Unicode のコード順に基づいて比較されます。




■数値・文字列の混在ソート

【デモはこちら】
【ソースファイルはこちら】

先ほど作成したテーブルに
「名前」「年齢」「性別「血液型」「メールアドレス」のヘッダーを追加します。

main.js に下記の内容を記述します。


var UserCollection = Backbone.Collection.extend({
model : UserModel,
sortAttribute : '',
sortDirection : 1,
initialize : function() {
var that = this;
this.sortComparators = { // (3)
'name' : that.comparatorString,
'age' : that.comparatorInteger,
'gender' : that.comparatorString,
'blood' : that.comparatorString,
'mailAddress' : that.comparatorString
}
},
sortToggle : function(attribute) {
if (this.sortAttribute == attribute) {
this.sortDirection *= -1;
} else {
this.sortDirection = 1;
}
this.sortAttribute = attribute;
this.comparator = this.sortComparators[attribute];
this.sort();
},
comparatorString : function(a, b) { // (1)
var a = a.get(this.sortAttribute), b = b.get(this.sortAttribute);
if (a == b) {
return 0;
}
if (this.sortDirection == 1) {
return b < a ? 1 : -1;
} else {
return a < b ? 1 : -1;
}
},
comparatorInteger : function(model) { // (2)
return model.get(this.sortAttribute) * this.sortDirection;
}
});


(1)(2) ヘッダーを追加した UserCollection では数値と文字列が混在しているため、数値用と文字列用の比較関数を作成します。
(3) データ毎に、比較関数を定義します。

以上です。



□過去の記事

Backbone.js入門その10「テーブルと登録ボタンを作る」
Backbone.js入門その9「リストと詳細ボタンを作る」
Backbone.js入門その8「リストと削除ボタンを作る」
Backbone.js入門その7「リストと登録ボタンを作る」
Backbone.js入門その6「ModelとCollection」
Backbone.js入門その5「静的HTMLでBackbone.Viewを作る」
Backbone.js入門その4「SuperViewとSubViewのアクセスを作成する」
Backbone.js入門その3「Backbone.ViewでSubViewを作る」
Backbone.js入門その2「Backbone.Viewで複数の要素を作る」
Backbone.js入門その1「Backbone.Viewで1つの要素を作る」


Backbone.js入門その12「ラジオボタンテーブルを作る」

$
0
0

こんばんは。ゆんぼうです。

今回は、Backbone.jsを使用して、ラジオボタンを表示するテーブルを作成します。

今回使用する環境は下記の通りです。

Webブラウザ
・Mozilla Firefox (v34.05) https://www.mozilla.org/ja/firefox/new/
・FireBug (v2.0.7) https://addons.mozilla.org/ja/firefox/addon/firebug/

JavaScriptライブラリ
・Backbone.js (v1.1.2) http://backbonejs.org/
・Underscore.js (v1.7.0) http://underscorejs.org/
・jQuery (v2.1.1) http://jquery.com/



■ラジオボタンのテーブル

【デモはこちら】
【ソースファイルはこちら】

ラジオボタン付きのテーブルと登録・削除ボタンを作成します。
名前のテキストボックスに入力して、登録ボタンを押下すると、
テーブルにラジオボタンと名前が追加されます。
ラジオボタンを選択して、削除ボタンを押下すると、そのテーブルデータが削除されます。

index.html のhead に下記の内容を記述します。


<script type="text/template" id="template-user-table-item">
<th>
<input class="cls_radio_button" type="radio" name="item">
</th>
<td><%= data.name %></td>
</script>


テーブルデータ1件分のテンプレート template-user-table-item を作成します。
ラジオボタンのスタイルクラス名は cls_radio_button と定義します。

main.js に下記の内容を記述します。


var UserCollection = Backbone.Collection.extend({
model : UserModel,
removeSelectedModel : function() {
this.remove(this.selectedModel); // (5)
},
selectedModel : null
});

var UserTableView = Backbone.View.extend({
name : 'UserTableView',
events : {
'click #id_button_delete' : 'onClickDeleteButton' // (3)
},
initialize : function(args) {
this.collection = args.collection;
this.listenTo(this.collection, 'add', this.onAdd);
this.listenTo(this.collection, 'remove', this.onRemove);
this.compiledTemplate = _.template($('#template-user-table').text());
},
render : function() {
var tempData = {
'header' : {
'name' : '名前'
},
'label' : {
'deleteButton' : '削除'
}
};
this.$el.append(this.compiledTemplate(tempData));
return this;
},
renderItem : function() {
this.$el.find('#id_tbody').html('');
_.each(this.collection.models, function(model) {
this.$el.find('#id_tbody').append((new UserTableItemView({
'model' : model,
'collection' : this.collection
})).render().el);
}, this);
},
onAdd : function() {
console.log(this.name + '#onAdd');
this.renderItem();
},
onRemove : function() {
console.log(this.name + '#onRemove');
this.renderItem();
},
onClickDeleteButton : function(event) {
console.log(this.name + '#onClickDeleteButton');
this.collection.removeSelectedModel(); // (4)
}
});

var UserTableItemView = Backbone.View.extend({
name : 'UserTableItemView',
tagName : 'tr',
events : {
'click .cls_radio_button' : 'onClickRadioButton' // (1)
},
initialize : function(args) {
this.model = args.model;
this.collection = args.collection;
this.compiledTemplate = _.template($('#template-user-table-item').text());
},
render : function() {
var templateData = {
'data' : this.model.toJSON()
};
this.$el.append(this.compiledTemplate(templateData));
return this;
},
onClickRadioButton : function(event) {
console.log(this.name + '#onClickRadioButton:' + event.target.checked);
this.collection.selectedModel = this.model; // (2)
}
});


(1) ラジオボタンのクリックイベントを監視します。
(2) ラジオボタンを押下すると、コレクションのプロパティ selectedModel にモデルを設定します。押下される度に、押下対象のモデルを設定します。
(3) 削除ボタンのクリックイベントを監視します。
(4) 削除ボタンを押下すると、コレクションの removeSelectedModel 関数を呼びます。
(5) コレクションの remove 関数で、selectedModel に設定されているモデルを削除します。



■ラジオボタンのテーブル (ソート・複数のヘッダー)

先ほどのラジオボタンのテーブルに
複数のヘッダーとソート機能を追加します。
しかし、そのまま追加すると、ソートの再描画時にラジオボタンが消去されてしまうため、
再描画時に、ラジオボタンの表示判定を行います。

【デモはこちら】
【ソースファイルはこちら】

main.js に下記の内容を記述します。


var UserCollection = Backbone.Collection.extend({
model : UserModel,
sortAttribute : '',
sortDirection : 1,
initialize : function() {
var that = this;
this.sortComparators = {
'name' : that.comparatorString,
'age' : that.comparatorInteger,
'gender' : that.comparatorString,
'blood' : that.comparatorString,
'mailAddress' : that.comparatorString
}
},
sortToggle : function(attribute) {
if (this.sortAttribute == attribute) {
this.sortDirection *= -1;
} else {
this.sortDirection = 1;
}
this.sortAttribute = attribute;
this.comparator = this.sortComparators[attribute];
this.sort();
},
comparatorString : function(a, b) {
var a = a.get(this.sortAttribute), b = b.get(this.sortAttribute);
if (a == b) {
return 0;
}
if (this.sortDirection == 1) {
return b < a ? 1 : -1;
} else {
return a < b ? 1 : -1;
}
},
comparatorInteger : function(model) {
return model.get(this.sortAttribute) * this.sortDirection;
},
removeSelectedModel : function() {
this.remove(this.selectedModel);
},
selectModel : function(model) {
_.each(this.models, function(model) { // (2)
model.isSelected = false;
}, this);
model.isSelected = true;
this.selectedModel = model;
}
});
var UserTableItemView = Backbone.View.extend({
name : 'UserTableItemView',
tagName : 'tr',
events : {
'click .cls_radio_button' : 'onClickRadioButton'
},
initialize : function(args) {
this.model = args.model;
this.collection = args.collection;
this.compiledTemplate = _.template($('#template-user-table-item').text());
},
render : function() {
var templateData = {
'data' : this.model.toJSON()
};
this.$el.append(this.compiledTemplate(templateData));
if (this.model.isSelected) { // (3)
this.$el.find('.cls_checkbox').prop('checked', true)
}
return this;
},
onClickRadioButton: function(event) {
console.log(this.name + '#onClickRadioButton:' + event.target.checked);
this.collection.selectModel(this.model); // (1)
}
});


(1) ラジオボタンを押下すると、モデルを引数にして、コレクションの selectModel 関数を呼び出します。
(2) モデルの選択フラグ isSelected を全て false にしてから、現在選択されているモデルの isSelected を true にします。また、selectedModel にモデルを設定します。
(3) テーブルデータの描画時に、モデルの isSelected を判定して、選択中であれば、ラジオボタンにチェックを入れます。

以上です。



□過去の記事

Backbone.js入門その11「ソートテーブルを作る」
Backbone.js入門その10「テーブルと登録ボタンを作る」
Backbone.js入門その9「リストと詳細ボタンを作る」
Backbone.js入門その8「リストと削除ボタンを作る」
Backbone.js入門その7「リストと登録ボタンを作る」
Backbone.js入門その6「ModelとCollection」
Backbone.js入門その5「静的HTMLでBackbone.Viewを作る」
Backbone.js入門その4「SuperViewとSubViewのアクセスを作成する」
Backbone.js入門その3「Backbone.ViewでSubViewを作る」
Backbone.js入門その2「Backbone.Viewで複数の要素を作る」
Backbone.js入門その1「Backbone.Viewで1つの要素を作る」

Backbone.js入門その13「チェックボックステーブルを作る」

$
0
0

こんばんは。ゆんぼうです。

今回は、Backbone.jsを使用して、チェックボックスを表示するテーブルを作成します。

今回使用する環境は下記の通りです。

Webブラウザ
・Mozilla Firefox (v34.05) https://www.mozilla.org/ja/firefox/new/
・FireBug (v2.0.7) https://addons.mozilla.org/ja/firefox/addon/firebug/

JavaScriptライブラリ
・Backbone.js (v1.1.2) http://backbonejs.org/
・Underscore.js (v1.7.0) http://underscorejs.org/
・jQuery (v2.1.1) http://jquery.com/



■チェックボックスのテーブル

【デモはこちら】
【ソースファイルはこちら】

チェックボックス付きのテーブルと登録・削除ボタンを作成します。
名前のテキストボックスに入力して、登録ボタンを押下すると、
テーブルにチェックボックスと名前が追加されます。
チェックボックスを選択して、削除ボタンを押下すると、そのテーブルデータが削除されます。
また、全てを選択するチェックボックスを押下すると、各チェックボックスが併せて変更されます。

index.html のhead に下記の内容を記述します。


<script type="text/template" id="template-user-table">
<input id="id_button_delete" type="button" value="<%= label.deleteButton %>">
<hr>
<table class="cls_table">
<thead>
<tr>
<th><input class="cls_all_checkbox" type="checkbox"></th>
<th><%= header.name %></th>
</tr>
</thead>
<tbody id="id_tbody">
</tbody>
</table>
</script>
<script type="text/template" id="template-user-table-item">
<th>
<input class="cls_checkbox" type="checkbox">
</th>
<td><%= data.name %></td>
</script>


テーブル全体のテンプレート template-user-table を作成します。
全て選択用のチェックボックスのスタイルクラス名は cls_all_checkbox と定義します。

テーブルデータ1件分のテンプレート template-user-table-item を作成します。
チェックボックスのスタイルクラス名は cls_checkbox と定義します。

main.js に下記の内容を記述します。


var UserCollection = Backbone.Collection.extend({
model : UserModel,
removeSelectedModel : function() {
var selectedCollection = this.models.filter(function(model) { // (17)
return model.isSelected;
});
this.remove(selectedCollection); // (18)
},
isSelectedAll : false,
selectModelAll : function(isSelected) {
this.isSelectedAll = isSelected;
_.each(this.models, function(model) { // (10)
model.isSelected = isSelected;
});
this.trigger('selectAll'); // (11)
},
selectModel : function() {
this.isSelectedAll = this.models.every(function(model) { // (3)
return model.isSelected
});
this.trigger('select'); // (4)
}
});

var UserTableView = Backbone.View.extend({
name : 'UserTableView',
events : {
'click .cls_all_checkbox' : 'onClickAllCheckbox', // (8)
'click #id_button_delete' : 'onClickDeleteButton' // (15)
},
initialize : function(args) {
this.collection = args.collection;
this.listenTo(this.collection, 'add', this.onAdd);
this.listenTo(this.collection, 'remove', this.onRemove); // (19)
this.listenTo(this.collection, 'select', this.onSelect); // (5)
this.listenTo(this.collection, 'selectAll', this.onSelectAll); // (12)
this.compiledTemplate = _.template($('#template-user-table').text());
this.currentHeader = null;
},
render : function() {
var tempData = {
'header' : {
'name' : '名前'
},
'label' : {
'deleteButton' : '削除'
}
};
this.$el.append(this.compiledTemplate(tempData));
return this;
},
renderItem : function() {
this.$el.find('#id_tbody').html('');
_.each(this.collection.models, function(model) {
this.$el.find('#id_tbody').append((new UserTableItemView({
'model' : model,
'collection' : this.collection
})).render().el);
}, this);
},
renderSelectAll : function() {
this.$el.find('.cls_all_checkbox').prop('checked', this.collection.isSelectedAll) // (7)
},
onAdd : function() {
console.log(this.name + '#onAdd');
this.renderItem();
},
onRemove : function() {
console.log(this.name + '#onRemove');
this.renderItem(); // (20)
},
onSelect : function() {
console.log(this.name + '#onSelect');
this.renderSelectAll(); // (6)
},
onSelectAll : function() {
console.log(this.name + '#onSelectAll');
this.renderItem(); // (13)
},
onClickAllCheckbox : function(event) {
console.log(this.name + '#onClickAllCheckbox');
this.collection.selectModelAll(event.target.checked); // (9)
},
onClickDeleteButton : function(event) {
console.log(this.name + '#onClickDeleteButton');
this.collection.removeSelectedModel(); // (16)
}
});

var UserTableItemView = Backbone.View.extend({
name : 'UserTableItemView',
tagName : 'tr',
events : {
'click .cls_checkbox' : 'onClickCheckbox' // (1)
},
initialize : function(args) {
this.model = args.model;
this.collection = args.collection;
this.compiledTemplate = _.template($('#template-user-table-item').text());
},
render : function() {
var templateData = {
'data' : this.model.toJSON()
};
this.$el.append(this.compiledTemplate(templateData));
if (this.model.isSelected) {
this.$el.find('.cls_checkbox').prop('checked', true) // (14)
}
return this;
},
onClickCheckbox : function(event) {
console.log(this.name + '#onClickCheckbox:' + event.target.checked);
this.model.isSelected = event.target.checked; // (2)
this.collection.selectModel();
}
});


各チェックボックスを押下したとき

(1) 各テーブルデータのチェックボックスのクリックイベントを監視します。
(2) チェックボックスを押下したとき、モデルの選択フラグ isSelected にチェック状態を設定します。また、コレクションの selectModel 関数を呼びます。
(3) コレクションの every 関数を使用して、全てのモデルの isSelected が true かどうかを真偽値を isSelectedAll に設定します。
(4) select イベントを発火します。
(5) select イベントを監視します。
(6) 全て選択用のチェックボックスを描画します。
(7) 全て選択用のチェックボックスのチェック状態は、コレクションの isSelectedAll プロパティの値で設定します。

全て選択用のチェックボックスを押下したとき

(8) 全て選択用のチェックボックスのクリックイベントを監視します。
(9) 全て選択用のチェックボックスを押下したとき、チェック状態を引数にして、コレクションの selectModelAll 関数を呼び出します。
(10) 全てのモデルの isSelected を同じ値に設定します。
(11) selectAll イベントを発火します。
(12) selectAll イベントを監視します。
(13) 各テーブルデータを描画します。
(14) モデルの isSelected が true のとき、チェックボックスのチェックを設定します。

削除ボタンを押下したとき

(15) 削除ボタンのクリックイベントを監視します。
(16) 削除ボタンを押下したとき、コレクションの removeSelectedModel 関数を呼びます。
(17) filter 関数を使用して、isSelected が true のモデル配列を取得します。
(18) コレクションの remove 関数を呼び出して、モデルを削除します。モデル配列が複数あるときは、複数のモデルを削除します。
(19) remove イベントを監視します。
(20) 各テーブルデータを描画します。



■チェックボックスのテーブル (ソート・複数のヘッダー)

【デモはこちら】
【ソースファイルはこちら】

先ほどのチェックボックスのテーブルに、複数のヘッダーとソート機能を追加します。
これらの追加に対する処理はありません。そのまま追加します。
リンク先のソースファイルをご覧ください。

以上です。



□過去の記事

Backbone.js入門その11「ソートテーブルを作る」
Backbone.js入門その10「テーブルと登録ボタンを作る」
Backbone.js入門その9「リストと詳細ボタンを作る」
Backbone.js入門その8「リストと削除ボタンを作る」
Backbone.js入門その7「リストと登録ボタンを作る」
Backbone.js入門その6「ModelとCollection」
Backbone.js入門その5「静的HTMLでBackbone.Viewを作る」
Backbone.js入門その4「SuperViewとSubViewのアクセスを作成する」
Backbone.js入門その3「Backbone.ViewでSubViewを作る」
Backbone.js入門その2「Backbone.Viewで複数の要素を作る」
Backbone.js入門その1「Backbone.Viewで1つの要素を作る」

Kibana 4.3 に Basic 認証機能をつけるプラグインをつくってみた。

$
0
0

どうも、ちかです
最近めっちゃ忙しい。。

Kibana ってこんなの

Elasticsearch のデータを可視化するツールです。

こういうブログ記事では
まず導入として
Kibana ってこんなの」っていう話をするものですが
すでに知ってる人向けの記事ということで。

Kibana プラグイン

Kibana 4.2 からプラグインで機能を追加できるようになりました。

サーバー側は Node.js のフレームワーク hapi ベースで
クライアント側は AngularJS ベースで
とりあえずなんでもできます。

特に Elasticsearch へのアクセスは
サーバーから手軽にできるようになっています。
サーバー側でプロキシを作ればクライアントからも簡単にアクセスできます。

# Elasticsearch への GET メソッドのクチはデフォルトで開いてます。
# elasticsearch.js の API を
# サーバーでは server.plugins.elasticsearch.client として使えます。
# クライアントでは es という名前で依存性注入できます。

Kibana プラグインの作り方

Qiita に
Kibanaプラットフォームでアプリケーションをつくってみよう
という濃いぃ記事があるのでご参考に。

ミニマム構成では index.js と package.json があれば OK。

認証プラグイン


Kibana を使ってみて気になったのが
認証機能がないこと。

Shield という Elasticsearch プラグインがあるらしいのですが
有償らしいので (セキュリティ向けプラグインShieldのリリース(日本語訳))
とりあえずあきらめて

Authentication という Issue ページも立っているのですが
4.2 ⇒ 4.3 ⇒ 4.4 と先延ばし先延ばしにされているので
とりあえずあきらめて

作ってみました。

  • index.js
const Boom = require('boom');var users = {  chica: {    password: 'secret'  }};module.exports = kibana => new kibana.Plugin({  init(server, options) {    server.auth.scheme('basic', basic);    server.auth.strategy('default', 'basic');    server.auth.default('default');  }});function basic(server, options) {  return {    authenticate(request, reply) {      const authorization = request.raw.req.headers.authorization;      if (!authorization) {        return reply(Boom.unauthorized('Authentication required', 'Basic'));      }      const parts = authorization.split(/\s+/);      if (parts.length !== 2 || parts[0].toLowerCase() !== 'basic') {        return reply(Boom.unauthorized('Authentication failed.', 'Basic'));      }      const credentials = new Buffer(parts[1], 'base64').toString().split(':', 2);      const user = users[credentials[0]];      if (!user || user.password !== credentials[1]) {        return reply(Boom.unauthorized('Authentication failed.', 'Basic'));      }      return reply.continue({ credentials: { user: user } });    }  };}
  • package.json
{  "name": "basic-authentication",  "version": "0.0.1"}

index.js と package.json を tar.gz に固めて

kibana/bin/kibana plugin -i basic-authentication -u file://path/to/plugin.tar.gz

こんなので
一応ちゃんと認証が求められるようになります。

まぁでも
これを洗練するより
4.4 が出るのを待つかな。(本末転倒)

今回はこんなところで。

インラインスクリプトは本当に悪か? 「控えめなJavaScript」再考

$
0
0

はじめに

ちかです
毎度のことですが個人の見解です。
会社の見解ではありません。
むしろ他の社員からは反対意見が出そうな話です。

Web ページの構成について「控えめな JavaScript (Unobtrusive JavaScript)」という慣行があります。
太古の昔、 JavaScript は馬に乗って行列を作って HTML 上を走り回っていたそうです。
「控えめな JavaScript」は革命を起こし、横柄な JavaScript とは対極の立場を取りました。

『インラインスクリプトは絶対に書きません!』

でもこれはあまりに極端では?? というのが今回の記事です。

「控えめな JavaScript」信奉者の主張

『Rails で JavaScript を使用する』の例を見てみます。

最もシンプルなJavaScriptを例にとって考えてみましょう。以下のような書き方は'インラインJavaScript'と呼ばれています。

<a href="#" onclick="this.style.backgroundColor='#990000'">Paint it red</a>

このリンクをクリックすると、背景が赤くなります。しかし早くもここで問題が生じ始めます。クリックした時にJavaScriptでもっといろんなことをさせるとどうなるでしょうか。

<a href="#" onclick="this.style.backgroundColor='#009900';this.style.color='#FFFFFF';">Paint it green</a>

だいぶ乱雑になってきました。ではここで関数定義をclickハンドラの外に追い出し、CoffeeScriptで書き換えてみましょう。

paintIt = (element, backgroundColor, textColor) ->  element.style.backgroundColor = backgroundColor  if textColor?    element.style.color = textColor

ページの内容は以下のとおりです。

<a href="#" onclick="paintIt(this, '#990000')">Paint it red</a>

これでコードがだいぶ良くなりました。しかし、同じ効果を複数のリンクに与えるとどうなるでしょうか。

<a href="#" onclick="paintIt(this, '#990000')">Paint it red</a><a href="#" onclick="paintIt(this, '#009900', '#FFFFFF')">Paint it green</a><a href="#" onclick="paintIt(this, '#000099', '#FFFFFF')">Paint it blue</a>

これではDRYとは言えません。今度はイベントを活用して改良してみましょう。最初にdata-*属性をリンクに追加しておきます。続いて、この属性を持つすべてのリンクで発生するクリックイベントにハンドラをバインドします。

paintIt = (element, backgroundColor, textColor) ->  element.style.backgroundColor = backgroundColor  if textColor?    element.style.color = textColor $ ->  $("a[data-background-color]").click (e) ->    e.preventDefault()     backgroundColor = $(this).data("background-color")    textColor = $(this).data("text-color")    paintIt(this, backgroundColor, textColor)
<a href="#" data-background-color="#990000">Paint it red</a><a href="#" data-background-color="#009900" data-text-color="#FFFFFF">Paint it green</a><a href="#" data-background-color="#000099" data-text-color="#FFFFFF">Paint it blue</a>

私たちはこの手法を「控えめなJavaScript」と呼んでいます。この名称は、HTMLの中にJavaScriptを混入させないという意図に由来しています。JavaScriptを正しく分離することができたので、今後の変更が容易になりました。今後は、このdata-*属性をリンクタグに追加するだけでこの動作を簡単に追加できます。

直接 onclick とは書いていないだけ

> JavaScriptを正しく分離することができたので、今後の変更が容易になりました。今後は、このdata-*属性をリンクタグに追加するだけでこの動作を簡単に追加できます。

とおっしゃっておられますが
分離前でも onclick="paintIt(this, '#990000')" のような記述を追加するだけでこの動作を簡単に追加できていました。
なにをもって分離後の方が「変更が容易」と言えるのでしょう?
「JavaScriptを正しく分離することができたので」では全く理由になっていません。
(「これではDRYとは言えません。」という部分も何が DRY でないのか自明でなく説明もありません。。)

コンテンツ (HTML) と振る舞い (JavaScript) と見た目 (CSS) とを別ファイルにする目的は
関心を分離して互いを抽象化することにあると思うのですが
マークアップには振る舞いを表現するためのクラスや data-* 属性が必要で
抽象化は実現できておらず
何が変わったかと言えば単に「直接 onclick とは書いていない」だけです。

実際には関心は分離できていないのです。

grep しないと理解できないコード

それどころか
無視できない大きなデメリットがあります。
関係のある記述が散らばることです。

端的にビフォーアフターで比較しましょう。
レンダリング結果が予測しやすいのはどちらでしょうか?

<a href="#" onclick="paintIt(this, '#000099', '#FFFFFF')">Paint it blue</a>
<a href="#" data-background-color="#000099" data-text-color="#FFFFFF">Paint it blue</a>

コードを書いているときはまだいいのですが
後になって HTML を読んでみると
data-background-color 属性が何を表しているのか
見た目にどう影響するのか
振る舞いにどう影響するのか
grep しなければ分かりません。

やがて HTML は得体の知れないクラス名や属性で埋め尽くされます。
そして JavaScript はイベントリスナーの登録処理で埋め尽くされます。
ビジネスロジックに集中できない!

『間違ったコードは間違って見えるようにする』 の言葉を少し借りると、

関係するものを隣り合わせにしておくことでコードを改善できる例はたくさんある。コーディング規則の多くは以下のような規則を含んでいる:

  • 関数を短くする。
  • 変数は使う場所のできるだけ近くで宣言する。
  • 自分のプログラミング言語を作ろうとしてマクロを使わない。
  • gotoを使わない。
  • 閉じる括弧は、対応する開く括弧から1画面以上離さない。

これらのルールに共通しているのは、1行のコードが実際にすることに関連した情報を可能な限り物理的に近づけるということだ。そうすることによって、あなたの目玉が何が起きているのか理解できる可能性が高くなる。

(だからといってハンガリアン記法はオススメしませんが。。)

もっと地味な問題: コードの恣意性

もっと地味ぃ~な問題もあります。
コードの恣意性が強くなることです。

例えば:
div 要素 A ではクリック時に処理 1 の結果をもとに処理 2 を行いたいとします。
div 要素 B ではクリック時に処理 1 の結果をもとに処理 3 を行いたいとします。

インラインスクリプトで実現するならこうです。

<div onclick="procedure2(procedure1('arg1'));"> ... </div><div onclick="procedure3(procedure1('arg2'));"> ... </div>

インラインスクリプトを使わないコードを想像してみてください。
しっくり来るでしょうか?
その「しっくり来たコード」は誰が考えても似たようになるでしょうか?
しっくり来るまでにかかる時間はインラインスクリプトで実現するのに比べて長くならないでしょうか?
(この例ではインラインスクリプトが見えているのでアレですが)

これらは「やり方がいろいろある」ことの弊害です。
スクリプトを分離しようと考えると、コードの恣意性が高まります。
(恣意性が高くなる原因についてはパッと説明できませんが。。)

やり方がいろいろあると
思いつく限りの方法の良し悪しを比較する必要があり
「しっくり来る」のに時間がかかるのです。
間違えようのないやり方がひとつだけあるのがいいと考えるのは私だけではないはずです。

React

コンテンツとその振る舞いは切っても切れません。
コンテンツとその振る舞いは同じ場所に記述した方がメンテナンスしやすくなります。
などと私ごときが言っても伝わらないかもしれませんが...
React の JSX コードがどうして次のようになるのか (次のように書けると何が嬉しいのか)
考えてみてはいかがでしょうか。

  • DOM Event Listeners in a Component | React
    var Box = React.createClass({  getInitialState: function() {    return {windowWidth: window.innerWidth};  },  handleResize: function(e) {    this.setState({windowWidth: window.innerWidth});  },  componentDidMount: function() {    window.addEventListener('resize', this.handleResize);  },  componentWillUnmount: function() {    window.removeEventListener('resize', this.handleResize);  },  render: function() {    return <div>Current window width: {this.state.windowWidth}</div>;  }});ReactDOM.render(<Box />, mountNode);

だいたい以上です

インラインスクリプト禁止の弊害について書いてきました。
やたらめったらインラインスクリプトを使えと言うつもりはありませんし
プロジェクトのポリシーとしてインラインスクリプト禁止と言われれば私もインラインスクリプトは使いません。
ただ
思考を停止してインラインスクリプトを悪とする風潮に対しては冷静になりたいと思った次第です。

p.s. インラインスタイルについて

インラインスタイルが悪かどうかについては検討できていません。
いろいろ思うところはありますが
ノーコメントにしておきます。

[Python 3.5] async for 文で利用するスリープソートを実装してみた。

$
0
0

ちかです
最近は TypeScript に浸かっています。
もう何年も Python に触れていなかったのですが
Python 3.5 には型ヒントやらコルーチンのための構文 (async/await) やら追加されて
少し気になっていました。

型ヒントについては Python と型ヒント (Type Hints) を読んで
構造的部分型の型チェック (TypeScript みたいな) ができるようになることに期待しつつ放置。

async/await は (少なくとも他の言語では神機能だし) 使ってみようかと。
特に非同期イテレーション専用の構文 async for がおもしろそう!
ということで
以前 Go 言語と D 言語でスリープソートを実装してみたのとの比較も込めて
スリープソートを実装してみました。

import asynciofrom contextlib import closingclass ArrivalOrderedIterator:    ''' コルーチン (または Future) のリストを受け取り、結果の到着順に結果値を返す非同期イテレーターを表します。 '''    def __init__(self, coroutines):        self.completed = set()        self.incomplete = [asyncio.ensure_future(co) for co in coroutines]     async def __aiter__(self):        return self     async def __anext__(self):        if self.completed:            return self.completed.pop().result()        if not self.incomplete:            raise StopAsyncIteration()        self.completed, self.incomplete = await asyncio.wait(self.incomplete, return_when=asyncio.FIRST_COMPLETED)        return self.completed.pop().result()class sleepsort:    ''' 数値のリストをスリープソートする非同期シーケンスを表します。 '''    def __init__(self, values):        self.values = values     async def __aiter__(self):        return ArrivalOrderedIterator(asyncio.sleep(x, x) for x in self.values)if __name__ == '__main__':    async def main():        async for value in sleepsort([5, 2, 19, 11, 7, 13, 3, 17]):            print(value)    with closing(asyncio.get_event_loop()) as loop:        loop.run_until_complete(main())

故意に async for 文を使っています (31 行目)。
そのせいで必要以上に長いコードになっているかもしれません。

async for 文を使うことで非同期のイテレーションを読みやすく記述できます。
...利用側では。

async for value in async_iterable:  do_something(value)

非同期イテレーターの実装側では
__anext__() メソッドで「次の値」を返すか StopAsyncIteration を発生させてループを終了させます。
基本的にはクラスを作成して現在の状態をフィールドに持たせることになると思います。
ジェネレーター関数みたいに記述できたらラクなのですが。。

また
Reactive Extensions (Rx) を彷彿とさせますが (私だけ?)
内包表記や map/filter などは使えないようで
必要なら自前で用意する必要がありそうです。。
((i * i async for value in sleepsort(values) if i < 10) みたいな構文はないみたいです。)
参考: python - how can I asynchronously map/filter an asynchronous iterable? - Stack Overflow

ということで Python 3.5 の非同期イテレーション構文の紹介でした。

追記: ちなみに async for を使わず素直にコールバック形式で書くなら:

import asynciofrom contextlib import closingasync def sleepsort(values, callback):    ''' 数値のリストをスリープソートします。 '''    incomplete = [asyncio.ensure_future(asyncio.sleep(x, x)) for x in values]    while incomplete:        completed, incomplete = await asyncio.wait(incomplete, return_when=asyncio.FIRST_COMPLETED)        while completed:            callback(completed.pop().result())if __name__ == '__main__':    with closing(asyncio.get_event_loop()) as loop:        loop.run_until_complete(sleepsort([5, 2, 19, 11, 7, 13, 3, 17], print))

追々記: map/filter を実装してみました。

import asynciofrom functools import partialfrom contextlib import closingclass AsyncIterator:    ''' 「次の値を取得する関数」から非同期イテレーターを構築します。 '''    def __init__(self, anext):        self.anext = anext    async def __aiter__(self):        return self    async def __anext__(self):        return await self.anext()def async_iterable(next_getter_creator):    ''' 「「次の値を取得する関数」を取得する関数」から非同期シーケンスを構築します。 '''    class AsyncIterable:        def __init__(self, *args, **kwds):            self.args = args            self.kwds = kwds        async def __aiter__(self):            return AsyncIterator(await next_getter_creator(*self.args, **self.kwds))    return AsyncIterable@async_iterableasync def async_map(selector, source):    ''' 非同期シーケンスを射影します。 '''    iterator = await source.__aiter__()    async def next():        return selector(await iterator.__anext__())    return next@async_iterableasync def async_filter(predicate, source):    ''' 非同期シーケンスを絞り込みます。 '''    iterator = await source.__aiter__()    async def next():        while True:            value = await iterator.__anext__()            if predicate(value):                return value    return next@async_iterableasync def sleepsort(values):    ''' 数値のリストをスリープソートする非同期シーケンスを表します。 '''    # tasks = (completed, incomplete)    tasks = [set(), [asyncio.ensure_future(asyncio.sleep(x, x)) for x in values]]    async def next():        if tasks[0]:            return tasks[0].pop().result()        if not tasks[1]:            raise StopAsyncIteration()        tasks[0], tasks[1] = await asyncio.wait(tasks[1], return_when=asyncio.FIRST_COMPLETED)        return tasks[0].pop().result()    return nextdef chain(source, *selectors):    ''' f(g(h(x))) を chain(x, h, g, f) と記述するメソッドチェイン機構を提供します。 '''    for selector in selectors:        source = selector(source)    return sourceif __name__ == '__main__':    async def main():        async for value in chain(            sleepsort([5, 2, 19, 11, 7, 13, 3, 17]),            partial(async_map, lambda x: x * x),            partial(async_filter, lambda x: 10 < x < 200)        ):            print(value)    with closing(asyncio.get_event_loop()) as loop:        loop.run_until_complete(main())

出力結果

2549121169

(開始 5 秒後に 25, 7 秒後に 49, ... が出力されます)

Windows2012とWindows2008でのサービスの状態欄の差異

$
0
0

サービスマネージャで任意のサービスを開始した時、
開始したサービスの状態欄がOSによって下記のように
異なりますが、同じ状態を表しています。

・Windows2012→「実行中」
・Windows2008→「開始」

サービスの状態は、コマンドプロンプトで所定の場所で
以下のコマンドを打つことでも確認できます。

C:\Windows\System32 > sc query <任意のサービス名>

※sc queryについては下記参照
https://technet.microsoft.com/ja-jp/en-vs/library/dd228922.aspx




Windows10のSilentCleanupについて

$
0
0
SilentCleanup(ディスクのクリーンアップ処理)はWindows8.1から導入
されましたが、Windows8.1でSilentCleanupが自動実行されるのは、
ディスク容量が逼迫した時だけでした。

Windows10では上記に加え、PCがidle(何の処理もしていない)状態で
あると判断された時もSilentCleanupが自動実行されます。

なお、SilentCleanupが自動実行されるのは管理者ユーザでログオン
している時です。

ダウンロードしたActiveXがいつのまにか消えていたなどという時は、
SilentCleanupが実行されていないか確認してみてください。

SilentCleanupは、タスクスケジューラから無効に設定すれば、
自動実行されなくなります。

Rails5へのアップデート(見送り)

$
0
0

こんにちは、中村です。

Railsの5.0がリリースされた(http://weblog.rubyonrails.org/2016/6/30/Rails-5-0-final/)ので、作成中のRailsアプリを早速5.0にアップデートしてみました。

↓のコマンドを叩くと設定ファイル諸々のファイルを更新してくれました。
(書き換えているファイルはその都度マージが必要です)
$ rails app:update

その後、RubyGemsに上がっているdraperがRails
5.0に未対応ということがわかり、Gemfileを↓のように修正して bundle 実行。
-- before
gem 'draper'

-- after
gem 'draper', github: 'audionerd/draper', branch: 'rails5'
gem 'activemodel-serializers-xml'

とりあえずはこれで動くようになりました。
けど、個人的にはdraperのようなメジャーなライブラリが5.0に対応するまではアップデートを少し見送って、しばらくは4.2を使おうかなと思います。

Rails5へのアップデート(その後)

$
0
0

こんにちは、中村です。

 

前回からかなり時間が空きましたが、最近のRails案件ではRails5.0を積極的に採用するようになりました。

弊社のホームページもRails4.2から5.0へアップデートし、この機会にフロントエンドのフレームワークもFoundationからBootstrapに乗り換えてみました。

(あまり見た目は変えていませんが)

 

また、案件によってはWebSocketが便利に使えるActionCableの機能も使っています。

 

ActionCableを使ってみての感触ですが、やはりPub/Subの機能を簡単に実現したい時には便利だと思います。

RSpecの最新版(3.5)がActionCableには対応していないようなので、テストをどうしようか迷っているところではありますが。

それに加えて、調べていく中でパフォーマンスを求められるところでは(Go言語やElixirなどの)別言語のフレームワークを考える必要があるのかなというのが今のところの感触でした。

SikuliX(Python) インストール

$
0
0

Sikuliとは

   画像認識を利用した、UI操作を自動化するソフトウェアです。

 

インストール前提条件

  SikuliXの動作には、Java7 or Java8が必要となります。インストールが必要となります。

 

SikuliX(Python) インストール

 

  ダウンロード

 

    1.http://www.sikuli.org/ へアクセス

     「SikuliX Package – Learn More and Get It」を選択

 

      

    2.「Get Version 1.1.0」を選択

       

       

 

    3.「sikulixsetup-1.1.0.jar」をダウンロード

 

       

 

  インストール

    1.ダウンロードした、「sikulixsetup-1.1.0.jar」を任意のフォルダに配置

     (「C:\sikuliX」を作成し実施しています。)

 

    2.「sikulixsetup-1.1.0.jar」を実行

     「1-Pack1:~」を選択 → 「Setup Now」をクリック

       

        

    3.ダウンロード確認

     はい」を選択(※「jython-standalone-2.7.0.jar」も同様に「はい」選択)

       

 

    4.インストール中 ⇒しばらく待つと完了です

       

 

  SikuliX起動方法

 

    1.インストールフォルダに作成された「runsikulix.cmd」を実行

       

     

 

 

 

SikuliX Region(領域)を利用して範囲指定

$
0
0

 

Region(領域)
   複数の同一オブジェクトが存在し、特定のオブジェクトを選択させたい場合に有効です。

 

  1.以下のような、同一イメージのボタンを上から順にクリックするする場合

   

   

 

  2.Region(領域)を指定することにより実行可能です。

    Regionの指定は、上段のRegionボタンをクリックし、FindやClickに利用したい範囲を指定します。

    以下、サンプル

    

 

  3.実行結果

    

 

 

 

 

 

SikuliX Findallを利用して同一オブジェクト一括処理

$
0
0

Findall
   複数の同一オブジェクトが存在し、全てのオブジェクトに対して同一処理をさせたい場合に有効です。(全てのボタンをONからOFFに切り替えたい場合等)

   ※イメージの選択順は、Sikuliに依存するため順番にクリックさせたい場合などは注意が必要です。

 

  1.以下のような、同一イメージのボタンを全てクリックするする場合

 

    

 

  2.Findallを指定することにより実行可能です。

    以下、サンプル

    

 

  3.実行結果

    

 

 

 

 

Viewing all 138 articles
Browse latest View live