ui-routerとcodeigniterを組み合わせてSPAをつくるときのメモ

できるまで色々と試行錯誤して大変だったからメモする。

環境

angular.version.full "1.3.15"

codeigniter define('CI_VERSION', '2.0.3');

やりたいこと

SPAというものを試してみたかったので、お手軽にcodeIgniterとangularで作ってみよう

調べてみて

angularにはデフォルトにng-routeというものがデフォルトであったが、 angular-ui-routerの方ができることの幅が広く、評判もよかったのでそっちを使うことにした

やり方

  1. SPA用のページテンプレートを作成

controller、viewに下記のようにテンプレートを作成する

controllers/ang.php

<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');

class Ang extends CI_Controller {
    public function index()
    {
        $this->load->view('ang/index');
    }

    public function template1()
    {
        $this->load->view('ang/template1');
    }

    public function template2()
    {
        $this->load->view('ang/template2');
    }
}

views/ang/index.html

<base href="<?= base_url(); ?>">
<!doctype html>
<html ng-app="app">
  <head>
    <meta charset="utf-8">
    <title></title>
    <meta name="description" content="">
    <meta name="viewport" content="width=device-width">
    <script src="js/bower_components/angular/angular.min.js"></script>
    <script src="js/bower_components/angular-ui-router/release/angular-ui-router.js"></script>
    <script src="js/ang/app.js"></script>
  </head>

  <body>
    <h3>ui.router</h3>
    <p>ui-routerでそれぞれpageを指定してみた</p>
    <div ng-controller="MainCtrl">
        <!-- multiple ページバージョン -->
        <a ui-sref="contacts_state">template1を読み込む</a>
        <a ui-sref="question_state">template2を読み込む</a>
        <section ui-view><i>templateが読み込まれる</i></section>
    </div>
  </body>
</html>

views/ang/template1.php

template1読み込みok!!!
<br />
{{title}}

views/ang/template2.php

template2読み込みok!!!
<br />
{{title}}
  1. js部分をつくる

js/ang/app.js

'use strict';

var app = angular.module('app', ['ui.router'])
    .config(['$stateProvider', function($stateProvider){
        $stateProvider
            .state('contacts_state', {
                url: '/contacts',
                templateUrl: 'ang/template1',
                controller: function($scope) {
                    $scope.title = 'contactsに入りました!';
                }
            })
            .state('question_state', {
                url: '/question',
                templateUrl: 'ang/template2',
                controller: function($scope) {
                    $scope.title = 'questionに入りました!';
                }
            })
    }]);

わかったこと

  1. codeigniter側では、きちんとコントローラーとviewをセットしてあげないとだめ。 angular側でうまくやるのかと勘違いしていたが、結局URLを参照するから セットしないと何も起きなくなる。

  2. app.jsにstateを記述する。 その際例でいうと、contacts_stateがstate名になり、 index.htmlのように<a ui-sref="contacts_state">template1を読み込む</a> のようにすると、クリックするとURLが#/contactsにかわり <a ui-sref="contacts_state">template1を読み込む</a> にang/template1が挿入される

  3. app.jsのcontroller. function($scope)・・・ のように記載すると各種ページの中でその$scopeがつかえる

その他試したこと

デフォルトルートと引数

views/ang/index.html

        <a ui-sref="detail({empId:12345})">template7へ遷移</a>

        <div id="template">
            <section ui-view><i>templateが読み込まれる</i></section>
        </div>

js/ang/app.js

'use strict';

var app = angular.module('app4', ['ui.router'])
    .config(['$stateProvider', '$urlRouterProvider', function($stateProvider, $urlRouterProvider){
        $stateProvider
            .state('detail', {
                url: '/detail/{empId:[0-9]{5}}',
                templateUrl: 'ang/template7',
                controller: function($stateParams, $scope) {
                    $scope.empId = $stateParams.empId;
                    $scope.title = 'detailに入りました!';
                }
            })
            .state('error_state', {
                url: '/error',
                templateUrl: 'ang/template_error',
                controller: function($scope) {
                    $scope.error = 'errorだよ';
                }
            });
        $urlRouterProvider
            .otherwise('/error');
    }])
    .controller('MainCtrl', ['$scope', function($scope){
        $scope.context = 'year!!';
    }]);
  • 上記の$urlRouterProviderのように定義すれば、指定URL以外は/errorに飛ばすことができる (stateに定義を忘れずに)
  • URLに引数を指定させる場合はdetail部分のように正規表現をつかえる
  • 引数の取得は$scope.empIdのように取得する

ui-viewに名前をつける

views/ang/index.html

        <a ui-sref="contacts">template2とtemplate4を読み込む</a>
        <a ui-sref="question">template3を読み込む</a>
        <section ui-view="main"><i>template2が読み込まれる</i></section>
        <section ui-view="main2"><i>template2が読み込まれる</i></section>
        <section ui-view="sub"><i>template3が読み込まれる</i></section>

js/ang/app.js

'use strict';

var app = angular.module('app2', ['ui.router'])
    .config(['$stateProvider', function($stateProvider){
        $stateProvider
            .state('contacts', {
                views: {
                    "main" : {
                        url: '/contacts',
                        templateUrl: 'ang/template2',
                        controller: function($scope) {
                            $scope.title = 'contactsに入りました!';
                        }
                    },
                    "main2" : {
                        url: '/contacts',
                        templateUrl: 'ang/template4',
                        controller: function($scope) {
                            $scope.title = 'contactsに入りました!';
                        }
                    }
                }
            })
            .state('question', {
                views: {
                    "sub" : {
                        url: '/question',
                        templateUrl: 'ang/template3',
                        controller: function($scope) {
                            $scope.title = 'questionに入りました!';
                        }
                    }
                }
            })
    }]);

上記のように定義すればui-viewに名前を指定できる。

ただし、

  • URLを変更できない。
  • 同時に2つ以上のviewを呼び出せない。

だった。(僕のやり方が悪い?)

angularは覚えることが多いけど、面白い機能がいっぱいなので、もっと勉強しよう。