在 Rails 上構建 Angular 單頁應用并集成 Devise 和 Bootstrap

分類:技術 時間:2017-01-13

在我開始為自己的首個 Angular 單頁面應用程序 (SPA) 編寫代碼時,我意識到用于設置和集成 Devise 的資源都比較零碎。我找到的堪稱最實用的指南,也只是照常將 Angular 同 Rails 的運用過一遍了,其內容只有一小節。其它的一些資源不是太復雜就是太高深,而且步驟描述也不夠詳盡。對于一個新手而言,入門成了一大問題。

我在線了解的課程多是讓我們積累高級組件,課程實驗也已經準備好了基礎框架,我們沒法自己從零開始演練。而在我看來,學習的關鍵就是要自己動手從領開始構建幾個應用。

所有工作都完成后,我的第一個 Angular 項目就正式啟動并運行了。我為在 Rails 上構建 Angular 單頁應用并集成 Devise 和 Bootstrap 制作了一份指導手冊。 以下內容是我在相關主題上做的初步研究。

不可否認,Web開發中的很大一部分功能都能直接解決復雜問題而不需要額外的解決方案。我希望這篇文章能給新手程序員帶來幫助。

該指南只做入門介紹。適合對 Angular,Rails, Devise 和 Bootstrap有基本了解的用戶群體。本文不深入探究Active Record,但會提及Active Model Serializer,因其是發送模型到 JavaScript 前端的必要途徑。為深化主題,我會安裝Bootstrap,并做相應的驗證。

你可通過以下視頻進行輔助學習:

視頻: https://youtu.be/CtsC0iRxrAk

開發配置

打開一個終端窗口,進入你想要創建項目的工作目錄,就可以開始開發工作了。在這個演示示例里,我使用的是 Desktop (桌面)目錄。

在終端窗口中,執行 `$ rails new YOUR-APP` 即可初始化 Rails 項目,初始化程序創建了一個目錄,目錄將會包含了整個框架,以及相關依賴包,目前這些依賴關系都被記錄在 gems 配置文件 Gemfile 里面。(PS, 文中 $ 符合表示終端提示符)

接著,打開 Gemfile ,刪除 `gem turbolinks`,然后添加以下內容:

gem 'bower-rails'
gem 'devise'
gem 'angular-rails-templates' #=gt; allows us to place our html views in the assets/javascript directory
gem 'active-model-serializer'
gem 'bootstrap-sass', '~gt; 3.3.6' #=gt; bootstrap also requires the 'sass-rails' gem, which should already be included in your gemfile

盡管 Bower (一個前端包管理工具) 非本項目必須,但是我還是選擇使用 Bower ,原因很簡單:增加經驗,為之后可能用到 Bower 的情況做準備。

Bower 是和 Ruby gems 或者 Node npm 類似的包管理工具,更多信息請參閱: https://bower.io 。你可以通過 npm (npm install -g bower) 將其安裝,但是在這份指南中,我使用 bower-rails 來安裝 bower。

初始化 Gem 資源庫,創建一個數據庫并添加一個遷移文件

現在我們準備進行這些 gem 資源庫的安裝與初始化,然后創建數據庫,并添加一個遷移文件以便用戶可以使用一個用戶名進行注冊操作,然后用如下命令將這些遷移應用到我們方案中去:

$ bundle install
$ rake db:create #=gt; create database
$ rails g bower_rails:initialize json  #=gt; generates bower.json file for adding quot;dependenciesquot;
$ rails g devise:install #=gt; generates config/initializers/devise.rb, user resources, user model, and user migration with a TON of default configurations for authentication
$ rails g migration AddUsernametoUsers username:string:uniq #=gt; generates, well, exactly what it says.
$ rake db:migrate

現在你已經有了用來構建應用的驅動,你也許還想要一些其它的依賴,或者說“包”,不過這里所擁有的已足夠你進行構建。將如下這些第三方依賴添加到 bower.json 中去:

...
quot;vendorquot;: {
  quot;namequot;: quot;bower-rails generated vendor assetsquot;,
  quot;dependenciesquot;: {
    quot;angularquot;: quot;v1.5.8quot;,
    quot;angular-ui-routerquot;: quot;latestquot;,
    quot;angular-devisequot;: quot;latestquot;
  }
}

一旦你將這些修改保存到 bower.json 中,就會想要用到下面的命令來安裝這些包,然后從早先安裝的“active-model-serializer”gem 庫中生成用戶串號器:

$ rake bower:install
$ rails g serializer user

找到 app/serializers/user_serializer.rb 文件,然后在當 Devise 從 Rails 請求用戶信息的時候直接在屬性 :idso 后面添加 :username。這樣要比提示說 “Welcome, [email protected]” 要好看許多,不過如果提示成了 “Welcome, 5UPer$3CREtP4SSword”,那也蠻糟糕的。這只是開個玩笑,不過說真的,不要這樣做。

在 config/application.rb 文件中直接在 Applicationlt;Rails::Application 類下面添加下面這幾行代碼:

config.to_prepare do
  DeviseController.respond_to :html, :json
end

因為 Angular 會使用 .json 格式的文件請求用戶的相關信息,所以我們就需要確保 DeviseController 會給出適當的響應,不能是默認的提示。

完成后端的設置

后端已經接近于完成了,但還需更一些調整。

打開 config/routes.rb 文件,在devise_for:users:root 'application#index' 這一行底下加入下面這幾行。然后用這一整塊代碼替換 app/controllers/application_controller.rb 的內容:

class ApplicationController lt; ActionController::Base
  protect_from_forgery with: :exception
  before_action :configure_permitted_parameters, if: :devise_controller?
  skip_before_action :verify_authenticity_token

  respond_to :json

  def index
    render 'application/index'
  end

  protected

  def configure_permitted_parameters
    added_attrs = [:username, :email, :password, :password_confirmation, :remember_me]
    devise_parameter_sanitizer.permit :sign_up, keys: added_attrs
    devise_parameter_sanitizer.permit :account_update, keys: added_attrs
  end
end

到此我們已經做好了幾件事情。第一,我們告訴Rails我們要輸出json;第二,我們唯一的視圖存在于views/application/index.html.erb 之中;第三,當你要從 Devise 獲取一次調用時,不要擔心有關于認證令牌的事情;第四,用戶將會有一個用戶名稱(username)。

接下來要打開 app/controllers/users_controller.rb 文件,然后確定你可以使用任何 /users/:id.json 這樣 JSON 形式的請求訪問用戶:

class UsersController lt; ApplicationController
  def show
    user = User.find(params[:id])
    render json: user
  end  
end

不要擔心 routes.rb 文件中的 :show 的設置問題。Devise 已為我們處理好!

Rails 會默認使用 views/layouts/application.html.erb 進行初始化,但這不是我們所需要的,所以我們需要做進一步操作:

  • 將這個文件挪到 app/views/application/ 目錄下。

  • 將它重新命名為 index.html.erb。

  • 使用lt;ui-viewgt;lt;/ui-viewgt;替換lt;%=yield%gt;(除了頭部的 scritp/style 標記,我們不會對其它任何的 erb 進行渲染)。

  • 移除腳本和樣式表的 erb 標記中所有的 “turoblinks” 提示信息。

  • 向 lt;bodygt;標簽添加一個 ng-app=quot;myAppquot; 的屬性。當我們啟動服務器的時候,Angular 將會進行加載操作,并會為此在初始化應用之前加緊對 DOM 的查找。

配置好后端的最后一個步驟就是放置好資源管道。Bower 已經在 vendor/assets/bower_components 中為我們安裝好系列東西。同時我們也早將較好的 gem 資源庫安裝好。現在,我們要確保應用可以找到下列腳本和樣式表:

在 app/assets/javascript/application.js 文件中需要以下內容:

//= require jquery
//= require jquery_ujs
//= require angular
//= require angular-ui-router
//= require angular-devise
//= require angular-rails-templates
//= require bootstrap-sprockets
//= require_tree

注意:不要忘了刪掉 require 的 turbolink。

最后,我們必須將 app/assets/stylesheets/application.css 重新命名為 application.scss,然后在樣式的最后加上這兩行 @import 代碼:

*
 *= require_tree .
 *= require_self
 */
@import quot;bootstrap-sprocketsquot;;
@import quot;bootstrapquot;;

Duang!! 現在,我們已經把所有設置都搞定,可以著手弄前端了。

這里可以預覽一下 Angular 應用程序樹的樣子。因為我們已經安裝了 ‘angular-templates’ 的 gem 庫,所以可以將所有的 HTML 文件保持在 assets/javascript 目錄中,里面有我們所有其它的 Angular 文件:

/javascript/controllers/AuthCtrl.js
/javascript/controllers/HomeCtrl.js
/javascript/controllers/NavCtrl.js
/javascript/directives/NavDirective.js
/javascript/views/home.html
/javascript/views/login.html
/javascript/views/register.html
/javascript/views/nav.html
/javascript/app.js
/javascript/routes.js

第一件要事就是 : 我們要在 app.js 中對應用程序進行聲明,并注入必要的依賴:

(function(){
  angular
    .module('myApp', ['ui.router', 'Devise', 'templates'])
}())

這里我用到了一個 IIFE,理由引用了下面這段話:

要將 AngularJS 組件封裝到一個立即執行函數表達式(IIFE)里面。這樣做可以幫助你防止變量和函數聲明在全局范圍內存活時間過長而超出你的預期, 也可以幫助你避免變量沖突。這個為你想要把自己寫的代碼最小化,然后打包到一個單獨的文件部署到生產服務器上提供了方便,以為每一個文件提供一個變量作用域。 Codestyle.co 上的 AngularJS 指南

Routes.js

接下來我們將對 routes.js 文件進行設置。其中一些操作去前面一致,但我更喜歡現在重新設置:

angular
  .module('myApp')
  .config(function($stateProvider, $urlRouterProvider){
    $stateProvider
      .state('home', {
        url: '/home',
        templateUrl: 'views/home.html',
        controller: 'HomeCtrl'
      })
      .state('login', {
        url: '/login',
        templateUrl: 'views/login.html',
        controller: 'AuthCtrl',
        onEnter: function(Auth, $state){
          Auth.currentUser().then(function(){
            $state.go('home')
          })
        }
      })
      .state('register', {
        url: '/register',
        templateUrl: 'views/register.html',
        controller: 'AuthCtrl',
        onEnter: function(Auth, $state){
          Auth.currentUser().then(function(){
            $state.go('home')
          })
        }
      })
    $urlRouterProvider.otherwise('/home')
  })

我剛剛所做的就是調用應用‘myApp’, 然后調用 config 函數,傳入 $stateProvider和$routerUrlProvider 作為參數。我們可以立即調用 $stateProvider 然后啟動鏈式的 .state() 方法, 它需要倆個參數,狀態的名稱(例如“home”), 以及一個描述了狀態的數據的對象, 比如它的 URL,HTML 模板,還有使用了的控制器。我們也用到了 $urlRouterProvider,只為了確保用戶不會隨意的跳轉,而只是跳轉到我們預設的狀態中去。

在這一點上,你可能對 onEnter,$state , 以及Auth 還不熟悉。稍后我們會講到。

現在,讓我們對 home.html和HomeCtrl.js 進行構建:

lt;div class=quot;col-lg-8 col-lg-offset-2quot;gt;
lt;h1gt;{{hello}}lt;/h1gt;
lt;h3 ng-if=quot;userquot;gt;Welcome, {{user.username}}lt;/h3gt;
lt;/divgt;
angular
  .module('myApp')
  .controller('HomeCtrl', function($scope, $rootScope, Auth){
    $scope.hello = quot;Hello Worldquot;
  })

你可以嘗試注釋掉 login/register 狀態,然后運行 $ rails 來看看是否一切正常。如果正常的話,就可以看到一個好看的 “Hello World”大字。如果它就在界面的中上部,那就屏息靜待,因為 Bootstrap 正在介入,然后 col-lg 會將它放到一個漂亮的位置,而不是死死的杵在左上角。

Angular 所做的就是搜尋 DOM,找到屬性ng-app,初始化 “myApp”, 從路由程序默認導航到/hom, 定位到 lt;ui-viewgt; 指令,實例化 HomeCtrl,注入 $scope 對象,加入一個 hello 的鍵,給它分配一個值 quot;Hello Worldquot;,然后用 lt;ui-viewgt; 元素中的信息渲染出 home.html。一旦到了視圖中,Angular 就可以掃描到任何有意義的諸如{{...}} 綁定以及 ng-if 指令這樣的命令,然后按照需要渲染出控制器的信息。這些是接下來的操作的要點,但操作順序會有不同。

附加 AuthCtrl.js 和 login.html/register.html 文件

因為我們已經有了所有這些在幕后的細節信息,讓我們再附加上AuthCtrl.js 和login.html/register.html 文件:

# login.js
lt;div class=quot;col-lg-8 col-lg-offset-2quot;gt;
  lt;h1 class=quot;centered-textquot;gt;Log Inlt;/h1gt;
  lt;form ng-submit=quot;login()quot;gt;
    lt;div class=quot;form-groupquot;gt;
      lt;input type=quot;emailquot; class=quot;form-controlquot; placeholder=quot;Emailquot; ng-model=quot;user.emailquot; autofocusgt;
    lt;/divgt;
    lt;div class=quot;form-groupquot;gt;
      lt;input type=quot;passwordquot; class=quot;form-controlquot; placeholder=quot;Passwordquot; ng-model=quot;user.passwordquot;gt;
    lt;/divgt;
    lt;input type=quot;submitquot; class=quot;btn btn-infoquot; value=http://www.tuicool.com/articles/quot;Log Inquot;gt;
  lt;/formgt;
lt;/divgt;
# register.js
lt;div class=quot;col-lg-8 col-lg-offset-2quot;gt;
  lt;h1 class=quot;centered-textquot;gt;Registerlt;/h1gt;
  lt;form ng-submit=quot;register()quot;gt;
    lt;div class=quot;form-groupquot;gt;
      lt;input type=quot;emailquot; class=quot;form-controlquot; placeholder=quot;Emailquot; ng-model=quot;user.emailquot; autofocusgt;
    lt;/divgt;
    lt;div class=quot;form-groupquot;gt;
      lt;input type=quot;usernamequot; class=quot;form-controlquot; placeholder=quot;Usernamequot; ng-model=quot;user.usernamequot; autofocusgt;
    lt;/divgt;
    lt;div class=quot;form-groupquot;gt;
      lt;input type=quot;passwordquot; class=quot;form-controlquot; placeholder=quot;Passwordquot; ng-model=quot;user.passwordquot;gt;
    lt;/divgt;
    lt;input type=quot;submitquot; class=quot;btn btn-infoquot; value=http://www.tuicool.com/articles/quot;Log Inquot;gt;
  lt;/formgt;
  lt;brgt;

  lt;div class=quot;panel-footerquot;gt;
    Already signed up? lt;a ui-sref=quot;home.loginquot;gt;Log in herelt;/agt;.
  lt;/divgt;
lt;/divgt;

之前我覆蓋了AuthCtrl,我只想指出,因為你看到的 大多數 是 Bootstrap 的 CSS 類,所以會對其渲染留下不錯的印象。如果忽略所有類的屬性,其他內容都很類似,例如:asng-submit,ng-model,andui-sref,通常它們都有 href 錨的 tag 屬性。現在,為了AuthCtrl ,你準備怎么做?

angular
  .module('myApp')
  .controller('AuthCtrl', function($scope, $rootScope, Auth, $state){
    var config = {headers: {'X-HTTP-Method-Override': 'POST'}}

    $scope.register = function(){
      Auth.register($scope.user, config).then(function(user){
        $rootScope.user = user
        alert(quot;Thanks for signing up, quot;   user.username);
        $state.go('home');
      }, function(response){
        alert(response.data.error)
      });
    };

    $scope.login = function(){
      Auth.login($scope.user, config).then(function(user){
        $rootScope.user = user
        alert(quot;You're all signed in, quot;   user.username);
        $state.go('home');
      }, function(response){
        alert(response.data.error)
      });
    }
  })

大多數代碼都派生于 Angular Devise 文檔 ,因此在這里我不表述過多的細節。現在你知道Auth 是一個服務,它是由angular-device 創建的,并且有一些非常棒的功能,例如:sAuth.login(userParameters,config) 和Auth.register(userParameters,config),它們創建了一個promise,并由登陸用戶一次性處理。


Tags: AngularJS Devise RubyonRails

文章來源:https://www.oschina.net/translate/setting-up-an-an


ads
ads

相關文章
ads

相關文章

ad