albatrosary's blog

UI/UXとエンタープライズシステム

YEOMAN Advent Calendar 4日目:generator-angular を紹介します

Webアプリケーションを作る入門的なジェネレータ generator-angular を紹介します。このエントリーは「YEOMAN Advent Calendar 2014」12月4日の記事です。

YEOMAN Advent Calendar 2014 - Adventar

ジェネレータ generator-angular は AngularJS を利用するためのものです。

インストールから実行まで

インストール

いつものようにジェネレータをインストールしてプロジェクトディレクトリを作りyoとgruntの実行です。

$ npm install -g generator-angular
$ mkdir angular && cd $_
$ yo angular
$ grunt serve

途中聞いてくるオプションは

  • Sassを利用するか?
  • Bootstrapを利用するか?
  • AngularJSのモジュールはどれを使うか

です

? Would you like to use Sass (with Compass)? Yes
? Would you like to include Bootstrap? Yes
? Would you like to use the Sass version of Bootstrap? Yes
? Which modules would you like to include? (Press <space> to select)
❯◉ angular-animate.js
 ◯ angular-aria.js
 ◉ angular-cookies.js
 ◉ angular-resource.js
 ◯ angular-messages.js
 ◉ angular-route.js
 ◉ angular-sanitize.js
 ◉ angular-touch.js

実行

実行結果は次の通りです。

f:id:albatrosary:20141202193014p:plain

bower でインストールされているモジュールは次の通りです。

    "angular": "^1.3.0",
    "json3": "^3.3.0",
    "es5-shim": "^4.0.0",
    "bootstrap-sass-official": "^3.2.0",
    "angular-animate": "^1.3.0",
    "angular-cookies": "^1.3.0",
    "angular-resource": "^1.3.0",
    "angular-route": "^1.3.0",
    "angular-sanitize": "^1.3.0",
    "angular-touch": "^1.3.0"

grunt タスクについては AngularですのでJavaScriptスティングフレームワークが karma です。その他諸々あり:

    "grunt": "^0.4.1",
    "grunt-autoprefixer": "^0.7.3",
    "grunt-concurrent": "^0.5.0",
    "grunt-contrib-clean": "^0.5.0",
    "grunt-contrib-compass": "^0.7.2",
    "grunt-contrib-concat": "^0.4.0",
    "grunt-contrib-connect": "^0.7.1",
    "grunt-contrib-copy": "^0.5.0",
    "grunt-contrib-cssmin": "^0.9.0",
    "grunt-contrib-htmlmin": "^0.3.0",
    "grunt-contrib-imagemin": "^0.8.1",
    "grunt-contrib-jshint": "^0.10.0",
    "grunt-contrib-uglify": "^0.4.0",
    "grunt-contrib-watch": "^0.6.1",
    "grunt-filerev": "^0.2.1",
    "grunt-google-cdn": "^0.4.0",
    "grunt-karma": "^0.9.0",
    "grunt-newer": "^0.7.0",
    "grunt-ng-annotate": "^0.4.0",
    "grunt-svgmin": "^0.4.0",
    "grunt-usemin": "^2.1.1",
    "grunt-wiredep": "^1.7.0",
    "jasmine-core": "^2.1.3",
    "jshint-stylish": "^0.2.0",
    "karma-jasmine": "^0.3.2",
    "karma-phantomjs-launcher": "^0.1.4",
    "load-grunt-tasks": "^0.4.0",
    "time-grunt": "^0.3.1"

サブジェネレータ

YEOMANテンプレートのコマンドとして各コンポーネントを生成するサブジェネレータも用意されています:

yo angular:controller
yo angular:directive
yo angular:filter
yo angular:route
yo angular:service
yo angular:provider
yo angular:factory
yo angular:value
yo angular:constant
yo angular:decorator
yo angular:view

yo angular:controller

$ yo angular:controller controller
   create app/scripts/controllers/controller.js
   create test/spec/controllers/controller.js
$ 

作成されるコントローラは次の通りです:

'use strict';

/**
 * @ngdoc function
 * @name angularApp.controller:ControllerCtrl
 * @description
 * # ControllerCtrl
 * Controller of the angularApp
 */
angular.module('angularApp')
  .controller('ControllerCtrl', function ($scope) {
    $scope.awesomeThings = [
      'HTML5 Boilerplate',
      'AngularJS',
      'Karma'
    ];
  });

スペックについては次の通りです:

'use strict';

describe('Controller: ControllerCtrl', function () {

  // load the controller's module
  beforeEach(module('angularApp'));

  var ControllerCtrl,
    scope;

  // Initialize the controller and a mock scope
  beforeEach(inject(function ($controller, $rootScope) {
    scope = $rootScope.$new();
    ControllerCtrl = $controller('ControllerCtrl', {
      $scope: scope
    });
  }));

  it('should attach a list of awesomeThings to the scope', function () {
    expect(scope.awesomeThings.length).toBe(3);
  });
});

yo angular:directive

$ yo angular:directive directive
   create app/scripts/directives/directive.js
   create test/spec/directives/directive.js
$

作成されるディレクティブは次の通りです:

'use strict';

/**
 * @ngdoc directive
 * @name angularApp.directive:directive
 * @description
 * # directive
 */
angular.module('angularApp')
  .directive('directive', function () {
    return {
      template: '<div></div>',
      restrict: 'E',
      link: function postLink(scope, element, attrs) {
        element.text('this is the directive directive');
      }
    };
  });

スペックについては次の通りです:

'use strict';

describe('Directive: directive', function () {

  // load the directive's module
  beforeEach(module('angularApp'));

  var element,
    scope;

  beforeEach(inject(function ($rootScope) {
    scope = $rootScope.$new();
  }));

  it('should make hidden element visible', inject(function ($compile) {
    element = angular.element('<directive></directive>');
    element = $compile(element)(scope);
    expect(element.text()).toBe('this is the directive directive');
  }));
});

yo angular:filter

$ yo angular:filter filter
   create app/scripts/filters/filter.js
   create test/spec/filters/filter.js
$

作成されるフィルタは次の通りです:

'use strict';

/**
 * @ngdoc filter
 * @name angularApp.filter:filter
 * @function
 * @description
 * # filter
 * Filter in the angularApp.
 */
angular.module('angularApp')
  .filter('filter', function () {
    return function (input) {
      return 'filter filter: ' + input;
    };
  });

スペックについては次の通りです:

'use strict';

describe('Filter: filter', function () {

  // load the filter's module
  beforeEach(module('angularApp'));

  // initialize a new instance of the filter before each test
  var filter;
  beforeEach(inject(function ($filter) {
    filter = $filter('filter');
  }));

  it('should return the input prefixed with "filter filter:"', function () {
    var text = 'angularjs';
    expect(filter(text)).toBe('filter filter: ' + text);
  });

});

yo angular:route

$ yo angular:route route
   create     app/scripts/controllers/route.js
   create     test/spec/controllers/route.js
   create     app/views/route.html
$

作成されるルータは次の通りです:

'use strict';

/**
 * @ngdoc function
 * @name angularApp.controller:RouteCtrl
 * @description
 * # RouteCtrl
 * Controller of the angularApp
 */
angular.module('angularApp')
  .controller('RouteCtrl', function ($scope) {
    $scope.awesomeThings = [
      'HTML5 Boilerplate',
      'AngularJS',
      'Karma'
    ];
  });

スペックについては次の通りです:

'use strict';

describe('Controller: RouteCtrl', function () {

  // load the controller's module
  beforeEach(module('angularApp'));

  var RouteCtrl,
    scope;

  // Initialize the controller and a mock scope
  beforeEach(inject(function ($controller, $rootScope) {
    scope = $rootScope.$new();
    RouteCtrl = $controller('RouteCtrl', {
      $scope: scope
    });
  }));

  it('should attach a list of awesomeThings to the scope', function () {
    expect(scope.awesomeThings.length).toBe(3);
  });
});

テンプレートであるHTMLは次の通りです:

<p>This is the route view.</p>

yo angular:service

$ yo angular:service service
   create app/scripts/services/service.js
   create test/spec/services/service.js
$

作成されるサービスは次の通りです:

'use strict';

/**
 * @ngdoc service
 * @name angularApp.service
 * @description
 * # service
 * Service in the angularApp.
 */
angular.module('angularApp')
  .service('service', function () {
    // AngularJS will instantiate a singleton by calling "new" on this function
  });

スペックについては次の通りです:

'use strict';

describe('Service: service', function () {

  // load the service's module
  beforeEach(module('angularApp'));

  // instantiate service
  var service;
  beforeEach(inject(function (_service_) {
    service = _service_;
  }));

  it('should do something', function () {
    expect(!!service).toBe(true);
  });

});

yo angular:provider

$ yo angular:provider provider
   create app/scripts/services/provider.js
   create test/spec/services/provider.js
$

作成されるプロバイダーは次の通りです:

'use strict';

/**
 * @ngdoc service
 * @name angularApp.provider
 * @description
 * # provider
 * Provider in the angularApp.
 */
angular.module('angularApp')
  .provider('provider', function () {

    // Private variables
    var salutation = 'Hello';

    // Private constructor
    function Greeter() {
      this.greet = function () {
        return salutation;
      };
    }

    // Public API for configuration
    this.setSalutation = function (s) {
      salutation = s;
    };

    // Method for instantiating
    this.$get = function () {
      return new Greeter();
    };
  });

スペックについては次の通りです:

'use strict';

describe('Service: provider', function () {

  // load the service's module
  beforeEach(module('angularApp'));

  // instantiate service
  var provider;
  beforeEach(inject(function (_provider_) {
    provider = _provider_;
  }));

  it('should do something', function () {
    expect(!!provider).toBe(true);
  });

});

yo angular:factory

$  yo angular:factory factory
   create app/scripts/services/factory.js
   create test/spec/services/factory.js
$ 

作成されるファクトリは次の通りです:

'use strict';

/**
 * @ngdoc service
 * @name angularApp.factory
 * @description
 * # factory
 * Factory in the angularApp.
 */
angular.module('angularApp')
  .factory('factory', function () {
    // Service logic
    // ...

    var meaningOfLife = 42;

    // Public API here
    return {
      someMethod: function () {
        return meaningOfLife;
      }
    };
  });

スペックについては次の通りです:

'use strict';

describe('Service: factory', function () {

  // load the service's module
  beforeEach(module('angularApp'));

  // instantiate service
  var factory;
  beforeEach(inject(function (_factory_) {
    factory = _factory_;
  }));

  it('should do something', function () {
    expect(!!factory).toBe(true);
  });

});

yo angular:value

$ yo angular:value value
   create app/scripts/services/value.js
   create test/spec/services/value.js
$ 

作成されるファイルは

'use strict';

/**
 * @ngdoc service
 * @name angularApp.value
 * @description
 * # value
 * Value in the angularApp.
 */
angular.module('angularApp')
  .value('value', 42);

スペックについては次の通りです:

'use strict';

describe('Service: value', function () {

  // load the service's module
  beforeEach(module('angularApp'));

  // instantiate service
  var value;
  beforeEach(inject(function (_value_) {
    value = _value_;
  }));

  it('should do something', function () {
    expect(!!value).toBe(true);
  });

});

yo angular:constant

$ yo angular:constant constant
   create app/scripts/services/constant.js
   create test/spec/services/constant.js
$

作成されるファイルは

'use strict';

/**
 * @ngdoc service
 * @name angularApp.constant
 * @description
 * # constant
 * Constant in the angularApp.
 */
angular.module('angularApp')
  .constant('constant', 42);

スペックについては次の通りです:

'use strict';

describe('Service: constant', function () {

  // load the service's module
  beforeEach(module('angularApp'));

  // instantiate service
  var constant;
  beforeEach(inject(function (_constant_) {
    constant = _constant_;
  }));

  it('should do something', function () {
    expect(!!constant).toBe(true);
  });

});

yo angular:decorator

$ yo angular:decorator decorator
   create app/scripts/decorators/decoratordecorator.js
$

作成されるデコレータは次の通りです

'use strict';

/**
 * @ngdoc function
 * @name angularApp.decorator:Decorator
 * @description
 * # Decorator
 * Decorator of the angularApp
 */
angular.module('angularApp')
  .config(function ($provide) {
    $provide.decorator('decorator', function ($delegate) {
      // decorate the $delegate
      return $delegate;
    });
  });

yo angular:view

HTMLテンプレートを作成します。

$ yo angular:view view
   create app/views/view.html
$

作成されたHTMLは

<p>This is the view view.</p>

最後に

generator-angularのディレクトリは機能毎にディレクトリが定義されています。AngularJSは機能が多いので、こうしたサブジェネレータを利用するとモジュールの書き始めは楽ではないかと思います。