over 3 years ago

為什麼是選擇 AngularJS 作為前端架構?

以我自己的對 AngularJS 認識,我認為 AngularJS 和 Rails 一樣,符合 DRY(Don't Repeat Yourself)的精神。

AngularJS 本身 Directive 與 Module 的設計對於套件封裝非常方便,加上他逐漸蓬勃發展的社群,

讓開發者在AngularJS的架構下有了豐富的套件支援,開發任何應用都比以往方便,快速,

另外如果你是 TDD 的信仰者,AngularJS也提供了豐富的測試工具,讓你可以無憂無慮的寫程式,

這兩點是我會如此愛不釋手的原因。

AngularJS 與 Rails 功能重疊上的取捨?

但是由於 AngularJS 本身的設計,很依賴 Client-Side Rendering, Routing,

這部分的功能與 Rails 部分重疊,我會做一些取捨,我認為 AngularJS 本身想要攬很多事情去做,

但有些事情完全抓到前端去做,效益似乎不大,管理上會變得複雜,而且就沒有發揮到 Rails 的優勢。

Routing 我認為應該單純化用 Rails 去處理,否則前後端都有各自的 Routing,架構會變得過於複雜,不好維護。

Rendering 對於 AngularJS 則是一個優勢(data-binding),如果 Rendering 交給 AngularJS 去做,

就不用在 Rails 中造大量的 Helper 去產生 html,可以減少伺服器處理 Request 的負擔。

使用 Bower 及 Rails-Assets 做 Javascript 套件相依管理

Bower 是 Twitter 推出的 前端套件相依性管理器,非常推薦,

在往後我們如果用到任何 Angular 套件,我們都可以從這個來源來安裝,

Rails-Assets 則是 Bower 在 Bundler 中的 Proxy,讓你可以簡單在 Gemfile 裡面,安裝 Bower 中的套件。

https://rails-assets.org,他的官網中,有使用方法,及套件列表查詢。

那我們就來做看看吧

首先先創造一個Project來練習吧:

$ rails new angular-rails-basic

安裝 AngularJS

單純一點,我們先暫時不用CoffeeScript,先註解掉吧:

# gem 'coffee-rails', '~> 4.0.0'

由於我們要用 Rails-Assets 安裝 Bower 上的套件,我們在 Gemfile 中上方,先加入 Rails-Assets 的 Source:

source 'https://rails-assets.org'

我們先安裝一個穩定版本的 AngularJS 吧:

gem 'rails-assets-angular', '1.2.10'

在 Console 下執行 bundle install:

$ bundle install

在 asset pipeline 中加入 AngularJS

turbolink 是 Rails 4 以後,幫助 Rendering 加速的一個套件,

但由於它與 AngularJS 初始化時會互斥,所以不能同時使用,

如果你還是很想要同時使用這兩個功能,可以藉由調整 AngularJS 初始化的時間點達成 (Stackoverflow 討論)

但在此我們還是先在 application.js 中將它拿掉,並加入 AngularJS:

//= require jquery

//= require jquery_ujs

//= require angular

//= require_tree .

開始第一個實作

我們新增一個 Scaffold:

$ rails g scaffold language name
$ rake db:migrate

並且在 LanguagesController 中新增一個 action,ng_index,來實作 AngularJS 的單頁式應用。

記得在 config/routes.rb 上增加路由設定:

resources :languages do
  collection do
    get 'ng_index'
  end
end

準備開始寫 AngularJS

我自己習慣將一個頁面當作ㄧ個 Angular App 看待,

但是往往在一個 Rails 中會有很多頁面,所以往往會需要有很多 Angular App 的物件被產生

但這時如果有共用的設定,或者共用的元件該如何處理?

這邊有一個秘訣就是使用 Factory Pattern,我們不直接透過 angular.module 造出頁面App,而是透過自訂方法造出

我們新增一個 angularApplication 物件來生產初始化的 Angular App

新增一個 angular_application.js.erb 在 assets pipeline 中

var angularApplication = (function() {

  function set_rails_csrf(app) {
    app.config(function($httpProvider) {
      $httpProvider.defaults.headers.common['X-CSRF-Token'] =
        $('meta[name=csrf-token]').attr('content');
    });
  }

  return {
    module: function(name, modules) {
      var new_app = angular.module(name, modules);

      set_rails_csrf(new_app);

      return new_app;
    }
  };
})();

以這個例子來說,這個工廠模式幫我做了一件事,每當我透過 angularApplication 造出來的 Angular App,

他都會幫我加上 CSRF 的設定,而不用一一設定,讓我透過 AngularJS 和後端的互動行為都可以通過 Rails 的驗證。

我們在 Rails Layout 和 Controller 中指定 AngularJS APP

layout/application.html.erb 中修改 <body> 為:

<body ng-app="<%= @ng_app %>">

在 LanguagesController 中加上:

before_action :set_ng_app

及在 private 底下加入這個 method:

def set_ng_app
    @ng_app = 'languageApp'
end

開始寫 AngularJS 吧

language.js 中我們新增一個 languageApp,供 LanguagesController 底下的頁面使用:

var languageApp = angularApplication.module('languageApp', []);

我們想在剛剛建立的 LanguagesController#ng_index 中透過 AngularJS 列出所有的 language

ng_index.html.erb 中新增一個 ul list,他用到一個 Angular Controller:listCtrl,列出 languages 的內容

<ul ng-controller="listCtrl">
  <li ng-repeat="language in languages">{{ language.name }}</li>
</ul>

language.js 中新增對應的 Angular Controller 吧:

languageApp.controller('listCtrl', function($scope, $http) {
  $http.get('/languages.json').success(function(languages) {
    $scope.languages = languages;
  });
});

裡面我們透過 ajax 到 /languages.json 取得 languages,並 Assign 給 $scope.languages

由於 $scope 有 data-binding 的特性,因此 $scope.languages 一被更改,html 中透過 languages 迭代出的內容也跟者增加。

到 Rails Console 中新增一些資料吧。

$ rails c
Language.create(name: 'English')
Language.create(name: 'Japanese')
Language.create(name: 'Chinese')

看看成果

$ rails s

瀏覽剛剛寫好的頁面 http://localhost:3000/languages/ng_index

原始碼在這裏:Github Repo

恭喜你!!已經完成一個 Rails 與 AngularJS 混搭風的 HelloWorld

下一篇我們來玩玩看更多 AngularJS 的功能,實現單頁式的 CRUD 應用!

← 使用 Mac 連線到 Raspberry Pi (USB to TTL) Rails 與 AngularJS 混搭風 - 2. 單頁式CRUD →
 
comments powered by Disqus