igGrid のパフォーマンスを体感できるオンラインサンプル

- 2017/01/31(火)時点の情報に基づいています -

こんにちは、インフラジスティック・ジャパン デベロッパーサポート の桐生です。

弊社のjQuery/HTML5用コントロール Ignite UI のコントロールの中で最もご利用頻度の高い igGrid ですが、

・何行 × 何列 まで表示できるの?

・パフォーマンスはどうなの?

といったお問い合わせをよく頂きます。そこで、弊社では、お客様ご自身でパフォーマンスを確認できるオンラインサンプルをご用意しております。

 

オンラインサンプル:Grid パフォーマンスオプション
http://jp.igniteui.com/grid/grid-performance

 

データに関わるパラメーター

・行数(1000 ~ 20000 まで選択可能)

・列数(1 ~ 20 まで選択可能)

作成するアプリケーションで想定されるデータ量を設定ください。オンラインサンプルは、jsfiddle 上で確認することもできます。jsfiddle 上であれば、行数や列数の上限を自由に変更して試すこともできます。

 

igGridの構成に関わるパラメーター

・ページング (有効 / 無効)

・フィルタリング (有効 / 無効)

・仮想化タイプ(none / continuouse / fixed)

ページングと仮想化は同時に設定することもできますが、どちらか一方のみを設定されるケースが多いです。

どういった場合に仮想化を使用したらよいかについて参考となるヘルプがあるのでご紹介します。

 

【パフォーマンスガイド  ベストプラクティス】
http://jp.igniteui.com/help/iggrid-performance-guide#best-practices

 

1000件を超えるレコードを扱う場合、ページングを使用していない場合は、行の仮想化を行うことを推奨しています。ただし仮想化だけでは限界もあります。その場合は、ページングを検討しましょう。


より大量データを扱う場合は、仮想化よりもさらにページングの方がパフォーマンスに効果的です。また、ページングを使用すると1ページあたりに表示する行数が少なくなるため、仮想化を併用する必要がない場合が多いです。

 

このオンラインサンプルを使えば、想定しているデータ量を igGrid で扱ってもパフォーマンス上問題なさそうかどうかの目安になるかと思いますので、ぜひご利用ください!

igGrid のライフサイクルイベントを使ってカスタマイズの幅を広げよう

- 2017/01/30(月)時点の情報に基づいています -

こんにちは、インフラジスティック・ジャパン デベロッパーサポート の桐生です。

弊社のjQuery/HTML5用コントロール Ignite UI のコントロールの中で最もご利用頻度の高い igGrid のライフサイクルイベントについてご説明いたします。

Ignite UI のコントロールは、組み込みオプションでは実現できないことも、イベントを駆使することでより高度なカスタマイズを実現することができます。

特に igGrid の場合、生成時に発生するライフサイクルイベントが豊富なため、細かいレベルでの制御が可能となります。ライフサイクルイベントの発生タイミングと使いどころを押さえて、カスタマイズの幅を広げていただければと思います。

 

 

igGrid のライフサイクルイベント

http://www.igniteui.com/help/api/2016.2/ui.iggrid#events

APIリファレンスのページには計20のイベントがありますが、そのうち、

・cellClick
・cellRightClick
・columnsCollectionModified
・requestError

の4つを除いた16イベントがライフサイクルイベントに該当します。ライフサイクルというだけあって、グリッドの生成時、破棄時に発生します。

 

◆グリッド生成時
1. rendering
    2. schemaGenerated
    3. dataBinding
        4. headerRendering
           5. headerCellRendered
        6. headerRendered
    7. dataBound
    8. dataRendering
          9. footerRendering
        10. footerRendered
        11. rowsRendering
        12. rowsRendered
    13. dataRendered
14. rendered
15. igcontrolcreated

1 - 15 の順でイベントが発生します。よく使われるイベントとしては以下があります。

headerRendered:
グリッドのヘッダーが描画された後に発生します。ヘッダーに対してスタイルを適用したい、といった用途でよく使用されます。

また、ヘッダーセルクリック時にカスタムの処理を組み込むために、このイベントを使って、ヘッダーセルへのクリックイベントハンドラを設定する、といったことも可能です。

rowsRendered / dataRendered:
グリッドの行が描画された後に発生します。行に対してスタイルを適用したい、といった用途でよく使用されます。
rowsRendered イベントは行そのもののDOMが全て描画された直後に発生し、dataRenderedイベントはその後、グリッドの高さやスクロール、カラムの幅などのレイアウトを調整したあとに発生します。行そのものに作用させるだけで良ければ rowsRendered のタイミングで構わないと思いますが、グリッドの高さなどの情報も処理に必要な場合は dataRendered のタイミングで処理をしておいた方がより安全と思われます。

また、行に対してカスタムイベントハンドラを設定するために使用されたりします。

後述しますが、これらのイベントは、グリッド生成後も繰り返し発生することがあります。

rendered:
グリッドのすべてが描画された後に発生します。このタイミングではグリッド内のすべてのDOMにアクセスできます。ヘッダーや行に対してまとめて処理を適用したいといった場合に、このイベントで行うことが可能です。

また、グリッドの生成が完了したタイミングでアプリケーションに対して何かしらの通知を行いたい、といった用途でも使用されることがあります。

 

◆2回目以降 ※フィルター処理、ページング処理などが行われた場合に発生
1. dataBinding
2. dataBound
3. dataRendering // このイベントの直後に行のDOMが一旦すべて削除されます
    4. rowsRendering
    5. rowsRendered  // このイベントの直前に新たな行のDOMがすべて描画されます
6. dataRendered

rendering(ed), headerRendering(ed), footerRendering(ed) イベントが発生しないことに注意してください。

自分もよくやってしまう間違いとして、行のスタイル変更などを rendered イベントで行ってしまうことがあります。しかし、上述の通り、フィルターやページング、ソーティング機能を使用している場合、その操作を行うと、グリッドの行の部分のDOMは一旦削除されてから再描画されるため、せっかく rendered イベントで行ったカスタマイズが消えてしまいます

これを防ぐためには、rendered イベントの代わりに rowsRendered イベントを使用して、フィルター、ページング、ソーティングが行われるたびに毎回処理を行うようにします。

 

◆グリッド破棄時
1. igcontroledestroyed

グリッドの破棄時はこのイベントのみ発生します。グリッドが破棄された後の後処理として何かを行いたいという場合に使用できるかと思います。

 

 

サンプル

igGrid のライフサイクルイベントの発生順序を実際に動かして確認できるサンプルHTMLを作成致しました。HTMLファイルをブラウザで表示したのち、開発者ツールを表示させた状態でグリッドの生成/破棄をお試し頂くと、コンソール上にイベント名が出力されるのをご確認いただけます。

サンプルのダウンロード(本サンプルは Ignite UI 16.2.2040 (2017/1/30時点) で作成しました)  

Ignite UI components for Angular 2 を見てみよう

- 2016/08/17(水)時点の情報に基づいています -

こんにちは、インフラジスティック・ジャパン デベロッパーサポート の桐生です。

弊社のjQuery/HTML5用コントロールIgnite UI が Angular 2 に対応した Ignite UI components をご紹介したいと思います。

Ignite UI components for Angular 2 は、Angular 2 beta 版からサポートを開始し、現時点では RC4 に対応しています。

Angular 2 については、つい先日8/10(水) にRC5がリリースされましたので、Ignite UI components もそのうちアップデートがあるのではないかと期待しています。

ソースコードは GitHub 上にホストされていますので、自由にクローンして試すことが可能です。

実際にサンプルを動かしてみましょう。

 

サンプルプロジェクトの実行

作業フォルダに移動したのち、コンソール上で

git clone https://github.com/IgniteUI/igniteui-angular2
cd igniteui-angular2
npm install
npm start

と実行します。 (事前に Git、 Node.js がインストールされていることを確認してください)


ブラウザに以下のように表示されれば成功です。Ignite UI components for Angular 2

 

 

サンプルプロジェクトフォルダ構成

igniteui-angular2
│  index.html
│  systemjs.config.js
├─app
│     boot.ts
│     igniteui.angular2.ts
│     igniteui.d.ts
│     jquery.d.ts
│     samples.ts
└─samples
   ├─igGrid
   ├─igDataChart
   ├─igEditor
   └─など各コンポーネントごとのサンプルが格納されたフォルダ


ソースコードを読んでみる

動作を確認するのに最低限必要なソースに限定して、読んでみます。

npm start を実行すると index.html が表示されますので、まずは index.html を見てみます。重要な部分のみをピックアップすると、以下のようになります。

index.html

<!DOCTYPE html>
<html>

<head>

    <link href="http://cdn-na.infragistics.com/igniteui/latest/css/themes/infragistics/infragistics.theme.css" rel="stylesheet" />
    <link href="http://cdn-na.infragistics.com/igniteui/latest/css/structure/infragistics.css" rel="stylesheet" />

    <!-- 1. Load libraries -->
    <script src="http://code.jquery.com/jquery-1.12.3.js"></script>
    <script src="http://code.jquery.com/ui/1.11.4/jquery-ui.min.js"></script>

    <!-- IE required polyfills, in this exact order -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/es6-shim/0.35.0/es6-shim.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/systemjs/0.19.20/system-polyfills.js"></script>
    <script src="https://npmcdn.com/angular2/es6/dev/src/testing/shims_for_IE.js"></script>

    <script src="https://npmcdn.com/zone.js@0.6.12?main=browser"></script>
    <script src="https://npmcdn.com/typescript@1.8.10/lib/typescript.js"></script>
    <script src="https://npmcdn.com/reflect-metadata@0.1.3"></script>
    <script src="https://npmcdn.com/systemjs@0.19.27/dist/system.src.js"></script>
    <script src="./systemjs.config.js"></script>

    <!-- Ignite UI Required Combined JavaScript Files -->
    <script src="http://cdn-na.infragistics.com/igniteui/latest/js/infragistics.core.js"></script>
    <script src="http://cdn-na.infragistics.com/igniteui/latest/js/infragistics.lob.js"></script>

    <script>
        System.import('app/boot.ts')
            .then(null, console.error.bind(console));
    </script>
</head>

<body>
    <my-app>Loading...</my-app>
</body>

</html>

いろいろとファイルを読み込んだ後、SystemJS を使って app/boot.ts ファイルを動的にロードしています。

以前書いた記事「TypeScriptをブラウザ上で手軽に試す」でも SystemJS を使って TypeScript を動的にロードする方法をご紹介しましたが、SystemJS を使うにあたっては設定が必要で、このプロジェクトでは system.config.js として別JSファイルで定義しています。

※本家の Angular 2 5 min QuickStart でも同様に、SystemJS の設定は system.config.js として切り出されていますね。

それでは system.config.js ファイルの中を見てみましょう。

system.config.js

(function(global) {
	・・・
    var config = {
        map: map,
        packages: packages,
        transpiler: 'typescript',
        typescriptOptions: {
            emitDecoratorMetadata: true
        }
    }
    System.config(config);
})(this);

こちらもいろいろ書いてありますが、最終的には config オブジェクトを作って System.config でセットしています。生成されたconfigオブジェクトをコンソール上に出力すると、以下のようになっています。

{
	"map": {
		"app": "app",
		"rxjs": "https://npmcdn.com/rxjs@5.0.0-beta.6",
		"angular2-in-memory-web-api": "https://npmcdn.com/angular2-in-memory-web-api",
		"@angular": "https://npmcdn.com/@angular",
		"@angular/common": "https://npmcdn.com/@angular/common@2.0.0-rc.4",
		"@angular/compiler": "https://npmcdn.com/@angular/compiler@2.0.0-rc.4",
		"@angular/core": "https://npmcdn.com/@angular/core@2.0.0-rc.4",
		"@angular/http": "https://npmcdn.com/@angular/http@2.0.0-rc.4",
		"@angular/platform-browser": "https://npmcdn.com/@angular/platform-browser@2.0.0-rc.4",
		"@angular/platform-browser-dynamic": "https://npmcdn.com/@angular/platform-browser-dynamic@2.0.0-rc.4",
		"@angular/router": "https://npmcdn.com/@angular/router@2.0.0-rc.4",
		"@angular/router-deprecated": "https://npmcdn.com/@angular/router-deprecated@2.0.0-rc.4",
		"@angular/testing": "https://npmcdn.com/@angular/testing@2.0.0-rc.4",
		"@angular/upgrade": "https://npmcdn.com/@angular/upgrade@2.0.0-rc.4"
	},
	"packages": {
		"app": {
			"main": "boot.ts",
			"defaultExtension": "ts"
		},
		"rxjs": {
			"defaultExtension": "js"
		},
		"angular2-in-memory-web-api": {
			"defaultExtension": "js"
		},
		"@angular/common": {
			"main": "index.js",
			"defaultExtension": "js"
		},
        ・・・
	},
	"transpiler": "typescript",
	"typescriptOptions": {
		"emitDecoratorMetadata": true
	}
}
  • map
  • packages
  • transpiler
  • typescriptOptions

の設定が行われていますが、packages の設定から app/boot.ts がこのプロジェクトのエントリーポイントであることが分かります。

少し脱線しますが、map の役割について触れてみたいと思います。
ES6 の import シンタックス を使ってモジュールをインポートする場合、map を使わないと、

import * from 'https://npmcdn.com/@angular/core@2.0.0-rc.4'

のように参照パスをハードコードすることになりますが、後々パスが変わってしまった場合修正が大変になります。そこで、map を使うことで、

import * from '@angular/core'

と書くことができるようになります。つまり、map で指定した名前がそのモジュールのエイリアスとして機能します。パスが変わってしまった場合は  map の部分を修正するだけで済みますね。

 

続いてエントリーポイントである app/boot.ts の中を見てみましょう。

app/boot.ts

import {bootstrap}    from '@angular/platform-browser-dynamic'
import {AppComponent} from './samples'

bootstrap(AppComponent);

app/samples.ts から AppComponent というコンポーネントをインポートしてきて実行しているだけですね。最後に app/samples.ts を見てみます。

app/samples.ts

import {Component, Inject, ElementRef, EventEmitter, HostListener} from '@angular/core';
import {IgGridComponent} from "../src/igniteui.angular2.ts";

declare var jQuery: any;
@Component({
	selector: 'my-app',
	template: `<ig-grid [(options)]="gridOptions"
                        [(widgetId)]='id'
                        (cellClick)="cellClickHandler($event)"
                        (rendering)='renderedEventHandler($event)'
                >
                </ig-grid>`,
	directives: [IgGridComponent]
})
export class AppComponent {
	private gridOptions: IgGrid;
	private cellClickHandler: any;
	private renderedEventHandler:any;
    private data: Array<any>;
    private id: string = "grid1";

	constructor() {
		this.data = [
            // ・・・
        ];

        this.cellClickHandler= function(ui){
            console.log("grid cell click");
            
        };

        this.renderedEventHandler= function(ui){
            console.log("grid is rendered.");
            
        };

        this.gridOptions = {
            dataSource: this.data,
            width: "100%",
            primaryKey: "ProductID",
            autoCommit: true,
            autoGenerateColumns: false,
            columns: [
                   { "headerText": "Product ID", "key": "ProductID", "dataType": "number", "width": "10%" },
                   { "headerText": "Name", "key": "ProductName", "dataType": "string", "width": "40%" },
                   { "headerText": "Quantity per unit", "key": "QuantityPerUnit", "dataType": "string", "width": "25%" },
                   { "headerText": "Unit Price", "key": "UnitPrice", "dataType": "string", "width": "25%" }
            ],
            features: [{
                name: "Updating",
                columnSettings: [{
                    columnKey: "ProductID",
                    readOnly: true
                }]
            }, {
                name: "Paging",
                pageSize: 10
            }, {
                name: "Filtering"
            }, {
                name: "Sorting"
            }]
        };
	}
}

最後にようやく Angular 2 らしいソースコードが現れました。

まずは2行目に注目してください。

import {IgGridComponent} from "../src/igniteui.angular2.ts";

Angular 2 用の Ignite UI components を使用するには igniteui.angular2.ts から使いたいものをインポートしてくる必要があります。ここでは、igGrid 用のコンポーネントである IgGridComponent を1つインポートしています。

複数のコンポーネントをインポートする必要がある場合は、

import {IgGridComponent, IgDataChartComponent} from "../src/igniteui.angular2.ts";

のように記述します。

igGrid → IgGridComponent
igDataChart → IgDataChartComponent

のように、先頭の小文字「i」を大文字「I」に変え、末尾に「Component」をつけるのがコンポーネントのネーミングルールとなっているようです。

続いて7行目。template 内では <ig-grid> として使用することができます。ただし、13行目でやっているように directives に登録しておく必要がありますので忘れないように注意しましょう。

Ignite UI components には「optins」「widgetId」という2種類の必須属性がありますので、使う側(親コンポーネントなど)からはこれら2つのデータを渡してあげる必要があります。このサンプルでは

  • options ← gridOption
  • widgetId ← id

のデータをそれぞれ渡しています。

gridOption の内容を見てみると、オプションはすべて1つのオブジェクトにまとめられていことが分かります。これは Ignite UI を使ったことがある方にとってはなじみのある形ではないでしょうか。

AngularJS 1 向けに提供していた ディレクティブでは、オプションの設定方法が分からないというお問い合わせがよくあったのですが、この形であれば迷うことは少ないのではないかと思います。オンライサンプルにあるjQuery版の設定コードをそのまま貼り付けすることができますね。

 

まとめ

自分で実際にサンプルを動かしたり、ソースコードを読んでみたりした感想として、実際に Angular2 でプロジェクトを作成しようと思ったら、そもそも Angular 2 のセットアップ自体が大変だなと感じました。このサンプルでさえも index.html で読み込むべきスクリプトファイルの種類と、system.config.js で書かれているSystemJSの設定について、理解するまですこし苦労しましたので。

一方で、Ignite UI のコンポーネントのオプション設定は、これまでやってきたjQuery版のオプション設定がそのまま書けるので、難しいところは全くありませんでした。

一にも二にも Angular 2 のしくみを知ることが必要だということですね。Angular 2 についてこれから始めようとお考えの方はng-japan 2016 セッション資料まとめ などを確認されるとよいと思います。

TypeScriptをブラウザ上で手軽に試す

- 2016/08/11(木)時点の情報に基づいています -

こんにちは。デベロッパーサポートの桐生です。

今回、TypeScript をブラウザ上で手軽に試すための方法をご紹介したいと思います。

Angular2 では TypeScript が使われており(必ずしも TypeScript を使わなければいけないわけではありませんが)、今後Angular2が正式リリースされた暁には TypeScript の需要がい一気に高まるのではないでしょうか。

※弊社のjQuery/HTMl5用コントロールIgnite UIでも、TypeScriptへの対応を行っており、またAngular2 ベータ版用のコンポーネントもバージョン16.1から提供開始となりました。

一方で、ここ数年でWebフロントエンド開発は非常に高度化、複雑化してきており、開発環境自体を構築するのも難しくなってきています。 実際、TypeScriptを使って開発を行うためには、Node.jsのインストールから始まり、Grunt、Gulp、browserifyやWebpack などのビルドツールをセットアップして・・・、と本来やりたいことよりも開発環境を構築することに時間がかかってしまうことがよくあります。Webフロントエンド開発に慣れていない場合は中々に大変な作業となります。

ちょっと試したいだけなのに正直そこまでやるのは面倒、という方に朗報です。
実は、SystemJSというモジュールローダー用のJSライブラリを使うことにより、 ブラウザ上で TypeScript  を変換(トランスパイルと言います)し実行することができるのです。

 

サンプルのフォルダ構成

Sample
│  index.html
└─app
    │  main.ts
    │  module1.ts
    └─module2.ts

サンプルはこちらです。

この Sample をサーバーに配置して、
http://localhost/Sample/index.html
などでアクセスしてみてください。

※このサンプルを実行するにあたって、Grunt、Gulp、browserifyやWebpack などのビルドツールは一切使用しません。

 

サンプルの解説

index.htmlにアクセス

app/main.tsのロード

app/module1.tsのロード

app/module2.tsのロード

という流れでTypeScriptファイルが動的にロードされ、トランスパイルされます。

index.html

<table id="grid"></table>

<!-- SystemJS + TypeScript のインポート -->
<script src="http://blogs.jp.infragistics.com//cdnjs.cloudflare.com/ajax/libs/systemjs/0.19.36/system.js"></script>
<script src="http://blogs.jp.infragistics.com//cdnjs.cloudflare.com/ajax/libs/typescript/2.0.0/typescript.min.js"></script>

<!-- いつも通り jQuery と Ignite UI のインポート -->
<script src="http://blogs.jp.infragistics.com//code.jquery.com/jquery-2.2.4.min.js"></script>
<script src="http://blogs.jp.infragistics.com//code.jquery.com/ui/1.12.0/jquery-ui.min.js"></script>
<script src="http://cdn-na.infragistics.com/igniteui/2016.1/latest/js/infragistics.core.js"></script>
<script src="http://cdn-na.infragistics.com/igniteui/2016.1/latest/js/infragistics.lob.js"></script>
<script src="http://jp.igniteui.com/data-files/adventureworks.min.js"></script>
<script>
    /**
     * SystemJS 設定
     */
    System.config({
        transpiler: 'typescript', // typescriptでトランスパイルします
        typescriptOptions: {
            // https://www.typescriptlang.org/docs/handbook/compiler-options.html
            // で定義されているオプションを指定可能です。
        },
        packages: {
            // appパッケージについて
            'app': {
                main: './main.ts', // エントリーポイントの指定
                defaultExtension: 'ts', // デフォルト拡張子の指定
            }
        },
        meta: {
            // appパッケージ配下のtsファイルに対して
            'app/*.ts': {
                // // 必ずトランスパイルされるよう ECMAScript Module であることを明示
                // https://github.com/systemjs/systemjs/blob/master/docs/module-formats.md
                // ※どうやらソースコード中に import などのES6キーワードが含まれていないと、
                // トランスパイルされないようですが const や let だけではダメでした。
                format: 'esm'
            }
        }
    });

    /**
     * SystemJS 実行
     */
    // appパッケージの読み込み(エントリーポイントである app/main.ts が読み込まれる)
    System.import('app')
        .catch(console.error.bind(console));
</script>

System.configでの設定は、おそらくこのサンプルの中において最も面倒なところですが、今回の目的であるTypeScriptを手軽に試すという点においては、この設定を変更することはありませんので、このようなものとご認識ください。


もしご自身の環境で試されたい場合は、この設定をコピーしつつ
・25,32,46行目:パッケージ名(つまり ts ファイル格納しているフォルダ名)
・26行目:index.ts、app.ts などエントリーポイントの名前
を環境に合わせて変更するだけで、動作するようになるかと思いますので、お試しください。


設定について詳しく知りたい方は SystemJS Configuration API をご確認ください。


モジュールをロードするには、46行目で System.import('app') とやります。
するとエントリーポイントである app/main.ts がロードされます。

app/main.ts

import Module1 from './module1';

let module1 = new Module1();
module1.method();

Module1 クラスの method メソッドを実行していますが、 Module1 クラスを使用するためには module1 モジュールを import しておかなければならないため、app/module1.ts がロードされます。

 

app/module1.ts

import Module2 from './module2';

export default class Module1 {
    constructor() {

    }

    method() {
        console.log('Module1 method called');

        let module2 = new Module2();
        module2.method();
    }
}

Module1 クラスの method メソッドの中では、Module2 クラスの method メソッドをコールしていますが、 Module2 クラスを使用するためには module2 モジュールを import しておかなければならないため、app/module2.ts がロードされます。

 

app/module2.ts

export default class Module2 {
    constructor() {

    }

    method() {
        console.log('Module2 method called');

        $("#grid").igGrid({
            width: "100%",
            height: '500px',
            dataSource: adventureWorks,
            primaryKey: 'ProductID',
            columns: [
                { headerText: "製品 ID", key: "ProductID", dataType: "number", width: "15%" },
                { headerText: "製品名", key: "Name", dataType: "string", width: "40%" },
                { headerText: "製品番号", key: "ProductNumber", dataType: "string", width: "30%" },
                { headerText: "メーカー フラグ", key: "MakeFlag", dataType: "bool", width: "15%" }
            ],
            features: [
                {
                    name: "Updating",
                },
            ],
            rendered(evt, ui) {
                console.log('igGrid rendered');
            }
        });
    }

}

 

最終的に、Module2 クラスの method メソッドが実行され、画面上に 弊社Ignite UI コントロールの igGrid が生成されます。
実行結果は以下のようになります。実行結果

Chrome DevTools の Network タブを確認すると、確かに main.ts > module1.ts > module2.ts の順でロードされていることが分かります。SystemJSのおかげでTypeScriptモジュールが適切な順序で動的にロードされ、トランスパイルされ、実行されています。

 

まとめ

SystemJS を使うことで、 ブラウザ上で TypeScript をトランスパイル実行することができることを確認頂けたかと思います。

ただし、今回の方法はあくまで手軽さを目的としており、パフォーマンスを考慮していません。
TypeScript のロード時にトランスパイルするオーバーヘッドがあるため、パフォーマンス的にプロダクション環境での使用にはあまり適さないと思われます。 プロダクション環境では、やはりビルドツールを使ってトランスパイル済みのコードを使うのがよいと考えます。

 

※ところで、型定義ファイルは?

今回のサンプルでは、型定義ファイルは使用しませんでした。
※VS Code などのエディタ上では赤い波線のエラー警告が表示されますが、動作上は問題ありません。

ですが、実際にTypeScriptを使って開発を行う時には、やはり必要になってきますので、 その時は、TypingsTypeScript 2.0 であればnpm (Qiita で分かりやすい記事がありました)でインストールして使用するようにしましょう。

igGrid 動的にセル編集可/不可を制御する

- 2016/08/03(水)時点の情報に基づいています -

 

こんにちは。デベロッパーサポートの桐生です。

弊社のjQuery/HTMl5用コントロールIgnite UIのグリッド igGridの更新機能をお使いで、動的にセルの編集可/不可を制御したい、というお問い合わせがよくありますので、 その方法をご紹介したいと思います。

 

基本:イベントキャンセルによるセル編集制御

Ignite UI の各コントロールには、大抵の場合において「XXX」の処理をする際の
・前イベントとして「XXXing」イベント
・後イベントとして「XXXed」イベント
が用意されています。
「XXXing」イベントハンドラ内では false を返却することで「XXX」の動作をキャンセルすることができます。

今回の場合 igGridUpdating の editCellStarting イベントを使用して制御を行います。

$('#grid').igGrid({
    width: '600px',
    height: '400px',
    dataSource: dataSource,
    columns: [
        { headerText: 'ID(number)', key: 'id', dataType: 'number', width: '100px' },
        { headerText: 'Name(string)', key: 'name', dataType: 'string', width: '300px' },
        { headerText: 'Flag(bool)', key: 'flag', dataType: 'bool', width: '200px', format: 'checkbox' },
    ],
    primaryKey: 'id',
    features: [
        {
            name: 'Updating',

            // セル編集モード
            editMode: 'cell',

            // nameセル 動的に編集可/不可を切替
            editCellStarting: function(evt, ui) {
                // nameセルでない場合は、編集可
                if (ui.columnKey !== 'name') {
                    return true;
                }

                // 動的に編集可/不可を切り替える
                // 例えば、同じ行のflagセルがチェックONなら編集可、チェックOFFなら編集不可
                var flag = ui.owner.grid.getCellValue(ui.rowID, 'flag');
                if (flag) {
                    // チェックOFFなら編集不可で終了
                    // (false返却でセル編集開始処理がキャンセル = 編集不可)
                    return false;
                }
            },
        },
    ]
});

グリッドには、ID、Name、Flagの3つのセルがあり、それぞれ編集可能となっていますが、
Nameセルは編集可/不可を、Flagセルの値に応じて動的に制御しています。

サンプル1はこちらです。
(本サンプルは16.1バージョンで作成されました)

 

応用:セル編集を制御しつつ、タブ移動を快適にする

上記サンプルでは、 該当セルの編集は確かにキャンセルできるものの、
タブ移動をしている場合、編集がキャンセルされたセルから次のセルへ移動できなくなってしまいます。

期待値としては、編集不可なセルはスキップして、編集可能なセルにのみタブ移動してほしいところです。

この動作を実現してみましょう。

// 動的に編集可/不可を切り替える
// 例えば、同じ行のflagセルがチェックONなら編集可、チェックOFFなら編集不可
var flag = ui.owner.grid.getCellValue(ui.rowID, 'flag');
if (flag) {
    // チェックOFFなら編集不可で終了
    // (false返却でセル編集開始処理がキャンセル = 編集不可)
    return false;
}

の箇所を以下のように変更します。

// 動的に編集可/不可を切り替える
// 例えば、同じ行のflagセルがチェックONなら編集可、チェックOFFなら編集不可
var flag = ui.owner.grid.getCellValue(ui.rowID, 'flag');
if (!flag) {
    // チェックONなら、編集可
    return true;
}

// クリックしてnameセルの編集モードに入ってきた場合
if (evt.originalEvent.type === 'click') {
    // false を返却することにより、編集不可にする。
    return false;
}

// Tabキーを押下してnameセルの編集モードに入ってきた場合
// 次(または前)のセルにスキップさせることにより、編集不可にする。
$(document).one('iggridupdatingeditcellstarted', function (evt2, ui2) {
    // 処理対象が同じでない場合はキャンセル
    if (ui !== ui2) {
        return;
    }

    // Tabキーキーダウン用のEventオブジェクト
    var emulateTabKeyEvent = jQuery.Event('keydown', {
        which: $.ui.keyCode.TAB,
        keyCode: $.ui.keyCode.TAB,
        shiftKey: evt.originalEvent.shiftKey
    });

    // Tabキー(あるいはShift+Tab)キーダウンイベントをトリガーすることにより、次(前)のセルにスキップさせる
    $(ui2.editor.find('input')).trigger(emulateTabKeyEvent);
});

ポイントは、false を返す代わりに、jQueryのEventオブジェクトとtriggerメソッドを使ってタブキーダウンイベントをシミュレートして、該当セルの編集を瞬間的に終了させている点です。
これにより実質的に編集不可にしています。

通常のイベントハンドリングと同様に、タブキーダウンイベントを受けてコントロールが次のセルを探して編集状態にしてくれるので、わざわざ次のセルを探して、編集状態にして、と自前で実装する必要がありません。

サンプル2はこちらです。
(本サンプルは16.1バージョンで作成されました)

 

まとめ

動的に~したい、といった場合には「XXXing」イベントを使用して制御するケースが多いです。
今回は、igGridUpdatingのeditCellStartingを使って制御する方法をご紹介しましたが、
この考え方は他のコントロール、機能においても通ずるものがありますので、ご参考になれば幸いです。

Ignite UI のカスタムビルドを使用してパフォーマンス改善を図る

- 2016/07/21(水)時点の情報に基づいています -

 

Ignite UI のJSファイルのロードに時間がかかってパフォーマンスが遅い場合は・・・

Ignite UI の機能を使用するためには

https://jp.igniteui.com/getting-started

  • infragistics.core.js    410KB   // 基本ロジックおよびデータ ソース コンポーネント
  • infragistics.dv.js      3.28 MB // チャートおよびマップ コントロール
  • infragistics.lob.js     2.77 MB // グリッド、OLAP、および業務用コントロールなどの他のコントロール

のJSファイル(に加えてCSSファイルも)が必要となりますが、

すべての機能を使う チャート系を使う グリッドその他を使う
必要なJSファイル infragistics.core.js
infragistics.dv.js
infragistics.lob.js
infragistics.core.js
infragistics.dv.js
infragistics.core.js
infragistics.lob.js
合計サイズ 6.46MB 3.69MB 3.18MB

ファイルサイズが大きく、ネットワークの状況によってはロードに時間がかかってしまい、画面の表示速度上のボトルネックとなることがあります。

そのような場合には、カスタムビルドを使ってパフォーマンス改善を図ることができます。

 

※igLoaderを使用して、必要なコントロールのJSファイルを動的に読み込むことにより
初期ロードの時間短縮を図る方法もあります。

 

カスタムビルドを作る

カスタムビルドとは、必要なコントロールのみをパッケージングしたビルドファイルのことです。
使わないコントロールはファイルに含まれないため、ファイルサイズを削減することができます。
結果として、ロード時間の短縮になり、パフォーマンス改善につながります。

 

早速カスタムビルドを作成してみましょう。
IgniteUIのダウンロードページでログインし、必要なコントロールを選択するだけです。

1) サインイン

https://jp.igniteui.com/download

のページにアクセスし、「製品版のダウンロード:サインイン」からサインインを行ってください。

custom-build-sign-in

※ここでは、サインインせずにトライアル版のまま進めます。

 

2) バージョンの選択

サインインが完了すると、バージョン選択で製品版のバージョンを選択することができるようになりますので、
お使いのバージョンを選択してください。

custom-build-select-version

※ここでは、「2016.1 トライアル版」を選択します。

 

3) コンポーネントの選択

続いて、お使いのコントロールやその機能を選択してください。

依存関係のある他のコントロールにも自動でチェックが入りますので、誤ってチェックを外さないようにご注意ください。

選択が終わったら、画面下部にある「カスタム ビルドのダウンロード」ボタンをクリックして、ダウンロードしてください。

custom-build-select-controls 

※ここでは、

Gridコントロール

GridShared ※グリッドの共有機能なのでチェック必須です。

RowSelectors(行選択)

Selection(選択)

Updating(更新)

を選択します。

以上でカスタムビルドの作成は終了です。

 

カスタムビルドを使う

ダウンロードしたzipファイルを解凍すると、

infragistics.js

というJSファイルが入っていますので、これを core、 lob、dv の代わりに使用するだけでOKです。

なお、解凍されたフォルダにCSSファイルも一緒に入っていますが、CSSファイルも必要なコントロールの分だけがパッケージングされています。

ちなみに、今回作成したカスタムビルドではいったいどのくらい削減できたのか確認してみましょう。

  グリッドその他 igGirdの更新・選択機能のみの
カスタムビルド
必要なJSファイル infragistics.core.js
infragistics.lob.js
infragistics.js
合計サイズ 3.18MB 1.04MB

単に core + lob を読み込む場合に比べて、2MB 以上もファイルサイズを削減することができました。
これによりロード時間も1/3程度に短縮されます。
特にネットワークの遅い環境においては、この違いが顕著に表れてくるはずです。

 

まとめ

一口にパフォーマンス改善といっても、原因によって対処方法も全く異なってきますが、今回のようにJSファイルのロード時間が原因となっている場合には、ファイルサイズの削減は効果的な方法です。

カスタムビルドを使用することによって、ファイルサイズを削減し、ロード時間を短縮し、パフォーマンス改善を図る方法をご紹介しました。