AngularJSとRuby on Railsに基づいた単一ページアプリケーションを構築するためのアーキテクチャ

Ruby on RailsでSPAアプリケーションを構築する方法論に興味を持った私は、各アプリケーションに実装され、その後個別のOxymoron gemに分離されるアイデアを思いつきました。 現時点では、Oxymoronは20を超えるかなり大きな商用鉄道アプリケーションを作成しています。 ヘムを公の裁判所に持ち込みたい。 したがって、私はそれに基づいてさらに物語を進めていきます。



完成したアプリケーションの例。



Oxymoronはどのようなタスクを解決しますか?



私にとって、このgemはルーチンコードの量を1桁削減し、その結果、開発速度を大幅に向上させます。 これにより、AngularJSとRoRの相互運用性を非常に簡単に構築できます。



  1. routes.rbに基づいて、AngularJSルーティングを自動的に構築する
  2. routes.rbからAngularJSリソースを自動生成
  3. AngularJSコントローラーのアーキテクチャの厳密な設定
  4. 常に使用される構成の処方
  5. フォーム検証
  6. FormBuilderが自動的にngモデルを付加する
  7. 通知
  8. 一般的に使用されるディレクティブ(ajax fileupload、click-outside、content-for、check-list)
  9. JsRoutesのコンパクトなアナログの実装


どのように機能しますか?



まず、gemをGemfileに接続する必要があります。



gem 'oxymoron'
      
      





これで、 routes.rbを変更するたびに 、またはアプリケーションを再起動するたびに、アプリケーションを構築するために必要なすべてのコードを含むoxymoron.jsファイルがapp / asset / javascriptsに生成されます。



次のステップは、アセットを構成することです。 最も単純な場合、次のようになります。



application.jsの場合:



 /* = require oxymoron/underscore = require oxymoron/angular = require oxymoron/angular-resource = require oxymoron/angular-cookies = require oxymoron/angular-ui-router = require oxymoron/ng-notify = require oxymoron = require_self = require_tree ./controllers */
      
      





application.cssの場合:



 /* *= require oxymoron/ng-notify *= require_self */
      
      





UIルーターを使用するため、レイアウトでui-viewタグを定義する必要があります。 アプリケーションはHTML5ルーティングを使用するため、ベースタグを指定する必要があります。 私たちの場合、これはapplication.html.slimです。 私はSLIMをプリプロセッサとして使用し、すべての人に強くアドバイスします。



 html ng-app="app" head title  base href="/" = stylesheet_link_tag 'application' body ui-view = javascript_include_tag 'application'
      
      





すべてのAJAXリクエストについて、レイアウトをオフにする必要があります。 これを行うには、ApplicationControllerに必要なロジックを記述します。



 layout proc { if request.xhr? false else "application" end }
      
      





フォームの正しい処理とng-modelのインストールのために、デフォルトのFormBuilderをOxymoronFormBuilderにオーバーライドする初期化子を作成する必要があります。



 ActionView::Base.default_form_builder = OxymoronFormBuilder
      
      





最後に行う必要があるのは、アプリケーションにoxymoronモジュールを挿入し、自動生成されたルーティングが使用されることをUIルーターに伝えることです。



 var app = angular.module("app", ['ui.router', 'oxymoron']); app.config(['$stateProvider', function ($stateProvider) { $stateProvider.rails() }])
      
      





すべてが本格的なSPAアプリケーションを作成する準備ができました!



最も簡単なSPAブログを書きましょう



だから。 最初に、このモデルを管理するためのPostモデルとRESTfulコントローラーを準備します。 これを行うには、コンソールで次のコマンドを実行します。



 rails g model post title:string description:text rake db:migrate rails g controller posts index show
      
      





routes.rbで、postsリソースを作成します。



 Rails.application.routes.draw do root to: "posts#index" resources :posts end
      
      





次に、コントローラーのメソッドについて説明します。 多くの場合、同じメソッドが応答でJSON構造とHTMLマークアップの両方を返すことがあるため、そのようなメソッドはrespond_toでラップする必要があります。



典型的なRailsコントローラーの例
 class PostsController < ActiveRecord::Base before_action :set_post, only: [:show, :edit, :update, :destroy] def index respond_to do |format| format.html format.json { @posts = Post.all render json: @posts } end end def show respond_to do |format| format.html format.json { render json: @post } end end def new respond_to do |format| format.html format.json { render json: Post.new } end end def edit respond_to do |format| format.html format.json { render json: @post } end end def create @post = Post.new post_params if @post.save render json: {post: @post, msg: "Post successfully created", redirect_to: "posts_path"} else render json: {errors: @post.errors, msg: @post.errors.full_messages.join(', ')}, status: 422 end end def update if @post.update(post_params) render json: {post: @post, msg: "Post successfully updated", redirect_to: "posts_path"} else render json: {errors: @post.errors, msg: @post.errors.full_messages.join(', ')}, status: 422 end end def destroy @post.destroy render json: {msg: "Post successfully deleted"} end private def set_post @post = Post.find(params[:id]) end def post_params params.require(:post).permit(:title, :description) end end
      
      







各Railsコントローラーには、AngularJSコントローラーがあります。 マッチングルールは非常に簡単です。



 PostsController => PostsCtrl Admin::PostsController => AdminPostsCtrl #    namespace Admin
      
      





app / javascripts / controllers / post_ctrl.jsに適切なコントローラーを作成します。



典型的なAngularJSコントローラーの例
 app.controller('PostsCtrl', ['Post', 'action', function (Post, action) { var ctrl = this; //     '/posts' action('index', function(){ ctrl.posts = Post.query(); }); //    '/posts/:id' action('show', function (params){ ctrl.post = Post.get({id: params.id}); }); //   '/posts/new' action('new', function(){ ctrl.post = Post.new(); //   ,       . . . ctrl.save = Post.create; }); //   '/posts/:id/edit' action('edit', function (params){ ctrl.post = Post.edit({id: params.id}); //      ctrl.save = Post.update; }) //  .     edit  new. action(['edit', 'new'], function(){ // }) action(['index', 'edit', 'show'], function () { ctrl.destroy = function (post) { Post.destroy({id: post.id}, function () { ctrl.posts = _.select(ctrl.posts, function (_post) { return _post.id != post.id }) }) } }) //     routes.rb     .  : '/posts/some_method' action('some_method', function(){ // }) // etc }])
      
      







工場出荷時の措置に注意してください。 これを使用すると、アプリケーションのページ間でコードを分割するのに非常に便利です。 ファクトリーは、oxymoron.jsで生成された状態を介して解決し、その結果、コントローラーの現在のrailメソッドを認識します。



 action(['edit', 'new'], function(){ //      posts/new  posts/:id/edit })
      
      





次に、 ポストファクトリに注意してください。 このファクトリは、routes.rbで定義されたリソースから自動的に生成されます。 適切に生成するには、リソースでshowメソッドを定義する必要があります。 次のリソースの操作方法は、ボックスから利用できます。



 Post.query() // => GET /posts.json Post.get({id: id}) // => GET /posts/:id.json Post.new() // => GET /posts/new.json Post.edit({id: id}) // => GET /posts/:id/edit.json Post.create({post: post}) // => POST /posts.json Post.update({id: id, post: post}) // => PUT /posts/:id.json Post.destroy({id: id}) // => DELETE /posts/:id.json
      
      





カスタムリソースメソッド(メンバーとコレクション)はまったく同じように機能します。 例:



 resources :posts do member do get "comments", is_array: true end end
      
      





AngularJSリソースに適切なメソッドを作成します。



  Post.comments({id: id}) //=> posts#comments
      
      





応答で配列が予期されることが予想される場合は、 is_array:trueオプションを設定します。 そうでない場合、AngularJSは例外をスローします。



不足しているビューを作成するために残ります。



投稿/ index.html.slim
 h1 Posts input.form-control type="text" ng-model="search" placeholder="" br table.table.table-bordered thead tr th Date th Title th tbody tr ng-repeat="post in ctrl.posts | filter:search" td ng-bind="post.created_at | date:'dd.MM.yyyy'" td a ui-sref="post_path(post)" ng-bind="post.title" td.w1 a.btn.btn-danger ng-click="ctrl.destroy(post)"  a.btn.btn-primary ui-sref="edit_post_path(post)" 
      
      







投稿/ show.html.slim
 .small ng-bind="ctrl.post.created_at | date:'dd.MM.yyyy'" a.btn.btn-primary ui-sref="edit_post_path(ctrl.post)"  a.btn.btn-danger ng-click="ctrl.destroy(ctrl.post)"  h1 ng-bind="ctrl.post.title" p ng-bind="ctrl.post.description"
      
      







投稿/ new.html.slim
 h1 New post = render 'form'
      
      







投稿/ edit.html.slim
 h1 Edit post = render 'form'
      
      







投稿/ _form.html.slim
 = form_for Post.new do |f| div = f.label :title = f.text_field :title div = f.label :description = f.text_area :description = f.submit "Save"
      
      







form_forヘルパーの生成結果に特に注意を払う必要があります。



 <form ng-submit="formQuery = ctrl.save({form_name: 'post', id: ctrl.post.id, post: ctrl.post}); $event.preventDefault();"></form>
      
      





コントローラー内でctrl.saveメソッドを定義するだけで十分です。フォームを送信して表示されるパラメーターを渡すたびに実行されます。 ただし、これらのパラメーターはリソースメソッドの更新および作成の引数として理想的であるため、コントローラーではctrl.save = Post.createのみを記述できます。 PostsCtrlリストでは、このポイントには対応するコメントが付けられています。



ng-model属性は、 text_fieldおよびtext_areaタグに対して自動的に追加されました。 ng-modelコンパイルルールは次のとおりです。



 ng-model="ctrl._._"
      
      





JSON機能のレンダリング:{}



PostsControllerのレールリストで、おそらくrenderメソッドのmsg、redirect_toなどのフィールドに気付いたでしょう。 これらのフィールドには特別なインターセプターが機能し、結果がコントローラーに送信される前に必要なアクションを実行します。



msg-画面上部の緑色のポップアップに内容が表示されます。 エラーのステータスをレンダリングに渡すと、色が赤に変わります



errors-エラーオブジェクトを受け取り、フォームフィールド自体にエラーを直接表示するのに役立ちます。



redirect_to-必要なUIルーター状態にリダイレクトします



redirect_to_options-状態にオプションが必要な場合、たとえば、show pageにはidが必要な場合、このフィールドでそれらを指定する必要があります



redirect_to_url-指定されたURLにジャンプします



reload-ページをユーザーに完全にリロードします



これらすべてのアクションは、ユーザーページをリロードすることなく実行されます。 UIルーターに基づくHTML5ルーティングを使用します。



今link_toなし



以前は、ルートの名前に応じてリンクを定義する場合、 link_toヘルパーを使用する必要がありました。 現在、この機能はルートを記述する通常の方法でui-srefによって実装されています。



  a ui-sref="posts_path"   a ui-sref="post_path({id: 2})"2 a ui-sref="edit_post_path({id: 2})"2 a ui-sref="new_post_path"   
      
      





js-routesの軽量アナログ。 対立



グローバルスコープでは、Routes変数を見つけることができます。 js-routesとほぼ同じように機能します。 唯一の違いは、この実装はオブジェクトのみを受け入れ、数値引数として砂糖を持たないことです。 競合がある可能性があるため、js-routesを無効にすることをお勧めします。



 Routes.posts_path() // => "/posts" Routes.new_post_path() // => "/post/new" Routes.edit_posts_path({id: 1}) // => "/post/1/edit" //    Routes.defaultParams = {id: 1} Routes.post_path({format: 'json'}) // => "/posts/1.json"
      
      





まとめ



プリミティブなSPAアプリケーションを作成しました。 同時に、コードは完全にレールに見え、ロジックは最小限に記述されており、すでに最も一般的なものです。 完璧に制限はなく、Oxymoronは理想からほど遠いことを理解していますが、私のアプローチに誰かに興味を持てたことを望みます。 私は宝石の人生に対する批判と積極的な参加に喜んでいます。



完成したアプリケーションの例。



All Articles