Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to render partial view of one controller to another controller in ruby on rails

Hi I am trying to implement subscribe button in vendor site so need to render a subscribe form with hidden attribute for vendor_site_id. The form work fine within the scope of subscription controller but I need to render it in vendor_site/show page as partial _new.html.erb. I couldn't figure out the way to fix this problem at my own can any body suggest me with this problem.

My partial view subscriptions/_new.html.erb

<%= form_for(@subscription) do |f| %>
  <div class="field">
    <%= f.hidden_field @subscription.vendor_site_id, :value=>"1" %>
  </div>
  <%= button_tag(type: 'submit', class: "btn btn-primary") do %>
    <i class="fa fa-user fa-fw"></i> Subscribe
  <% end %>
<% end %>

Now when I render this in vendor_sites/index.html.erb

<h3>This is vendor site with id 1 just for the testing of subscription method</h3>
<%= render :partial => "subscriptions/new" %>

it throws a error which says:

First argument in form cannot contain nil or be empty

which I understood so far is that I need to tell this vendor_site controller about the @subscription from other scope. But I cannot figure out how? My subscriptions class looks like this:

class SubscriptionsController < ApplicationController
  def new
    @subscription=Subscription.new
  end
end
like image 314
surajnew55 Avatar asked Jul 08 '14 03:07

surajnew55


3 Answers

A couple of changes you'll need to make here. One thing I'll recommend before getting into the details is the Rails Guides for Layouts and Rendering (and the Rails Guides in general. The Layouts and Rendering Guide in particular will help you greatly with the VC part of Rails.

Now, on to the details. First, a partial is not a controller action. There is no controller method that corresponds to it. You can go about what you're trying to do in one of two ways, and both ways involve removing the new method from your subscriptions controller first.

First way: Initialize @subscription in your vendor site controller

The simplest way (in term of changes relative to what you already have) is to initialize @subscription in your vendor site controller's index method. Then, the rendered _new partial will know exactly what subscription is.

class VendorSiteController
  def index
    @subscription = Subscription.new # add this anywhere in the index method.
  end
end

Second way: Pass @subscription as a local to the _new partial

This way will involve changes to your controller, your view, and your partial. In your controller, you'll still need to make the change I gave above.

class VendorSiteController
  def index
    @subscription = Subscription.new # add this anywhere in the index method.
  end
end

Next, you'll need to make @subscription a local variable in your form, by simply removing the @.

<%= form_for(@subscription) do |f| %>
  <div class="field">
    <%= f.hidden_field @subscription.vendor_site_id, :value=>"1" %>
  </div>
  <!-- no changes to the button tag -->
<% end %>

Finally, you'll need to pass @subscription as subscription to your partial in your index view.

<%= render :partial => "subscriptions/new", :locals => { :subscription => @subscription } %>

While this way involves a lot more changes, it is much more flexible than the first way. For instance, say you want to render more than one new subscription in a page:

<% Subscription.where(condition: true).find_each do |s| %>
  <%= render :partial => "subscriptions/new", :locals => { :subscription => s } %>
<% end %>

As another example, maybe you want to use the @subscription variable name for something other than a new subscription, and you're going to use @new_sub for a new subscription instead.

<%= render :partial => "subscriptions/new", :locals => { :subscription => @new_sub } %>

Hopefully my answer helps you out! Feel free to comment if you need further clarification, and be sure to check out those Rails Guides!

like image 108
Joe Kennedy Avatar answered Nov 12 '22 07:11

Joe Kennedy


You can do it by in following way, you need to initialize a @subscription object in index action of vendor_sites controller.

class VendorSiteController
   def index
     #here your showing list of vendors logic

     @subscription = Subscription.new #initialize a Subscription object
   end
end

Then you can pass @subscription object to your partial in your index view by following way(For Rails 3)

<%= render :partial => "subscriptions/new", :subscription => @subscription } %>
like image 2
Deepti Kakade Avatar answered Nov 12 '22 05:11

Deepti Kakade


Haven't checked Joe Kennedy's answer, so apologies if this touches on what he has written:


Partials

The whole point of a partial is that's meant to be able to be used in any part of your application. They are not tied to specific controllers, which is especially true as they primarily rely on local variables to help populate their data

This means if you want to render a partial outside the "scope" of a controller, you just need to be able to send the correct data to the partial, through its local variables:

#app/views/controllers/your_view.html.erb
<%= render partial: "controller/partial", locals: {variable: @value} %>

A very simplistic example, but hopefully should demonstrate how you can call partials anywhere you want.


MVC

You must also consider the MVC programming pattern, as described here:

enter image description here

The importance of this is that Rails is built using the MVC pattern - in that the controller works to manipulate the data to show in your views, the model is meant to collect / compile the data, and the view is just meant to show it

This means the partial should not be "tied" to a particular controller - it just needs to create the data available in the views, and as such, you should be more focused on how you can show this data, rather than having to call your partial with a particular controller


Data

To summarise so far - you need can put a partial anywhere, and through the use of the MVC programming pattern, populate it using data from your controller.

The important thing now is to populate the @subscription value. This could be done in the view (not recommended), or it could be done using a method in your various controllers:

#app/views/subscriptions/_new.html.erb
<%= form_for(subscription) do |f| %>
  <div class="field">
    <%= f.hidden_field @subscription.vendor_site_id, :value=>"1" %>
  </div>
  <%= button_tag(type: 'submit', class: "btn btn-primary") do %>
    <i class="fa fa-user fa-fw"></i> Subscribe
  <% end %>
<% end %>

#app/views/controller/your_action.html.erb
<%= render partial: "subscriptions/new", partials: {subscription: Subscription.new} %>
like image 2
Richard Peck Avatar answered Nov 12 '22 06:11

Richard Peck