Help with RoR partial views

Hi, and thank you for reading this.

First of all let me explain that i am a developer learning Ruby and
RubyOnRails for future projects, so i am basically a noob that is in
need of some help. :slight_smile:

I created a little RoR project (im using netbeans 6.1 as IDE, but that
should make no difference), that is a very simple product management
web app.
I created it using scaffolding, and CRUD works just fine when called
from the views that the scaffold created. I have some other
controller, the “main” controller with a “welcome” view. This
controller is not tied to any model since it only exists has the
welcome page for the site.

My problem is that i wanted to call the “new” view from “welcome” view
using render :partial=>“products/new”, but when ever i call it from
there it gives me an error.

I would like that this partial view would work just like any other
view.
How can i do this?

sample code is bellow

products.rb

class Product < ActiveRecord::Base validates_uniqueness_of :name end

products_controller.rb

[code=]class ProductsController < ApplicationController

GET /products

GET /products.xml

def index
@products = Product.find(:all)

respond_to do |format|
  format.html # index.html.erb
  format.xml  { render :xml => @products }
end

end

GET /products/1

GET /products/1.xml

def show
@product = Product.find(params[:id])

respond_to do |format|
  format.html # show.html.erb
  format.xml  { render :xml => @product }
end

end

GET /products/new

GET /products/new.xml

def new
@product = Product.new

respond_to do |format|
  format.html # new.html.erb
  format.xml  { render :xml => @product }
end

end

GET /products/1/edit

def edit
@product = Product.find(params[:id])
end

POST /products

POST /products.xml

def create
@product = Product.new(params[:product])

respond_to do |format|
  if @product.save
    flash[:notice] = 'Product was successfully created.'
    format.html { redirect_to(@product) }
    format.xml  { render :xml => @product, :status

=> :created, :location => @product }
else
format.html { render :action => “new” }
format.xml { render :xml => @product.errors, :status
=> :unprocessable_entity }
end
end
end

PUT /products/1

PUT /products/1.xml

def update
@product = Product.find(params[:id])

respond_to do |format|
  if @product.update_attributes(params[:product])
    flash[:notice] = 'Product was successfully updated.'
    format.html { redirect_to(@product) }
    format.xml  { head :ok }
  else
    format.html { render :action => "edit" }
    format.xml  { render :xml => @product.errors, :status

=> :unprocessable_entity }
end
end
end

DELETE /products/1

DELETE /products/1.xml

def destroy
@product = Product.find(params[:id])
@product.destroy

respond_to do |format|
  format.html { redirect_to(products_url) }
  format.xml  { head :ok }
end

end
end
[/code]

new.html.erb

[code=]

New product

<% form_for(@product) do |f| %>
<%= f.error_messages %>

<%= f.label :name %>
<%= f.text_field :name %>

<%= f.label :price %>
<%= f.text_field :price %>

<%= f.label :type %>
<%= f.text_field :type %>

<%= f.submit "Create" %>

<% end %>

<%= link_to ‘Back’, products_path %>
[/code]

show.html.erb

[code=]


Name:
<%=h @product.name %>

Price: <%=h @product.price %>

Type: <%=h @product.type %>

<%= link_to ‘Edit’, edit_product_path(@product) %> |
<%= link_to ‘Back’, products_path %>
[/code]

index.html.erb

[code=]

Listing products

<% for product in @products %>

<% end %>
Name Price Type
<%=h product.name %> <%=h product.price %> <%=h product.type %> <%= link_to 'Show', product %> <%= link_to 'Edit', edit_product_path(product) %> <%= link_to 'Destroy', product, :confirm => 'Are you sure?', :method => :delete %>

<%= link_to ‘New product’, new_product_path %>
[/code]

edit.html.erb

[code=]

Editing product

<% form_for(@product) do |f| %>
<%= f.error_messages %>

<%= f.label :name %>
<%= f.text_field :name %>

<%= f.label :price %>
<%= f.text_field :price %>

<%= f.label :type %>
<%= f.text_field :type %>

<%= f.submit "Update" %>

<% end %>

<%= link_to ‘Show’, @product %> |
<%= link_to ‘Back’, products_path %>
[/code]

the partial view _new.html.erb (its a copy of new.html.erb)

[code=]

New product

<% form_for(@product) do |f| %>
<%= f.error_messages %>

<%= f.label :name %>
<%= f.text_field :name %>

<%= f.label :price %>
<%= f.text_field :price %>

<%= f.label :type %>
<%= f.text_field :type %>

<%= f.submit "Create" %>

<% end %>

<%= link_to ‘Back’, products_path %>
[/code]

main_controller.rb

class MainController < ApplicationController def welcome end end
welcome.html.erb

[code=]

Main#welcome

Find me in app/views/main/welcome.html.erb

<%= render :partial=>“products/new” %>
[/code]

So my question is, shouldn’t the partial view that is being called
from the welcome view, work just like it was being called from
products/new view?

I am aware that this is probably not the best of examples, but imagine
that i have a welcome page, and on that same view i would like to
allow users to search and create new products. And show all the error
messages on the welcome page.
How is it done?

Thank you once more for your time!
CrazyMenConnected

Heh–“it gives me an error” covers a lot of ground. :wink:

I’m guessing you get a complaint about there not being a @product
variable when you hit that form_for call in the partial? If that’s it,
changing your main_controller.rb’s welcome action like so should be a
fix:

def welcome
@product = Product.new
end

If my guess is wrong, write back w/the error message & stack trace.

Cheers,

-Roy

the partial view _new.html.erb (its a copy of new.html.erb)
Although this isn’t technically wrong, it also isn’t DRY. What you would
actually want to do is decide what part of the new.html.erb page you
want to share in multiple places. Then, if it were me. I would create a
separate folder in the project to contain partials that will be shared
between different views. Maybe something like app/views/shared. In this
case you probably only want to share the actual form. This way any other
details of the page can be formatted independently (such as headings,
layouts, etc.).

And as mentioned previously you’ll still need to make sure you load all
the resources the page needs from the appropriate controller action.

So new.html.erb would end up looking something like this:

#--------------------------
new.html.erb

[code=]

New product

<%= render :partial => “shared/new_product_form” %>
#--------------------------
Note: check that path, I wrote that off the top of my head. It may not
be exactly correct, but should be a relative path to the location of the
partial, wherever that might be.

And of course your welcome page would render that same partial.

Take note that the first line of the partial: <% form_for(@product) do
|f| %> accesses @product so the controller action that got you to the
welcome page is responsible to loading that resource.

Roy P. wrote:

Heh–“it gives me an error” covers a lot of ground. :wink:

I’m guessing you get a complaint about there not being a @product
variable when you hit that form_for call in the partial? If that’s it,
changing your main_controller.rb’s welcome action like so should be a
fix:

def welcome
@product = Product.new
end

If my guess is wrong, write back w/the error message & stack trace.

Cheers,

-Roy

Hello, and thank you for your reply.

You are right when you say the this code is not DRY. I am aware if
lacks a bit of DRYness, but this is just a simple and fast example to
demonstrate my question.

Has said above i changed the main_controller and added the instance
variable on the welcome proc with @product = Product.new.

I can render the partial fine now, but one question still remains.
How do i redirect that partial output to the welcome view?

Because when i hit the “Create” button on the for the welcome view is
not rendered any more, it changes to the products views.
I would like to stay in the welcome view and that and output from the
product partial would still be rendered on the welcome view.

How is this done?

Thanks for your help
CrazyMenConnected

I and thank you for your replies.

Heh–“it gives me an error” covers a lot of ground. :wink:

Yes your right, and i am sorry for that, my message was a little
ambiguous.

I have changed the main_controller.rb and i’ve added the instance
variable has you said. It now looks like this

main_controller.rb

class MainController < ApplicationController
  def welcome
    @product = Product.new
  end
end

There is no error now, the partial is rendered. But now i have another
question.
When i press the “Create” button on the form, all of the output is
redirected back to the products view.
I want the error messages to the displayed on the welcome view if a
user tried to enter the same product twice.
Is it possible to do that? How?

Your partial uses a simple form_for(@product) call. By default that
will do a POST to the products/create action, which if it’s successful,
does a redirect to products/show/<> (see the
create method in your products_controller.rb file).

My advice is to just play around w/rails for a bit & get used to its
conventions before you try to bend it to your will. :wink:

HTH,

-Roy