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 :)
Related
I've been following a pretty standard UJS model for a lot of my AJAX and it works pretty well - however there's one situation where I'm not sure exactly what the best practice is. I have a model with a has_many relationship say:
class User < ActiveRecord::Base
has_many :aliases
accepts_nested_attributes_for :aliases, :allow_destroy => true
end
class Alias < ActiveRecord::Base
belongs_to :User
end
Now, in my NEW view for a User object, I'd like the user to be able to click a button and add as many aliases as they like - each time they press the button it fires off a remote request to a controller action that looks like this:
def new_ajax
#alias = Alias.new({})
respond_to do |format|
format.js
end
end
This replies with my new_ajax.js.erb template which looks like this:
$('#aliases-container').append('<%= j(render 'ajax_new')%>');
Which in turn renders a template which looks like this:
<%=simple_fields_for #alias do |f|%>
<%= f.input :name %>
<% end %>
This all works and I get my Alias form field rendered - but the problem is the ID and the NAME attribute are not set correctly in the nested fields. The ID and the Alias should be numbered in sequence to provide for proper association building in the create method and for error handling. Does anyone have a solution to this problem?
The problem you have is the base problem with adding new nested fields.
The simple answer is to use child_index: Time.now.to_i for your fields_for; however, you've got to invoke it within a form_for object so that Rails creates the names etc correctly. Sounds complicated but it's quite simple when you get your head around it:
#app/controllers/users__controller.rb
class UsersController < ApplicationController
respond_to :js, :html, only: :new
def new
#user = User.new
#user.aliases.build if request.xhr?
respond_with #user
end
end
#app/views/users/new.html.erb
<%= render "form", locals: {user: #user} %>
#app/views/users/_form.html.erb
<%= form_for user do |f| %>
<%= f.fields_for :aliases, child_index: Time.now.to_i do |a| %>
<%= a.text_field :name %>
<% end %>
<% end %>
#app/views/users/new.js.erb
var data = $("<%=j render "users/ajax_new", locals: {user: #user} %>").html();
$('#aliases-container').append(data);
You can see a more rudimentary implementation I wrote back in 2013:
Rails accepts_nested_attributes_for with f.fields_for and AJAX
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.
New to rails so I am implementing a basic app.
Text field and a submit button, on clicking the submit button the text to be displayed on the div placed below the submit button of the index page.
Code and things I trying is as follows -
Siteurl - http://localhost:3000/lists/index
list controller code -
class ListsController < ApplicationController
def index
List.all
end
def add
#message = params[:todo_text]
List.create(:title => #message)
redirect_to '/lists/index', :notice => "Notice"
end
end
Model list.rb -
class List < ActiveRecord::Base
attr_accessible :title
end
Views list index.html.erb code -
<div id="search">
<%= form_tag("/lists/add", :method=>"post") do %>
<%= text_field_tag(:todo_text, '',:class=>"formtext" ) %>
<%= submit_tag("Add Message", :class=>"submit") %>
<% end %>
</div>
Now when I submit some text it redirects to the same..but data is not reverting back to the page. Let me know what I am doing wrong and how do i fix it ?
in controller
def index
#lists = List.all
end
in index.html.erb
<% #lists.each do |list| %>
...
<% end %>
try
<%= form_for #list, :url => { :action => "add" } do |f| %>
controller
def index
#lists = List.all
end
def add
#list = List.new(params[:title])
redirect_to #lists, :notice => "Notice"
end
I have an app where users can create courses, and each course has_one syllabus. How could I go about configuring my courses and syllabuses (I know it's Syllabi but apparently Rails doesn't) controller, and my routes, so on a course's page there is a link to create or show the course's syllabus, and a link back to the course from the show syllabus page?
In my routes I have:
resources :courses do
resources :syllabuses
member do
put :enroll #this is so users can enroll in the course
end
end
Currently , so the course_id will be saved in the syllabus table in my courses_controller, I have:
def create_syllabus
#course = Course.find(params[:id])
#syllabus = #course.build_syllabus(params[:syllabus])
if #syllabus.save
redirect_to #syllabus, notice: "Successfully created syllabus."
else
render :new
end
end
then in my courses show page I have:
<section>
<% if (current_user.courses.includes(#course) ||
current_user.coursegroups.find_by_course_id_and_role(#course.id, "admin")) %>
<%= render 'create_syllabus' %>
<% end %>
</section>
then in my create_syllabus form (in my courses views folder) I have tried starting it off with:
# I have #course = Course.find(params[:id]) defined in show in the
#courses_controller
<%= form_for #course.create_syllabus do |f| %>
<%= form_for #course.syllabus.create_syllabus do |f| %>
<%= form_for #course.syllabus.create do |f| %>
and I get an undefined method error for each of those.
If you want to create a new syllabus in your show action of a specific course, you can add this to your controllers and views:
courses_controller.rb
#course = Course.find(params[:id])
# Build a new #syllabus object, only if there is none for the current course
unless #course.syllabus
#syllabus = #course.build_syllabus
end
views/courses/show.html.erb
# Show the syllabus name if there is one, or show the form to create a new one
<% if #course.syllabus.name %>
<p>Syllabus: <%= #course.syllabus.name %></p>
<% else %>
<p>Create Syllabus:</p>
<%= form_for([#course, #syllabus]) do |f| %>
<div class="field">
<%= f.label :name %><br />
<%= f.text_field :name %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
<% end %>
syllabuses_controller.rb
def create
#course = Course.find(params[:course_id])
# Build new syllabus object based on form input
#syllabus = #course.build_syllabus(params[:syllabus])
if #syllabus.save
# redirect to /course/:id
redirect_to #course, notice: 'Syllabus was successfully created.' }
end
end
course.rb
class Course < ActiveRecord::Base
attr_accessible :name
has_one :syllabus
end
syllabus.rb
class Syllabus < ActiveRecord::Base
belongs_to :course
attr_accessible :name, :course_id
end
Some things that I left out but you should still include:
validations
rerendering form if something goes wrong
pulling things out into partials
fixing bad code like if #course.syllabus.name
pull out if/else logic into a helper
…
The models project and category are in a has_and_belongs_to_many relationship. The partial seen below is used on different views to show a dropdown-menu with all the available categories. The projects in the list below the dropdown-menu are shown according to the choice the user made in the dropdown-menu.
Besides other categories, there is a category named "Everything" that every project is member of. It's also the first entry in the category-db-table, because it got inserted in while loading the migrations into the database.
Right now there is no error, but regardless of which category I choose it reloads the page showing the "Everything" category.
Any idea what I need to change in the code mentioned below to make it work the way I want to? Thanks for your help!
Partial with dropdown-menu and project-list
<!-- category dropdown -->
<% form_for category_url(:id), :html => {:method => :get} do |f| %>
<label>Categories</label>
<%= f.collection_select(:category_ids , Category.find(:all), :id , :name) %>
<%= f.submit "Show" %>
<% end %>
<!-- project list -->
<ul class="projectlist">
<% #projects.each do |_project| %>
<li>
<%= link_to(_project.name, _project) %>
</li>
<% end %>
</ul>
Logoutput after choosing category with id 2 on the dropdown-menu
Processing ProjectsController#index (for 127.0.0.1 at 2009-02-20 17:26:10) [GET]
Parameters: {"commit"=>"Show", "http://localhost:3000/categories/id"=>{"category_ids"=>"2"}}
Category Model
class Category < ActiveRecord::Base
has_and_belongs_to_many :projects, :join_table => "categories_projects"
end
Categories Controller
class CategoriesController < ApplicationController
def show
#projects = Category.find(params[:id]).projects.find(:all)
respond_to do |format|
format.html # show.html.erb
end
end
end
Project Model
class Project < ActiveRecord::Base
has_and_belongs_to_many :categories, :join_table => "categories_projects"
end
Projects Controller
class ProjectsController < ApplicationController
def show
#projects = Project.find(:all)
#project = Project.find(params[:id])
respond_to do |format|
format.html # show.html.erb
end
end
def index
#projects = Project.find(:all)
respond_to do |format|
format.html # index.html.erb
end
end
end
part of 'rake routes' output
category GET /categories/:id {:controller=>"categories", :action=>"show"}
You're passing a parameter called :category_ids, but you're not accessing that anywhere.
form_for category_url(:id)
This will submit your form to the path /categories/1 or whatever id you're currently viewing. You're then using that :id for finding your category projects:
#projects = Category.find(params[:id]).projects.find(:all)
So you're just showing the same ones over again. Because it's a form, you're submitting a query with the :category_ids parameter:
POST /categories/1?category_ids=2
You could just change your Category.find to use the other parameter instead. But, normally to view category 2 you would just use the url /categories/2, where 2 is your :id parameter. You have two ids in that path, and you should decide how you want to resolve that.
One option is to use the categories_path for the form action, and change the collection_select :category_ids parameter to just :id:
/categories?id=2 # from the form
/categories/2 # from a link
But if you're just listing projects, I would move this logic into the projects controller (index action), so your URLs would look like:
/projects?category_id=2` # from the form
/categories/2/projects # from a link
Thanks Andrew, but I solved it myself this way:
I got rid of collection_select, changed to select, added the filter-action (with the according route in config/routes.rb) and now everything works as I expected.
...I'm trying to get an observer on the select-menu, that submits it's value to the filter-action as soon as the user changes the selection in the select-menu, but that's another story. ;-)
New partial with dropdown-menu and project-list
<% form_tag(filter_category_path(:id), :method => :post, :class => 'categories') do %>
<label>Categories</label>
<%= select_tag(:category, options_for_select(Category.all.map {|category| [category.name, category.id]}, #category.id)) %>
<%= submit_tag "Go" %>
<% end %>
<ul class="projects">
<% #projects.each do |_project| %>
<li>
<%= link_to(_project.name, _project) %>
</li>
<% end %>
</ul>
New categories controller
class CategoriesController < ApplicationController
def show
#category = Category.find(params[:id])
#projects = #category.projects.find(:all)
respond_to do |format|
format.html
end
end
def filter
#category = Category.find(params[:category])
#projects = #category.projects.find(:all)
respond_to do |format|
format.html
end
end
end