2

I'm in need of some help regarding setting up the routing (I think that is the problem) for a view that is set up with bootstrap tabbed navigation. What I want to do is set up a form in each tab. Sounds easy enough.

However I can't figure out how the routing works when you want to submit two different actions (one to save to the db, and the other to get info from the db), each from their own tab. I can get it all to work perfectly if I give the "get_users" controller action a view, but when I try to bring it back together on the same page, just in different tabs, it goes a little awry and I'm not sure how to route correctly.

The two controller actions I have are:

class Users::RegistrationsController < Devise::RegistrationsController

    def user_accounts
        @user = User.new
    end

    def get_users
        @users = User.search(params[:search]).paginate
                (:per_page => 20, :page => params[:page] )
    end
end

EDIT ------------------------------------

Include routes.rb - excluding the extra devise related routes. Can get the create_internal_user action working not the other. I understand that the routes are incorrect as rails could never understand them. The first is correct, the second is simply what I would imagine it to look like if it were possible

# Authentication
as :user do

#add new internal users
get '/useraccounts' => 'users/registrations#user_accounts', as: 'new_internal_user'
post '/useraccounts' => 'users/registrations#create_internal_user', as: 'internal_user'

**# searching users - this is where I am uncertain**
get '/useraccounts' => 'users/registrations#get_users', as: 'get_internal_user'
post '/useraccounts' => 'users/registrations#get_users', as: 'getting_internal_user'

# other devise routes here

end

The view that renders them looks like this and renders two partials:

    <div class="container">
     <h1>User Accounts</h1>
     <ul class="nav nav-tabs">
      <li class="active"><a href="#adduser" data-toggle="tab">Add User</a></li>
      <li><a href="#getusers" data-toggle="tab">Edit User</a></li>
     </ul>
     <!-- Tab panes -->
     <div class="tab-content">
      <div class="tab-pane fade in active" id="adduser">
       <%= render 'users/registrations/create_internal' %>
     </div>
      <div class="tab-pane fade in" id="getusers">
       <%= render 'users/registrations/get_users' %>
     </div>
    </div>
   </div>

The two partials

_create_internal.html.erb

    <%= form_for(resource, as: resource_name, 
                 url: new_internal_user_path(resource_name)) do |f| %>
      <div><%= f.label :email %><br />
    <% for role in User::ROLES %>
        <%= check_box_tag "user[roles][#{role}]", role, 
                        @user.roles.include?(role), 
                        {:name => "user[roles][]"}%>

        <%= label_tag "user_roles_#{role}", role.humanize %><br />
    <% end %>
    <%= hidden_field_tag "user[roles][]", "" %>
    <div class="col-md-10 center"><%= f.submit "Create User", 
                 class: "btn btn-primary"%></div>
<% end %>

_get_users.html.erb

  <%= form_tag({:controller => "users/registrations", :action => "get_users"},
                method: :get) do %>    
<p>  
  <%= text_field_tag :search, params[:search] %>  
  <%= submit_tag "Search", :name => nil %>  
</p>
  <% end %>
  <div class="center" id="users">
  <%  if defined?(@users) %>
  <%= render 'users/registrations/searchresults' %></div>
  <%=  will_paginate @users %> 
<% end %> 

I've spent many many hours trying to figure this out and just haven't been able to. Very new to Rails so this may be an easy question for someone. I've had a look at using AJAX, and resourceful routing, but as I'm still new I'm finding it a bit tough to wrap my head around it all at the one time. This is my first post on SO so apologies if I'm missing anything (please let me know).

twiggy
  • 21
  • 6
  • If I understand you correctly, you want a rout that maps to `def get_users end` and another that maps to `def create_internal_user end`. is that correct? – Wally Ali May 06 '14 at 06:12
  • post your routes.rb file – Wally Ali May 06 '14 at 06:16
  • @WaliAli Yes, that's correct. However my confusion is that I want to be able to go to /useraccounts, click on one tab and the submit to call create_internal_user and then click on the other tab and the submit to call get_users. I will update now to include routes.rb – twiggy May 06 '14 at 06:21
  • `get '/useraccounts' => 'users/registrations#get_users', as: 'get_internal_user'` to find out which action this route is pointing to, do `localhost:3000/rails/info` then on the right most column look under `Controller#Action` – Wally Ali May 06 '14 at 06:33
  • what's your controller's name? the format is `controller_name#action_name` – Wally Ali May 06 '14 at 06:37
  • @WaliAli when raking the routes it is exactly as you suggest it should be as 'users/registrations#get_users', however as I commented on your possible answer, because get_users is a partial inside the 'useraccounts' view it gets a missing template – twiggy May 06 '14 at 06:42
  • 1. What's the error? 2. Can you post the form views? 3. What are you trying to achieve exactly? – Richard Peck May 06 '14 at 06:49
  • @RichPeck 1. the error is a no method error because when I try to submit the form associated with the get_users action it goes and does the create_internal_user action. 2. Posting form views now 3. I have two completely different forms, each with their own tab inside one view. One to create users, one to find users. – twiggy May 06 '14 at 06:54
  • Okay thanks for the info. I'll look at the forms you post :) – Richard Peck May 06 '14 at 06:55
  • @RichPeck just posted the forms. You can see the main form is useraccounts.html.erb with the two partials being rendered inside it of _create_internal and _get_users. Thanks for having a look. – twiggy May 06 '14 at 07:06
  • i noticed both of your `get` and `post` are pointing to the same route `'users/registrations#get_users'`. did you catch that? – Wally Ali May 06 '14 at 07:13

3 Answers3

0

Let's say your controller is UsersController and assuming your routes are not nested, this is how you create named routes:

get '/useraccounts', to: 'users#get_users' ## this route maps to the get_users action 

post '/useraccounts' to: 'users#create_internal_user'  # this route maps to the create_internal_user action 

EDIT: the format is controller_name#action_name. So make sure to replace users with your controllers name in plural

UPDATE:

if your controller's name is RegistrationsController

try:

 get '/useraccounts', to: 'registrations#get_users'

post '/useraccounts' to: 'registrations#create_internal_user'
Wally Ali
  • 2,500
  • 1
  • 13
  • 20
  • if my understanding of the rails framework is correct this will only work if there is a get_users view. Get_users is actually a partial rendered in /useraccounts. Therefore I get a template missing error with this code. – twiggy May 06 '14 at 06:35
  • I couldn't see your controller's name from your question. so is your controller's name `UserAccountsController` ? – Wally Ali May 06 '14 at 06:42
  • Controller name is 'RegistrationsController'. I overrode the standard Devise Registrations controller to create a new create action named 'create_internal_user' in addition to the standard 'create' offered by the devise controller. – twiggy May 06 '14 at 06:45
  • Thanks for your suggestion Wali however in the question post I had tried this in my routes. The issue comes about because of the tabbed navigation and the multiple forms that come as partials. – twiggy May 06 '14 at 07:08
0

Looking at it, I think your error will likely be from your _get_users.html.erb partial.

Firstly, you're using form_for - why? Your use of form_for basically means you have to use the data inside @user to make it work, which I think will be causing the problem

I would use a form_tag as this does not persist your data:

<%= form_tag({:controller => "users/registrations", :action => "get_users"}, method: :get) do %>   
  <%= text_field_tag :search, params[:search] %>  
  <%= submit_tag "Search", :name => nil %>  
<% end %>

<%  if defined?(@users) #-> this might need to be passed as a local var %>
  <%= render 'users/registrations/searchresults' %></div>
  <%=  will_paginate @users %> 
<% end %> 

Routes

Your other problem is to do with your routes:

get '/useraccounts' => 'users/registrations#user_accounts', as: 'new_internal_user'
post '/useraccounts' => 'users/registrations#create_internal_user', as: 'internal_user'

**# searching users - this is where I am uncertain**
get '/useraccounts' => 'users/registrations#get_users', as: 'get_internal_user'
post '/useraccounts' => 'users/registrations#get_users', as: 'getting_internal_user'

Tell me how you expect Rails to know the difference between get '/useraccounts and get /useraccounts? The fact is it can't

You'll need to split up the routes so they don't all use the same path. I would do this:

get '/useraccounts' => 'users/registrations#user_accounts', as: 'new_internal_user'
post '/useraccounts' => 'users/registrations#create_internal_user', as: 'internal_user'

**# searching users - this is where I am uncertain**
get '/search' => 'users/registrations#get_users', as: 'get_internal_user'
Richard Peck
  • 76,116
  • 9
  • 93
  • 147
  • 1
    Okay I tried your suggestion - needed to add '(' around the curly braces to get it to pass syntax errors. However it still tries to do the 'create_internal_user' action when I submit the form in '_get_users'. Thanks for the little bit of learning on form_tag vs. form_for – twiggy May 06 '14 at 07:27
  • Thanks for reply! Okay you're getting a problem with it routing to `create_internal_user`. This is because the form will be sending a `post` request -- we need to add `method: : get` for you (see updated answer) – Richard Peck May 06 '14 at 08:18
  • So we have successfully removed any runtime errors or syntax errors, however it is still not hitting the correct action in the controller unfortunately. What I'm seeing in the log outputs is 'Started GET "/useraccounts?utf8=%E2%9C%93&search=test"' ... 'Processing by Users::RegistrationsController#user_accounts as HTML'. So as you can see it is still hitting the other controller actions from user_accounts. – twiggy May 06 '14 at 08:52
  • Hi Rich, I totally understand what you are saying - and I maybe wasn't clear in my question, as I have previously done what you are suggesting. That is by separating it all out and having a totally new page for the searching function. However my goal is to have both of the the forms (creating and searching) accessible from the original /useraccounts view with them being separated by a tabbed navigation menu. Having them in a tabbed navigation is the issue. – twiggy May 06 '14 at 09:10
  • Okay, I understand I think. But you have to remember that a route & a view are totally different. A route just sends a user to a specific controller / action; a view can have anything you want on there. You are asking about using two forms on the same view - that's great! However, those forms have no bearing on your routes – Richard Peck May 06 '14 at 09:20
  • Your `search` form can request data from any route. But it can't be the same route as another resource - it has to be its own route, as this is how Rails requests the data from the `controller` – Richard Peck May 06 '14 at 09:21
  • Yep, that's exactly what I want to do - I guess the fact they are separated by tabs doesn't really make a jot of difference. Understand that the route sends a user to the controller/action. In your last comment you raise that it can't be the same route as another resource - cool - this is what I am uncertain about. I understand it can't be the same, but what could I name the route for it to still point back to /useraccounts when the request has been completed. Does that make sense? (btw, really appreciate you taking the time to help) – twiggy May 06 '14 at 09:57
  • Okay, if you want to point back to the same resource, you can just use `redirect_to` in your controller action -- `def create @users = User.all redirect_to your_path end`. No problem - I want to get it fixed for you!! – Richard Peck May 06 '14 at 12:33
  • Hi Rich, I finally got it working, and frustratingly it ended up being quite simple. Really appreciate your help it led me to keep trying things until it eventually worked. See my answer as to what I did. Thanks again. Being new to SO is there a way I can thank you? – twiggy May 07 '14 at 02:29
  • lol it's normally the simplest stuff which works best!! If you appreciate the responses, you could upvote my answer ("up" arrow on left), and accept if it really helped you to get to your desired result – Richard Peck May 07 '14 at 07:14
  • Unfortunately I don't yet have enough reputation to do that - when I do I'll be sure to come back and upvote it when I can. Once again, thanks very much. – twiggy May 07 '14 at 08:01
0

After much frustration I have solved my own problem. With help from Rich Peck I realised that I was essentially creating a view with two forms on it, and that the tabbed navigation didn't really mean anything to the problem.

So I really only needed the two routes:

# #add new internal users
get '/useraccounts' => 'users/registrations#user_accounts', as: 'user_accounts'
post '/useraccounts' => 'users/registrations#create_internal_user', as: 
                                                 'create_internal_user'

And then in the controller just changed the user_accounts action to look like this:

def user_accounts
    @user = User.new
    if params[:search_button]
        @users = User.search(params[:search]).paginate(:per_page => 15, :page => params[:page] )
    end
end

Came about this discovery thanks to this question/answer here: form_for with multiple controller actions for submit

In the end the problem wasn't what I thought it was and ended up being simple. Was certainly an interesting learning journey. Thanks for your help again Rich Peck.

Community
  • 1
  • 1
twiggy
  • 21
  • 6