render 'new' page as partial - ruby-on-rails

i'm building this app where the user should be able to perform all rest actions in the same page. So below a table like the following:
<div class="row">
<div class="col-lg-3">Name</div>
<div class="col-lg-4">Lastname</div>
<div class="col-lg-3">Schedule</div>
<div class="col-lg-1"> </div>
<div class="col-lg-1"> </div>
</div>
<div id="table-body-clients">
<% #clients.each do |client| %>
<%= render :partial => 'client', :locals => { :client => client } %>
<% end %>
</div>
I should include a form to add a new client (same form will then load and edit selected clients. All on remote).
The easiest way i can think is to render 'new' page as a partial. But i don't seem to find the way to call the correct action and/or load the locals correctly.
This did not work:
<%= render 'new', :controller => "clients", :action => "new", :locals => { :client => #client } %>
'new' is actually a partial named '_new'
Anyway, I would appreciate a fix on this code as much as an insight for the right way to approach the issue.
Thanks

should be able to perform all rest actions in the same page
Don't worry about performing actions on the same page - make sure you keep your views structured in the resourceful style of Rails, but call them in the view you want (much the same as you would with partials)
As you have demonstrated, you can do this:
<%= render "clients/new", locals: { client: #client } %>
To give you a caveat here, you need to appreciate that since you're creating a new client, you'll want to build the resource in your controller:
#app/controllers/clients_controller.rb
class ClientsController < ApplicationController
def index
#clients = Client.all
#client = Client.new
end
end
--
As you mention you've got a nested route (IE client belongs to company), you want to make sure you have the following set up:
#config/routes.rb
resources :companies do
resources :clients #-> domain.com/companies/:company_id/clients
end
#app/controllers/clients_controller.rb
class ClientsController < ApplicationController
def index
#clients = Client.all
#company = Company.find params[:company_id]
#client = Client.new
end
end
#app/views/clients/index.html.erb
<%= render "new", locals: {company: #company, client: #client} %>
#app/views/clients/new.html.erb
<%= form_for [company, client] do |f| %>
...
<% end %>

Create a partial _form.html.erb as the following
<%= form_for client do |f| %>
your form fields here
<% end %>
Then, from new.html.erb and edit.html.erb, or whatever page where you need the form:
<%= render "form", client: #client %>
Replace #client with the actual client object you need the form for.

Related

Rails form rendering wrong URL after validation errors (not keeping passed parameter)

On a project show page, I pass a very simple parameter on my 'create new task' that stores which project its from:
#project.id), :class => "btn btn-info col-md-12" %>
so that when i create a new task for it, it stores it in the URL on my new task form like this:
http://localhost:3000/task/new?project_id=5
My New form is as follows:
<div class="container sign-in-register">
<div class="authform">
<%= form_for #task, :html => {:multipart => true} do |f| %>
<h3>Add a task for this project...</h3><br/>
<%= f.label :name %>
<%= f.text_field :name, class: 'form-control' %>
<%= hidden_field_tag 'project_id', #project_id %>
<%= f.fields_for :taskrelationships do |ff| %>
<%= ff.hidden_field :taskproject_id, value: #project_id %>
<%= ff.label :task_url %>
<%= ff.text_field :task_url, class: 'form-control' %>
<% end %>
<br clear="all">
<%= f.submit "Save Task", class: "btn btn btn-info" %>
<% end %>
</div>
</div>
As you can see i'm using nested attributes in the form (I am creating both a task and a TaskRelationship. Now, when I try to save without filling out all the required fields a validation is thrown but for some reason it redirects me to:
http://localhost:3000/tasks
instead of the original:
http://localhost:3000/tasks/new?project_id=5
I have read many posts and none seem to answer this particular case. The stackO post below was close but when I try it with task instead of user it still cannot find the task_ID
Render error on new page with customize url like http://localhost:3000/education_informations/new?user_id=10
How can I have rails simply render the same exact url as I started with - it seems like this should be easy so must be missing something small.
My controller actions:
def new
#project_id = params[:project_id]
#task = Task.new
#task.taskrelationships.build
end
def create
#project = Project.find(params[:project_id])
#task = Task.new(task_params)
if #task.save
flash[:success] = "This task has been added."
#task.taskrelationships.create!(#taskrelationships_params)
redirect_to tasks_project_path(#project)
else
#task.taskrelationships.build(#taskrelationships_params)
flash[:alert] = #task.errors.full_messages.to_sentence
render :new
end
end
private
def task_params
#taskrelationships_params = params.require(:task).permit(taskrelationships_attributes: [
:task_url,
:taskproject_id
])[:taskrelationships_attributes]["0"]
params[:task].delete(:taskrelationships_attributes)
params.require(:task).permit(
:name,
:user_id,
taskrelationships_attributes: [
:task_url,
:taskproject_id
]
).merge(owner: current_user)
end
UPDATE W / ROUTES
resources :projects do
resources :reviews, except: [:destroy]
member do
get :tasks
end
end
resources :tasks
resources :taskrelationships, only: [:create, :destroy] do
post :vote, on: :member, controller: "task_relationships"
end
thanks for any assistance...
Ok firstly an explanation as to what is going on here:
When you invoke http://localhost:3000/task/new?project_id=5 you are actually being routed to the new action on the task controller (with a project_id param).
Your new action then sets the variables and rails will render the new.html.erb which contains your new task form.
When you submit the form it actually is doing a http POST to /tasks, which routes to the create action of your tasks controller. That url and http method is a result of what is generated from the form_for helper:
<%= form_for #task, :html => {:multipart => true} do |f| %>
This is why the url changes from /tasks/new?project_id=5 to /tasks
Now the create action if it fails the validation simply renders the new form - it is not redirecting anywhere - the url remains unchanged from what it was when it entered this action - meaning, it remains as /tasks.
You do not actually need to navigate to /tasks/new?project_id=5 to render the new form but what you do need to do is set #project_id in the controller so the view has access to that variable (just as it does in the new action):
def create
#project = Project.find(params[:project_id])
#task = Task.new(task_params)
if #task.save
flash[:success] = "This task has been added."
#task.taskrelationships.create!(#taskrelationships_params)
redirect_to tasks_project_path(#project)
else
#task.taskrelationships.build(#taskrelationships_params)
#project_id = #project.id
flash[:alert] = #task.errors.full_messages.to_sentence
render :new
end
end
So, to clarify the change in url is not a redirection it's just that the form is posting to a different url than /tasks/new, and this is actually just a cosmetic issue.
Now if it is a concern to you, you can change the routing to something like the following:
resources :tasks, except: [:create, :new]
post 'new_task' => 'tasks#create'
get 'new_task' => 'tasks#new'
This is mapping the POST and GET http methods to /new_task so the url appears the same for the new and create action invocations. Note you do need to change the url in the form_for helper to use this new route:
<%= form_for #task, url: 'new_task', multipart: true do |f| %>
Since Rails default behaviour in forms is with remote: true, you can move the content of the form to a partial(let's name it _my_form.html.erb), add to the controller action (let's say in create action):
respond_to do |format|
format.js {}
end
Then add a create.js.erb file where you will render the form partial
$("#form").html(
"<%= j render partial: 'my_form', locals: { entity: #entity } %>"
);
Thus, validation errors and all attributes will be accessible inside the form and there is no need to hack the "Rails approach"
I had to adapt and experiment with the currently accepted answer,
and the following ended up working well (using schools rather than tasks):
In config/routes.rb:
resources :schools do
...
end
post 'schools/new' => 'schools#create'
And create the form like:
= simple_form_for [#school], url: 'new' do |f|
Thus the path /schools/new was preserved on validation :)

Why aren't instance variables defined in a controller's methods available in the corresponding partials?

For example, given a basic controller such as:
class PostsController < ApplicationController
def index
#post = Post.all
end
The instance variable #post is available in the view posts/index.html.erb but not the partial posts/_index.html.erb
My primary questions are: Why is this? and How can I define a method for a partial in a controller to for example, pass an instance variable?
On a more specific note:
I ask this because the way I am rendering data in a separate, primary view welcome/index.html.erb is via this posts partial. I have gotten around this limitation by directly calling the model with Post.all in the posts/_index.html.erb partial, but as far as I understand this ignores MVC architecture and severely limits me from developing more complex methods, unless I really break convention and write them into the view (which I assume may work but is poor design).
Currently, #post is passing a nil to the partial symbol :post in welcome/index.html.erb
Clearly I'm missing something, so any insight would be greatly appreciated!
Thank you
Relevant files:
views/
posts/
_index.html.erb
_new.html.erb
show.html.erb
welcome/
index.html.erb
controllers/
posts_controller.rb
welcome_controller.rb
posts/_index.html.erb
<% post.reverse.each do |post| %>
<div class="panel panel-default">
<div class="panel-body">
<%= post.text %>
</div>
</div>
<% end %>
posts/_new.html.erb
<%= form_for :post, url: posts_path do |f| %>
<p id="post_box">
<%= f.text_area :text %>
</p>
<p>
<%= f.submit %>
</p>
<% end %>
posts/show.html.erb
<%= #post.text %>
welcome/index.html.erb relevant portions (partials)
<%= render partial: "posts/index", locals: {post: #post} %>
<%= render partial: "posts/new" %>
controllers/posts_controller.rb
class PostsController < ApplicationController
def new
end
def create
#post = Post.new(post_params)
#post.save
redirect_to '/'
end
def show
#post = Post.find(params[:id])
end
def index
#post = Post.all
end
private
def post_params
params.require(:post).permit(:text)
end
end
controllers/welcome_controller.rb
class WelcomeController < ApplicationController
end
You're right that you're breaking MVC conventions by calling controller-level object lookups in your view.
When you call the render action on the partial you have a variety of arguments to send, one of which is a locals hash that takes a variable you want to pass directly into your partial.
It looks like this:
<%= render partial: "something", locals: {posts: #posts} %>
The rails guides are a great resource on the topic. More on that here.

Implementing ancestry on a nested resource throwing an error

I am implementing ancestry on a nested resource.
resources :loads do
resources :messages
end
Here is my index action
def index
load = Load.find(params[:load_id])
#messages = load.messages.scoped
#message = load.messages.new
end
My index.html.erb is throwing the following error.
Missing partial messages/message with {:locale=>[:en],
:formats=>[:html], :handlers=>[:erb, :builder, :coffee]}. Searched in:
* "C:/Sites/final/cloud/app/views"
My index.html.erb is as follow
<% title "Messages" %>
<%= nested_messages #messages.arrange(:order => :created_at) %>
<%= render "form" %>
Here is my nested_message definition
module MessagesHelper
def nested_messages(messages)
messages.map do |message, sub_messages|
render(message) + content_tag(:div, nested_messages(sub_messages), :class => "nested_messages")
end.join.html_safe
end
end
Here is my _message.html.erb
<div class="message">
<div class="created_at"><%= message.created_at.strftime("%B %d, %Y") %></div>
<div class="content">
<%= link_to message.content, message %>
</div>
<div class="actions">
<%= link_to "Reply", new_load_message_url(:parent_id => message) %> |
<%= link_to "Destroy", [message.load, message], :confirm => "Are you sure?", :method => :delete %>
</div>
</div>
Any help appreciated.
This error states that your application has tried to search for a partial _messages.html.erb, as a result of this the partial must not be in your /app/views/messages which results in the message you are being shown. Check your messages directory and check if you have this partial. Going by your nested resources I am guessing your association between Load and Message is:
class Load < ActiveRecord::Base
has_many :messages
end
class Message < ActiveRecord::Base
belongs_to :load
end
Further more I noticed that you have the following line in your index action: #message = load.messages.new surely this does not seem right. Because what your telling your application to do is when the controller recieves a response to render the index action it should also create message by doing #message = load.messages.new which is why it is trying to render the partial.
To clarify things a bit more for you. If in your application you had a link_to to create a new user. Upon clicking the new user it will do something like:
def new
#user = User.new
respond_to do |format|
format.html # new.html.erb
format.json { render json: #user }
end
end
And will render `app/views/users/new.html.erb which inside this will most probably have a
<%= form_for #user do |f| %>
<%= render :partial => 'form', :locals => { :f => f } %>
<% end %>
This will call your partial which in normal cases would be _form.html.erb. The create action of a particular controller will come into play when you attempt to save the partial. Usually a create block for a controller will look like this:
def create
#title = "Create a user"
#user = User.new(params[:user])
if #user.save
redirect_to usermanagement_path
flash[:success] = "Created successfully."
else
#title = "Create a user"
render 'new'
end
end
Here inside the create action when your _form.html.erb or _message.html.erb is submitted it will try to create a new user by passing in the user through the params. I do thoroughly believe that your issue may potentially well be:
1) Your missing your _message.html.erb
2) Also you are calling a .new inside your index action.
Hope this clears this up for you.

Rails - updating div w/ Ajax and :remote => true

I've followed this Railscast on submitting a form via Ajax and updating a div without reloading the page, but I'm having trouble with one portion of it.
Ryan has $("#products").html("<%= escape_javascript(render(#products)) %>"); in an index.js.erb file to update the #products div when the form is submitted. I'm struggling to understand whether an instance variable like #products is relevant for my situation, or if I can simply substitute it for a URL.
I'm trying to do the same thing as Ryan in this screencast, but instead of display search results I just want to display the updated value.
In show.html.erb I have:
<% #project.project_todos.order("created_at DESC").where(:status => false).each do |todo|%>
<%= form_for todo, :remote => true, :"data-replace" => "#dueon" do |f| %>
<%= f.text_field :due %>
<%= f.submit :class => "primary", :value => "Add" %>
<% end %>
<div id="dueon">
<%= render "duedate", :todo => todo %>
</div>
<% end %>
The partial _duedate.html.erb has one line in it: <%= todo.due %>
So in my index.js.erb I currently have this: $("#dueon").html("<%= escape_javascript(render("_duedate")) %>"); but it's clearly not working. Do I have to use a variable here in place of the _duedate? And if so, how would I set this up in the controller? I mean what does the variable have represent?
Also, for what it's worth, the partial is rendering correctly and displaying the todo.due value...it's just not updating when I submit the form.
ProjectsController:
def show
#project = Project.find(params[:id])
# Display the form to create a new todo
#project_todo = ProjectTodo.new
respond_to do |format|
format.html # show.html.erb
format.json { render :json => #project }
end
end
Try this
in you controller action , (say sample_action)
def sample_action
#todos = #your code
respond_to do |format|
format.js
end
end
and you have a sample_action.js.erb
$("#dueon").html("<%= raw escape_javascript(render(:partial => 'duedate')) %>")
then inside the partial, you have access to the new #todos instance variable
HTH
I will answer you separately as I believe your entire setup should be little change (IMO, this might not be the case)
I think you should have a todos controller with a project belongs to it,
--- models ----------------
class Project < ActiveRecord::Base
has_many :todos
end
class Todo < ActiveRecord::Base
belongs_to :project
end
---- routes ---------------
resources :projects do
resources :todos do
end
end
---- controllers ----------
class ProjectsController < ApplicationController
end
class TodosController < ApplicationController
def new
#project = Project.find(params[:project_id])
#todos = #project.todos.build
end
end
in your view (views/todos.html.erb)
<%= #project.name %>
<%= form_for([#Project, #todos]) do |f| %>
#todo form code
<% end%>
As per the relation, project has many todos, its always clear to show the project details in the todo add screen, rather than allowing users to add new todos from project screen.
and again, this is my personal view, feel free to ask any questions :)

how to catch mongoid validation erros from another controller?

I have 2 controllers: DocumentsController and DashboardController
After the user login successful, he is redirected to dashboard_path, which has a form to create a 'fast document' like this
<%= form_for #document, :html => {:class => 'well'} do |f| %>
<% if #document.errors.any? %>
<div id="alert alert-block">
<div class="alert alert-error">
<h2>Couldn't create your doc. :(</h2>
<ul>
<% #document.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
</div>
<% end %>
<label>A lot of fields</label>
<%= f.text_field :fields %>
<div class="form-actions">
<%= f.submit 'Create document', :class => 'btn btn-large' %>
</div>
<% end %>
but when an exception happen (like the user forgot to fill a field), I would like to show these exceptions, not just an alert saying 'Error'...actually, I didn't found a way to do this
here's my DashboarController
class DashboardController < ApplicationController
before_filter :authenticate
def index
#document = Document.new
end
end
and my DocumentsController
class DocumentsController < ApplicationController
respond_to :json, :html
def show
end
def create
#document = Document.new(params[:document])
#document.user = current_user
if #document.save
redirect_to dashboard_path, notice: 'Created!'
else
flash[:error] = 'Error!'
redirect_to dashboard_path
end
end
end
any help is appreciated :)
You are correctly redirecting on success; on failure, though, should not redirect; you need to render the template where the form was filled.
if #document.save
redirect_to dashboard_path, notice: 'Created!'
else
render 'dashboard/index'
end
You'll have to make sure that any variables needed by the index template are available in the create action of the documents_controller (you're just rendering the index template; you're not running the code from the dashboard controller's index action). Here's a excerpt from the relevant Rails Guide to clarify:
Using render with :action is a frequent source of confusion for Rails newcomers. The specified action is used to determine which view to render, but Rails does not run any of the code for that action in the controller. Any instance variables that you require in the view must be set up in the current action before calling render.
More at http://guides.rubyonrails.org/layouts_and_rendering.html#using-render

Resources