皆さんは普段Webアプリケーションを作るとき、デザインはどうしていますか?
私はよくBootstrapを使っていました。Bootstrapはデザインが得意ではない私でもレスポンシブデザインが簡単に作れるからです。
ですが、最近ではBootstrapのようなフラットデザインではなく、「Material Design(マテリアルデザイン)」を使うようになってきました。
その理由は、「Material Design」はユーザが操作を直感的に認識でき、操作に対してアニメーションで反応してくれる、という特徴があるからです。
「Material Design」の特徴についての詳細は、下記公式ドキュメントを読んで下さい。
公式ドキュメント: Introduction – Material design – Google design guidelines
今回はそんな「Material Design」をAngularJSで使用できるUIコンポーネントライブラリである「Angular Material」を初心者の方向けに徹底解説します。
目次
Angular Materialってナニ?
2014年のGoogle I/Oで、新しいデザインガイドライン「Material Design」と、それに準拠したWeb Componentsのラッパーライブラリである「Polymer」が発表されました。
今後のデザインは「Polymer」に限らず、「Material Design」が標準になることを感じさせてくれたのは記憶に新しい出来事でした。
「Angular Material」はそんな「Material Design」に準拠した「Polymer」のUIコンポーネントと同様のコンポーネントをAngularJSで使用できるようにライブラリ化されたものです。
公式サイト: Angular Material – Introduction
ちなみにAngular Materialのプロジェクトは、Googleがサポートしています。
また、Angular Materialはつい先日の2015年12月14日にバージョン1.0.0が正式リリースされ、2016年2月8日現在では、バージョン1.0.5となっています。
AngularJS + Polymerじゃダメなの?
「既にPolymerがあるんだから、Angular MaterialじゃなくてPolymerでいいじゃん」という人もいるかもしれません。
たしかにAngularJSにPolymerを組み込むことは可能です。しかもそこそこ動きます。
ですが、オススメはしません。
なぜなら、AngularJSの特徴の一つである「双方向データバインディング」が使えなくなってしまうからです。
※「双方向データバインディング」とは、データとUIを双方向に自動で結びつけてくれる機能です。
イマイチ文章で分からない人は、AngularJSの公式ページで実際に試して見て下さい。
公式ページの「The Basics」にある「Name」の入力欄に文字を入力すると、リアルタイムに入力した文字が画面へと出力されるかと思います。
AngularJSで双方向データバインディングの設定をするためには「ng-model」属性を使いますが、この属性を使用できるのは、<input>/<select>/<textare>の3つのエレメントだけなのです。
しかし、Polymerのエレメントはテキスト入力に使う<paper-input>を始め、「paper-◯◯」といった独自のものを使用するため、「ng-model」を付与しても双方向データバインディングのディレクティブが使えないのです。
普段からAngularJSを使っている私にとっては「双方向データバインディング」が使えないという問題は致命的に思えてしまい、Polymerは断念しました。
そんな時に見つけたのが、「Angular Material」です。
Angular Materialであれば、AngularJSの機能を全て使うことができ、なおかつMaterial Designで画面を作成できるのです。
Angular Materialを使ってみよう
では、ここからは入門編として、Angular Materialの導入方法から比較的によく使う入力部品について、作り方を説明していきたいと思います。
手順としては、最初に最低限必要なCSSとJavascriptの説明と、AngularJSでAngular Materialを利用できるようにモジュールの読み込み方法を紹介します。
その後、具体例として「テキスト入力欄」、「セレクトボックス」、「ラジオボタン」、「テキストエリア入力欄」の作成方法をご紹介します。
前提として各画面や用語などはEclipseというIDEをベースとした説明になります。
また、今回はJavaのプロジェクト上で動くことを前提に進めさせてもらいます。が、基本的にはAngular MaterialはJavascriptなのでサーバ側の記述言語には依存しませんので、その辺りはお好きな言語に読み替えてください。
必要なCSSとJavascriptを読み込む
まず、はじめにJavaのプロジェクトのwar直下のindex.htmlのHEADとBODYに最低限必要なCSSとJavascriptを書きます。
<最低限必要なCSS>
・angular-material.min.css
<最低限必要なJavascript>
・angular.min.js
・angular-animate.min.js
・angular-aria.min.js
・angular-material.min.js
※Angular Materialを動かすには、AngularJS 1.3以上が必須です。
必要なCSSもJavascriptもWeb上で一般公開されているので、導入はそのURLを指定するだけで非常に簡単です。
進め方としては、このindex.htmlにいろいろとコードを追加していきたいと思います。
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<!-- Angular Material CSS now available via Google CDN; version 1.0.0 used here -->
<link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/angular_material/1.0.0/angular-material.min.css">
<title>Hello Angular Material</title>
</head>
<body>
<!-- Angular Material Dependencies -->
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular-animate.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular-aria.min.js"></script>
<!-- Angular Material Javascript now available via Google CDN; version 1.0.0 used here -->
<script src="https://ajax.googleapis.com/ajax/libs/angular_material/1.0.0/angular-material.min.js"></script>
<h1>Hello Angular Material!</h1>
</body>
</html>
AngularJSを使えるように設定する
最低限必要なライブラリは読み込みました。しかし、それだけではAngularJSは使用できませんので、AngularJSを使えるようにindex.htmlを変更しましょう。 まずは、Angularアプリケーションのルート設定をします。 ルート設定は、Angularアプリケーションのディレクティブを適用する開始位置を宣言するものです。 なので、一番親の要素にあたる<html>に属性「ng-app」を追加します。
<html ng-app="AngularMaterialStartGuide">
<body>に下記の<script>を追加します。
<script type="text/javascript">
// Angular Materialのモジュール「ngMaterial」を読み込みます。
// モジュール「ngMaterial」がないとAngular Materialは動きませんので、忘れずに読み込みましょう。
// angular.moduleの第一引数は、前述した「ng-app」に指定した値と同じ値を指定して下さい。
var APP = angular.module("AngularMaterialStartGuide", ["ngMaterial","ngAnimate"]);
</script>
テキスト入力欄を作ってみる
それでは、テキスト入力欄を作ってみましょう。
テキスト入力欄は通常のHTMLの<input type=”text” />を使用します。
下記のコードをindex.htmlのBODY内に加えて下さい。
<div>
<md-input-container>
<label>TEXTの入力欄です。</label>
<input ng-model="inputText">
</md-input-container>
</div>
これが無いと正しく表示されません。覚えておきましょう。
公式URL: Angular Material – Directives > mdInputContainer
<label>内の文字列が入力欄のプレースホルダーとラベルの役割を担ってくれます。
では、ブラウザから表示を確認してみましょう。
下記のようなテキスト入力欄が表示されるかと思います。
URL:http://angular-material-start-guide-dot-apps-gcp-sample.appspot.com/input-text.html
入力欄をクリックすると、Polymerの<paper-input>と同じように動いてくれるのが確かめられると思います。
セレクトボックスを作ってみる
セレクトボックスは、<md-select>を使用します。
index.htmlのBODYに下記のコードを加えて下さい。
<div>
<md-input-container>
<label>SELECTの選択欄です。</label>
<md-select ng-model="selectList">
<md-option value="TEST1">テスト1</md-option>
<md-option value="TEST2">テスト2</md-option>
<md-option value="TEST3">テスト3</md-option>
<md-option value="TEST4">テスト4</md-option>
</md-select>
</md-input-container>
</div>
通常のHTMLの<select>の使い方とほとんど変わりませんね。
<select>を<md-select>に置き換えて、選択要素である<option>を<md-option>に置き換えるだけです。
公式URL: Angular Material – Directives > mdSelect
では、ブラウザから表示を見てみましょう。
要素未選択時:
要素一覧表示時:
URL: http://angular-material-start-guide-dot-apps-gcp-sample.appspot.com/select-box1.html
選択肢もちゃんと指定したものが表示されていますね。
選択すると、テキスト入力欄同様の動きをするかと思います。
ちょっと寄り道 – レイアウトを少しいじってみる
ここまでの状態では、画面の左上にテキスト入力欄があり、その下にセレクトボックスがあると思います。
ここで一旦レイアウトを変えてみましょう。
この2つの入力欄を横並びにし、画面の中央に配置しましょう。
配置の制御には、”layout”と”layout-align”、”flex”のattributeを使用します。
<div layout="row" layout-align="center">
<md-input-container flex="20">
<label>TEXTの入力欄です。</label>
<input ng-model="inputText">
</md-input-container>
<md-input-container flex="20">
<label>SELECTの選択欄です。</label>
<md-select ng-model="selectList">
<md-option value="TEST1">テスト1</md-option>
<md-option value="TEST2">テスト2</md-option>
<md-option value="TEST3">テスト3</md-option>
<md-option value="TEST4">テスト4</md-option>
</md-select>
</md-input-container>
</div>
layout-align=”center”で、DIV直下の子要素を中央に配置することができます。
<md-input-container>にある”flex”で、親要素内でのサイズを指定しています。
今回は、flex=”20”ですので、「max-width:20%」と同じ意味合いとなります。
では、ブラウザから画面を確認してみましょう。

URL: http://angular-material-start-guide-dot-apps-gcp-sample.appspot.com/select-box2.html
ちゃんとテキスト入力欄とセレクトボックスが横並びで、画面中央に表示されてますね。
”layout”と”layout-align”、”flex”は手軽に要素の配置を設定できるので、覚えておいて損はないと思います。配置のパターンは他にもあるので、下記公式ページを見てみて下さい。
公式URL1: Angular Material – Layout > Layout Containers
公式URL2: Angular Material – Layout > Child Alignment
ラジオボタンを作ってみる
ラジオボタンを作るには、<md-radio-group>と<md-radio-button>を使います。
index.htmlのBODYに下記コードを追加してください。
<div layout="row" layout-align="center">
<md-radio-group ng-model="radioBtns">
<md-radio-button value="RADIO_A">
ラジオボタンA
</md-radio-button>
<md-radio-button value="RADIO_B">
ラジオボタンB
</md-radio-button>
</md-radio-group>
</div>
ブラウザからは下記のように見えるかと思います。
URL:http://angular-material-start-guide-dot-apps-gcp-sample.appspot.com/radio.html
テキストエリア入力欄を作ってみる
ここで紹介する入力部品の最後は、テキストエリアの入力欄です。
テキストエリアの入力欄は、テキスト入力欄同様、標準のHTMLの<textarea>と<md-input-container>を使います。
<div layout="row" layout-align="center">
<md-input-container flex="20">
<label>テキストエリアの入力欄</label>
<textarea ng-model="textArea"></textarea>
</md-input-container>
</div>
とりあえず表示を確認してみましょう。
URL:http://angular-material-start-guide-dot-apps-gcp-sample.appspot.com/textarea.html
見た目はテキスト入力欄と変わりませんが、入力欄を選択し、改行したりするとテキストエリアであることが分かるかと思います。
※<textarea>の初期表示行数を設定する”rows”を指定すると、ラベルの位置が悪いので、設定しない方が良いかと思います。
汎用的な入力フォームを作ってみる
ここまでAngular Materialの入力部品についてご紹介してきました。
最後に、3.1?3.7で紹介した入力部品とAngularJSのバリデーション機能を使って、ある汎用的な入力フォームを作ってみましょう。
HTMLの全文は以下のようになりますので、コピペしてみて下さい。
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html ng-app="AngularMaterialStartGuide">
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<!-- Angular Material CSS now available via Google CDN; version 1.0.0 used here -->
<link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/angular_material/1.0.0/angular-material.min.css">
<title>Hello Angular Material</title>
</head>
<body>
<!-- Angular Material Dependencies -->
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular-animate.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular-aria.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular-messages.min.js"></script>
<!-- Angular Material Javascript now available via Google CDN; version 1.0.0 used here -->
<script src="https://ajax.googleapis.com/ajax/libs/angular_material/1.0.0/angular-material.min.js"></script>
<!-- AngularJS setting -->
<script type="text/javascript">
var APP = angular.module("AngularMaterialStartGuide", ["ngMaterial","ngAnimate"]);
APP.controller("FormCtrl", [ "$scope", function($scope) {
$scope.affiliations = ["バックオフィス", "技術部", "営業部"];
}]);
</script>
</script>
<style type="text/css">
<!--
.side-padding16 {padding: 0 16px 0 16px;}
.err-message {font-size: 12px; color: red}
-->
</style>
<div ng-controller="FormCtrl" layout="row" layout-align="center" style="width:100%">
<md-card flex="50" class="md-whiteframe-5dp" style="height:95%">
<md-toolbar>
<div class="md-toolbar-tools">
<h2>登録フォーム</h2>
</div>
</md-toolbar>
<md-content style="height:100%">
<form name="registerForm" novalidate>
<md-card-content>
<section>
<md-subheader class="md-primary">会社情報</md-subheader>
<div layout="row" class="side-padding16">
<md-input-container flex="40">
<label>会社名</label>
<input ng-model="companyName" name="companyName" required>
<div class="err-message" ng-if="registerForm.companyName.$error.required && registerForm.companyName.$touched" >会社名を入力してください。</div>
</md-input-container>
<md-input-container flex="20">
<label>所属</label>
<md-select ng-model="companyAffiliation" name="companyAffiliation">
<md-option ng-repeat="affiliation in affiliations" value="{{affiliation}}" >
{{affiliation}}
</md-option>
</md-select>
</md-input-container>
</div>
<div class="side-padding16">
<md-input-container flex="80">
<label>住所</label>
<textarea ng-model="companyAddress" name="companyAddress" required></textarea>
<div class="err-message" ng-if="registerForm.companyAddress.$error.required && registerForm.companyAddress.$touched" >住所を入力してください。</div>
</md-input-container>
</div>
<div class="side-padding16">
<md-input-container flex="40">
<label>電話番号</label>
<input ng-model="companyTelNumber" name="companyTelNumber">
</md-input-container>
</div>
</section>
<!-- ユーザ情報の入力パート -->
<section>
<md-subheader class="md-primary">ユーザ情報</md-subheader>
<div class="side-padding16" layout="row">
<md-input-container flex="30">
<label>氏名</label>
<input ng-model="userName" name="userName" required>
<span class="err-message" ng-if="registerForm.userName.$error.required && registerForm.userName.$touched">氏名を入力してください</span>
</md-input-container>
<md-input-container flex="35">
<label>メールアドレス</label>
<input ng-model="userEmail" name="userEmail" type="email" required>
<div class="err-message" ng-if="registerForm.userEmail.$error.required && registerForm.userEmail.$touched" >メールアドレスを入力してください。</div>
<div class="err-message" ng-if="registerForm.userEmail.$error.email && registerForm.userEmail.$touched">メールアドレスの形式が正しくありません。</div>
</md-input-container>
</div>
<div class="side-padding16">
<md-input-container flex="35" layout="row">
<div style="margin-top: 18px;margin-right: 16px;">性別</div>
<!-- ラジオボタンは、<md-radio-group>を使います。-->
<md-radio-group ng-model="userSex">
<!-- ラジオボタンの選択項目は、<md-radio-button>で指定します。-->
<md-radio-button value="MALE" class="md-primary">
男性
</md-radio-button>
<md-radio-button value="FEMALE">
女性
</md-radio-button>
</md-radio-group>
</md-input-container>
</div>
<div class="side-padding16" layout="row">
<md-input-container flex="20">
<label>生年月日</label>
<input ng-model="userBirthDay" name="userBirthDay" type="date" required>
<div ng-if="registerForm.userBirthDay.$error.required && registerForm.userBirthDay.$touched" class="err-message">生年月日を入力して下さい。</div>
<div ng-if="registerForm.userBirthDay.$error.date && registerForm.userBirthDay.$touched" class="err-message">生年月日の形式が正しくありません。</div>
</md-input-container>
<md-input-container flex="10">
<label>年齢</label>
<input ng-model="userAge" name="userAge" type="number" min="0" required>
<div ng-if="registerForm.userAge.$error.min && registerForm.userAge.$touched" class="err-message">年齢は0以上を入力して下さい。</div>
<div ng-if="registerForm.userAge.$error.required && registerForm.userAge.$touched" class="err-message">年齢を数値で入力して下さい。</div>
</md-input-container>
</div>
<div class="side-padding16">
<md-input-container class="md-block" class="side-padding16">
<label>備考</label>
<textarea ng-model="note" name="note" md-maxlength="150"></textarea>
<div ng-if="registerForm.note.$error.md-maxlength && registerForm.note.$touched" class="err-message">備考の入力文字数は150文字までです。</div>
</md-input-container>
</div>
</section>
</md-card-content>
<md-card-actions layout="row" layout-align="center">
<md-button class="md-primary md-raised">登録</md-button>
</md-card-actions>
</form>
</md-content>
</md-card>
</div>
</body>
</html>
さて、このHTMLコードをブラウザから見てみると、下記のような入力フォームになります。
シンプルフォームURL: http://angular-material-start-guide-dot-apps-gcp-sample.appspot.com
まとめ
今回は代表的な入力部品をAngular Materialで紹介しましたが、いかがだったでしょうか?
入力部品について説明しただけですが、簡単に導入から作成までができるのは体感していただけたのではないでしょうか。
記事の冒頭でも触れましたが、Angular Materialは2015年12月14日に正式リリースを迎えたばかりです。
まだまだ認知度は低いかもしれませんが、AngularJSを使う開発者にとってはこれから重宝するものになる確実だと思っています。
吉積情報ではこのような最新の開発手法も積極的に取り入れ、Google Cloud Platform 上での開発を請け負っております。Google Cloud Platformや開発に関する相談・お問い合わせはこちらより宜しくお願い致します。