At the moment, I have a list of projects in the projects/index view. What the user currently has to do is click 'Show' on the project, then click 'Select Project'. This calls a custom action I've created in the controller, which passes the id of the project into the session, so only relevant tasks etc. are shown in the following pages.
What I want to happen is to have a dropdown menu on the index view, with a list of all the projects. Then, when the submit button is clicked, it will pass the id of that project into the session, exactly the same. I've tried every way I can think of doing this, but I can't get anything to work - mainly because it appears as if the id of the project isn't getting passed from the dropdown.
My question is - how can I get the submit button to call a custom action that will take the id from the dropdown menu's project and pass that into the session?
I don't know if I need to add the code to the index action of the controller, or whether the submit button can call the custom action. I'm pretty new to rails, so the more people can spell stuff out, the better!!
Here's the projects/index:
<%= form_for(#project) do |f| %>
<% if #project.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#project.errors.count, "error") %> prohibited this project from being saved:</h2>
<ul>
<% #project.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.hidden_field :company_id, :value => session[:company_id] %>
</div>
<div class="field">
<%= collection_select :project, :id, Project.all, :id, :name %>
</div>
<div class="actions">
<%= f.submit 'Select Project', :class => "btn btn-primary" %>
</div>
<% end %>
The controller code so far:
def index
#projects = Project.all
respond_to do |format|
format.html # index.html.erb
format.json { render json: #projects }
end
end
def select_project
project = Project.find(params[:id])
session[:project_id] = project.id
redirect_to root_url, notice: "Current project set to: #{project.name}, ID: #{project.id}"
end
I can't put
#project = Project.find(params[:id])
into the index action, otherwise it says that it can't find a project without an id.
When you are submitting a form this will be done through the either the update or create action in your controller.
May I ask why you are loading your dropdown from session? since you could just use the rails helpers to find (filtered) records using :where etc.
Once you get an array returned as you can list them in your dropdown as option attributes. and submit them to your update or create action. once you are there you can take value's from the submitted hash using
params[:key][:nested_key]
or just
params[:key]
While you are doing this here you are even able to bind these to sessions or variables for later use.
Just try to make the flow as robust and easy as you can.
Have fun
Related
I have a "contributions" model. The create form has two submit actions: "Preview" and "Submit for Review". When the user clicks "Preview", I would like to collect the form data and then show a preview on that same page. Here's how I'm approaching that:
Form:
<%= form_with(model: #contribution, local: true) do |form| %>
<% if #contribution.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#contribution.errors.count, "error") %> prohibited this contribution from being saved:</h2>
<ul>
<% #contribution.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= form.url_field :link, placeholder: "Unsplash photo link", required: true %>
</div>
<div class="actions">
<%= submit_tag 'Preview', id: 'preview-button', name: 'preview_button' %>
<%= submit_tag 'Submit for Review', id: 'submit-button' %>
</div>
<% end %>
Contributions controller:
def create
#contribution = Contribution.new(contribution_params)
if params[:preview_button]
/// do some stuff to collect data
render action: "new"
else
...
Routes:
resources :contributions
get "/contribute" => "contributions#new"
Current Result
The page refreshes and the data processed in the controller is available (yay!). However, the url changes from /contribute to /contributions. If the user happens to refresh the page, they run into an error because I don't have a contributions#index view.
Desired Result
The page refreshes, the data is available, but the url stays as /contribute.
This is because the endpoint for create action is POST /contributions. And "rendering" a different action does nothing to the url.
You need to redirect instead.
Replace
render action: 'new'
with
return redirect_to action: :new
I can think of 2 options:
redirect_to + flash hash
You can redirect to /contribute but store the collected data in the Flash Hash:
flash[:collected_data] = something
redirect_to "/contribute"
You can store strings, arrays and hashed there, though I wouldn't abuse it.
Then you can do #collected_data = flash[:collected_data] in your new action to know if it comes from a previous POST request.
Render 'new' and change the url after load
You can use some javascript after the page is loaded to push a new state like:
document.addEventListener('DOMContentLoaded', () => {
history.pushState({}, 'Some title', '/contribute')
})
This will change your current path to /contribute without actually loading that url, so you keep the response from POST /contributions but the url changes so your refresh is safe.
My form gets passed a 'new' Quiz (not saved to the database). My form partial looks like this:
<%= form_for(#quiz) do |f| %>
<p>
<%= f.check_box(:answer1) %>
<%= f.check_box(:answer2) %>
<%= f.check_box(:answer3) %>
<%= f.check_box(:answer4) %>
<%= f.check_box(:answer5) %>
<%= f.check_box(:answer6) %>
<%= f.check_box(:answer7) %>
<%= f.check_box(:answer8) %>
</p>
<p>
<%= f.submit("Get my results!") %>
</p>
<% end %>
Here is my QuizzesController#create action:
def create
#results = Quiz.create(post_params) #from private method
if #results.save
redirect_to results_path
else
#error handle here
end
end
...which gets triggered when the user clicks 'get my results' on my quiz form. And the post_params method looks like this:
def post_params
params.require(:quiz).permit(:id, :user_id, :answer1, :answer2, :answer3, :answer4, :answer5, :answer6, :answer7, :answer8) #add other attributes here
end
My results/index.html.erb looks like this:
<div class="container">
<!-- Example row of columns -->
<div class="row">
<h1>Results</h1>
<p><%= #results.inspect %></p>
</div>
</div>
But that 'inspected' Quiz instance returns 'nil' for all the answers1, answers2 etc attributes. Any idea why that would be? Is there something I'm NOT doing to save the user's answers to the database?
The reason it shows nil is because you are not setting the variable.
After creating and saving, you redirect to results_path and the variable #results does not persist during a redirect. Without seeing the full code, I'll have to guess at your naming conventions but there are two ways to do this.
1) If you want to redirect to the index then in the code for your index action, you can set the variable:
#results = Quiz.last
This is easy to work with in development because you are the only user and this will always return the last quiz you created. Not so great in production.
2) The alternative is to redirect to the show action for that quiz.
def create
#results = Quiz.new(post_params)
if #results.save
redirect_to result_path(#results)
else
# error handle here
end
end
Again, I have had to guess that result_path is the correct path. Without seeing the full routes file, I cannot be sure but you can rename accordingly if necessary.
I created a sample webpage in rubyonrails which has two textbox and a button . When i enter some data in the text box and click the button no error appears . But the data is not stored in the data base . What is the mistake that i committed .
login.html.erb file :
<%= form_for #product, url:{action: "login"} do |f| %>
<% if #product.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#product.errors.count, "error") %> prohibited this product from being saved:</h2>
<ul>
<% #product.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :username %><br>
<%= f.text_field :username %>
</div>
<div class="field">
<%= f.label :password %><br>
<%= f.text_field :password %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
routes.rb file:
Sample::Application.routes.draw do
get "sample/login"
root 'sample#login'
post 'sample/:id' => 'sample#login'
end
sample controller file :
class SampleController < ApplicationController
def login
#product=Logz.new
end
end
and the model class name Logz contains the necessary field names
username and password . If there was any error i could manage . but it shows no errors.
I had the same problem once. I dont know how its happened . but i just changed the content in database.yml
localhost:yourdb_development
into
127.0.0.1:ypurdb_development
and i got it working.
You are sending the data to method login, but it just instantiate a new Product and it aren't receiving any attributes... and even if received... it are not saving the Product at all, so does not persist the data.
Try add a method create... that will be responsible for receive the data and save
class SampleController < ApplicationController
def create
#product = Logz.new(params[:product])
if #product.save
format.html { redirect_to 'sample#login', notice: 'Data saved successfully' }
else
flash[:notice] = 'A wild error appeared'
end
end
After that, create the route to post 'sample/create' and change the action to where your form send the data... form_for #product, action: "create", method: 'post'
Doing that... i will be possible to persist the data on your database...
PS:
You can use the content of that method inside your login method... but I dont recommend that... it is ugly and does not follos the conventions of rails.
I even recommend you to do a refactory... because it doesn't make sense access a SampleController in order to create a Product... that is persisted in an object called Logz...
The best practice is all follow the same name... LogzController, #logz, and finally your model Logz. and preferably your routes following the same pattern...
Another thing is, it would be nice to change your method login to a method call 'new' because that method you use to fill a new Logz... not to login...
I'm working on a reddit mock-up through a tutorial. When on my localhost I am on my new page(submit new link) where I can submit a title and url.
Whenever I submitted the information, I would previously end up on a blank create view.
The tutorial is asking for us to find a way to populate our database and end up on a show view with our submitted information & also have our newly submitted information be available on our index page.
This is my attempt at editing my controller for this, but I've failed miserably:
class LinksController < ApplicationController
def index
#link = Link.all
end
def show
#link = Link.find(params[:id])
end
def new
#link = Link.new
end
def create
#link = Link.new(link_params)
if #link.save
redirect_to #link
else
render action: 'new'
end
end
end
I am getting an error that reads:
undefined local variable or method `link_params'
Any advice on how to fix this?
This is my new view:
This is new view:
<%= form_for(#link) do |f| %>
<% if #link.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#link.errors.count, "error") %> prohibited this link from being saved:</h2>
<ul>
<% #link.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :url %><br />
<%= f.text_field :url %>
</div>
<div class="field">
<%= f.label :title %><br />
<%= f.text_field :title %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
My show view is currently empty save for some header text:
Also, if someone had some advice on how to go about further improving my controller & views as well to get the desired result that would be appreciated, I am new & trying to learn, thanks.
You need a link_params method in your controller
def link_params
params.require(:link).permit(:url, :title)
end
When you submit a form from browser, it hits the controller action. The form will submit some parameters which needs to be processed. Follow rails logs to see what is being returned, this is the best simple way to know and debug things.
Here in your create action, #link = Link.new(link_params) you are initializing Link object with link_params data, when the form is submitted check what parameter contains the hash. Parse over that hash to pass data in Link class object. You need to define link_params or use something like params[:link] after confirming from the server logs.
I am trying to create a compare functionality for an index of schools. Currently I am using the following code which takes any checked school and adds it to the school_ids[] in the params.
In my gemfile:
gem 'will_paginate'
In my school's index.html.erb:
<%= form_tag compare_path, :method => 'get' do %>
<%= submit_tag "Compare" %>
<ul>
<% #schools.each do |school| %>
<li>
<%= check_box_tag'school_ids[]', school.id %>
<%= link_to school.name, school %><br>
<%= school.city %>, <%= school.state %>
</li>
<% end %>
</ul>
<% end %>
In my school controller I have:
def compare
#schools = School.find(params[:school_ids])
end
This works great as long as all of the check schools are on the same page. But since I'm using will_paginate to paginate the list of schools, if I change pages, the check boxes do not persist. I'm assuming I need to save to sessions somehow.
Do you mean you want to be able to add a check mark to a school A on page 1 of the index, go to page 2 of the index and add another check mark for school B, then submit the compare form and see schools A and B? If that's the case, then you're correct, you need to get the check boxes into the session. Attach a js click event, like
$('.checkbox_class').click(function(){
$.post('update_session_method', { school_id: $(this).val(), checked: $(this).is(:checked)]);
});
then add a controller method
def update_session_method
session[:school_ids] ||= []
if params[:checked]
session[:school_ids] << params[:school_id]
else
session[:school_ids].delete(params[:school_id])
end
end
then your compare method
def compare
#schools = School.find(params[:school_ids].merge(session[:school_ids] || []))
end