简体   繁体   中英

Ruby On Rails ajax multiple-model combined view

I am developing a Ruby-on-Rails web app that is essentially used to track product technical specifications. This is my first go at a RoR app since following Michael Hartl's Rails Tutorial. I'm not too sharp on Ajax or RoR yet, and didn't realize I was tackling something so intense, but I'm eager to see it through.

To understand the model relationships, see my ER diagram .

I have 3 models:

@entity: { :id, :name, :label, :img }
@group: { :id, :name, :default_label }
@property: { :id, :name, :units, :units_short, :default_label, :default_value, :default_visibility }

Then each of these models has unique many-to-many associations (where I need to track specifics about the relationships) so I created models for those associations.

@group_property_relationship: { :group_id, :property_id, :order }
@entity_group_relationship: { :entity_id, :group_id, :order, :label }
@entity_property_relationship: { :entity_id, :property_id, :group_id, :label, :order, :value, :visibility }

I already have these models and their associations talking to one another and it works wonderfully from the console. But setting up the client is where it gets complicated. See my mock-up for an idea of how I want it to look, hopefully this will inspire an understanding of the functionality.

I am trying to create a one-stop view where everything can be managed on one page using ajax. The user can create an entity, property, or group, create associations between groups and properties, and create associations between entities and groups.

See a static view of my progress here:

https://trello-attachments.s3.amazonaws.com/519e948c97d3fd4579000a79/537fa898eef9844886200be0/463x710/b04e83d4e4e56253a1818048264e1fa3/spex_hub_140617.png

Apparently I don't have a high enough SO reputation for more links . The search, sorting, and pagination all work via ajax, for both models, independently of one another. I followed this tutorial:

http://asciicasts.com/episodes/240-search-sort-paginate-with-ajax

to get most of that functionality, but had to tweak it a bit to update for Rails 4 and to work for multiple models in one view.

But, where it isn't working , is when an entity is created –successfully or not–from this view using the form seen near the top of the 'progress' image, the instance is successfully created, the list is updated, sorting works, but search is broken .

So, in order to track down the error, I need to provide a ton of code...here goes:

The view: hub

app/views/hub/main.html.erb

<div id="hub-alert"></div>
<div class="row">
    <aside class="col-xs-4 v-divider">
        <a style="color: #494A4A" href="<%= entities_path %>">Entities</a>
        <section>
            <h6>
                Create New Entity
            </h6>
            <%= render 'new_entity' %>
        </section>
        <section>
            <h6>
                Existing Entities
            </h6>
            <%= render partial: 'entities/search', locals: { path: hub_path } %>
            <div id="entities">
                <%= render 'entities/entities' %>
            </div>
            <div id='ajax-test'></div>
        </section>
    </aside>
    <aside class="col-xs-4 v-divider">
        <a style="color: #494A4A" href="<%= groups_path %>">Groups</a>
        <section>
            <h6>
                Selected Entity's Groups
            </h6>
            <div id="entitys_groups"> 
            </div>
        </section>
        <section>
            <h6>
                Create New Group
            </h6>
            <%= render 'groups/new' %>
        </section>
        <section>
            <h6>
                Existing Groups
            </h6>
            <%= render partial: 'groups/search', locals: { path: hub_path } %>
            <div id="groups">
                <%= render 'groups/groups' %>
            </div>
            <div id='ajax-test'></div>
        </section>
    </aside>
</div>

Create Entity Partial: _create_entity.html.erb

app/views/hub/_create_entity.html.erb

<h1> Create New Entity</h1>

<div class="row">
  <div class="col-xs-10 col-xs-offset-1">
    <%= bootstrap_form_for(@entity, url: { controller: "hub", action: "create_entity" }, inline_errors: false, method: :post, remote: true) do |f| %>
      <%= render 'entities/fields', f: f %>
      <%= f.submit "Create Entity", class: "btn btn-sm btn-primary btn100", id: "create-entity" %>
    <% end %>
  </div>
</div>

Entity Search Partial: _search.html.erb

app/views/entities/_search.html.erb

<%= form_tag path, method: 'get', remote: true do %>
    <%= hidden_field_tag :entity_direction, params[:entity_direction] %>
    <%= hidden_field_tag :entity_sort, params[:entity_sort] %>
    <%= hidden_field_tag :entity_event, true %>
    <div class="input-group" style="margin-bottom: 15px">
        <%= text_field_tag :entity_search, params[:entity_search], class: "form-control" %>
        <span class="input-group-btn" >
            <%= submit_tag "Search", name: nil, class: "btn btn-default" %>
        </span>
    </div>
<% end %>

The controller: hub_controller .

app/controllers/hub_controller.rb

class HubController < ApplicationController
  helper_method :entity_sort_column, :entity_sort_direction, :group_sort_column, :group_sort_direction

  before_action do
    unless current_user.nil?
      redirect_to root_url unless current_user.admin?
    else
      redirect_to root_url
    end
  end

  def main
    @entities = Entity.search(params[:entity_search]).order(entity_sort_column + ' ' + entity_sort_direction).paginate(page: params[:entities_page], per_page: 10, order: 'created_at DESC')
    @groups = Group.search(params[:group_search]).order(group_sort_column + ' ' + group_sort_direction).paginate(page: params[:groups_page], per_page: 10, order: 'created_at DESC')
    @entity = Entity.new
    @group = Group.new
  end

  def create_entity
    @entity = Entity.find_by(name: entity_params[:name])
    @result = {msg: "", r: -1}
    @entities = Entity.search(params[:entity_search]).order(entity_sort_column + ' ' + entity_sort_direction).paginate(page: params[:entities_page], per_page: 10, order: 'created_at DESC')

    respond_to do |format|
      if @entity.nil?
        @entity = Entity.new(entity_params)
        if !@entity.save
          @result[:r] = 0
          @result[:msg] = "'#{@entity.name}' failed to save."
        else
          @result[:r] = 1
          @result[:msg] = "'#{@entity.name}' was saved."
          #entities needs to be updated to get the latest addition
          @entities = Entity.search(params[:entity_search]).order(entity_sort_column + ' ' + entity_sort_direction).paginate(page: params[:entities_page], per_page: 10, order: 'created_at DESC')
        end
      else
        @result[:r] = 0
        @result[:msg] = "Name: '#{@entity.name}' is already taken."
      end
      format.js
      format.html { redirect_to hub_path }
    end
  end

  def create_group
    #not yet implemented
  end

  def create_property
    #not yet implemented
  end

  def create_entity_group_relation
    #not yet implemented
  end

  def create_group_property_relation
    #not yet implemented
  end

  private

    def entity_params
      params.require(:entity).permit(:name, :label, :img)
    end

    def group_params
      params.require(:group).permit(:name, :default_label)
    end

    def entity_sort_column
      Entity.column_names.include?(params[:entity_sort]) ? params[:entity_sort] : "name"
    end

    def entity_sort_direction
      %w[asc desc].include?(params[:entity_direction]) ? params[:entity_direction] : "asc"
    end

    def group_sort_column
      Group.column_names.include?(params[:group_sort]) ? params[:group_sort] : "name"
    end

    def group_sort_direction
      %w[asc desc].include?(params[:group_direction]) ? params[:group_direction] : "asc"
    end

end

The routes: routes.rb

Spex::Application.routes.draw do
  get "hub/main"
  post "hub/create_entity"
  post "hub/create_group"
  post "hub/create_property"
  post "hub/create_entity_group_relation"
  post "hub/create_group_property_relation"
  get "groups/new"
  get "entities/new"
  get "entities/hub"
  get "properties/new"
  resources :users
  resources :group_property_relationships
  resources :entity_group_relationships
  resources :entity_property_relationships
  resources :properties do
    member do
      get :groups, :entities
      post :serve
    end  
  end
  resources :groups do
    member do
      get :properties, :entities
      post :own
    end  
  end
  resources :entities do
    member do
      get :groups, :properties
      post :own
    end  
  end

  resources :sessions, only: [:new, :create, :destroy]
  root 'static_pages#home'
  match '/hub/create_entity', to: 'hub#main',   via: 'get'
  match '/signup',  to: 'users#new',            via: 'get'
  match '/hub',     to: 'hub#main',             via: 'get'
  match '/signin',  to: 'sessions#new',         via: 'get'
  match '/signout', to: 'sessions#destroy',     via: 'delete'
  match '/help',    to: 'static_pages#help',    via: 'get'
end

The application.js which ajaxifies the sorting and some other basic functions– Nothing here for the create call :

/app/assets/javascripts/application.js

$(function()
{

    $('#entities').on('click', "th a", function () {
        $.getScript(this.href);
        return false;
    });

    $('#groups').on('click', "th a", function () {
        $.getScript(this.href);
        return false;
    });

    $("body").on("click", '.pagination a', function(e){
      e.preventDefault();
      $.getScript(this.href);
      return false;
    });

    $("body").on("click", '.table tr.entity', function(e){
      // e.preventDefault();
      $.getScript(this.id+"/groups");
      // return false;
    });

    $("[data-toggle='tooltip']").tooltip();

});

Here's the callback js for the create_entity action:

app/views/hub/create_entity.js.erb

var entities_html = "  <%= j render 'entities/entities' %>";

$('#entities').html(entities_html)

//clear the entity form fields
$('input[id^=entity]').val('');

var alert_html = "";

<% unless @result.nil? %>
    <% if @result[:r] == 1 %>
        alert_html += "    <div class='alert alert-success'>";
        alert_html += "    <%= @result[:msg] %>";
        alert_html += "    </div>";
    <% else %>
        alert_html += "    <div class='alert alert-danger'>";
        alert_html += "        <%= @result[:msg] %>";
        alert_html += "    </div>";
    <% end %>
<% else %>
    alert_html += "    <div class='alert alert-danger'>";
    alert_html += "        There was an error saving #{@entity.name}";
    alert_html += "    </div>";
<% end %>

$('#hub-alert').html(alert_html);

So at this point, there is something funky happening with the routes or something. I can see a change in what the server prints out before a call to create_entity and after the call. Mostly notice the change in the parameters. Why would they change?

Before:

Started GET "/hub?utf8=%E2%9C%93&entity_direction=&entity_sort=&entity_event=true&entity_search=o+v" for 127.0.0.1 at 2014-06-17 14:48:13 -0600
Processing by HubController#main as JS
  Parameters: {"utf8"=>"✓", "entity_direction"=>"", "entity_sort"=>"", "entity_event"=>"true", "entity_search"=>"o v"}
  User Load (0.2ms)  SELECT "users".* FROM "users" WHERE "users"."remember_token" = 'e39061f2d24bec8416f5319586f87f27def3804c' LIMIT 1
DEPRECATION WARNING: #apply_finder_options is deprecated. (called from main at /Users/astoutj/Documents/Work/_sync/Work-sync/ror/workspace/spex/app/controllers/hub_controller.rb:13)
DEPRECATION WARNING: #apply_finder_options is deprecated. (called from main at /Users/astoutj/Documents/Work/_sync/Work-sync/ror/workspace/spex/app/controllers/hub_controller.rb:14)
  Entity Load (0.5ms)  SELECT "entities".* FROM "entities" WHERE ((name LIKE '%o%' OR label LIKE '%o%' OR created_at LIKE '%o%' OR updated_at LIKE '%o%') AND (name LIKE '%v%' OR label LIKE '%v%' OR created_at LIKE '%v%' OR updated_at LIKE '%v%')) ORDER BY name asc, created_at DESC LIMIT 10 OFFSET 0
  Rendered entities/_entity.html.erb (1.5ms)
  Rendered entities/_entities.html.erb (8.0ms)
  Group Load (0.4ms)  SELECT "groups".* FROM "groups" ORDER BY name asc, created_at DESC LIMIT 10 OFFSET 0
   (0.1ms)  SELECT COUNT(*) FROM "groups"
  Rendered groups/_group.html.erb (10.4ms)
  Rendered groups/_groups.html.erb (19.2ms)
  Rendered entities/_entity.html.erb (1.2ms)
  Rendered entities/_entities.html.erb (6.3ms)
  Rendered hub/main.js.erb (45.5ms)
Completed 200 OK in 53ms (Views: 49.3ms | ActiveRecord: 1.2ms)

After:

Started GET "/hub?utf8=%E2%9C%93&entity_direction=&entity_sort=&entity_event=&entity_search=o+v" for 127.0.0.1 at 2014-06-17 14:48:33 -0600
Processing by HubController#main as JS
  Parameters: {"utf8"=>"✓", "entity_direction"=>"", "entity_sort"=>"", "entity_event"=>"", "entity_search"=>"o v"}
  User Load (0.2ms)  SELECT "users".* FROM "users" WHERE "users"."remember_token" = 'e39061f2d24bec8416f5319586f87f27def3804c' LIMIT 1
DEPRECATION WARNING: #apply_finder_options is deprecated. (called from main at /Users/astoutj/Documents/Work/_sync/Work-sync/ror/workspace/spex/app/controllers/hub_controller.rb:13)
DEPRECATION WARNING: #apply_finder_options is deprecated. (called from main at /Users/astoutj/Documents/Work/_sync/Work-sync/ror/workspace/spex/app/controllers/hub_controller.rb:14)
  Group Load (0.4ms)  SELECT "groups".* FROM "groups" ORDER BY name asc, created_at DESC LIMIT 10 OFFSET 0
   (0.1ms)  SELECT COUNT(*) FROM "groups"
  Rendered groups/_group.html.erb (6.0ms)
  Rendered groups/_groups.html.erb (14.8ms)
  Entity Load (0.7ms)  SELECT "entities".* FROM "entities" WHERE ((name LIKE '%o%' OR label LIKE '%o%' OR created_at LIKE '%o%' OR updated_at LIKE '%o%') AND (name LIKE '%v%' OR label LIKE '%v%' OR created_at LIKE '%v%' OR updated_at LIKE '%v%')) ORDER BY name asc, created_at DESC LIMIT 10 OFFSET 0
  Rendered entities/_entity.html.erb (2.1ms)
  Rendered entities/_entities.html.erb (12.8ms)
  Rendered hub/main.js.erb (35.7ms)
Completed 200 OK in 44ms (Views: 39.4ms | ActiveRecord: 1.5ms)

The question: How do I get the create action to not interfere subsequent search actions?

Let me know if anything else is needed.

Okay here's what I had to do.

I also had a main.js.erb which would be the callback for the main action. In there, I was unintentionally blocking changes to my partials by checking the params. The param I was checking was the entity_event and the group_event which would be true for any action for the respective model, (ie sort, search, page, new). So I added a param to the search fields params[:search] and when the search was for an entity , params[:search] was set to "entity" and when the search was for a group , it was set to "group" . Here are the updated files.

Search Entity

app/views/hub/_search_entity.html.erb

<%= form_tag path, method: 'get', url: { controller: "hub", action: "main" }, remote: true do %>
    <%= hidden_field_tag :entity_direction, params[:entity_direction] %>
    <%= hidden_field_tag :entity_sort, params[:entity_sort] %>
    <%= hidden_field_tag :entity_event, true %>
    <%= hidden_field_tag :search, "entity" %>
    <div class="input-group" style="margin-bottom: 15px">
        <%= text_field_tag :entity_search, params[:entity_search], class: "form-control", id: "entity_search_field" %>
        <span class="input-group-btn" >
            <%= submit_tag "Search", name: nil, class: "btn btn-default", id: "entity_search" %>
        </span>
    </div>
<% end %>

Search Group

app/views/hub/_search_group.html.erb

<%= form_tag path, method: 'get', url: { controller: "hub", action: "main" }, remote: true do %>
    <%= hidden_field_tag :group_direction, params[:group_direction] %>
    <%= hidden_field_tag :group_sort, params[:group_sort] %>
    <%= hidden_field_tag :group_event, true %>
    <%= hidden_field_tag :search, "group" %>
    <div class="input-group" style="margin-bottom: 15px">
        <%= text_field_tag :group_search, params[:group_search], class: "form-control", id: "group_search_field" %>
        <span class="input-group-btn" >
            <%= submit_tag "Search", name: nil, class: "btn btn-default", id: "group_search" %>
        </span>
    </div>
<% end %>

Main action callback

app/views/hub/main.js.erb

<% unless params[:search] == "entity" %>
    $('#groups').html('<%= j render "groups/groups"  %>');
    console.log("entity event empty");
<% end %>

<% unless params[:search] == "group" %>
    $('#entities').html('<%= j render "entities/entities"  %>');
    console.log("group event empty");
<% end %>

Now it is fully responsive before and after a create action.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM