スタッフネット株式会社 http://www.staffnet.co.jp 新技術でビジネスを加速する Thu, 27 Oct 2016 10:25:37 +0000 ja hourly 1 https://wordpress.org/?v=4.6.1 http://www.staffnet.co.jp/wp-content/uploads/2016/07/cropped-staffnet_icon-32x32.png スタッフネット株式会社 http://www.staffnet.co.jp 32 32 Angular2とTypeScriptによるモダンWeb開発セミナー http://www.staffnet.co.jp/pickup20160526/ Sat, 24 Sep 2016 23:10:37 +0000 http://localhost/?p=43 「Angular2とTypeScriptによるモダンWeb開発セミナー」受講予約の受付を開始しました。モダンWebを効率よく開発するための情報が満載です。詳しくはこちら

]]>
業務アプリにおけるHTML5の効果的導入 http://www.staffnet.co.jp/201608091111f/ Tue, 09 Aug 2016 02:16:20 +0000 http://www.staffnet.co.jp/?p=1237 業務アプリにおけるHTML5の効果的導入

概要

html5_logo一般消費者向けWebサイトの構築では、HTML5の活用がすすんでおり、見栄えや使い勝手の良い画面を、効率良く開発できるようになりました。
一方、企業向け業務Webアプリについては技術的課題が多く、これまでHTML5の導入は困難とされてきました。しかし、技術的課題は解消しつつあります(次項で説明)。これからは、他の技術と同様に導入コストに見合う効果が問われる段階になってきます。

本記事では、業務アプリにおいてHTML5の効果的な導入ステッブについて解説します。

1.技術的障壁の解消

まず、業務アプリ開発におけるHTML5の現状把握をします。下記のような技術的障壁は解消しつつあります。

1)WebブラウザがHTML5に未対応
業務アプリの安定稼動のため、HTML5未対応の古いバージョンのブラウザを利用している企業が多くあります。しかし、来年4月以降、Internet Explorer 11未満はサポート切れになるため、HTML5に対応したブラウザが前提になります。

2)JavaScript特有の実装スキル
HTML5の開発言語はJavaScript限定でした。JavaScriptのオブジェクト指向開発はJava等とは全く異なるため、新たなスキル取得が必要でした。しかし、TypeScriptの利用で、Javaと同じクラス定義による開発が可能になりました。型定義、名前空間、実行前のコードチェックなど大規模開発に必要な機能も提供され、Java等の経験者も抵抗なく利用できます

3)開発効率が悪い
JavaScriptには標準のクラスライブラリが付属しません。自社でライブラリを開発したり、オープンソースのライブラリを組み合わせたり、追加の作業が発生していました。しかし、Angular2等の開発フレームワークにより、アプリ開発に必要な機能(クラスライブラリ、MVC構造、自動テスト等)が提供され、開発効率が大幅に改善しました。

2.サーバー集中から分散処理へ

技術的な課題は解消しつつありますが、HTML5はブラウザで利用する技術です。HTML5の本格的導入は、サーバー集中処理から、ブラウザで分散処理するアーキテクチャの変更を意味します。アプリの移行作業や設計方針、運用の変更も必要になります。

実際は、現状のサーバー集中型のままHTML5を一部活用するレベルから始め、徐々に分散型に移行してHTML5のフル活用を目指すのが現実的です。

HTML5の効果的導入

弊社では、以下のような段階的導入を推奨しています。

STEP-1.画面単位の導入

HTML5導入トライアルの位置づけです。現在のシステムの一部の画面にHTML5による機能追加を加えたプロトタイプを作成します。ユーザや管理者への導入効果アピールと、開発環境・開発ノウハウの基礎スキルを獲得します。追加機能のお勧めが無限スクロールです。手軽に導入できて、誰でも効果が実感できます。
・対象範囲
一部の画面にHTML5による機能を追加します。
・導入効果
操作性の大幅改善が確認できます。例えば無限スクロールの場合、大量データを短時間で目視検索できます。
・補足
無限スクロールは、業種・業務を問わず業務アプリに含まれるリスト表示画面に適用でき、サーバー集中型のままで実装できるので開発工数は最小限で済みます。
[参考]
無限スクロールの実装
http://www.staffnet.co.jp/201608081810e/

STEP-2.アプリ単位の導入

アプリ単位の導入であれば、HTML5の導入効果を顕著に示すモバイルアプリから始めるのがお勧めです。モバイルに要求される、迅速な画面切り替え、ネットワーク圏外での利用を実現した上で、Webアプリのメリットであるクロスプラットフォームでの動作、アプリバージョンの集中管理が可能です。モバイルOS用のアプリ開発とくらべ、コストを大幅に削減できます。
・対象範囲
モバイル向けアプリ開発
・導入効果
ひとつのソースコードで、スマートフォン・タブレット・PCすべてに対応できるため、プラットフォームごとの開発と比べ、大幅なコスト削減ができます。
・補足
サーバー集中型のままでも運用可能ですが、軽快な操作感やネットワーク圏外対応ができないため、分散型で開発します。すでにモバイルOS用アプリを導入済であっても、保守・運用コスト削減のためにWebアプリへ移行することもあります。

STEP-3.全面導入

Webアプリ全体の分散型への移行で、コスト削減、システム全体の機能と性能向上を実現して、大きな導入効果が期待できます。
・対象範囲
全システム
・導入効果
Webの根本的制約から解放され、コスト削減が期待できます。
・モバイルで重要なネットワーク圏外のアプリ利用
・通信完了を待つことなく瞬時に行われる画面遷移
・サーバーとネットワークの負荷軽減によるインフラコストの削減
・補足
システム基盤再構築を検討中の場合は、このステップから始めることもあります。

弊社では、「Angular2とTypeScriptによる モダンWeb開発セミナー」を開催します。
Angular2の実践的な知識とノウハウを習得できます。
http://www.staffnet.co.jp/hp/semi/modern/

]]>
無限スクロールの実装 http://www.staffnet.co.jp/201608081810e/ Mon, 08 Aug 2016 09:49:28 +0000 http://www.staffnet.co.jp/?p=1227 Angular2による無限スクロールの実装
1.無限スクロールとは

無限スクロールは、件数の多いリスト表示に便利なユーザーインターフェースです。

例えば、Googleで検索すると、まず10件分の結果が表示され、残りは複数の画面に分割されます。画面の一番下に、下図のようなページ送りアイコンが表示されます。
google_pageindex
求めるデータが1ページ目に無いときは、ページ送りを繰り返しています。この操作を面倒に感じませんか。ExcelやWordのように全体を高速でスクロールしたいと思いませんか。

無限スクロールを使うと、件数の多いリスト表示であっても、画面の切り替えなしに、どこまでもスクロールして表示できます。無限スクロールを体験した人で、いままでのページ送り操作に戻りたいと言う人はいません。

[参考]
無限スクロールのサンプルビデオ

操作性の向上だけでなく、大量の書類・図面・写真などの流し見をして、素早くチェックすることも可能になり、業務効率が向上します。無限スクロールは、ブラウザに処理を分散する「モダンWeb」ならではの機能です。幅広い用途に効果があります。
infini_example

2.無限スクロールのしくみ

無限スクロールは、通信中もユーザーの操作を妨げない非同期通信を使用し、画面表示とデータ受信を同時に行います。
infinite_flow

次に表示するデータを次々と事前取得することで、どこまでもスクロールできます。表示済データを廃棄することで、どんなに大量のデータであっても、容量オーバーにはなりません。また、すべてのデータを受信してからの表示ではなく、最小限のデータが準備できた時点で表示が始まりますので、ページ送り方式に比べて大幅に待たされることはありません。

3.無限スクロールの実装

無限スクロール用のJavaScriptライブラリは多数公開されています。いくつか試しましたが例外処理が不十分だったり、ライブラリのファイルサイズが大きかったりしたため、Angular2で新たに実装したところ、無限スクロール実装部分は、300行程度で済みました。

2016年8月12日更新

import {Component, OnInit} from '@angular/core';
import {RequestOptions} from "@angular/http";
import {HttpService} from "../service/http.service"
import {DataService} from "../service/data.service";
import {SharedService} from "../service/shared.service";
import {I18nDatePipe} from '../pipe/i18nDate.pipe';
import {Router, ActivatedRoute} from "@angular/router";
import {TransitionService} from "../service/transition.service";

const htmlStr: string =
    require('component/reporthistory.component.html');
const cssStr: string =
    require("component/reporthistory.component.css");
const cssNav: string =
    require("../assets/css/navBar.css");

@Component({
    selector: 'report-history',
    template: htmlStr,
    styles: [cssStr, cssNav],
    pipes: [I18nDatePipe]
})

export class ReporthistoryComponent implements OnInit {
    record;
    nextRecord;
    prevRecord;
    startRow = 1;
    endRow = 1;
    BLOCK_SIZE = 30;
    isInitLoadDone = false;
    HEADER_HEIGHT = 50;
    BLOCK_HEIGHT;
    isEnd = false;
    isNextBufferReady = false;
    isPrevBufferReady = false;
    customerId;

    constructor(private httpService: HttpService,
                private dataService: DataService,
                private sharedService: SharedService,
                private router: Router,
                private route: ActivatedRoute,
                private transitionService: TransitionService) {
    }

    ngOnInit() {
        this.initData();
    }

    //初期処理
    initData() {
        //モバイル画面の判定
        this.sharedService.onResize();

        //UrlからcustomerId取得
        let customerId;
        this.route.params
            .map(params => params['Id'])
            .subscribe((Id) => {
                console.log("@@@@ Id:" + Id);
                this.customerId = Id;
            });

        //スクロール位置の初期化
        setTimeout(()=> {
            window.scroll(0, 0)
        }, 1);

        //位置固定ヘッダで隠れる部分を位置補正
        let bodyEl= document.getElementById("myBody")
        bodyEl.style.paddingTop = this.HEADER_HEIGHT + "px";
        this.BLOCK_HEIGHT=bodyEl.clientHeight/3;

        //初期表示データ取得
        this.getRecord(1, this.BLOCK_SIZE * 4)
            .then((result: any)=> {
                let data = result.data.data;
                if (data.length !== this.BLOCK_SIZE * 4) {
                    alert("データ件数が不足しています");
                    return;
                }
                this.record = data.slice(0, this.BLOCK_SIZE * 3);//アクティブなバッファ
                this.nextRecord = data.slice(-this.BLOCK_SIZE * 3);//下方向の次バッファ
                this.isNextBufferReady = true;//下方向の次バッファ有効フラグ
                this.endRow = this.BLOCK_SIZE * 3;//バッファ末尾データの行番号
                this.isInitLoadDone = true;//初期処理完了フラグ
            });
    }

    //ーーーーーーー--------s-----------
    // メニュー選択処理
    //ーーーーーーー-------------------
    onMenuClick(str, event) {
        event.preventDefault();
        event.stopPropagation();
        switch (str) {
            case "back"://顧客情報画面へ戻る
                this.transitionService.canTransition = true;
                this.router.navigate(['/customerDetail', this.customerId]);
                break;
        }
    }

    //ーーーーーーー-------------------
    //スクロールイベント処理
    //ーーーーーーー-------------------
    onScroll(event) {
        //初期処理完了前のスクロールイベントは無視
        if (!this.isInitLoadDone) return;

        //画面のレイアウト情報取得
        let html = document.documentElement;//html要素
        let pageHeight = html.scrollHeight;//全体の高さ
        let clientHeight = html.clientHeight; //表示域の高さ
        let scrollPos = window.pageYOffset;//スクロール位置
        let bufferHeight = pageHeight / 3;//Buffer Blockの表示域の高さ
        let bottom_margine = //下方向スクロール可能サイズ
            pageHeight - clientHeight - scrollPos;
        let top_margine = scrollPos; //上方向スクロール可能サイズ

        //バッファ枯渇の場合はBottomで反転バウンド(下方向)
        if (!this.isEnd && bottom_margine === 0) {
            setTimeout(()=> {
                scrollBy(0, -20)
            }, 1);
            return;
        }

        //バッファ枯渇の場合はTopで反転バウンド(上方向)
        if (this.startRow !== 1 && top_margine === 0) {
            setTimeout(()=> {
                scrollBy(0, 20)
            }, 1);
            return;
        }

        //バッファ追加の判定(下方向)
        if (bottom_margine < bufferHeight * 0.5 && !this.isEnd &&
            this.isNextBufferReady) {
            this.isNextBufferReady = false;
            console.log("@@@@ onScrollDown");
            this.onScrollDown();
            return;
        }

        //バッファ追加の判定(上方向)
        if ((scrollPos < bufferHeight * 0.5) &&
            (this.startRow !== 1) &&
            (this.isPrevBufferReady || this.prevRecord === null)) {
            this.isPrevBufferReady = false;
            console.log("@@@@ onScrollUp");
            this.onScrollUp();
            return;
        }
    }

    //バッファ追加(下方向)
    onScrollDown() {
        this.log();

        //次の上方向バッファを取得
        this.prevRecord = this.record.slice(0, this.BLOCK_SIZE * 3);
        this.isPrevBufferReady = true;

        //表示データ書き換え
        this.record = this.nextRecord;

        //表示位置カウンタの更新
        this.startRow += this.BLOCK_SIZE;
        this.endRow += this.record.length - this.BLOCK_SIZE * 2;

        //表示位置補正
        let bodyEl= document.getElementById("myBody")
        this.BLOCK_HEIGHT=(bodyEl.clientHeight+50)/3;

        let offset = 0 - this.BLOCK_HEIGHT;
        console.log("@@@@ Scroll offset: " + offset);
        setTimeout(()=> {
            window.scrollBy(0, offset)
        }, 1);

        //次のデータを準備
        setTimeout(()=> {
            this.getNextBuffer();
        }, 1);
    }

    //バッファ追加(上方向)
    onScrollUp() {

        //次の下方向バッファを取得
        this.nextRecord = this.record.slice(0, this.BLOCK_SIZE * 3);
        this.isNextBufferReady = true;

        //表示データ書き換え
        this.record = this.prevRecord;

        //表示位置カウンタの更新
        this.startRow -= this.record.length - this.BLOCK_SIZE * 2;
        this.endRow -= this.BLOCK_SIZE;

        //表示位置補正
        let offset = this.BLOCK_HEIGHT;
        console.log("@@@@ Scroll offset: " + offset);
        setTimeout(()=> {
            window.scrollBy(0, offset)
        }, 1);

        //次のデータを準備
        setTimeout(()=> {
            this.getPrevBuffer();
        }, 1);
    }

    //次に追加するバッファ作成(下方向)
    getNextBuffer() {
        this.getRecord(this.endRow + 1, this.BLOCK_SIZE)
            .then((result: any)=> {
                let rec = result.data.data;

                if (rec.length === this.BLOCK_SIZE) {//ブロックサイズ分の追加データあり
                    this.isEnd = false;
                    this.nextRecord = this.record
                        .slice(-this.BLOCK_SIZE * 2).concat(rec);
                } else if (rec.length === 0) { //追加データなし
                    this.isEnd = true;
                } else { //末尾データでブロックサイズ分のデータなし
                    this.isEnd = true;
                    this.record = this.record.concat(rec);
                }

                //バッファ準備完了
                this.isNextBufferReady = true;
            })
    }

    //次に追加するバッファ作成(上方向)
    getPrevBuffer() {
        this.getRecord(this.startRow - this.BLOCK_SIZE, this.BLOCK_SIZE)
            .then((result: any)=> {
                let rec = result.data.data;
                this.prevRecord = rec.concat
                (this.record.slice(0, this.BLOCK_SIZE * 2));

                //バッファ準備完了
                this.isPrevBufferReady = true;
                this.isEnd = false;
            });
        this.log();
    }

    //サーバーからデータ取得
    getRecord(begin: number, size: number): Promise<any> {
        console.log("@@@@@ getRecord begin=" + begin + " size=" + size +
            " startRow=" + this.startRow + " endRow=" + this.endRow);

        let arr = new Array(size);
        //HTTPリクエスト条件設定
        const config = new RequestOptions();
        config.url = this.httpService.AJAX_URL + "history";
        config.body = ({begin: begin, size: size});
        //HTTPリクエスト開始
        let promise = this.httpService.send("post", config);
        promise.then(
            (result: any)=> {
                return result;
            },
            (error)=> {
                alert("通信エラー" + error.message);
                return "error";
            });
        return promise;
    }

    log() {
        if (this.record)
            console.log("@@@@@ current=" + this.record[0].id + "|" + this.record[this.record.length - 1].id);
        if (this.nextRecord)
            console.log("@@@@@ next=" + this.nextRecord[0].id + "|" + this.nextRecord[this.nextRecord.length - 1].id);
        if (this.prevRecord)
            console.log("@@@@@ prev=" + this.prevRecord[0].id + "|" + this.prevRecord[this.prevRecord.length - 1].id);

    }
}

弊社では、「Angular2とTypeScriptによる モダンWeb開発セミナー」を開催します。
Angular2の実践的な知識とノウハウを習得できます。
http://www.staffnet.co.jp/hp/semi/modern/

]]>
第6回 はじめてのAngular2 ~テンプレート構文~ http://www.staffnet.co.jp/201608052045c/ Fri, 05 Aug 2016 11:45:30 +0000 http://www.staffnet.co.jp/?p=1204 テンプレート構文(Template Syntax)

概要

ここまでコンポーネント・クラスが利用するHTMLテンプレートの中に、[(ngModel)]などAngular2独自の拡張構文を記述してきました。しかし、Angular2独特であるため、分かりにくかったかもしれません。今回は、その他の拡張構文を含め、Angular2のテンプレート構文(Template Syntax)を解説します。

[参考]
Angular2公式サイトのTemplate Syntax
https://angular.io/docs/ts/latest/guide/template-syntax.html
templateSyntax

[参考]
Template Syntax(テンプレート構文)早見表
http://www.staffnet.co.jp/wp-content/uploads/2016/08/templatesyntax_summary_20160806_2.pdf

テンプレート記述の制限

1)HTML記述の制限
1.以下のHTML要素は利用できません。
script,html,body,base

2)Angular2独自の拡張記述の制限
1.ブラウザのグローバルスコープのオブジェクト(windowやdocument等)は利用できません。例えば、windowオブジェクトから画面幅を取得したり、console.logの出力はできません。該当するコンポーネント・クラスで内で定義されたもののみ利用できます。
2.拡張記述内で以下の演算子は利用できません。
new,++, –, +=, -=, |, &

拡張構文

1.文字列の挿入

構文: {{式}}
機能: 式の結果を文字列として出力します

//value = "Hello!"の時

{{value}}
//出力 => Hello
//-------------------------
//value = 100の時

{{value * 3}}
//出力 => 300
//-------------------------
//getID()の戻り値が"123"の時

あなたのIDは、[[getID()]]です
//出力 => あなたのIDは、123です
2.双方向データバインド

構文: [(ngModel)]=”変数”
機能: データ入力の値とコンポーネント・クラスの変数の値が同期します
補足: 入力機能をもつHTML要素にのみ記述可能、双方向処理のため右辺と左辺を入れ替えた代入ができない[(ngModel)]=”変数 * 3″のような記述はできません

//value = 100の時

<input [(ngModel)="value"]>
//入力欄の表示 => 100
//-------------------------
//value=0
//入力欄に300を入力したとき

<input [(ngModel)="value"]>
//valueの値 => 300
3.リスト表示

構文: *ngFor=”let 一時変数 of 式; let インデックス変数=index”
機能: HTML要素を繰り返し生成します
補足: indexが不要な時は、その記述は不要です
*ngFor=”let 一時変数 of 式”
補足: 表示データの一部のみ変更して再表示する場合は、trackByパラメータの指定で高速化できます。詳細は、NgForTrackByを参照してください。
https://angular.io/docs/ts/latest/guide/template-syntax.html#ngfortrackby

//values = [10,20,30]'の時
<div *ngFor="let v of values; let i=index>
  {{i+1}}番目の値は、{{v}}</div>
//出力 =>
//<div>1番目の値は、10</div>
//<div>2番目の値は、20</div>
//<div>3番目の値は、30</div>
4.HTML要素の表示・非表示

構文: *ngIf=”式”
機能: 式の値がtrueの時は何もしません、false時は指定したHTML要素が削除されます

//value = falseの時

<button *ngif="value" (click)="save($event)">保存</button>
//結果=> button要素が削除されます
5.HTML要素の表示切替え

構文: [ngSwitch]=”式” //条件受け取り
   *ngSwitchCase=”‘case1′”//条件がcase1の場合に表示
   *ngSwitchCase=”‘case2′”//条件がcase2の場合に表示
   *ngSwitchDefault //いずれ条件にも一致しない場合に表示
機能: 複数のHTML子要素から、指定した条件と一致する子要素を残し、その他の子要素を削除します

//value = 'case2'の時
<div [ngSwitch]="value">
	<div *ngSwitchCase="'case1'">red</div>
	<div *ngSwitchCase="'case2'">yellow</div>
	<div *ngSwitchDefault>green</div>
</div>
//結果=> yellowの文字が表示される
//出力=> 
//<div>
//	<div>yellow</div>
//</div>
6.HTML要素のプロパティ指定

構文: [プロパティ名]=”式”
機能: 指定したプロパティに式の値を代入します
補足: プロパティで指定できない属性については、[attr.属性名]=”式”を使用します

//isDisabled = trueの時

<button [disabled]="isDisabled">保存</button>
//結果=> ボタン操作は、無効
//-------------------------
//img01="photo01.jpg"の時

<img [src]="img01">
//結果=> photo01.jpgの表示
7.CSSクラスの追加・削除

構文: [class.クラス名]=”式”
機能: 式の値がtrueの時は指定クラスの追加、falseの時は指定クラスの削除を行います

//isActive = trueの時

<div [class.myClass]="isActive">Hello</div>
//出力=> <div class="myClass">Hello</div>
//-------------------------
//isActive = falseの時

<div [class.myClass]="isActive">Hello</div>
//出力=> <div class="">Hello</div>
8.CSSクラスの複数追加

構文: [ngClass]=”式”
機能: 指定した複数のクラスを追加します

//myClasses = {
//	largeText:true,
//	infoColor:true,
//	solidFrame: false
//}の時

<div [ngClass]="myClasses">Hello</div>
//出力=> <div class="infoColor largeText">Hello</div>
9.CSSスタイルの指定

構文: [style.プロパティ名]=”式”
機能: 指定したスタイルのプロパティを指定します

//value = blueの時

<div [style.color]="value">Hello</div>
//結果=> Helloの文字色は青
//出力=> <div style="color:blue">Hello</div>
//-------------------------
//size = 1.5の時

<div [style.font-size.em]="size">Hello</div>
//結果=> フォントサイズは1.5em
//出力=> <div style="font-size:1.5em">Hello</div>
10.CSSスタイルの複数指定

構文: [ngStyle]=”式”
機能: 指定した複数のスタイルのプロパティを指定します

//myStyle ={
//  'color':      'red',
//  'font-weight': 'bold',
//  'font-size':  '32px' 
//} の時

<div [ngStyle]="myStyle">Hello</div>
//結果=> Helloの文字色は赤、太字、32pxサイズ
//出力=> <div style="color:red; font-weight:bold; font-size:32px">Hello</div>
11.イベント処理

構文: (イベント名)=”式”
機能: 指定したイベントが発生した時、指定した式が呼び出されます。補足: 式の引数に$event変数を指定するとイベントの情報を受け取れます

<button (click)="save($event)">保存</button>
//結果=> 保存ボタンのクリックでsave()メソッドが呼び出されます
//$event変数からイベントの関連情報を取得できます
12.HTML要素の参照取得

構文: #変数名
機能: HTML要素の参照を変数に代入します
この変数を使い、該当要素のメソッド呼び出しや属性の取得ができます
補足: ここで記述する変数は、コンポーネント・クラスでの定義不要でテンプレート内で独自に定義できます

<form method="post" action="/auth" #loginFormElement>
	<input type="text" name="userId">
	<input type="password" name="password">
	<button type="submit"
		(click)="loginFormElement.submit()">
	ログイン
	</button>
</form>

1行目 変数loginFormElementに、form要素の参照を代入します
5行目 loginFormElement.submit()でフォーム内容を送信します

13.パイプの利用

構文: 式 | パイプ名:パラメータ //パイプによってはパラメータなし
機能: 式の値をパイプで変換します
補足: パイプ処理は、式|パイプ1|パイプ2 のように連結できます

//value="aBc"の時
//Angular2の組み込みパイプUpperCasePipeを使用

{{value | uppercase}}
//出力 => ABC
14.プロパティ未定義エラーの回避

構文: オブジェクト名?.プロパティ名
機能: 指定したプロパティが未定義の場合でもエラーが発生しません
補足: Angular2.0.0-rc.4では未定義オブジェクトのプロパティを参照すると例外が発生します

//オブジェクト未定義の時

{{value.length}}
//結果=> 例外発生

{{value?.length}}
//結果=> 何もしない

弊社では、「Angular2とTypeScriptによる モダンWeb開発セミナー」を開催します。
Angular2の実践的な知識とノウハウを習得できます。
http://www.staffnet.co.jp/hp/semi/modern/

]]>
第5回 はじめてのAngular2 ~アプリ作成(データ保存と表示)~ http://www.staffnet.co.jp/201608050100b/ Thu, 04 Aug 2016 16:01:47 +0000 http://www.staffnet.co.jp/?p=1187 Angular2 アプリ作成(データ保存と表示)
1.概要

calcArray2複利計算のサンプルアプリの機能拡張をします。
1)入力データをWebブラウザ内のストレージ(localStorage)に保存します。PCの電源を切っても、次にアプリを起動したときに元本と金利の値を復元します。
2)配列データのデータバインドを行い、リスト表示をします。10年間の金額の推移が確認できます。
今回は単純な例ですが、実際のアプリでは上記2つの機能を組みあわせて、複雑なデータベース処理を行います。

[オンラインデモ]
http://www.staffnet.co.jp/ngdemo/sample05/

2.アプリの機能拡張

機能拡張のために、CalcComponentクラスの構成ファイルのうち、HTMLとTypeScriptファイルを変更します。

1)proj01\src\app\calc.component.htmlの変更
6行目以降のコードを追加します。

<h1>複利計算</h1>
<p>元本 <input [(ngModel)]="principal"> 円</p>
<p>金利 <input [(ngModel)]="interest"> %</p>
<p>10年後 {{calc()}}円</p>

<p>
  <button (click)="save()">保存</button>
</p>
<p *ngFor="let value of calcArray(); let i = index">
{{i}}年目: {{value}}円
</p>

7行目 (イベント名)=”メソッド”は、Angular2独自のテンプレート構文で、該当するイベントが発生した時に呼び出されるメソッドの定義です。ここでは、保存ボタンがクリックされた時に、save()メソッドが呼び出されます。
9行目 *ngFor=”式”は、Angular2独自のテンプレート構文で、式の中で展開した配列やオブジェクトの要素数分のHTML要素を生成します。ここでは、calcArray()で取得した10年分の金額推移の配列データを展開し、11個のpタグを生成しています。

2)proj01\src\app\calc.component.tsの変更
1行目に「,OnInit」を追加、9行目に「implements OnInit」を追加、24行目calcArray() メソッド以降を追加します。

import {Component, OnInit} from '@angular/core';

@Component({
  moduleId: module.id,
  selector: 'app-root',
  templateUrl: 'calc.component.html',
  styleUrls: ['calc.component.css']
})
export class CalcComponent implements OnInit{

  principal: number; //元本
  interest: number;  //金利

  //複利計算メソッド
  calc() {
    if (isNaN(this.principal) || isNaN(this.interest)) return null;
    let answer = this.principal;
    for (let i = 0; i < 10; i++) {
      answer = answer * (1 + this.interest / 100);
    }
    return Math.floor(answer);
  }

  calcArray() {
    if (isNaN(this.principal) || isNaN(this.interest)) return null;
    let answer = this.principal;
    let ret = [answer];
    for (let i = 0; i < 10; i++) {
      answer = answer * (1 + this.interest / 100);
      ret.push(Math.floor(answer));
    }
    return ret;
  }

  save(){
    localStorage['principal']= this.principal.toString(); //元本
    localStorage['interest']= this.interest.toString(); //金利
  }

  ngOnInit() {
    if(localStorage['principal'])
    this.principal=localStorage['principal'];
    this.interest=localStorage['interest'];
  }
}

24~33行目 calcArray()メソッドは、10年間の複利計算の経過を配列で返します。
35~38行目 save()メソッドは、保存ボタンをクリックした時に呼び出され、元本と金利の値をブラウザの永続ストレージ(localStorage)に保存します。
40~44行目 ngOnInit()メソッドは、アプリの起動時に呼び出されます。ここでは、永続ストレージ(localStorage)からデータを読み込み、principalとinterest変数に代入しています。2つの変数は、元本と金利の入力欄と双方向バインドしていますので、その値がそのまま表示されます。

7)動作確認
1.Webサーバーが起動中であることを確認します。
2.Webブラウザから、http://localhost:4200にアクセスします。
3.複利計算の画面が表示されます。表示されない場合は、ng build コマンドの後、ブラウザの再読み込みを行ってください。それでも表示されない時は、ブラウザを表示した状態で、キーボードのF12ボタンを押下してエラーメッセージを確認してください。
4.元本と金利の欄に数字を入力すると、10年後の金額と1年ごとの金額の推移が表示されます。
5.元本と金利の値を変更すると、リアルタイムに反映されます。
6.計算結果が表示された状態で「保存」ボタンをクリックします。
7.Webブラウザを終了、または電源再起動後に再度、http://localhost:4200にアクセスします。
8.保存時のデータの復元を確認します。

弊社では、「Angular2とTypeScriptによる モダンWeb開発セミナー」を開催します。
Angular2の実践的な知識とノウハウを習得できます。
http://www.staffnet.co.jp/hp/semi/modern/

]]>
第4回 はじめてのAngular2 ~アプリ作成(データ入力)~ http://www.staffnet.co.jp/201608042128a/ Thu, 04 Aug 2016 11:33:17 +0000 http://www.staffnet.co.jp/?p=1166 Angular2 アプリ作成(データ入力)
1.概要

今回は、基本機能であるデータ入力機能の実装を解説します。Angular2には、データ入力をカンタンに実装する「双方向データバインド」という便利な機能があります。データ入力欄の値とコンポーネント・クラスの変数の値をプログラミングなしで常に一致させますので、コーディング量を大幅に削減できます。解説文だけではわかりづらいので、サンプルアプリをつくりながら解説します。
calc001
サンプルアプリは、元本と金利から10年後の金額を算出する複利計算です。図の例は、10万円を金利3%で運用すると10年後、13,4381円になると表示しています。

[オンラインデモ]
http://www.staffnet.co.jp/ngdemo/sample04/

2.サンプルアプリの実装

複利計算のコンポーネント・クラスの名前は、CalcComponentとします。これを、現在のプロジェクトで呼び出されているAppComponentと置き換えます。下図のうち、CalcComponentクラスを構成する3つのファイルは新規作成、他のファイルは呼び出し先を変更します。
calc_flow

1)proj01\src\index.htmlの変更
変更はありません。

2)proj01\src\main.tsの変更

import { bootstrap } from '@angular/platform-browser-dynamic';
import { enableProdMode } from '@angular/core';
import { CalcComponent, environment } from './app/';
if (environment.production) {
  enableProdMode();
}
bootstrap(CalcComponent);

3行目と7行目のAppComponentをCalcComponentに置き換えます。

3)proj01\src\app\index.tsの変更

export * from './environment';
export * from './calc.component'

2行目のapp.componentをcalc.componentに置き換えます。

4)proj01\src\app\calc.component.htmlの新規作成
以下のコードを記述します。

<h1>複利計算</h1>
<p>元本 <input [(ngModel)]="principal"> 円</p>
<p>金利 <input [(ngModel)]="interest"> %</p>
<p>10年後 {{calc()}}円</p>

2,3行目 [(ngModel)]=”式や変数”は、Angular2独自のテンプレート構文で、双方向データバインドの定義です。入力欄の値と、この後定義するCalcComponentクラスの変数の値が同期します。ここでは、元本の入力欄の値が変数principalに、金利の入力欄の値が変数interestに代入されます。双方向ですから、逆に変数principalの値を変えると、元本入力欄の表示がその値に変更されます。変数の値変更の実装は、次回行います。
4行目 {{式や変数}}も、Angular2独自のテンプレート構文で、片方向データバインドの定義です。式や変数の値を文字列として出力します。calc()は、この後CalcComponentクラスで定義するメソッドです。変数principalとinterestの値から、10年後の金額を計算して返します。

5)proj01\src\app\calc.component.cssの新規作成
以下のコードを記述します。

h1{
  color:royalblue;
}
input{
  text-align: right;
}

1~3行目 タイトル文字を青色にします。
4~6行目 入力欄には数字を入力するため、右寄せにして見やすくします。

6)proj01\src\app\calc.component.tsの新規作成
以下のコードを記述します。

import {Component} from '@angular/core';

@Component({
  moduleId: module.id,
  selector: 'app-root',
  templateUrl: 'calc.component.html',
  styleUrls: ['calc.component.css']
})
export class CalcComponent {

  principal: number; //元本
  interest: number;  //金利

  //複利計算メソッド
  calc() {
    if (isNaN(this.principal) || isNaN(this.interest)) return null;
    let answer = this.principal;
    for (let i = 0; i < 10; i++) {
      answer = answer * (1 + this.interest / 100);
    }
    return Math.floor(answer);
  }
}

11,12行目 画面で入力された元本と金利の値が、変数principalと変数interestに自動的に代入されます。
16行目 元本または金利の値が、空白等の数字でないときは、nullを返します。
17~21行目 変数principalと変数interestの値を使って、10年後の金額を計算して返します。

7)動作確認
1.Webサーバーが起動中であることを確認します。
2.Webブラウザから、http://localhost:4200にアクセスします。
3.複利計算の画面が表示されます。表示されない場合は、ng build コマンドの後、ブラウザの再読み込みを行ってください。それでも表示されない時は、ブラウザを表示した状態で、キーボードのF12ボタンを押下してエラーメッセージを確認してください。
calc_blank
4.元本と金利の欄に数字を入力すると、10年後の金額が表示されます。
5.元本と金利の値を変更すると、双方向データバインドのおかげで、リアルタイムで再計算が行われ表示に反映されます。計算処理をブラウザで行っているので、サーバーの通信待ちはありません。送信ボタンを押すことなく、Excelのような快適な操作感を実現しています。

弊社では、「Angular2とTypeScriptによる モダンWeb開発セミナー」を開催します。
Angular2の実践的な知識とノウハウを習得できます。
http://www.staffnet.co.jp/hp/semi/modern/

]]>
第3回 はじめてのAngular2 ~内部のしくみ~ http://www.staffnet.co.jp/201608032120/ Wed, 03 Aug 2016 12:52:56 +0000 http://www.staffnet.co.jp/?p=1107 Angular2 内部のしくみ
1.ファイル構成

rootdr

前回生成したプロジェクト(ここではProj01フォルダ)の内部を見ると、左上図のように8個のフォルダと9個のファイルが確認できます。そのうち以下の3つが重要フォルダです。
1)distフォルダ
ビルド処理で出力された実行可能なプログラムを保存しています。Webサーバーにアップロードしてアプリを公開できます。
2)node_moduleフォルダ
npmでインストールしたモジュールを保存しています。依存モジュールも同時に保存されるため大量のフォルダを含んでいます。Angular2関連のモジュールはnode_module\@angularフォルダに含まれています。
3)srcフォルダ
アプリのソースコードを保存しています。HTML、スタイルシート、TypeScriptファイル等が含まれています。

3つのフォルダと処理の流れの関係は、右上図のようになります。
1)ソースコードとその関連モジュールを入力としてビルドします。
2)ビルドツールが、実行可能プログラムをdistフォルダに出力します。
3)distフォルダの内容をWebサーバーにアップロードします。
4)ブラウザからWebサーバーにアクセスしてアプリを実行します。
Angular-CLIでインストールした環境では、ソースコードの変更を感知して、1~4までの工程を自動化できます。

2.アプリ実行のしくみ

Angular2では、基本的に1画面ごとに最低1つのコンポーネント・クラスを作成します。また、コンポーネントごとに、HTML,JavaScriptまたはTypeScript,CSSファイルを分離して作成します。ソースコードフォルダ(srcフォルダ)の内容とアプリ起動からの処理の流れは下図のようになっています。

src_folder

ブラウザがURLにアクセスしてから画面が表示するまでの流れを説明します。右上図を参照してください。
1)index.htmが読み込まれます。

<body>
  <app-root>Loading...</app-root>
	...  
    <script>
      System.import('system-config.js').then(function () {
        System.import('main');
      }).catch(console.error.bind(console));
    </script>
</body>

2行目 画面出力エリア。コンポーネント・クラスは、このタグを指定して画面表示を行います。タグ名は任意ですが、ここではapp-rootとしています。
6行目 mainという名前のモジュールがアプリ起動用に実行されます。ここではmain.tsが該当します。

2)main.tsが読み込まれます。

import { bootstrap } from '@angular/platform-browser-dynamic';
import { enableProdMode } from '@angular/core';
import { AppComponent, environment } from './app/';
if (environment.production) {
  enableProdMode();
}
bootstrap(AppComponent);

1~2行目 必要なクラス等が読み込まれます。先頭に/,./,../等の記述がなくディレグトリで無いと識別された場合、読込み元はnode_modulesフォルダ内を参照します。

[参考]
TypeScriptのモジュール名解決ルール
https://www.typescriptlang.org/docs/handbook/module-resolution.html

3行目 ./app/の指定で、./app/index.tsから読み込みます。index.tsにはenvironment.tsとapp.component.tsへのリンクが記述されていますので、これらのファイル内で定義されたクラスを読み込めます。
7行目 AppComponentクラスを起動(bootstrap)します

3)AppComponentクラスを定義しているapp.component.tsが読み込まれます。

import { Component } from '@angular/core';

@Component({
  moduleId: module.id,
  selector: 'app-root',
  templateUrl: 'app.component.html',
  styleUrls: ['app.component.css']
})
export class AppComponent {
  title = 'app works!';
}

3~8行目 @Componentアノテーション内に、AppComponentクラスが出力する先のタグ名(selector)、出力時のHTMLテンプレート(templateUrl)、出力時のCSS(styleUrls)を指定します。
10行目 AppComponentクラスのインスタンス変数titleに’app works!’を代入します。

4)AppComponentクラスのテンプレート(app.component.html)とCSS(app.component.css)を使って出力します。CSSファイルは空白です。


<h1>
  {{title}}
</h1>

2行目 {{式や変数}}は、Angular2独自のテンプレート構文で、式や変数の値を文字列として出力します。ここでは、AppComponentクラスで定義された 変数titleの値である’app works!’が出力されます。h1タグを含めた文字列の出力先は、index.htmlのapp-rootタグ内部です。Loading…の文字列を上書きして書き換えます。

3.アプリを変更してみる

画面を出力するまでのしくみを理解しましたので、少し変更してみましょう。まず、前回の手順を参考に、ブラウザにapp works!が表示された状態にします。
1)データの書き換え
1.テキストエディタでapp.component.tsファイルを開きます。
2.title = ‘app works!’を、title = ‘Good work!’に書き換え、変更を保存します。
3.しばらくすると、変更がブラウザに自動的に反映されます。表示に反映されない場合は、ng build コマンドの後、ブラウザの再読み込みを行ってください。
goodworkBK

2)CSSの追加
1.テキストエディタでapp.component.cssファイルを開きます。
2.内容は空白ですので、以下を記述し、変更を保存します。

h1{
	color:red;
}

3.しばらくすると、変更がブラウザに自動的に反映されます。表示に反映されない場合は、ng build コマンドの後、ブラウザの再読み込みを行ってください。
goodWorkRed

弊社では、「Angular2とTypeScriptによる モダンWeb開発セミナー」を開催します。
Angular2の実践的な知識とノウハウを習得できます。
http://www.staffnet.co.jp/hp/semi/modern/

]]>
第1回 はじめてのAngular2 ~Angular2とは~ http://www.staffnet.co.jp/201608022250/ Tue, 02 Aug 2016 12:53:47 +0000 http://www.staffnet.co.jp/?p=1058 angularAngular2は、Googleが開発をリードするWebアプリ開発フレームワークです。いままでWebサーバーで行ってきた画面データの生成やデータベース検索等の処理をブラウザ上で可能にします。

ブラウザ側に処理を移すことで、Webの根本的な制約である「表示のたびの通信待ち」や「ネットワーク圏外で使えない」を解消します。さらに大量データ表示(無限スクロール)や、画像処理、インタラクティブなグラフなどもブラウザ上で可能にします。Angular2は、サーバー側の環境を問いませんので、現行のWebサーバーをそのまま利用できます。

同様の機能をもつフレームワークは、他にも多数ありますが、最も人気が高いのがAngularです。人気の高さは、インターネット上に大量の技術情報を生み出しています。開発中のトラブルや疑問は、インターネット検索でほとんど解決できます。

[参考リンク]人気上昇中のJavaScriptライブラリを調べてみた【2016年版】
http://www.buildinsider.net/web/popularjslib/2016

Angularには、現行版のAngularJS(バージョン1)と次バージョンのAngular2(バージョン2)があります。以下の理由から、新規導入であればAngular2を推奨します。
・新技術を積極的に取り込み大幅に機能アップしています。
・TypeScript対応によりJavaやC#のオブジェクト指向開発のスキルを生かせます。
・出荷候補版まで開発が進んでいます。

次回以降、Angular2入門者向けの記事を投稿予定です。

弊社では、「Angular2とTypeScriptによる モダンWeb開発セミナー」を開催します。
Angular2の実践的な知識とノウハウを習得できます。
http://www.staffnet.co.jp/hp/semi/modern/

]]>
第2回 はじめてのAngular2 ~自動インストール~ http://www.staffnet.co.jp/201608022240-2/ Tue, 02 Aug 2016 08:45:52 +0000 http://www.staffnet.co.jp/?p=1011 Angular2の自動インストール
1.概要

Angular2の最新版、2.0.0-rc.4(rc:Release Candidate 出荷候補版)をインストールします。Angular2のインストールは、ひな型に変更を加えたり、複数のツールを組み合わせたりする方法が一般的ですが、はじめてAngular2を使う人にとっては困難を伴います。そこで、Angular2の開発チームは、自動インストールを行うツール「Angular-CLI」(CLI:Command Line Interface)を開発中です。
Angular-CLI公式サイト https://cli.angular.io/
Angular-CLI公式サイト
現在ベータ版のため、一部の機能は未実装ですが、Angular2をはじめて使ってみるためには十分です。今回は、このツールで自動インストールを行います。以降の手順はWindows OSの例です。他のOSを利用する場合は、適宜読み替えてください。

2.前提ソフトウェアのインストール

Angular-CLIの前提ソフトウェアとして、Node.js(バージョン4以上)が必要です。既に、Node.jsがインストール済みであっても、バージョンが4未満の場合は再インストールを行ってください。
1.以下のURLにアクセスします。
Node.js公式サイト https://nodejs.org/en/
Node.js公式サイト
2.インストーラーは安定版と最新機能版の2種類があります。「Recommended for Most Users」と記載のある安定版のインストーラーを選択してダウンロードします。
3.インストーラーを実行します。
4.ウィザードが表示されますので、デフォルト設定のままインストールを行ってください。
5.ウィザード画面に「Complete the Node.js Setup Wizard」と表示されれば、Node.jsのインストール完了です。Finishボタンをクリックしてウィザードを閉じます。
6.新規にコマンドプロンプト画面を開きます。以下のnodeコマンド(node.jsのバージョン表示)を入力して、インストールしたバージョンとの一致を確認します。

C:\>node -v
v4.4.7

7.これでnode.jsのインストールは完了です。

[参考]
node.jsは、JavaScriptをブラウザ以外で動作させる実行環境です。当初は、サーバーでの利用が目的でしたが、その後さまざまなOSやCPU環境に移植され、JavaScriptをJavaのようなクロスプラットフォーム言語に位置づける役目をしています。また、node.jsに含まれるnpm(Node Package Manager)はブラウザとnode.jsで利用するライブラリやツール等を配布・インストールする標準的な手段として使われています。Angular-CLIもJavaScriptをべースに開発されていますので、npmを使ってインストールを行い、node.js上で動作します。

3. Angular-CLIのインストール

1.コマンドプロンプトを「管理者として実行」で、開きます。
2.コマンドプロンプトに、mkdirコマンドを使ってAngular-CLIをインストールするディレクトリを作成します。ディレクトリ名は任意です。ここでは、「angularCLI」とします。
3.cdコマンドを使って、作成したディレクトリへ移動します。

C:\>mkdir angularCLI
C:\>cd angularCLI
C:\angularCLI>

3.npmのインストールコマンドを使ってangular-CLIをインストールします。「@1.0.0-beta.10」はインストールするプログラムのバージョン指定です。Angular-CLIは頻繁にバージョンアップしていますので、以降の解説との相違が生じないようにバージョン固定します。-gはglobalの意味で、任意のディレクトリでインストールしたコマンドを利用できます。

C:\angularCLI>npm install -g angular-cli@1.0.0-beta.10

4.インストールが始まります。インストールが完了してプロンプトが表示されるまで数分以上かかります。途中で表示される警告文(WARN)は無視します。エラー(ERR!)表示でインストールが終了した場合は失敗ですので、同じ内容のインストールコマンドを再実行します。
5.Angular-CLIのバージョンを確認するng -vコマンドをコマンドプロンプトに入力して、動作確認をします。

[参考]
ngはAngularの2文字略語です。例えば、Angularのカンファレンスをng-confと呼びます。

C:\angularCLI>ng -v
angular-cli: 1.0.0-beta.10
node: 4.4.7
os: win32 x64

6.これでAngular-CLIのインストールは完了です。

4. サンプルプロジェクトの作成

1.コマンドプロンプトを「管理者として実行」して開き、Angular-CLIのインストールディレクトリに移動します。ここでは、C:\angularCLIです。
2.コマンドプロンプトに、ng new コマンドを使ってサンプルプロジェクトの生成を開始します。プロジェクト名は任意です。ここでは、「proj01」とします。

C:\angularCLI>ng new proj01
installing ng2
  create .editorconfig
  create README.md

3.プロジェクトの生成が完了してプロンプトが表示されるまで10分程度以上かかります。エラー表示で終了した場合は失敗ですので、同じ内容のコマンドを再実行します。
4.プロジェクトディレクトリに移動します。ここでは、C:\angularCLI\proj01です。
5.ng buildコマンドで、アプリをビルドします。

C:\angularCLI>cd proj01
C:\angularCLI\proj01>ng build
Built project successfully. Stored in "dist/".
C:\angularCLI\proj01>

6.ng serveコマンドで、Angular-CLIに組み込まれたWebサーバーを起動します。ファイアーウォールの警告ダイアログが表示された場合は、許可します。「It is either in use or you do not
have permission.」のメッセージが表示された場合は、同一ポートを使っている他のアプリはないか、サーバーを2重に起動していないか確認します。
node_firewall]waning

C:\angularCLI\proj01>ng serve
Livereload server on http://localhost:49157
Serving on http://localhost:4200/

Build successful - 4713ms.

7.Webブラウザを起動して、下記URLにアクセスします。
http://localhost:4200/
8.Webブラウザに、「loading」と表示され、その後「app works!」と表示されれば、インストール作業完了です。
app_works

弊社では、「Angular2とTypeScriptによる モダンWeb開発セミナー」を開催します。
Angular2の実践的な知識とノウハウを習得できます。
http://www.staffnet.co.jp/hp/semi/modern/

]]>
日経コンピュータに記事掲載 http://www.staffnet.co.jp/nc20160721/ Wed, 20 Jul 2016 23:01:34 +0000 http://192.168.0.160/?p=925 弊社執筆の記事「業務システムを強化するモダンWeb」が日経コンピュータ 7月21日号に掲載されました。。詳しくはこちら

]]>