Rails: Share variables between controller and view [duplicate] - ruby-on-rails

This question already has an answer here:
Closed 10 years ago.
Possible Duplicate:
Passing only two variables between controller and view - best practice?
There is my action:
def list
#codes = Code.order("created_at")
#languages = Language.order('name').collect {|l| [l.name, l.coderay]}
end
There is my view(I removed some lines):
<% #codes.each do |code| %>
<div class="code">
<%= link_to code.title, :action => 'show', :id => code.id %>
<% if code.author %>
#<%= code.author %>
<% end %>
</div>
<% end %>
<%= render :partial => 'shared/error_messages', :locals => {:object => #code} %>
<%= form_for :code, :url => {:action => 'create' }, :html => {:multipart => true} do |f| %>
<%= f.text_field :title %><br />
<%= f.text_area :content %><br>
<%= f.select(:language, #languages, {:selected => 'text'}) %>
<%= f.text_field :author %><br>
<%= f.submit "Submit code" %>
<% end %>
There are 3 variables in it: #codes(list of posts), #code(current post, used in another action) and #languages.
My IDE writes:
At most two instance variables should be shared between controller and
view
This inspection warns if there are more than two instance
variables shared between a controller and a view. A controller should
only manage one instance variable, plus a second one for the
current_user variable.
Usually I share more variables between Controller and View(in PHP), sometimes 10+.
How it's done in Rails?

You can save an instance var by making languages a helper:
def languages
Language.order('name').collect {|l| [l.name, l.coderay]}
end

Its a guideline some developers follow some of the time.
But I would read up on Rails Routing a bit more. Understanding how Rails routing works should give you a better idea on how to structure your code.
http://guides.rubyonrails.org/routing.html
I modified your code a bit, not tested. But hopefully gives you some good ideas.
Controller:
def new
#code = Code.new
#codes = Code.order("created_at")
end
def create
#code = Code.new(params[:code])
if #code.save?
# Do your thing.
else
# render your :new action passing your #code variable
end
end
View:
<% #codes.each do |code| %>
<div class="code">
# Use Rails Routing - In console, type rake routes to get list of routes.
<%= link_to code.title, code_path(code.id) %> # example.
<% if code.author %>
<%= code.author %>
<% end %>
</div>
<% end %>
<%= render 'shared/error_messages', :object => #code %>
<%= form_for #code, :html => {:multipart => true} do |f| %>
<%= f.text_field :title %><br />
<%= f.text_area :content %><br>
# language_list = helper method.
<%= f.select(:language, language_list, {:selected => 'text'}) %>
<%= f.text_field :author %><br>
<%= f.submit "Submit code" %>
<% end %>

Related

Rails 4 render partial for nested attributes without loop

Suppose I have a form like below
<%= form_for #uni, :html => {:multipart => true, :honeypot => true} do |uni_form| %>
<% 3.times { #uni.app.build } %>
<%= uni_form.fields_for :apps do |builder| %>
<%= render 'app', uni_form: builder %>
<% end %>
<% end %>
and my app partial is
<div>
<%= uni_form.label :uni_id, "University" %>
<%= uni_form.collection_select :uni_id, #unis, :id, :name, {:include_blank => true} %>
</div>
Now I want the first form code without the loop. Something like this
<%= form_for #uni, :html => {:multipart => true, :honeypot => true} do |uni_form| %>
<% 3.times { #uni.app.build } %>
<%= render 'app', uni_form: builder %>
<%= render 'app', uni_form: builder %>
<%= render 'app', uni_form: builder %>
<% end %>
How can I do this?
Firstly, don't build your associated objects in your view - do it in your controller:
#app/controllers/unis_controller.rb
class UnisConstroller < ApplicationController
def new
#uni = Uni.new
3.times do
#uni.apps.build
end
end
end
Secondly, the fields_for method is your friend here.
You'll gain what you need by using the following:
#app/views/unis/new.html.erb
<%= form_for #uni, :html => {:multipart => true, :honeypot => true} do |uni_form| %>
<%= uni_form.fields_for :apps do |builder| %>
<%= builder.label :uni_id, "University" %>
<%= builder.collection_select :uni_id, #unis, :id, :name, {:include_blank => true} %>
<% end %>
<% end %>
fields_for takes your model's associated objects and automatically creates the fields you need. There is literally no need to "loop" - fields_for does it for you... if you set it up correctly.
The problem you have is you're building your associated objects at runtime, which is not only inefficient & against convention, but I think it will prevent the form_for from recognizing them (which is what allows fields_for to loop through them).
The above code should fix this for you.

Ruby Rails submit two forms in one click

I have these forms:
<%= form_for(#user) do |f| %>
<div>
<%= f.number_field :money, :value => #user.money %>
</div>
<% end %>
and
<%= form_for #product, :url => product_path, :html => { :multipart => true } do |f| %>
<div>
<%= f.label :count, 'How Many product?' %><br />
<%= f.number_field :count, :value => "1" %>
</div>
<div>
<%= f.submit('submit') %>
</div>
<% end %>
is there any way to submit this two at once when clicking submit button ? Thanks!
A service object might be a good way to approach this.
class Order
include ActiveModel::Model
attr_accessor :money, :count
def initialize(user=nil, product=nil)
#user = user
#product = product
#money = user.money
#count = 1
end
def persisted?
false
end
def save
// this code needs to save to db
end
end

how to write seperate controllers for rails single page app?

I am creating list of forms say
Questions form
Answers form
Hints form
All these have different controller and view, question_controller , answers_controller, hints_controller.
Now I need to fetch all these views in tabbed UI in home page (say home_controller , home#index)
I tried render : partial ,render :template also with locals , I can't achieve.
It can be easily done by moving all the object to same controller ( home_controller , but i am not sure about this approach , since it will make home controller too complicated to manage ) , but I need to keep this in separate controllers (question_controller , answers_controller, hints_controller) and render it to same page. I am using client side validation, simple form gems.
Below is my question controller
class QuestionsController < ApplicationController
def index
#question = Question.new
#question_status = []
#question_mode = []
#question_type = []
#question_lookups = Lookup.where({:lookup_for => "question"})
#question_lookups.each do |lk|
case lk.lookup_type
when 'mode'
#question_mode << lk
when 'status'
#question_status << lk
else
#question_type << lk
end
end
#caa = Questioncaa.new
end
end
Question View ( with Simple form )
<%= simple_form_for #question, :validate => true do |q| %>
<%= q.input :question_info, :as => :ckeditor, :input_html => { :toolbar => 'Easy', :width => 750 } %>
<%= q.input :question_source %>
<%= q.input :is_mobile %>
<%= q.input :is_similar_question %>
<%= q.input :is_boss_question %>
<%= q.input :is_racing_question %>
<%= q.input :is_speed_question %>
<%= q.input :difficulty_level %>
<%= q.input :ideal_time %>
<%= q.input :lookups, :collection => #question_mode, :value_method => :id, :label_method => :lookup_value,:prompt => "Choose Mode", :label => :QuestionMode %>
<%= q.input :lookups, :collection => #question_status, :value_method => :id, :label_method => :lookup_value,:prompt => "Choose Status", :label => :QuestionStatus %>
<%= q.input :lookups, :collection => #question_type, :value_method => :id, :label_method => :lookup_value,:prompt => "Choose Type", :label => :QuestionType %>
<%= simple_fields_for #caa do |c| %>
<%= c.input :needs_hints %>
<%= c.input :needs_video_solution %>
<%= c.input :needs_tips_tricks %>
<%= c.input :needs_formulae %>
<%= c.input :needs_key_concepts %>
<% end %>
<%= q.button :submit %>
<% end %>
Home View
<div class="tab-content">
<div class="tab-pane active" id="learning_map">
<!-- I need to acheive this -->
<%= render :template => "learning_map/index" %>
</div>
<div class="tab-pane" id="questions">
<!-- I need to acheive this -->
<%= render :template => "questions/index", :collection => #question_mode %>
</div>
<div class="tab-pane" id="answers">.
<!-- I need to acheive this -->
<%= render :templates => "answers/index" %>
</div>
</div>
Pls advice me , it will be very helpful. Thanks for reading this.
You should change logic of your templates a little (here is example for 'question' view):
1) split your question template into 2 files:
- header with form declaration of simple_form_for
- _form.html.erb file with <%= fields_for #question do |q| %> and list of your questions fields like <%= q.input %>
2) add <%= render :partial => 'form' %> in your header file
3) use <%= render :partial => 'question\form' %> in your home view template
4) dont forget to initialize #question variable in home_controller.

rails form data not getting saved to db

I'm new to rails and trying to make a simple site to start learning. When I submit my form, however, the data isn't saved to the db. I'm really not sure what's wrong, I've been trying to figure it out for a while. If I make a record in the rails console and save it, that one successfully shows up in the db (and on the index page).
calculate.rb:
class Calculate < ActiveRecord::Base
attr_accessible :number, :root
end
calculates_controller.rb:
class CalculatesController < ApplicationController
def index
#calculate = Calculate.all
end
def new
#calculate = Calculate.new
end
def create
#calculate = Calculate.new(params[:calculate])
if #calculate.save
redirect_to '/calculates'
else
render 'new'
flash[:notice] = "Didn't work"
end
end
end
new.html.erb:
<%= form_for(#calculate) do %>
<%= label_tag(:number, "Enter the number") %>
<%= text_field_tag :number %>
<%= label_tag(:root, "root") %>
<%= text_field_tag :root %>
<%= submit_tag("Submit") %>
<% end %>
if you are using form_for, use the form_for syntax
<%= form_for(#calculate) do |form| %>
<%= form.label :number %>
<%= form.text_field :number %>
<%= form.label :root %>
<%= form.text_field :root %>
<%= form.submit "Submit" %>
<% end %>
this will automatically handle the routes if the #calculate is new object it will submit it to create or if it is already saved it will send a put request to edit action
Ah hah! I updated my view to:
<%= form_for #calculate, :url => { :action => "create" } do |f| %>
<%= f.label :number %>
<%= f.text_field :number %>
<%= f.label :root %>
<%= f.text_field :root %>
<%= submit_tag("Submit") %>
<% end %>
And now it works. Awesome.

Difference between :model and #model in form_for?

What is the difference between using form_for the following way:
<% form_for #user do |f| %>
<%= f.label :name %>:
<%= f.text_field :name, :size => 40 %>
...
<% end %>
and:
<% form_for :user, :url => {:action => 'create'} do |f| %>
<%= f.label :name %>:
<%= f.text_field :name, :size => 40 %>
...
<% end %>
Does using #user just automatically use CRUD methods for the URL actions?
If you just give a model instance like #user without specifying an action (as in your first example), Rails automatically uses the appropriate CRUD action for your form:
If #user is a new, unsaved User object, the form will point to your create action.
If #user is an existing User loaded from the database, the update action will be used instead.
This has the advantage that you can reuse the same form for both your edit and new views without changing the :url parameter for your forms.
As usual, the API docs provide more information.
If you give form_for a symbol without an instance variable it looks for an instance variable with the same name.
The documentation says:
For example, if #post is an existing
record you want to edit
<% form_for #post do |f| %>
...
<% end %>
is equivalent to something like:
<% form_for :post, #post, :url => post_path(#post), :html => { :method => :put, :class => "edit_post", :id => "edit_post_45" } do |f| %>
...
<% end %>

Resources