How to create multiple 'new' pages within a controller that behave the same with different text? - ruby

Ok - I have a simple application built that allows you to store action items based on business categories: Business Admin, Business Development, Business Financial, Etc.
Each one of the categories, will have the same exact form:
<div align="center"><h1>What are your important Business Admin Action Items?</h1>
<%= form_for #category do |f| %>
<p>
<p>Store Answer Below:</p>
<%= f.text_field :name, :size => 40, :style => 'height: 40px' %>
</p>
<p>
<%=f.submit 'Save action item' %>
</p>
<% end %> </div>
However, not all of these categories will have the same H1 tag (or other text/attributes within the view). So, how does that work?
Do I create new pages, that are: business_admin.html.erb, business_development.html.erb etc - and if so, I can't see how I'd use the same new action for all of them, especially since the answers to the user input are going to be stored on separate pages (the categories' respective pages: business_admin_storage.html.erb, etc.
I understand that naming the urls in that fashion may not be the best way, nor a conventional way. That's part of my question as well. How should I approach this situation. Will be trying a whole host of stuff in the meantime :) If I need to be more clear, please tell me.
New to rails. Loving every second.
Categories controller
class CategoriesController < ApplicationController
def index
#categories = Category.all
end
def new
#category = Category.new
end
def create
#category = Category.new(category_params)
#category.save
end
def show
end
def destroy
end
private
def category_params
params.require(:category).permit(:answer)
end
end
Index View
<h1>Select A Business Category To Begin Identifying Action Items</h1>
<ol><li><%= link_to 'Business Admin', new_category_path %></li><br><br>
<li><%= link_to 'Business Development/Marketing', new_category_path %></li><br><br>
<li><%= link_to 'Financial', new_category_path %></li>
</ol>
<%= link_to 'Store random action items', new_facilitate_path %>
Routes.rb
Rails.application.routes.draw do
resources :facilitates
resources :categories
# The priority is based upon order of creation: first created -> highest priority.
# See how all your routes lay out with "rake routes".
# You can have the root of your site routed with "root"
root 'categories#index'
get 'show' => 'facilitates#show'
get 'index' => 'categories#index'
get 'business_admin' => 'categories#new'
SHOW
Stored !!
<h1>Listing stored action items for this category... </h1>

I can't see your model for Categories but it seems like you're varying the page served based on the name of the category?
Wouldn't something like this cover you?
<div align="center">
<h1>What are your important <%= #category.name %> Action Items?</h1>
</div>
You would need to pass the relevant #category to the relevant view via the controller, here is the Rails 4 way:
before_action :set_category, only: [:show, :edit, :update, :destroy]
...
def show
end
...
private
def set_category
#category = Category.find(params[:id])
end
And your Categories model would need to have a name property defined, which if you don't have it already could be accomplished by a migration.

Related

How to create nested model from partial Rails 6

Newbie Here!
I'm unsuccessfully trying to submit a form of a nested model.
Aim:
Verify a translation by submitting a boolean to a reviews model to associate whether a translation is verified or not, with translation entry and user model associated references.
# routes.rb
resources :entries do
resources :reviews
end
# entry.rb
belongs_to :user
has_one :review
accepts_nested_attributes_for :review
# user.rb
has_many :entries
has_many :reviews
# review.rb
belongs_to :user
belongs_to :entry
From entry index, pass the entry instance to partial, works perfect
# /entries/index.html.erb
<% #entries.each do |entry| %>
...
<%= render 'reviews/new', entry: entry %>
...
<% end %>
Unsuccessfully setting up new/create form.
What happens is that the entry instance is well received, but I am failing to create a new model instance for review belonging to entry.
entry.review raises an nil error for review, while entry is fine when testing through browser console
First argument in form cannot contain nil or be empty
# reviews/_new.html.erb
<span>
<%= form_for entry.review do |f| %>
<div class="form-check form-switch">
<%= f.check_box :verified, class: "form-check-input" %>
</div>
<%= f.submit class: "btn btn-primary"%>
<% end %>
</span>
Another attempt was also to use just #review from the controller but that doesn't obey nested routes.
My controller looks like this
# reviews_controller.rb
def create
#entry = Entry.find(params[:entry_id])
#review = #entry.review.build(review_params)
#review.user_id = current_user.id
#review.save
end
private
def review_params
params.require(:review).permit(:verified, user: current_user, entry: #entry)
end
Am I suppose to implement my actions in the entries_controller?
I have also found the tutorial here useful but replication was unsuccessful.
Another StackOverflow reference here
I still get the error entry.review.build that review is nil.
First argument in form cannot contain nil or be empty
When building an associated record over a has_one relation, instead of
#review = entry.review.build(review_params)
you need to use the following:
#review = entry.build_review(review_params)
See the documentation for more details.
Am I suppose to implement my actions in the entries_controller?
It depends on what you're after. If you have a dedicated form for adding a new review and it is not embedded in another form for creating or updating an entry then implementing a create action in ReviewsController is the straightforward solution – in this case you should also not need accepts_nested_attributes_for in Entry.
If, however, you want to be able to create or update an entry as well as its review using the same form, then you should nest the review form in the form of the entry, keep accepts_nested_attributes_for, and use actions in EntriesController. The documentation should get you started there.

Not showing created blog entries

I'm fairly new to Rails and learning to create a blog using this tutorial. On step 10, once I define create and show, after creating a new post in browser I don't see any entries on show with id page. All I see is heading and and blank title and post header.
Following is my controller -
class PostController < ApplicationController
def index
end
def new
end
def create
#post = Post.new(params[:posts])
#post.save
redirect_to #post
end
def show
#post = Post.find(params[:id])
end
end
Show view ---
<h1>Show a post</h1>
<p>
<strong>Title:</strong>
<%= #post.title %>
</p>
<p>
<strong>Text:</strong>
<%= #post.text %>
</p>
Route ---
RailsBlog::Application.routes.draw do
resources :post
root :to => "post#index"
end
Form ---
<%= form_for :post, url: {action: 'create'} do |f| %>
<p>
<%= f.label :title %><br>
<%= f.text_field :title %>
</p>
<p>
<%= f.label :text %><br>
<%= f.text_area :text %>
</p>
<p>
<%= f.submit 'Submit' %>
</p>
<% end %>
May be this is just a spelling mistake, but since I've recently started learning Rails, I'm unable to resolve this.
Update: I can go to particular id using
http://localhost:3000/post/1
but am only seeing blank page with view headers
The problem is here:
#post = Post.new(params[:posts])
It should be params[:post] - singular, not plural.
Also note that the best practice with form_for is to pass an object instead of symbol:
form_for #post do |f|
Then:
You don't need to specify url
You can reuse the same form for an edit action or create action (if object creation failed due to failing validation)
This however requires to initialize new object in your new action:
def new
#post = Post.new
end
UPDATE:
Also your routes are incorrect. When defining plural resources, you need to use plural form (it's more the convention than requirement, but since you're learning stick with it). So change your routes to:
resources :posts
And rename your controller to PostsController (remember to rename file name as well). restart the server and all should work.
ANOTHER UPDATE:
You also need to rename folder app/views/post to app/view/posts.
AND YET ANOTHER UPDATE:
In rails 4, you are not allowed to mass assign any params which has not been whitelisted using strong parameters. You need to tell rails which fields you allow to be assigned first - this is a security thing. You need to make some changes to your controller:
class PostsController < ApplicationController
...
def create
#post = Post.new(post_params)
...
end
...
private
def post_params
params.require(:post).permit(:title, :text)
end
end
This is the way to tell your controller that you are expecting those attributes from your form and they can be safely assigned.
I had just similar problem on the same tutorial.
The code spelling was correct and clearly accorded to examples in tutorial and BroiSatse's answer above.
The mistake was in order of private method definition.
How it was:
...
private
def post_params
params.require(:post).permit(:title, :text)
end
def show
#post = Post.find(params[:id])
end
...
The working order:
def show
#post = Post.find(params[:id])
end
private
...
Anyway, this topic was rather helpful. Thak you for your answers!

2 instance variables of the same name in different controllers

I finished Michael Hartl's Ruby on Rails Tutorial. Now I'm working on the suggested exercises. The application he builds is basically a Twitter clone where one can post Microposts and they appear in your feed http://ruby.railstutorial.org/chapters/user-microposts#fig-micropost_created
The main page is in home.html.erb from the StaticPagesController and features a Micropost textbox where one can post Microposts. The code for the textbox looks like so:
<%= form_for(#micropost) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<div class="field">
<%= f.text_area :content, placeholder: "Compose new micropost..." %>
</div>
<%= f.submit "Post", class: "btn btn-large btn-primary" %>
<% end %>
The #micropost variable is initialized in the StaticPagesController like so:
class StaticPagesController < ApplicationController
def home
if signed_in?
#micropost = current_user.microposts.build
end
end
Now inside the MicropostsController there's a create action like so:
def create
#micropost = current_user.microposts.build(params[:micropost])
if #micropost.save
flash[:success] = "Micropost created!"
redirect_to root_url
else
#feed_items = []
render 'static_pages/home'
end
end
My question is what is the purpose of the first #micropost variable as opposed to the second?
thanks,
mike
The first #micropost becomes available to the view rendered by the first controller method; the second #micropost becomes available to the view rendered by the second controller method. And it just so happens that the two methods are rendering the same view.
The only wrinkle is that since the second controller is conditional. If the create succeeds (passes validation and saves) then there's a redirect, so there's no proper view (although there will be in a moment, after the client-side redirect). But if it fails, then the view gets an object that contains the user-entered values as well as the validation errors which the view can then show to the user.

Multiple Contact Forms - Rails 3

I am relative newbie to all this, so sorry if this sounds mad!
I have used this tutorial: http://www.railsmine.net/2010/03/rails-3-action-mailer-example.html
And I have a new contact form working great.
The controller is at app/controllers/support_controller.rb
class SupportsController < ApplicationController
def new
# id is required to deal with form
#support = Support.new(:id => 1)
end
def create
#support = Support.new(params[:support])
if #support.save
redirect_to('/', :notice => "Support was successfully sent.")
else
flash[:alert] = "You must fill all fields."
render 'new'
end
end
end
And the model at /app/models/support.rb
class Support
include ActiveModel::Validations
validates_presence_of :email, :sender_name, :support_type, :content
# to deal with form, you must have an id attribute
attr_accessor :id, :email, :sender_name, :support_type, :content
def initialize(attributes = {})
attributes.each do |key, value|
self.send("#{key}=", value)
end
#attributes = attributes
end
def read_attribute_for_validation(key)
#attributes[key]
end
def to_key
end
def save
if self.valid?
Notifier.support_notification(self).deliver!
return true
end
return false
end
end
The views however only work in views/supports/new.html.rb (rendered - views/supports/_form.html.erb)
So I can call the Model / Controller from localhost:3000/support/new but if I try and render the same form in another view from the root directory e.g. app/view/contact.html.erb I get:
undefined method `model_name' for NilClass:Class
I think this is because it is calling the support model away from the supports directory.
Do I have to create an instance on #support so it can be called? If so what is the best way of doing that? I think I am nearly there. I just want the contact form on multiple pages not just in suppport/new
Thanks
Charlie
Yes, you would need to create a #support variable in each action you wish to render your form.
Another option would be to refactor the form to take a parameter, that way you're a bit more flexible. For example, from your view:
<%= render :partial => "supports/form", :locals => {:support => #support} %>
Now, instead of referring to #support in your _form.html.erb, you'd refer to simply support as it's a local_assign.
Yet another option would be to refactor the form a little further, and worry about creating the actual form tag outside of the partial.
Such as:
app/views/supports/new.html.erb
<%= form_for #support do |form| %>
<%= render :partial => "suppports/form", :object => form %>
<% end %>
app/views/supports/_form.html.erb
<%= form.text_field :foo %>
<%= form.text_field :bar %>
...
In this case, when you render a partial with the object option, you will get a local variable in your partial with the same name as the partial. You maintain a little bit more flexibility in the path of your form, but can still render the meat of what a Support object is inside of the form while remaining consistent across your app.
To clarify, you could use this somewhere else by doing something like:
app/views/foos/_create_foo_support.html.erb
<%= form_for #foo.support do |form| %>
<%= render :partial => "supports/form", :object => form %>
<% end %>
You have to pass #support object wherever you use your contact form. It's working in SupportsController#new because you initialize the variable there. In all other places where you want to use the form, you'll have to do the same.

Rails3 routing error with ajax

UPDATED CODE at the bottom
I am creating a story voting app via Simply Rails 2 book. I am getting this error when I click the button to vote up a story:
No route matches "/stories/4-pure-css-icons-showcase"
My routing file looks like this:
Shovell::Application.routes.draw do
get "votes/create"
root :to => "stories#index"
resources :stories do
resources :votes
end
end
votes_controller.rb:
class VotesController < ApplicationController
def create
#story = Story.find(params[:story_id])
#story.votes.create
end
end
create.rsj :
page.replace_html 'vote_score', "Score: #{#story.votes.size}"
page[:vote_score].visual_effect :highlight
show.html.erb:
<h2>
<span id="vote_score">
Score: <%= #story.votes.size %>
</span>
<%= #story.name %>
</h2>
<p>
<%= link_to #story.link, #story.link %>
</p>
<div id="vote_form">
<%= form_tag :url => story_votes_path(#story), :remote => true do %>
<%= submit_tag 'shove it' %>
<% end %>
</div>
story.rb :
class Story < ActiveRecord::Base
validates_presence_of :name, :link
has_many :votes
def to_param
"#{id}-#{name.gsub(/\W/, '-').downcase}"
end
end
I've been working through a number of other errors before this having to do with deprecated code and so forth, so I feel somewhat lost at the moment. It seems like it should just be a routing a issue, but since I've been working through AJAX errors that also have to do with the vote function I wanted to post those files just in case it was more than routing.
It says no route matches "/stories/4-pure-css-icons-showcase" but when I visit "/stories" (my root) and click on the link to take me to "/stories/4-pure-css-icons-showcase" it works fine, however after clicking on the vote button I get this error. As you could probably tell after reading the code, it is suppose to update the vote count and do a :highlight via ajax.
UPDATE:
Changed code (all changes are per Sam's advice):
routes:
Shovell::Application.routes.draw do
resources :votes
root :to => "stories#index"
resources :stories do
resources :votes
end
show.html.erb:
<div id="vote_form">
<%= form_tag :url => new_story_vote_path(#story), :remote => true do %>
<%= submit_tag 'shove it' %>
<% end %>
</div>
votes_controller.rb
class VotesController < ApplicationController
def create
#story = Story.find(params[:story_id])
#story.votes.create
respond_to do |format|
format.html
format.js
end
end
The problem is still exactly the same, but I think (read: hope) we are making progress!
The scenario: My index (/stories) page randomly displays a story from the database, when you click the link it takes you to the story's internal page (ex. /stories/2-sitepoint-forums) on this page it displays the number of votes the story has and has a button to vote for it. When you click the vote button it is suppose to use ajax to update the #story.vote.size and use a :highlight visual effect. However, the problem is that when you click the vote button the page changes to a "Routing Error" page which displays:
No route matches "/stories/2-sitepoint-forums"
Its weird to me because you can in fact be routed to that address and you are from the link on the first page...
Here is the error in the console:
Started POST "/stories/2-sitepoint-forums?url=%2Fstories%2F2-sitepoint-forums%2F
votes%2F2-sitepoint-forums&remote=true" for 127.0.0.1 at 2010-11-08 16:30:17 -08
00
ActionController::RoutingError (No route matches "/stories/2-sitepoint-forums"):
Rendered C:/Ruby192/lib/ruby/gems/1.9.1/gems/actionpack-3.0.0.rc2/lib/action_dis
patch/middleware/templates/rescues/routing_error.erb within rescues/layout (1.0m
s)
Im not sure if this is any more telling, but I thought I'd add it incase.
New:
I have not been able to solve this problem as of yet. Because I still don't feel like I completely understand the issue I have decided to move over to the Ruby on Rails 3 Tutorial Book online and see if I can't figure it out while working through it. Since I was planning to do it next anyway (I have plans to combine both apps later) it appears now is the time.
<%= form_tag :url => new_story_vote_path(#story), :remote => true do %>
<%= submit_tag 'shove it' %>
<% end %>
That should send it to the create action.
def create
#story = Story.find(params[:story_id])
#story.votes.create
respond_to do |format|
format.html
format.js
end
end
And that should take care of your ajax.
take this get "votes/create" out of your routes
and add this
map.resources :votes
I'm not familiar with using form_tag and :remote (as I normally just write jQuery for stuff like this) but a couple of things definitely pop out at me with what you're doing here that may help you resolve the issue.
First of all, I think you can rework the way you set up a vote and thus the way you set up the form for the vote. In the stories controller, for the show action, I'd set up the vote right away:
#vote = Vote.new(:story_id => #story.id)
This lets you set up your form as so:
= form_for(#vote), :remote => true do |f|
= hidden_field f.story_id
= submit_tag "Vote"
This is both a cleaner way of doing things, in my opinion, but also may fix the general issue you are dealing with, because you are now passing data with the form (the hidden field) in your POST request. Rails will behave unexpectedly if you perform AJAX POST requests that do not actually submit data.
In other words, your original form is likely running as an AJAX POST request but it would have worked better as an AJAX GET request, since it is not actually submitting data, it is simply "hitting" an URL.
I am not sure if you found the answer to your problem yet, but I wanted to post for others that may be looking for an answer similar to yours.
The code:
<%= form_tag :url => new_story_vote_path(#story), :remote => true do %>
<%= submit_tag 'shove it' %>
<% end %>
will result in /stories/:story_id/votes/new url with a :post request. It won't work because the new route is a :get method request. If you wanted to go to the new method, you'll need to tell the form to use the get http method.
<%= form_tag :url => new_story_vote_path(#story), :remote => true, :html => { :method => :get } do %>
<%= submit_tag 'shove it' %>
<% end %>
However, I think that you are wanting to route to the create method in your controller. I would do something like:
<%= form_tag :url => story_votes_path(#story), :remote => true do %>
<%= submit_tag 'shove it' %>
<% end %>
This should route correctly to the create method in your VotesController.

Resources