なんかタイトルが長いですが、Web 開発も Visaul Studio でやろう! みたいなノリで思ってもえらばいいんじゃないかな。
概要
- やろうと思ったキッカケ
- タイトルについて解説
- 実際に使ってみよう! Visual Studio
- 実際に MVVM してみよう!
- サンプル
- まとめ
1. やろうと思ったキッカケ
もともと TypeScript に興味があり、また Web で MVVM な開発をやりたかった、ってところが本音。そんなこんなで、最終的には HTML5、JavaScript、CSS になるのだけど、その前の事前段階に HTML5、TypeScript with Knockout.js/Linq.js、Less を使おうというもの。
2. タイトルについて解説
2.1. TypeScript
JavaScript が型を持ったもの、って思えばいいんじゃないのかな。ActionScript 書いたことある人なら、まああの感じに近い。しいて言えば、JavaScript と違って型を厳密に評価するので、JavaScript ライブラリーは逆に型宣言された TypeScript 用宣言が必要になる。
2.2. Knockout.js
JavaScript で Binding ができるというやつ。xaml 触ったことある人ならなじみやすいと思う。
2.3. Linq.js
ループ書くと 4 行ぐらいになってだるいやつを 1 行で済ませたり。メソッドチェーンで書けるので流れは読みやすい。インテリセンスが効く TypeScript 化ではすいすいかける。
3. 実際に使ってみよう! Visual Studio
なお、この解説は Visual Studio 2013 Professional Update 2 RC をもとに作られています。
- 起動して、「新しいプロジェクト…」を選びます。
- 「インストール済み」「テンプレート」「TypeScript」から「TypeScript を使用した HTML アプリケーション」を選びます。
- 名前(N): に “FirstTimeMvvmTestApp” とでも入れておきましょう。
これでプロジェクトの作成は完了しました。次に Knockout.js などもろもろのものを入れます。
- 「ソリューション エクスプローラー」から “FirstTimeMvvmTestApp” を右クリック、「NuGet パッケージの管理…」を開きます。
- 「オンライン」「nuget.org」を選択し、右上の “オンライン の検索 (Ctrl+E)” に knockoutjs といれ、knockoutjs を選択し「インストール」します。
-
同様に knockout.TypeScript といれ、knockout.TypeScript.DefinitelyTyped を選択し「インストール」します。
-
今回は linq.js も使うので同様にいれます。
これで準備完了です。
4. 実際に MVVM してみよう!
今回はサーバーから JSON として受け取ったデータ (Model によって処理済み) を表示、編集することにします。次のような状況を想定します:
DataClient クラスから User[] データを受け取り、それをテーブルとして表示。必要に応じてユーザーを選択、編集し即時リストに反映。そしてデータをストア。
簡単のため、実際に通信は行いません。それでは行きましょう。
4.1. User.ts
ユーザーを格納するためのクラス。
/// <reference path="Scripts/typings/knockout/knockout.d.ts" />
class User
{
name: KnockoutObservable<string> = ko.observable( "" );
age: KnockoutObservable<number> = ko.observable( 0 );
constructor( name: string, age: number )
{
this.name( name );
this.age( age );
}
toString()
{
return this.name() + " (" + this.age() + ")";
}
}
簡単ですね! C# で書くとこんな感じ!
namespace Sample
{
public class User: NotificationObject
{
private string _name;
private uint _age;
User( string name, uint age )
{
this.Name = name;
this.Age = age;
}
property string Name
{
get { return this._name; }
set { this.SetValue( ref this._name, value ); }
}
property uint Age
{
get { return this._age; }
set { this.SetValue( ref this._age, value ); }
}
}
}
※ NotificationObject は通知関連の機能を実装済みとする。
4.2. DataClient.ts
JSON.parse、JSON.stringify を使った簡単実装。
/// <reference path="Scripts/typings/knockout/knockout.d.ts" />
/// <reference path="Scripts/linq.d.ts" />
class DataClient
{
static data: string = '[{"name":"太郎", "age": 12}, {"name":"花子", "age": 11}]';
static load()
{
var json = JSON.parse( this.data );
return Enumerable.from( json ).select( ( user: any ) => new User( user.name, user.age ) ).toArray();
}
static store( value: User[] )
{
this.data = JSON.stringify( Enumerable.from( value ).select( user => { return { name: user.name, age: user.age }; }).toArray() );
alert( "stored: " + this.data );
}
}
4.3. IndexPageViewModel
/// <reference path="Scripts/typings/knockout/knockout.d.ts" />
/// <reference path="Scripts/linq.d.ts" />
class IndexPageViewModel
{
users: KnockoutObservableArray<User> = ko.observableArray( [] );
selectedUser: KnockoutObservable<User> = ko.observable( null );
avarageAge: KnockoutComputed<number> = ko.computed(
() => this.users().length > 0 ? Enumerable.from( this.users() ).average( user => user.age() ) : 0 );
minimumAge: KnockoutComputed<number> = ko.computed(
() => this.users().length > 0 ? Enumerable.from( this.users() ).min( user => user.age() ) : 0 );
maximumAge: KnockoutComputed<number> = ko.computed(
() => this.users().length > 0 ? Enumerable.from( this.users() ).max( user => user.age() ) : 0 );
load()
{
this.users( DataClient.load() );
}
store()
{
DataClient.store( this.users() );
}
addUser()
{
var newUser = new User( "", 0 );
this.users.push( newUser );
this.selectedUser( newUser );
}
removeUser( user: User )
{
this.users.remove( user );
}
}
4.4. App.ts
エントリーポイントです。
/// <reference path="Scripts/typings/knockout/knockout.d.ts" />
window.onload = () =>
{
var viewModel = new IndexPageViewModel();
ko.applyBindings( viewModel ); // C# でいう this.DataContext = viewModel;
viewModel.load();
};
4.5. HTML とか CSS とか
テキトーに作ります (割愛)。
たとえば、テーブル表示なら、
<table>
<thead>
<tr>
<th>No.</th>
<th>name</th>
<th>age</th>
<th> </th>
</tr>
</thead>
<tbody data-bind="foreach: users">
<tr>
<td class="content-only content-number"><span data-bind="text: $index"></span></td>
<td><input data-bind="value: name" style="width: 7em" /></td>
<td><input data-bind="value: age" style="width: 4em" /></td>
<td><input type="button" data-bind="click: function() { $parent.removeUser( $data ); }" value="x" /></td>
</tr>
</tbody>
</table>
少しだけ解説。
4.5.1. foreach
xaml の DataTemplate 的なことしたいときに使う。
4.5.2. click
Click イベントハンドラ―を貼るときに使う
4.5.3. text
xaml の Path で Binding するみたいなやつ。テキスト要素 span や p に使う。Object の場合、toString が用いられる
4.5.4. value
xaml の Path で Binding するみたいなやつ。入力要素 input に使う。select の単一選択にも使われる。複数選択は selectedOptions
4.5.5. options
select の options に使う。中身は text と同じ挙動。
5. サンプル
上のやつを実際に実装したサンプルを作りました。GitHub にあげております。
https://github.com/mntone/MvvmWithTypeScript
また、すぐ触れるサンプルも。
http://mntone.minibird.jp/mwts/
6. まとめ
今日の集大成。疲れました。半日これにどっぷり。いい経験になったかな。ちなみになぜやったのかというと、ドットピッチ計算機の改良をするためですね。一応貼っておきます。
おしまい!