Angular CLI を試してみる

ちょっと前の社内勉強会で紹介されてた Angular CLI を試してみた。

ngコマンドのインストール

まずは、angular-cliをグローバルにインストール。

sudo npm install -g angular-cli
ng --help

ndenv や anyenv の利用環境下で「ng コマンドなんてないよ」ってなっちゃう場合は、「ndenv rehash」してみる

プロジェクトの生成とサーバの起動

事前にtypings(TypeScriptの型定義管理ツールTypingsについて - Qiita)をグローバルにインストールしておく。

npm install typings -g

サーバ起動時おこられないようにするため、watchmanもインストールしておく。

brew install watchman

プロジェクトの作成とサーバの起動。

ng new myproj
cd myproj
ng serve

こんな感じでサーバが起動する。

Livereload server on http://localhost:49153
Serving on http://localhost:4200/

Build successful - 1095ms.

Slowest Trees                                 | Total               
----------------------------------------------+---------------------
BroccoliTypeScriptCompiler                    | 662ms               
vendor                                        | 336ms               

Slowest Trees (cumulative)                    | Total (avg)         
----------------------------------------------+---------------------
BroccoliTypeScriptCompiler (1)                | 662ms               
vendor (1)                                    | 336ms               

localhost:4200/にアクセス。ソースを変更すればライブリロードしてくれる。

sample01

ポートを変えたい場合は以下のように起動する。

ng serve --port 4201 --live-reload-port 49153

画面に表示されてる部分のソースは以下。

/app/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!';
}

/app/app.component.html

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

コンポーネントの雛形を作る

コンポーネントやらサービスの雛形が以下のコマンドで作れる。

Scaffold Usage
Component ng g component my-new-component
Directive ng g directive my-new-directive
Pipe ng g pipe my-new-pipe
Service ng g service my-new-service
Class ng g class my-new-class
Interface ng g interface my-new-interface
Enum ng g enum my-new-enum

コンポーネントを作ってみる

ng g component hoge-component

こんなふうに生成される。

installing component
  create src/app/hoge-component/hoge-component.component.css
  create src/app/hoge-component/hoge-component.component.html
  create src/app/hoge-component/hoge-component.component.spec.ts
  create src/app/hoge-component/hoge-component.component.ts
  create src/app/hoge-component/index.ts

中身はこんな感じ。

index.ts

export * from './hoge-component.component';

hoge-component.component.ts

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

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

  constructor() {}

  ngOnInit() {
  }

}

hoge-component.component.html

<p>
  hoge-component works!
</p>

hoge-component.component.spec.ts

/* tslint:disable:no-unused-variable */

import { By }           from '@angular/platform-browser';
import { DebugElement } from '@angular/core';

import {
  beforeEach, beforeEachProviders,
  describe, xdescribe,
  expect, it, xit,
  async, inject
} from '@angular/core/testing';

import { HogeComponentComponent } from './hoge-component.component';

describe('Component: HogeComponent', () => {
  it('should create an instance', () => {
    let component = new HogeComponentComponent();
    expect(component).toBeTruthy();
  });
});

hoge-component.component.css の中身は空。

生成したコンポーネントを使ってみる

/app/app.component.ts

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

// 追加
import { HogeComponentComponent } from './hoge-component';

@Component({
  moduleId: module.id,
  selector: 'app-root',
  templateUrl: 'app.component.html',
  styleUrls: ['app.component.css'],

  // 追加
  directives: [HogeComponentComponent],
})
export class AppComponent {
  title = 'app works!';
}

生成したコンポーネントをインポートして、directivesに定義。 名前がHogeComponentComponentってなっちゃうのが気になるけど、そういうものとして受け入れるしかなさそう。

/app/app.component.html

<h1>
  {{title}}
</h1>
<!-- 追加 -->
<app-hoge-component></app-hoge-component>

app-hoge-componentを適当な場所に追加。 こうなる。

sample02

CSSプリプロセッサを使う

以下のCSSプリプロセッサに対応。

  • sass (node-sass)
  • less (less)
  • compass (compass-importer + node-sass)
  • stylus (stylus)

sassを使ってみる。node-sassをインストール。

npm i --save-dev node-sass

これでcssファイルをscssに変えれば変換してくれる。 例えば、hoge-component.component.css を .scss にして以下のようにしてみる。

$color: red;
p {
  color: $color;
}

ビルドする

以下でdistディレクトリにビルド結果が出力される。

ng build

-prod オプションでproductionモードで生成できるっぽい。

ng build -prod
ng serve -prod

サードパーティー製ライブラリを組み込む

moment.jsを組み込んでみる

npm でインストール。

npm install moment --save

angular-cli-build.js の vendorNpmFiles に、node_modulesからみたmoment.jsのパスを追記する。

var Angular2App = require('angular-cli/lib/broccoli/angular2-app');

module.exports = function(defaults) {
  return new Angular2App(defaults, {
    vendorNpmFiles: [
      'systemjs/dist/system-polyfills.js',
      'systemjs/dist/system.src.js',
      'zone.js/dist/**/*.+(js|js.map)',
      'es6-shim/es6-shim.js',
      'reflect-metadata/**/*.+(ts|js|js.map)',
      'rxjs/**/*.+(js|js.map)',
      '@angular/**/*.+(js|js.map)',

      // 追加!
      'moment/moment.js'
    ]
  });
};

src/system-config.ts の map と packages にも追記。

/** Map relative paths to URLs. */
const map: any = {
  // 追加
  'moment': 'vendor/moment/moment.js'
};

/** User packages configuration. */
const packages: any = {
  // 追加
  'moment':{
    format: 'cjs'
  }
};

これで import して moment.js が利用できるようになる。

import * as moment from 'moment';
console.log(moment().format());

angular2-material を組み込んでみる

npm でインストール。

npm install @angular2-material/core --save

今回はチェックボックスを使用。

npm install @angular2-material/checkbox --save

angular-cli-build.js の vendorNpmFilesに追加!

var Angular2App = require('angular-cli/lib/broccoli/angular2-app');

module.exports = function(defaults) {
  return new Angular2App(defaults, {
    vendorNpmFiles: [
      'systemjs/dist/system-polyfills.js',
      'systemjs/dist/system.src.js',
      'zone.js/dist/**/*.+(js|js.map)',
      'es6-shim/es6-shim.js',
      'reflect-metadata/**/*.+(ts|js|js.map)',
      'rxjs/**/*.+(js|js.map)',
      '@angular/**/*.+(js|js.map)',
      'moment/moment.js',

      // 追加!
      '@angular2-material/**/*.js'
    ]
  });
};

src/system-config.ts の map と packages にも追記。

/** Map relative paths to URLs. */
const map: any = {
  'moment': 'vendor/moment/moment.js',

  // 追加
  '@angular2-material': 'vendor/@angular2-material'
};

/** User packages configuration. */
const packages: any = {
  'moment':{
    format: 'cjs'
  },

  // 追加
  '@angular2-material/core': {
    format: 'cjs',
    defaultExtension: 'js',
    main: 'core.js'
  },
  '@angular2-material/checkbox': {
    format: 'cjs',
    defaultExtension: 'js',
    main: 'checkbox.js'
  },
};

/app/app.component.ts

import { Component } from '@angular/core';
import { HogeComponentComponent } from './hoge-component';

// 追加
import { MdCheckbox } from '@angular2-material/checkbox';

@Component({
  moduleId: module.id,
  selector: 'app-root',
  templateUrl: 'app.component.html',
  styleUrls: ['app.component.css'],
  directives: [
    HogeComponentComponent,

    // 追加
    MdCheckbox
  ],
})
export class AppComponent {
  title = 'app works!';
}

/app/app.component.html

<h1>
  {{title}}
</h1>
<app-hoge-component></app-hoge-component>

<!-- 追加 -->
<md-checkbox></md-checkbox>

sample03

underscore.js を組み込んでみる

npm でインストール。

npm install underscore --save

型定義ファイルをインストール。 ちなみに moment.js の場合は、npm installすると moment.d.ts も node_modules に配置されるので typings install 不要っぽい。

typings install dt~underscore --global --save

angular-cli-build.js の vendorNpmFilesに追加。

var Angular2App = require('angular-cli/lib/broccoli/angular2-app');

module.exports = function(defaults) {
  return new Angular2App(defaults, {
    vendorNpmFiles: [
      'systemjs/dist/system-polyfills.js',
      'systemjs/dist/system.src.js',
      'zone.js/dist/**/*.+(js|js.map)',
      'es6-shim/es6-shim.js',
      'reflect-metadata/**/*.+(ts|js|js.map)',
      'rxjs/**/*.+(js|js.map)',
      '@angular/**/*.+(js|js.map)',
      'moment/moment.js',
      '@angular2-material/**/*.js',

      // 追加!
      'underscore/underscore.js'
    ]
  });
};

src/system-config.ts の map と packages にも追記。

/** Map relative paths to URLs. */
const map: any = {
  'moment': 'vendor/moment/moment.js',
  '@angular2-material': 'vendor/@angular2-material',

  // 追加
  'underscore': 'vendor/underscore/underscore.js'
};

/** User packages configuration. */
const packages: any = {
  'moment':{
    format: 'cjs'
  },
  '@angular2-material/core': {
    format: 'cjs',
    defaultExtension: 'js',
    main: 'core.js'
  },
  '@angular2-material/checkbox': {
    format: 'cjs',
    defaultExtension: 'js',
    main: 'checkbox.js'
  },

  // 追加
  'underscore':{
    format: 'cjs'
  }
};

これで <reference path="../../typings/globals/underscore/index.d.ts" で型定義を読めば import できるとのことだがうまくいかず...

なんでだろ... 余裕があったら調べる

GitHub Pages にデプロイする

AngularCLIで作成したプロジェクト名で、GitHubにリポジトリを作れば簡単にGitHub Pagesにデプロイできる。

git init
git add .
git commit -m 'first commit'
git remote add origin git@github.com:cyokodog/myproj.git
ng github-pages:deploy --message "Optional commit message"

これで https://cyokodog.github.io/myproj/ にアクセスすると画面が表示される。

GitHub上には gh-pages のブランチのみができてて実行用のモジュールのみがのる感じになるので、ソースコードを管理したい場合は以下のように別ブランチ(master)で push すればよい。

git push origin master

感想

大規模案件やってると強めな実装規約的な縛りがほしいなぁ、、、と思うことがしばしあるので良いかも。