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は理想からほど遠いことを理解していますが、私のアプローチに誰かに興味を持てたことを望みます。 私は宝石の人生に対する批判と積極的な参加に喜んでいます。

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

Source: https://habr.com/ru/post/J283214/


All Articles