albatrosary's blog

Azure と Angular と Wercker CI とか

Angularを簡単に済ませるには - AngularJS Advent Calendar 2015

Angularでさくっと何か作ってみます。"5 min quickstart"のようなものです。このエントリーは「AngularJS Advent Calendar 2015」12月12日の記事です。

Step0: 作成するファイルの構成

f:id:albatrosary:20151130201452p:plain

Step1: 環境の作成

作業ディレクトリを作りindex.htmlを用意する

$ mkdir TodoApp && cd $_
$ touch index.html

index.htmlの中身はHTML5の基本構成で。

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <base href="/">
  <title>Angular1</title>
  <meta name="viewport" content="width=device-width">
</head>
  <body>

  </body>
</html>

Step2: 必要なパッケージを登録

AngularはCSSなUIはもってないのでBootstrapを利用します。みんな大好きnpmでインストール。せっかくのAngularなのでSPAで、使うルータはngRouteui RouterNew RouterがありますがngRouteで。 まずはpackage.jsonの生成、色々利かれるから良いように応える。

$ npm init

パッケージの登録

$ npm install jquery bootstrap angular@1.5.0-beta.2 angular-resource@1.5.0-beta.2 angular-route@1.5.0-beta.2 --save 

Step1で作ったindex.htmlにパッケージを読み込ませる。bodyタグにng-app="TodoApp"を忘れずに。

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <base href="/">
  <title>Angular1</title>
  <meta name="viewport" content="width=device-width">
  <link rel="stylesheet" href="node_modules/bootstrap/dist/css/bootstrap.min.css">
</head>
<body ng-app="TodoApp">
  <!-- package -->
  <script src="node_modules/jquery/dist/jquery.min.js"></script>
  <script src="node_modules/bootstrap/dist/js/bootstrap.min.js"></script>
  <script src="node_modules/angular/angular.min.js"></script>
  <script src="node_modules/angular-route/angular-route.min.js"></script>
  <script src="node_modules/angular-resource/angular-resource.min.js"></script>
</body>
</html>

Step3: メニューを作る

これはAngularというよりBootstrap。新しくheader.htmlを作りng-includeでファイルを読み込む。

(index.html)
<body ng-app="TodoApp">
  <!-- contents -->
  <div ng-include="'header.html'"></div>
  <!-- package -->

メニューに関しては色んなパターンで作れるのでBootstrapのサイトを見る

(header.html)
<div class="navbar navbar-default navbar-fixed-top">
  <div class="container">
    <div class="navbar-header">
      <button type="button" class="navbar-toggle collapsed"
              data-toggle="collapse" data-target="#app-nav">
        <span class="sr-only">Toggle</span>
        <span class="icon-bar"></span>
        <span class="icon-bar"></span>
        <span class="icon-bar"></span>
      </button>
      <a class="navbar-brand" href="">TodoApp</a>
    </div>
    <div class="collapse navbar-collapse" id="app-nav">
      <ul class="nav navbar-nav">
        <li><a>Home</a></li>
        <li><a>Todo</a></li>
      </ul>
    </div>
  </div>
</div>

固定メニューを設定するとコンテンツを表示させたときにメニューに隠れるのでカスケードスタイルシートに若干手をいれる。

(main.css)
body {
  padding-top: 70px;
  padding-bottom: 20px;
}

Step4: ルーティングの設定とコンポーネントの作成

ng-hrefでリンク先のコンポーネントを指定。

(header.html)
<li><a ng-href="#/home">Home</a></li>
<li><a ng-href="#/todo">Todo</a></li>

JavaScriptを読みこませるのとコンポーネントを表示させるための定義ng-viewを記述。

(index.html)
<!-- contents -->
<div ng-include="'header.html'"></div>
<div class="container">
  <div ng-view></div>
</div>
…
<!-- module -->
<script src="scripts/main.js"></script>
<script src="components/home/home.js"></script>
<script src="components/todo/todo.js"></script>

main.jsはルーティングの設定を定義。ここでコンポーネントを構成するJavaScriptとHTMLを定義する。

(main.js)
(function () {
  'use strict';

  angular
    .module('TodoApp', [
      'ngRoute',
      'TodoApp.components.home',
      'TodoApp.components.todo'
    ])
    .config(AppConfig);

  AppConfig.$inject = ['$routeProvider'];

  function AppConfig($routeProvider) {
    $routeProvider
      .when('/', {
        templateUrl: 'components/home/home.html',
        controller: 'HomeController as home'
      })
      .when('/todo', {
        templateUrl: 'components/todo/todo.html',
        controller: 'TodoController as todo'
      })
      .otherwise({
        redirectTo: '/'
      });
  }
})();

homeがクリックされたときのコンポーネントを定義

(home.html)
<p>home</p>

対応するJavaScriptは

(home.js)
(function () {
  'use strict';

  angular
    .module('TodoApp.components.home', [])
    .controller('HomeController', HomeController);

  HomeController.$inject = [];

  function HomeController() {
    console.log('HomeController Constructor');
  }
})();

続いてtodoがクリックされたときのコンポーネントを定義

(todo.html)
<p>todo</p>

対応するJavaScriptは

(todo.js)
(function () {
  'use strict';

  angular
    .module('TodoApp.components.todo', [])
    .controller('TodoController', TodoController);

  TodoController.$inject = [];

  function TodoController() {
    console.log('TodoController Constructor');
  }
})();

Step5: Todoの作成

todo.htmlを変更、8割はBootstrapで2割がAngularのディレクティブ。見るとわかるがng-*がディレクティブ。ng-repeatで繰り返しを表現しng-click, ng-submitでボタンが押されたときのイベントを処理。

(todo.html)
<h2>Todo</h2>
<!-- Todos input -->
<form role="form" ng-submit="todo.addTodo()">
  <div class="input-group">
    <input type="text" ng-model="todo.item" placeholder="What needs to be done?" class="form-control">
    <span class="input-group-btn">
      <input type="submit" class="btn btn-primary" value="Add" name="add">
    </span>
  </div>
</form>
<hr>
<!-- Todos list -->
<div>
  <p class="input-group" ng-repeat="data in todo.todoList track by $index">
    <input type="text" ng-model="data" class="form-control">
    <span class="input-group-btn">
      <button class="btn btn-danger" ng-click="todo.removeTodo($index)" aria-label="Remove">X</button>
    </span>
  </p>
</div>

対応するイベントを定義。todoの追加と削除のみで、データはリストで保存している。

(todo.js)
  var vm;

  function TodoController() {
    vm = this;
    vm.todoList = [];
  }
  
  TodoController.prototype.addTodo = function () {
    vm.todoList.push(vm.item);
    vm.item = '';
  };

  TodoController.prototype.removeTodo = function (index) {
    vm.todoList.splice(index, 1);
  };

めちゃくちゃ簡単にTodoが完成。AngularというよりBootstrapなコーティングボリューム。

Step6: $resourceを利用

有無も言わさずサービスを作成

(lists.js)
(function () {
  'use strict';

  angular
    .module('TodoApp.service.lists', [
      'ngResource'
    ])
    .factory('ListsService', ListsService);

  ListsService.$inject = ['$resource'];

  function ListsService($resource) {
    return $resource('/api/lists.json', {
      'query': {}
    });
  }
})();

表示するリストを定義

(lists.json)
[
  {
    "name": "Angular",
    "note": "HTML is great for declaring static documents, but it falters when we try to use it for declaring dynamic views in web-applications. AngularJS lets you extend HTML vocabulary for your application. The resulting environment is extraordinarily expressive, readable, and quick to develop."
  },
  {
    "name": "Bootstrap",
    "note": "Sleek, intuitive, and powerful mobile first front-end framework for faster and easier web development."
  }
]

homeで表示。ほとんどBootstrap。

(home.html)
<div class="col-lg-6" ng-repeat="data in home.list">
  <div class="card card-block">
    <h4 class="card-title">{{data.name}}</h4>
    <p class="card-text">{{data.note}}</p>
  </div>
</div>

対応するJavaScriptを定義。自分で作ったサービスをインジェクトしサービスから取得されるプロミスを処理する。

(home.js)
(function () {
  'use strict';

  angular
    .module('TodoApp.components.home', [
      'TodoApp.service.lists'
    ])
    .controller('HomeController', HomeController);

  HomeController.$inject = ['ListsService'];

  var vm;

  function HomeController(ListsService) {
    console.log('HomeController Constructor');
    vm = this;
    ListsService.query().$promise
      .then(function (list) {
        vm.list = list;
      })
      .catch(function (e) {
        console.log(e);
      });
  }
})();

終わり

こんな画面ができる

f:id:albatrosary:20151130201939p:plain f:id:albatrosary:20151130201948p:plain

コードはGithub

github.com

ここに書いたことだけを覚えれば管理画面くらいはサクッといける。学習コスト超低い(と思う)