コンテンツへスキップ

hifiveのクラスモジュール紹介(その3)「フィールド/アクセサ」

JavaScript(ECMAScript5)ではクラスを作成することができません。ECMAScript2015になって、ようやくクラスを使えるようになりました。しかしレガシーなブラウザでは実装されていないため、Babelのようなライブラリを使ってコードを変換するのが一般的です。

JavaScriptではprototypeやdefinePropertyなどを使ってクラス(的なもの)を実装しますが、hifiveではそういった面倒な仕組みを意識することなくクラスを実装できるモジュールを開発しました。この機能は1.3.1以降で利用できます。

今回はクラスのフィールド、アクセサについて紹介します

フィールドについて

フィールドはインスタンスで用いる変数になります。基本的に外部から直接変更することはありません。defaultValueを使って規定値を設定できます。


var HelloClass = h5.cls.RootClass.extend(function (super_) {
return {
// クラス名(完全修飾名)
name: 'HelloClass',
// クラスのフィールド定義
field: {
_name: null,
_scale: {
defaultValue: 0
},
_read: {
defaultValue: "This is read only"
}
},
}

view raw

index.js

hosted with ❤ by GitHub

アクセサについて

フィールドにアクセスするためのget/setメソッドを提供するのがアクセサです。


var HelloClass = h5.cls.RootClass.extend(function (super_) {
return {
// クラス名(完全修飾名)
name: 'HelloClass',
// クラスのフィールド定義
field: {
_name: null,
_scale: {
defaultValue: 0
},
_read: {
defaultValue: "This is read only"
}
},
accessor: {
accessible: null,
scale: {
get: function() {
return this._scale;
},
set: function(val) {
this._scale = val * 100;
}
},
read: {
get: function() {
return this._read;
}
}
},
}
}

view raw

index.js

hosted with ❤ by GitHub

アクセサ名をnullで定義した場合、自動的に _p_アクセサ名 でget/setが定義されます。また、getだけを定義した場合は読み取り専用のアクセサになります。

アクセサはフィールドにそのまま値を入れるだけでなく、計算した上で入力/出力できます。


scale: {
get: function() {
return this._scale;
},
set: function(val) {
this._scale = val * 100;
}
},
hello.scale = 200;
hello.scale // -> 20000

view raw

index.js

hosted with ❤ by GitHub

読み取り専用にするために

JavaScriptの 'use strict'; を使うことで読み取り専用のアクセサに書き込みを行おうとしたり、存在しないアクセサへのアクセスをエラーにできます。


// これはエラーになります
try{
hello.read = "Update string";
}catch(e) {
result.val(result.val() + "\n" + e);
}
result.val(result.val() + "\n" + hello.read);
// これはエラーになります
try{
hello.unknown = "Update!";
}catch(e) {
result.val(result.val() + "\n" + e);
}

view raw

index.js

hosted with ❤ by GitHub

適切に開発していくためにも 'use strict'; を使っていくべきでしょう。


今回のコードは JSFiddle にアップロードしてあります。実装時の参考にしてください。

サーバサイドの開発に慣れている方にとってはクラスや継承は当たり前のものでしょう。これによって実装がとても簡単に、生産性高いものになるはずです。ぜひhifiveのクラス機能を使いこなしてください。

hifiveのクラスモジュール紹介(その2)「継承」

JavaScript(ECMAScript5)ではクラスを作成することができません。ECMAScript2015になって、ようやくクラスを使えるようになりました。しかしレガシーなブラウザでは実装されていないため、Babelのようなライブラリを使ってコードを変換するのが一般的です。

JavaScriptではprototypeやdefinePropertyなどを使ってクラス(的なもの)を実装しますが、hifiveではそういった面倒な仕組みを意識することなくクラスを実装できるモジュールを開発しました。この機能は1.3.1以降で利用できます。

今回はクラスの基本機能でもある継承について紹介します。

ベースになるクラスについて

まずベースになるクラス、AnimalClassについて紹介します。名前と鳴き声を紹介するメソッドがあります。


var AnimalClass = h5.cls.RootClass.extend(function (super_) {
return {
// クラス名(完全修飾名)
name: 'AnimalClass',
// クラスのフィールド定義
field: {
_name: null,
_voice: null
},
// クラスのメソッド定義
method: {
constructor: function (params) {
// 親クラスのコンストラクタ呼び出し
super_.constructor.call(this);
this._name = params;
},
hello: function() {
return this._name;
},
cry: function(name, voice) {
return name + "'s crying voice is " + voice;
}
}
};
});

view raw

index.js

hosted with ❤ by GitHub

継承する

このAnimalClassを継承するクラスとして、DogClassとCatClassを作成します。この時、 AnimalClass.extend を使います。コンストラクタは必須のメソッドになります。また、この時点で親のフィールド(voice)にアクセスできます。親のコンストラクタを呼び出す際には
`super
.constructor.call(this, params);` を使います。


var DogClass = AnimalClass.extend(function (super_) {
return {
name: 'DogClass',
method: {
constructor: function (params) {
super_.constructor.call(this, params);
this._voice = 'bow wow';
},
cry: function() {
return super_.cry(this._name, this._voice);
}
}
}
});

view raw

index.js

hosted with ❤ by GitHub

CatClassも殆ど同じ実装になります。


var CatClass = AnimalClass.extend(function (super_) {
return {
name: 'CatClass',
method: {
constructor: function (params) {
super_.constructor.call(this, params);
this._voice = 'mew';
},
cry: function() {
return super_.cry(this._name, this._voice);
}
}
}
});

view raw

index.js

hosted with ❤ by GitHub

このように共通化される機能は親クラスに入れられるようになります。

使い方

継承したクラスは通常のクラスと同じように実装できます。


var dog = Animal.DogClass.create('Hachi');
$("#dog").val(dog.cry());
// -> Hachi's crying voice is bow wow
var cat = Animal.CatClass.create('Tama');
$("#cat").val(cat.cry());
// -> Tama's crying voice is mew

view raw

index.js

hosted with ❤ by GitHub


サーバサイドの開発に慣れている方にとってはクラスや継承は当たり前のものでしょう。これによって実装がとても簡単に、生産性高いものになるはずです。ぜひhifiveのクラス機能を使いこなしてください。

hifiveのコントローラを連携させてみよう

大きなシステムが機能やサービスごとに分割して開発されるように、フロントエンドにおいても一つのコントローラですべてを管理するのではなく、複数のコントローラに分けて実装する方が効率的です。

今回はhifiveで複数のコントローラを分ける実装方法について紹介します。

実装例はJSFiddleにあります。参考にしてください。

概要

まず大事なのは全体を統括するコントローラの存在です。その中に個々の機能を実装するコントローラが複数存在します。つまり全体を統括するコントローラは機能実装はせず、コントローラ同士をつなぐ役割に徹するのが良いでしょう。

HTMLについて

HTMLは次のようになります。全体として #container があり、その中に子コントローラ用の #container1 、 #container2 があります。


<div id="container">
<div id="container1">
<input type="text" id="text" />
</div>
<div id="container2">
<p class="message">ここにメッセージがきます
</p>
</div>
</div>

view raw

index.html

hosted with ❤ by GitHub

JavaScriptについて

JavaScriptの実装ですが、まず子コントローラを定義します。まずテキストが入力されたタイミングでイベントハンドリングを行うコントローラです。


$(function() {
var controller = {
__name: 'aController',
'#text keyup': function(cotnext, $el) {
let text = this.$find("#text").val();
this.trigger('sendMessage', {
text: text
})
}
}
h5.core.expose(controller);
});

view raw

index.js

hosted with ❤ by GitHub

次に親コントローラから送られてきたメッセージを表示するコントローラです。


$(function() {
var controller = {
__name: 'bController',
getMessage: function(message) {
this.$find(".message").text(message);
}
}
h5.core.expose(controller);
});

view raw

index.js

hosted with ❤ by GitHub

最後にこの2つのコントローラをつなぐ親コントローラです。大事なのは __meta の中で子コントローラごとにrootElementとして監視するDOMを指定しておくことです。


$(function() {
var controller = {
__name: 'parrentController',
_sendController: aController,
_buttonController: bController,
__meta: {
_sendController: {
rootElement: '#container1'
},
_buttonController: {
rootElement: '#container2'
}
},
__ready: function() {
},
'{rootElement} sendMessage': function(context) {
let text = context.evArg.text;
this._buttonController.getMessage(text);
}
}
h5.core.controller('#container', controller);
});

view raw

index.js

hosted with ❤ by GitHub

実際の処理のハンドリングについては以下のように、まず子コントローラからトリガーを使って親コントローラへ通知します。


this.trigger('sendMessage', {
text: text
})

view raw

index.js

hosted with ❤ by GitHub

親コントローラでは {rootElement} という定義を使って受け取ります。こちらでは子コントローラのメソッドを直接呼び出します。


'{rootElement} sendMessage': function(context) {
let text = context.evArg.text;
this._buttonController.getMessage(text);
}

view raw

index.js

hosted with ❤ by GitHub

そして子コントローラで表示処理を行います。


getMessage: function(message) {
this.$find(".message").text(message);
}

view raw

index.js

hosted with ❤ by GitHub


このように実装することでDOMごとにコントローラを分けて権限を管理しつつ、相互にメッセージのやり取りができるようになります。ごく小さなWebアプリケーションであれば一つのコントローラで良いですが、ある程度大きくなるのが予想される場合はあらかじめコントローラ連携を念頭に作るのが良いでしょう。

詳細は13.コントローラの連携 – hifiveを参照してください。

hifiveのクラスモジュール紹介(その1)「基本的な定義方法」

JavaScript(ECMAScript5)ではクラスを作成することができません。ECMAScript2015になって、ようやくクラスを使えるようになりました。しかしレガシーなブラウザでは実装されていないため、Babelのようなライブラリを使ってコードを変換するのが一般的です。

JavaScriptではprototypeやdefinePropertyなどを使ってクラス(的なもの)を実装しますが、hifiveではそういった面倒な仕組みを意識することなくクラスを実装できるモジュールを開発しました。この機能は1.3.1以降で利用できます。

今回はまず基本的な構造を紹介します。次のように書きます。重要なのは h5.cls.RootClass.extend になります。そして、 _super (親クラス)を引数として、オブジェクトを返却します。


var SampleClass = h5.cls.RootClass.extend(function (super_) {
return {
// クラス名(完全修飾名)
name: 'SampleClass',
// クラスのフィールド定義
field: {
},
// クラスのプロパティ(アクセサ)定義
accessor: {
},
// クラスのメソッド定義
method: {
constructor: function (initialValue) {
},
}
};
});

view raw

index.js

hosted with ❤ by GitHub

オブジェクトには基本的なキーとして name/field/accessor/method があります。それぞれクラス名、フィールド名、プロパティ、メソッドの4つになります。

フィールドの利用

例えばフィールドは次のようになります。_ではじめるのがルールです。


field: {
_count: null
},

view raw

index.js

hosted with ❤ by GitHub

プロパティの利用

プロパティは次のようになります。こちらは_を使いません。


accessor: {
scale: null
},

view raw

index.js

hosted with ❤ by GitHub

メソッドの利用

メソッドは初期で読み込まれるconstructor以外は自由に定義できます。クラスのインスタンスを作成する際に値を送ることもできます。


method: {
constructor: function (initialValue) {
// 親クラスのコンストラクタ呼び出し
super_.constructor.call(this);
// 初期値を設定(インスタンス生成時に指定)
this._count = initialValue;
// 初期値を設定(直接指定)
this.scale = 1;
},
increment: function () {
++this._count;
return;
},
getValue: function () {
return this._count * this.scale;
}
}

view raw

index.js

hosted with ❤ by GitHub

インスタンス生成

そしてクラスのcreateメソッドを使ってインスタンスを生成します。


var sample = SampleClass.create(1);

view raw

index.js

hosted with ❤ by GitHub

後はこのインスタンスを使って自由にメソッドを呼び出せます。


sample.getValue();

view raw

index.js

hosted with ❤ by GitHub

外部に公開する

最後に外部(グローバル)に公開する場合です。これは h5.u.obj.expose を使います。例えば以下の例では hoge.fuga.SampleClass でアクセスできます。


h5.u.obj.expose('hoge.fuga', {
SampleClass: SampleClass
});

view raw

index.js

hosted with ❤ by GitHub


クラスを使ったサンプルをJSFiddleにアップしてあります。ごく基本的な機能だけですが、実装時の参考にしてください。

これから何回かに分けてクラスの使い方を紹介します。JavaやC#相当まではいきませんが、クラスを使うことでより生産性の高いコードが書けるようになるはずです。

クラスの作成と使用 – hifive

hifiveとjQuery UIを組み合わせる【モーダル表示】

hifiveは業務システム開発に特化しているのでjQuery UIやBootstrapと組み合わせて使われることが多くなっています。そこで今回はよく使われるライブラリについて、その組み合わせ方を紹介します。

今回はモーダルウィンドウです。

デモコードについて

実際に動作するデモはJSFiddleにアップロードしてあります。ボタンを押すとモーダルウィンドウが表示されて、情報を入力すると一覧が更新されます。

HTMLについて

HTMLは主に3つの分かれて構成されます。まずモーダルとして表示する部分があります。これは最初表示されません。


<div id="dialog-form" title="Create new user" style="display: none;">
<form>
<fieldset>
<label for="name">ID</label> <input type="text" name="id" id="id"
class="text ui-widget-content ui-corner-all" /> <label
for="email">名前</label> <input type="text" name="userName"
id="userName" value=""
class="text ui-widget-content ui-corner-all" /> <label
for="password">パスワード</label> <input type="password"
name="password" id="password" value=""
class="text ui-widget-content ui-corner-all" />
</fieldset>
</form>
</div>

view raw

index.js

hosted with ❤ by GitHub

次にデータの一覧を表示するテーブルがあります。


<table id="result" class="ui-widget ui-widget-content"
style="margin: 1em 0; border-collapse: collapse;">
<tr class="ui-widget-header">
<th>ID</th>
<th>名前</th>
<th>パスワード</th>
</tr>
<tr>
<td>test_id</td>
<td>test_name</td>
<td>test_password</td>
</tr>
</table>
<input type="button" id="register" value="register" />

view raw

index.html

hosted with ❤ by GitHub

そして一覧部分のテンプレートがあります。


<script type="text/ejs" id="row">
<tr>
<td>[%= id %]</td>
<td>[%= userName %]</td>
<td>[%= password %]</td>
</tr>
</script>

view raw

index.html

hosted with ❤ by GitHub

JavaScriptについて

JavaScriptの実装ですが、今回はコントローラとロジックに分かれています。それぞれ目的は次のようになります。

  • コントローラ
    UI側のクリックなどをハンドリング
  • ロジック
    ユーザ作成処理などを担当(今回はモック)

まずロジックから紹介します。

ロジックの実装

今回のロジックは DialogLogic としています。今回はモックですが、本来であればAjaxを使ってデータを保存したりします。


var dialogLogic = {
__name: 'DialogLogic',
createUser: function() {
this._private();
},
_private: function() {
alert('create user!');
}
};

view raw

index.html

hosted with ❤ by GitHub

コントローラの実装

コントローラは次のような実装になります。まず全体像を紹介します。


var controller = {
// コントローラ名の定義
__name: 'DialogController',
// ロジックの定義
dialogLogic: dialogLogic,
// コントローラが作成された際のイベント
__ready: function() {
},
// 登録ボタンを押した時のイベント
'#register click': function() {
},
// ダイアログの"Create User"ボタンがクリックされた時のコールバック
createUser: function(originalThis, event) {
},
// ダイアログを閉じる際のイベント
cancel: function(target, event) {
},
// ダイアログ内のフォームをクリアする
clearForm: function() {
}
};
// コントローラの作成と要素へのバインド
h5.core.controller('#container', controller);

view raw

index.js

hosted with ❤ by GitHub

コントローラが作成された際のイベント

__ready イベントでは次のように実装します。ポイントとしてはボタンを押した際のコールバック先をhifiveのコントローラとしていることです。


__ready: function() {
// registerボタンの装飾
$('#register').button();
var that = this;
// dialogの設定
// ボタンを押した時のコールバックはhifive化したコントローラのメソッドを設定している。
$('#dialog-form').dialog({
autoOpen: false,
height: 320,
width: 350,
modal: true,
buttons: {
'Create User': that.ownWithOrg(that.createUser),
Cancel: that.ownWithOrg(that.cancel)
},
close: that.own(that.clearForm)
});
},

view raw

index.js

hosted with ❤ by GitHub

登録ボタンを押した時のイベント

登録ボタンを押した時には単にモーダルを開くだけです。


'#register click': function() {
$('#dialog-form').dialog('open');
},

view raw

index.js

hosted with ❤ by GitHub

ダイアログの”Create User”ボタンがクリックされた時のコールバック

モーダルでユーザ作成のボタンが押された際にはロジック側の createUser を呼び出し、その後HTMLに入力された内容を反映します。そして最後にモーダルを閉じて完了です。


createUser: function(originalThis, event) {
// DialogLogicのcreateUserメソッドを呼ぶ
this.dialogLogic.createUser();
// 画面に追加する文字列を取得
var row = this.view.get('row', {
id: $('#id').val(),
userName: $('#userName').val(),
password: $('#password').val()
});
$('#result tbody').append(row);
$('#dialog-form').dialog('close');
},

view raw

index.js

hosted with ❤ by GitHub

ダイアログを閉じる際のイベント

モーダルを閉じるのはjQuery UIのdialogの機能をそのまま使います。


cancel: function(target, event) {
$('#dialog-form').dialog('close');
},

view raw

index.js

hosted with ❤ by GitHub

ダイアログ内のフォームをクリアする

最後にフォームの内容をクリアする処理です。


clearForm: function() {
$('#id').val('');
$('#userName').val('');
$('#password').val('');
}

view raw

index.js

hosted with ❤ by GitHub


このように実装することでhifiveとjQuery UIのdialogをシームレスに連携させられるようになります。モーダルウィンドウの表示はよくある機能なので、実装時の参考にしてください。

実際に動作するデモはJSFiddleにアップロードしてあります。こちらも合わせてご覧ください。

hifiveとjQuery UIを組み合わせる【バリデーション】

hifiveは業務システム開発に特化しているのでjQuery UIやBootstrapと組み合わせて使われることが多くなっています。そこで今回はよく使われるライブラリについて、その組み合わせ方を紹介します。

今回はバリデーションです。

デモコードについて

実際に動作するデモはJSFiddleにアップロードしてあります。入力して送信ボタンを押すとバリデーションが実行されます。

HTMLについて

HTMLは次のようになります。通常のHTMLと変わりません。


<form method="get" action="#" id="form1">
<fieldset>
<legend>アンケート</legend>
<div>
<div>好きな食べ物を選択して下さい</div>
<input type="checkbox" value="1" name="food">和食 <input
type="checkbox" value="2" name="food">中華 <input
type="checkbox" value="3" name="food">イタリアン <input
type="checkbox" value="4" name="food">フレンチ <span
class="errorMsg"></span>
</div>
<br>
<div>
<div>タバコを吸っていますか?</div>
<div>
<input type="radio" value="yes" name="conf_tobacco">はい <input
type="radio" value="no" name="conf_tobacco">いいえ <span
class="errorMsg"></span>
</div>
<br>
<div>1日の喫煙本数</div>
<div>
<input type="text" name="count">本 <span class="errorMsg"></span>
</div>
</div>
</fieldset>
<p>
<fieldset>
<legend>ユーザ情報登録</legend>
<div>
<div>URL(任意)</div>
<input type="text" name="url"> <span class="errorMsg"></span>
</div>
<p>
<div>
<div>ユーザID&nbsp;&nbsp;("hifive"以外を入力すると「入力されたユーザ名は既に使用されています」がエラーメッセージに表示されます)</div>
<input type="text" name="userid"> <span class="errorMsg"></span>
</div>
<div>
<div>パスワードを入力(8~12文字)</div>
<input type="password" name="pass"> <span class="errorMsg"></span>
</div>
<div>
<div>パスワードを再入力</div>
<input type="password" name="conf_pass"> <span
class="errorMsg"></span>
</div>
<br>
</fieldset>
<br />
<input type="submit" value="送信" />
</p>
</form>

view raw

index.html

hosted with ❤ by GitHub

CSSについて

CSSはエラー時の色をつけるクラスなどを追加しています。


@charset "utf-8";
#form1 {
border: 1px solid black;
padding: 15px;
}
.errorMsg {
color: red;
}
.strong {
font-weight: bold;
}
.rules {
border-collapse: collapse;
}
.rules td {
border: 1px solid black;
}

view raw

index.css

hosted with ❤ by GitHub

JavaScriptについて

JavaScriptは長いので、幾つかに分けて紹介します。まず、hifiveのコントローラ化が完了した際の__readyイベントにてバリデーションの設定を行います。


this.$find('#form1').validate({
// Submitイベントが実行された場合に、自動でバリデーションを実行
onsubmit: true,
// フォーカスが当たった場合に現在のエラー内容を一旦クリア
focusCleanup: true,
// バリデーションルール
rules: {
food: {
required: true
},
conf_tobacco: {
required: true
},
count: {
required: {
depends: '[name="conf_tobacco"][value="male"]:checked'
},
number: {
depends: '[name="conf_tobacco"][value="male"]:checked'
},
min: {
param: 1,
depends: '[name="conf_tobacco"][value="male"]:checked'
},
max: {
param: 999,
depends: '[name="conf_tobacco"][value="male"]:checked'
}
},
lastname: {
required: true
},
firstname: {
required: true
},
email: {
required: true,
email: true
},
url: {
url: true
},
userid: {
required: true,
minlength: 6,
remote: '/exists' // useridをパラメータのキーにして、/existsに結果を問い合わせる (サーバは"true"または"false"の文字列を返す必要がある)
},
pass: {
required: true,
rangelength: [8, 12]
},
conf_pass: {
equalTo: '[name="pass"]'
},
policy: {
required: true
}
},
messages: { // エラー時に表示するメッセージ
food: {
required: '1つ以上選択して下さい。'
},
conf_tobacco: {
required: '「はい」または「いいえ」のいずれかを選択して下さい'
},
count: {
required: '「はい」を選択した場合は、喫煙本数を入力して下さい',
number: '本数は数字で入力して下さい',
min: '{0}以上の値を入力して下さい',
max: '{0}以下の値で入力して下さい'
},
lastname: {
required: '姓を入力して下さい'
},
firstname: {
required: '名を入力して下さい'
},
email: {
required: 'メールアドレスを入力して下さい',
email: '正しいメールアドレスを入力して下さい'
},
url: {
url: '正しいURLを入力して下さい'
},
userid: {
required: 'ユーザIDを入力して下さい。',
minlength: '{0}以上で入力して下さい。',
remote: '入力されたユーザ名は既に使用されています'
},
pass: {
required: 'パスワードを入力して下さい',
rangelength: '{0}以上{1}文字以下で入力して下さい'
},
conf_pass: {
equalTo: 'パスワードが一致しません'
},
policy: {
required: '同意される場合はチェックを入れて下さい'
}
},
// エラーメッセージの表示先を指定する
errorPlacement: function(error, el) {
el.parent().children('.errorMsg').append(error);
},
// エラーがある要素毎に実行するコールバック
showErrors: function(errorMap, errorList) {
this.defaultShowErrors();
},
// 入力項目でエラーがあったときに実行されるハンドラ
highlight: this.ownWithOrg(this.highLightArea),
// エラーが解消されたときに実行されるハンドラ,
unhighlight: this.ownWithOrg(this.unHighLightArea),
// validateのチェックが通り、submitが行われるときに実行するハンドラ
submitHandler: this.ownWithOrg(this.submitHandler),
// validateでエラーが1つでもあると実行されるハンドラ
invalidHandler: this.ownWithOrg(this.invalidHandler),
// 入力項目でエラーが解消されるたびに実行されるハンドラ
success: this.ownWithOrg(this.successHandler)
});

view raw

index.js

hosted with ❤ by GitHub

この際、入力チェックを行う際などのイベントハンドラも設定もします。this.ownWithOrgを使うことでhifiveのコントローラ自身を渡せるようになります。


// エラーがある要素毎に実行するコールバック
showErrors: function(errorMap, errorList) {
this.defaultShowErrors();
},
// 入力項目でエラーがあったときに実行されるハンドラ
highlight: this.ownWithOrg(this.highLightArea),
// エラーが解消されたときに実行されるハンドラ,
unhighlight: this.ownWithOrg(this.unHighLightArea),
// validateのチェックが通り、submitが行われるときに実行するハンドラ
submitHandler: this.ownWithOrg(this.submitHandler),
// validateでエラーが1つでもあると実行されるハンドラ
invalidHandler: this.ownWithOrg(this.invalidHandler),
// 入力項目でエラーが解消されるたびに実行されるハンドラ
success: this.ownWithOrg(this.successHandler)

view raw

index.js

hosted with ❤ by GitHub

後は各イベントハンドラの実装になります。


// 入力項目でエラーがあったときに実行されるハンドラ
highLightArea: function(orgThis, el, error, errorClass) {
$(el).css('background-color', 'red');
},

view raw

index.js

hosted with ❤ by GitHub


// エラーが解消されたときに実行されるハンドラ,
unHighLightArea: function(orgThis, el, error, errorClass) {
var $el = $(el);
$el.css('background-color', '');
// 操作した要素が「1日の喫煙本数」テキストボックスでかつ、「たばこを吸っていますか」チェックボックスが「いいえ」の場合は、
// 「1日の喫煙本数」テキストボックスのハイライトを除去する
if ($el.is('[name="conf_tobacco"]') && this.$find('[name="conf_tobacco"]:checked').val() === 'female') {
this.$find('[name="count"]').css('background-color', '');
}
},

view raw

index.js

hosted with ❤ by GitHub


// validateのチェックが通り、submitが行われるときに実行するハンドラ
submitHandler: function(orgThis, form, ev) {
alert('FORMの内容をサーバに送信します。');
form.submit();
},

view raw

index.js

hosted with ❤ by GitHub


// validateでエラーが1つでもあると実行されるハンドラ
invalidHandler: function(orgThis, ev, validator) {
alert('入力内容に誤りがあります。');
},

view raw

index.js

hosted with ❤ by GitHub


// 入力項目でエラーが解消されるたびに実行されるハンドラ
successHandler: function(orgThis, errMsgElement, inputElement) {
if (window.console) {
console.log('入力OK: '+ inputElement.name);
}
},

view raw

index.js

hosted with ❤ by GitHub

最後にチェックボックス、ラジオボタンのバリデーションについて別途実装します。


//「好きな食べ物を選択して下さい」のバリデーション
'[name="food"] click': function(context, $ct) {
// input[name="food"]の要素に対してバリデーションを実行する
$ct.valid();
},
//「1日の喫煙本数」テキストボックスのバリデーション
'[name="count"] focusout': function(context, $ct) {
// input[name="count"]の要素に対してバリデーションを実行する
$ct.valid();
}

view raw

gistfile1.txt

hosted with ❤ by GitHub


$(function() {
h5.core.controller('body', {
__name: 'sample.plugin.validationSampleController',
__ready:function() {
// バリデーターにルールやメッセージ等を設定
this.$find('#form1').validate({
// Submitイベントが実行された場合に、自動でバリデーションを実行
onsubmit: true,
// フォーカスが当たった場合に現在のエラー内容を一旦クリア
focusCleanup: true,
// バリデーションルール
rules: {
food: {
required: true
},
conf_tobacco: {
required: true
},
count: {
required: {
depends: '[name="conf_tobacco"][value="yes"]:checked'
},
number: {
depends: '[name="conf_tobacco"][value="yes"]:checked'
},
min: {
param: 1,
depends: '[name="conf_tobacco"][value="yes"]:checked'
},
max: {
param: 999,
depends: '[name="conf_tobacco"][value="yes"]:checked'
}
},
url: {
url: true
},
// useridをパラメータのキーにして、/existsに結果を問い合わせる
// サーバは"true"または"false"の文字列を返す必要がある
userid: {
required: true,
minlength: 6,
remote: '/exists'
},
pass: {
required: true,
rangelength: [8, 12]
},
conf_pass: {
equalTo: '[name="pass"]'
},
policy: {
required: true
}
},
messages: { // エラー時に表示するメッセージ
food: {
required: '1つ以上選択して下さい。'
},
conf_tobacco: {
required: 'はい、またはいいえを選択して下さい'
},
count: {
required: '「はい」を選択した場合は、喫煙本数を入力して下さい',
number: '本数は数字で入力して下さい',
min: '{0}以上の値を入力して下さい',
max: '{0}以下の値で入力して下さい'
},
url: {
url: '正しいURLを入力して下さい'
},
userid: {
required: 'ユーザIDを入力して下さい。',
minlength: '{0}以上で入力して下さい。',
remote: '入力されたユーザ名は既に使用されています'
},
pass: {
required: 'パスワードを入力して下さい',
rangelength: '{0}以上{1}文字以下で入力して下さい'
},
conf_pass: {
equalTo: 'パスワードが一致しません'
}
},
// エラーメッセージの表示先を指定する
errorPlacement: function(error, el) {
el.parent().children('.errorMsg').append(error);
},
// エラーがある要素毎に実行するコールバック
showErrors: function(errorMap, errorList) {
this.defaultShowErrors();
},
// 入力項目でエラーがあったときに実行されるハンドラ
highlight: this.ownWithOrg(this.highLightArea),
// エラーが解消されたときに実行されるハンドラ,
unhighlight: this.ownWithOrg(this.unHighLightArea),
// validateのチェックが通り、submitが行われるときに実行するハンドラ
submitHandler: this.ownWithOrg(this.submitHandler),
// validateでエラーが1つでもあると実行されるハンドラ
invalidHandler: this.ownWithOrg(this.invalidHandler),
// 入力項目でエラーが解消されるたびに実行されるハンドラ
success: this.ownWithOrg(this.successHandler)
});
},
highLightArea: function(orgThis, el, error, errorClass) {
$(el).css('background-color', 'red');
},
unHighLightArea: function(orgThis, el, error, errorClass) {
var $el = $(el);
$el.css('background-color', '');
// 操作した要素が「1日の喫煙本数」テキストボックスでかつ、「たばこを吸っていますか」チェックボックスが「いいえ」の場合は、
// 「1日の喫煙本数」テキストボックスのハイライトを除去する
if ($el.is('[name="conf_tobacco"]') && this.$find('[name="conf_tobacco"]:checked').val() === 'no') {
this.$find('[name="count"]').css('background-color', '');
}
},
submitHandler: function(orgThis, form, ev) {
alert('FORMの内容をサーバに送信します。');
form.submit();
},
invalidHandler: function(orgThis, ev, validator) {
alert('入力内容に誤りがあります。');
},
successHandler: function(orgThis, errMsgElement, inputElement) {
if (window.console) {
console.log('入力OK: '+ inputElement.name);
}
},
//「好きな食べ物を選択して下さい」のバリデーション
'[name="food"] click': function(context, $ct) {
// input[name="food"]の要素に対してバリデーションを実行する
$ct.valid();
},
//「1日の喫煙本数」テキストボックスのバリデーション
'[name="count"] focusout': function(context, $ct) {
// input[name="count"]の要素に対してバリデーションを実行する
$ct.valid();
}
});
});

view raw

index.js

hosted with ❤ by GitHub


hifiveでもvalidation機能を提供していますが、使い慣れた方を採用するケースもあるでしょう。そうした時にはコールバックの管理などはhifiveで行うことで、よりメンテナンスしやすいバリデーション実装ができるようになります。

実際に動作するデモはJSFiddleにアップロードしてあります。お試しください。

own/ownWithOrgの使い方

JavaScriptは非同期で処理を行う言語です。そして、オブジェクト自身を意味する this が頻繁に使われます。しかし、非同期処理の後はthisが思ったものではなくなっていたりして、困った経験があるのではないでしょうか。

例えば次のような処理です。


var Hello = {
name: "hifive",
message: function() {
return "Hello, " + this.name;
}
}
Hello.message(); // "Hello, hifive"

view raw

index.js

hosted with ❤ by GitHub

これは普通ですが、非同期処理に変更します。


var Hello = {
name: "hifive",
message: function() {
setTimeout(function() {
return "Hello, " + this.name;
}, 1000);
}
}
Hello.message();

view raw

index.js

hosted with ❤ by GitHub

この時の this は Windowオブジェクトになるので、nameはありません。このようにスコープが変わってしまうと this の値も変化します。

その解決策として、次のように実行してみます。


var Hello = {
name: "hifive",
message: function() {
setTimeout(function() {
alert("Hello, " + this.name);
}.call(this), 1000);
}
}
console.log(Hello.message());

view raw

index.js

hosted with ❤ by GitHub

setTimeoutを使っているのは変わりませんが、関数をそのまま渡すのではなく、 .call(this) を追加します。そうすると、this.nameはhifiveという値が取れるようになります。callの引数に渡す値が、その関数内でのthisとして使えるようになります。

via Function.prototype.call() – JavaScript | MDN

このようにして、thisのスコープを変えることでプログラミングしやすくなります。

own/ownWithOrgの使い方

hifiveではcall(this)ではなく、ownメソッドを提供しています。例えばコントローラの中で、次のように使います。


$.ajax({
:
})
.then(this.own(function(response) {
this // hifiveのコントローラ
}));

view raw

index.js

hosted with ❤ by GitHub

さらにhifiveのコントローラだけでなく、本来のthisを使いたい場合にはownWithOrgがあります。


$.ajax({
:
})
.then(this.ownWithOrg(function(original, response) {
this // hifiveのコントローラ
original // 元々のthis
}));

view raw

index.js

hosted with ❤ by GitHub

このようにして this による混乱を抑えられるようになります。


サンプルコードをJSFiddleにアップロードしています。各処理における this の値の違いについて確認してみてください。

hifiveとjQuery UIを組み合わせる【DOMの並び替え】

hifiveは業務システム開発に特化しているのでjQuery UIやBootstrapと組み合わせて使われることが多くなっています。そこで今回はよく使われるライブラリについて、その組み合わせ方を紹介します。

今回はDOMのソートです。

デモコードについて

実際に動作するデモはJSFiddleにアップロードしてあります。各要素をドラッグして上下に移動したり、別なカラムにドロップできます。

HTMLについて

少し長いですがHTMLは次のようになります。portlet クラスごとに移動できます。


<div id="container" style="padding: 30px;">
<div class="column">
<div class="portlet">
<div class="portlet-header">Feeds</div>
<div class="portlet-content" style="height: 320px;">Lorem
ipsum dolor sit amet, consectetuer adipiscing elit</div>
</div>
<div class="portlet">
<div class="portlet-header">News</div>
<div class="portlet-content">Lorem ipsum dolor sit amet,
consectetuer adipiscing elit</div>
</div>
</div>
<div class="column">
<div class="portlet">
<div class="portlet-header">Shopping</div>
<div class="portlet-content">Lorem ipsum dolor sit amet,
consectetuer adipiscing elit</div>
</div>
</div>
<div class="column">
<div class="portlet">
<div class="portlet-header">Links</div>
<div class="portlet-content">Lorem ipsum dolor sit amet,
consectetuer adipiscing elit</div>
</div>
<div class="portlet">
<div class="portlet-header">Images</div>
<div class="portlet-content">Lorem ipsum dolor sit amet,
consectetuer adipiscing elit</div>
</div>
</div>
<ul id="list" style="list-style: none;"></ul>
</div>

view raw

index.js

hosted with ❤ by GitHub

スタイルシートについて

スタイルシートはカラム部分の設定などになります。


.column {
width: 220px;
float: left;
padding-bottom: 100px;
}
.portlet {
margin: 0 1em 1em 0;
}
.portlet-header {
margin: 0.3em;
padding-bottom: 4px;
padding-left: 0.2em;
}
.portlet-header .ui-icon {
float: right;
}
.portlet-content {
padding: 0.4em;
}
.ui-sortable-placeholder {
border: 1px dotted black;
visibility: visible !important;
height: 50px !important;
}
.ui-sortable-placeholder * {
visibility: hidden;
}

view raw

index.html

hosted with ❤ by GitHub

JavaScriptについて

JavaScriptは次のようになります。まずは概要です。


$(function() {
var controller = {
__name: 'SortableController',
// 初期化処理
__ready: function() {
},
// ドロップ時のコールバック
stop: function(event, ui) {
}
};
// コントローラの作成と要素へのバインド.
h5.core.controller('#container', controller);
});

view raw

index.css

hosted with ❤ by GitHub

初期化処理

hifiveのコントローラが初期化された段階でソートの設定を行います。ここではドロップした時のイベント(stop)だけ指定しています。


__ready: function() {
var that = this;
$('.column').sortable({
connectWith: '.column',
stop: that.own(that.stop)
});
$('.portlet').addClass('ui-widget ui-widget-content ui-helper-clearfix ui-corner-all')
.find('.portlet-header').addClass('ui-widget-header ui-corner-all').prepend(
'<span class="ui-icon ui-icon-minusthick"></span>').end().find(
'.portlet-content');
$('.portlet-header .ui-icon').click(function() {
$(this).toggleClass('ui-icon-minusthick').toggleClass('ui-icon-plusthick');
$(this).parents('.portlet:first').find('.portlet-content').toggle();
});
$('.column').disableSelection();
},

view raw

index.js

hosted with ❤ by GitHub

ドロップ時のコールバック

次にドロップを止めた時のイベントです。


stop: function(event, ui) {
var target = $(ui.item);
$('#list').append('<li>' + target.find('.portlet-header').text() + 'が移動しました。</li>');
}

view raw

index.js

hosted with ❤ by GitHub


このように実装することでhifiveとjQuery UIのソート機能をシームレスに連携させられるようになります。ダッシュボードであったり、ユーザが自分でコンテンツを自由に設定できるページを作るのに使えるのでぜひ参考にしてください。

実際に動作するデモはJSFiddleにアップロードしてあります。こちらも合わせてご覧ください。

HTML5の便利なAPI

HTML5でWebの表現力は一気に拡大しています。もちろんJavaScriptでも多数のAPIが追加されており、リッチなWebシステムを構築するのに必須機能が揃ってきています。今回は多数あるHTML5 APIの中で、業務システム周りで使えるものをピックアップして紹介します。

## File API

従来のJavaScriptではローカルファイルへアクセスできませんでした。HTML5で追加されたFile APIによって、ファイル選択ダイアログであったり、ドラッグ&ドロップされたファイルを読み込めるようになりました。

CSVファイルを読み込んだり、画像を読み込んで加工すると言った処理もできるようになっています。また、読み込んだ内容をAjaxでサーバに送ることで、ファイルのドラッグ&ドロップでアップロード処理を行うと言った処理も可能になります。

[File – Web API インターフェイス | MDN](https://developer.mozilla.org/ja/docs/Web/API/File)

## Geolocation API

位置情報を取得するAPIです。一般的なPCにはGPSモジュールは入っていませんので、IPアドレス、WiFi、Bluetoothなど様々な情報を使って位置情報を特定します。一度だけ取得することも、継続的に取得し続けることもできます。精度はそこそこといったところなので、あまり厳密性を求めるような運用は難しいでしょう。

位置情報を使えば付近の情報を取得したり、駅名や住所の入力を省けるようになります。スマートフォンなど文字入力が面倒な場合において特に有効です。

[Geolocation の利用 – Web API インターフェイス | MDN](https://developer.mozilla.org/ja/docs/Web/API/Geolocation/Using_geolocation)

## クリップボードAPI

いわゆるクリップボードの操作が可能になるAPIです。文字列をコピーするだけでなく、HTMLやリンクなども対象になります。さらにファイルをコピーすることでアクセスできるようにもなります。

ユーザに任意の文字列をコピーしてもらうのに使ったり、画像やHTMLなどを含んだコンテンツをコピーしてもらうのに使えます。

[ClipboardEvent – Web API インターフェイス | MDN](https://developer.mozilla.org/ja/docs/Web/API/ClipboardEvent)

## localStorage

キーバリュー型でデータの保存、取り出しができるストレージです。Cookieに似ていますが、もっと容量は大きな仕組みです。KVSなので、検索などの機能はありません。恒久的なデータ保存場所として使うのが便利です。

[Window.localStorage – Web API インターフェイス | MDN](https://developer.mozilla.org/ja/docs/Web/API/Window/localStorage)

## History API

JavaScriptで画面を書き換えるようなWebアプリケーションの場合、History APIを使うことでブラウザの履歴として管理できるようになります。ハッシュまたはパーマネントURLを書き換える形になります。

History APIでパーマネントURLを使う場合、そのURLに対してダイレクトにアクセスしたとしてもきちんと表示されるようになっている必要があります。

[ブラウザの履歴を操作する – ウェブデベロッパーガイド | MDN](https://developer.mozilla.org/ja/docs/Web/Guide/DOM/Manipulating_the_browser_history)

## WebSocket API

サーバとクライアントで同期的にメッセージを送受信できる仕組みがWebSocketです。特定のチャンネルを購読したり、逆にブロードバンドにメッセージを流すこともできます。ソケットとついている通り、HTTPとは異なるプロトコルを使います。

そのためWebSocket用のサーバを別途用意したりする必要があります。その際セッションデータなどを連携させるなど実装には工夫が必要になるでしょう。

[WebSockets – Web API インターフェイス | MDN](https://developer.mozilla.org/ja/docs/Web/API/WebSockets_API)

—-

APIは他にもたくさんあります。WebGLであったり、Web NotificationといったAPIもあります。Webシステムをより便利にするためにHTML5 APIを活用してください。

hifiveとjQuery UIを組み合わせる【タブ表示】

hifiveは業務システム開発に特化しているのでjQuery UIやBootstrapと組み合わせて使われることが多くなっています。そこで今回はよく使われるライブラリについて、その組み合わせ方を紹介します。

今回はタブ表示です。

デモコードについて

実際に動作するデモはJSFiddleにアップロードしてあります。各タブをクリックするとその下にあるコンテンツが切り替わるのが確認できます。

HTMLについて

HTMLですが、各タブ(リスト)のリンク先にタブのIDを指定しています。


<div id="tabs">
<ul>
<li><a href="#tabs-1">tab1</a></li>
<li><a href="#tabs-2">tab2</a></li>
<li><a href="#tabs-3">tab3</a></li>
</ul>
<div id="tabs-1">tab1</div>
<div id="tabs-2">tab2</div>
<div id="tabs-3">tab3</div>
</div>

view raw

gistfile1.txt

hosted with ❤ by GitHub

JavaScriptについて

JavaScriptの実装は次のようになります。


$(function() {
var controller = {
__name: 'TabsController',
// 初期化処理
// タブが選択された時のコールバック
};
// コントローラの作成と要素へのバインド.
h5.core.controller('#tabs', controller);
});

view raw

index.html

hosted with ❤ by GitHub

初期化処理について

初期化処理ではタブの表示処理を行います。


// 初期化処理
__ready: function() {
var that = this;
// jQuery UI Tabsを設定
$(this.rootElement).tabs({
// タブが選択された時のコールバックを設定
// ownメソッドでラップしてイベントコンテキストやロジックを使えるようにしている。
select: that.own(that.selectTab)
});
},

view raw

index.js

hosted with ❤ by GitHub

タブがクリックされた時の処理

タブがクリックされた際にコールバックが受け取れます。Ajax処理などを行ったりするのに使えます。


// タブが選択された時のコールバック
selectTab: function(event, ui) {
$(ui.panel).html('tab' + (ui.index + 1) + 'が選択されました。');
}

view raw

index.js

hosted with ❤ by GitHub


このように実装することでhifiveとjQuery UIのdialogをシームレスに連携させられるようになります。多くの情報を表示する際にタブは便利な機能なので、実装時の参考にしてください。

実際に動作するデモはJSFiddleにアップロードしてあります。こちらも合わせてご覧ください。