12/08/2018, 14:37

Cài đặt Angular SPA trong Rails với Devise và Bootstrap

Angular là một bộ Javascript Framework rất mạnh và thường được sử dụng để xây dựng project Single Page Application (SPA). Nó hoạt động dựa trên các thuộc tính mở rộng HTML (các atributes theo quy tắc của Angular). Đây là một Framework mã nguồn mở hoàn toàn miễn phí. Trong bài viết này sẽ đề cập ...

Angular là một bộ Javascript Framework rất mạnh và thường được sử dụng để xây dựng project Single Page Application (SPA). Nó hoạt động dựa trên các thuộc tính mở rộng HTML (các atributes theo quy tắc của Angular). Đây là một Framework mã nguồn mở hoàn toàn miễn phí. Trong bài viết này sẽ đề cập cách setup cơ bản Angular trong Rails với Devise và Bootstrap

1. Cài đặt BackEnd:

  • Thêm các gem cần thiết:
gem 'devise'
gem 'angular-rails-templates' #=>  cho phép để file html trong folder assets/javascript
gem 'active-model-serializers'
gem 'bootstrap-sass', '~> 3.3.6'
  • Chạy bundle install, khởi tạo devise rails g devise:install và tạo database với bảng User,
rails g migration AddUsernametoUsers username:string:uniq
  • Tạo serializer cho model user : rails g serializer user
  • Angular sẽ nhận gía trị user kiểu json, nên để đảm bảo DeviseController trả về gía trị phù hợp, ta cần config:
#config/application.rb
class Application < Rails::Application
  config.to_prepare do
    DeviseController.respond_to :html, :json
  end
end
  • Thêm các setting cần thiết:
#config/routes.rb
devise_for :users
root 'application#index'

#app/controllers/application_controller.rb
class ApplicationController < 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
  • Trong user_controller.rb , đảm bảo gía trị response là JSON
class UsersController < ApplicationController
  def show
    user = User.find(params[:id])
    render json: user
  end  
end
  • Config asset pipeline:
#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 .

Nhớ bỏ require turbolinks

2. Cài đặt FrontEnd

  • Ta đã cài gem angular-templates nên có thể giu file HTML trong assets/javascript
  • Đây là cấu trúc cây của 1 app 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
  • Đầu tiên, khai bảo app trong app.js:
(function(){
  angular
    .module('myApp', ['ui.router', 'Devise', 'templates'])
}())
  • Edit file 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')
  })

$urlRouterProvider đảm bảo user không thể điều hướng tùy ý. Tiếp theo edit file home.html và HomeCtrl.js:

#home.html
<div class="col-lg-8 col-lg-offset-2">
<h1>{{hello}}</h1>
<h3 ng-if="user">Welcome, {{user.username}}</h3>
</div>

#HomeCtrl.js
angular
  .module('myApp')
  .controller('HomeCtrl', function($scope, $rootScope, Auth){
    $scope.hello = "Hello World"
  })
  • Xây dựng màn hình authen:
# login.js
<div class="col-lg-8 col-lg-offset-2">
  <h1 class="centered-text">Log In</h1>
  <form ng-submit="login()">
    <div class="form-group">
      <input type="email" class="form-control" placeholder="Email" ng-model="user.email" autofocus>
    </div>
    <div class="form-group">
      <input type="password" class="form-control" placeholder="Password" ng-model="user.password">
    </div>
    <input type="submit" class="btn btn-info" value="Log In">
  </form>
</div>

# register.js
<div class="col-lg-8 col-lg-offset-2">
  <h1 class="centered-text">Register</h1>
  <form ng-submit="register()">
    <div class="form-group">
      <input type="email" class="form-control" placeholder="Email" ng-model="user.email" autofocus>
    </div>
    <div class="form-group">
      <input type="username" class="form-control" placeholder="Username" ng-model="user.username" autofocus>
    </div>
    <div class="form-group">
      <input type="password" class="form-control" placeholder="Password" ng-model="user.password">
    </div>
    <input type="submit" class="btn btn-info" value="Log In">
  </form>
  <br>

  <div class="panel-footer">
    Already signed up? <a ui-sref="home.login">Log in here</a>.
  </div>
</div>

#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("Thanks for signing up, " + 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("You're all signed in, " + user.username);
        $state.go('home');
      }, function(response){
        alert(response.data.error)
      });
    }
  })

Auth là dịch vụ được tạo bởi angular-device, cung cấp chức năng login và register (Auth.login(userParameters, config) và Auth.register(userParameters, config))

  • Kết nối các thành phần với nhau:
#NavDirective.js
angular
  .module('myApp')
  .directive('navBar', function NavBar(){
    return {
      templateUrl: 'views/nav.html',
      controller: 'NavCtrl'
    }
})

#nav.html
<div class="col-lg-8 col-lg-offset-2">
  <ul class="nav navbar-nav" >
    <li><a ui-sref="home">Home</a></li>
    <li ng-hide="signedIn()"><a ui-sref="login">Login</a></li>
    <li ng-hide="signedIn()"><a ui-sref="register">Register</a></li>
    <li ng-show="signedIn()"><a ng-click="logout()">Log Out</a></li>
  </ul>
</div>

#NavCtrl.js
angular
  .module('myApp')
  .controller('NavCtrl', function($scope, Auth, $rootScope){
    $scope.signedIn = Auth.isAuthenticated;
    $scope.logout = Auth.logout;

    Auth.currentUser().then(function (user){
      $rootScope.user = user
    });

    $scope.$on('devise:new-registration', function (e, user){
      $rootScope.user = user
    });

    $scope.$on('devise:login', function (e, user){
      $rootScope.user = user
    });

    $scope.$on('devise:logout', function (e, user){
      alert("You have been logged out.")
      $rootScope.user = undefined
    });
  })

sử dụng các link điều hướng ng-hide="signedIn()" và ng-click="logout()" và thêm các bộ nghe $scope để chạy các hành động khi các sự kiện devise xảy ra. Ngoài ra ta gọi Auth.currentuser() để khi controller được khởi tạo, chúng ta có thể kiểm tra đối tượng $rootScope.user và hiển thị link điều hướng thích hợp.

Trên đây là các cài đặt một app đơn gian sử dụng angular trong rails dùng Devise và Bootstrap.

0