RJS: Ajaxified select_tag - javascript

Since I didn't get the expected answer on my last question I'll try to simplify and narrow my question:
How can I build a dropdown-menu that uses AJAX (no submit-button) to call the show action of a certain controller?
The following things are given:
Model-Association is Categories HABTM Projects, therefore the dropdown-menu consists of all category names.
The view partial where the dropdown-menu should be implemented. Below the dropdown menu is a list of projects that should change according to the choice made in the dropdown menu:
<!-- placeholder for AJAX dropdown menu -->
<!-- list of projects related to categories chosen by the select tag -->
<ul class="projects">
<% #projects.each do |_project| %>
<li>
<%= link_to(_project.name, _project) %>
</li>
<% end %>
</ul>
The Categories controller with the show-action that should be called:
class CategoriesController < ApplicationController
def show
# params[:id] should be the choice the user made in the dropdown menu
#category = Category.find(params[:id])
#projects = #category.projects.find(:all)
respond_to do |format|
format.html # show.html.erb
format.js # needed for ajax response?
end
end
def index
#projects = Category.find(params[:id]).projects.find(:all)
#category = #project.categories.first
respond_to do |format|
format.html # index.html.erb
end
end
end
The route to call the show-action in the Categories controller:
category GET /categories/:id {:controller=>"categories", :action=>"show"}
How would you implement this? Any help is very apreciated!

How about this:
<% form_for :category, :url => { :action => "show" } do |f| %>
<%= select_tag :id, options_from_collection_for_select(Category.find(:all), :id, :name),
{ :onchange => "this.form.submit();"} %>
<% end %>
That will call a traditional html call, so it will refresh the entire page (and respond to format.html).
Then the controller will find the category by the submitted [:id]
#category = Category.find(params[:id])

Related

Rails jQuery is rendering text instead of collection

I created a file inside my javascript folder "load-contacts.js" in hopes of trying to load the collections as well as the pagination via jquery.
$(document).on('turbolinks:load', function() {
$('#contacts').html("<%= j render(#contacts) %>");
$('#paginator').html("<%= j paginate #contacts, remote: true %>");
});
When I run this code it actually works and change the part where it intended to change. However, it is only rendering it as text as it instead of the collection and the pagination (kaminari gem).
Here's how it looks like:
Any idea what am i missing here?
UPDATES:
Here's my contacts controller
class ContactsController < ApplicationController
before_action :find_params, only: [:edit, :update, :destroy]
def index
# catch the group id from params in session
session[:selected_group_id] = params[:group_id]
#contacts = Contact.by_group(params[:group_id]).search(params[:term]).order(created_at: :desc).page params[:page]
end
def autocomplete
#contacts = Contact.search(params[:term]).order(created_at: :desc).page(params[:page])
render json: #contacts.map { |contact| { id: contact.id, value: contact.name } }
end
def new
#contact = Contact.new
end
def create
#contact = Contact.new(contact_params)
if #contact.save
flash[:success] = "Contact was successfully created."
redirect_to(previous_query_string)
else
render 'new'
end
end
def edit
end
def update
if #contact.update(contact_params)
flash[:success] = "Contact successfully updated."
redirect_to(previous_query_string)
else
render 'edit'
end
end
def destroy
#contact.destroy
flash[:success] = "Contact successfuly deleted."
redirect_to contacts_path
end
private
def contact_params
params.require(:contact).permit(:name, :email, :phone, :mobile, :company, :address, :city, :state, :country, :zip, :group_id, :avatar)
end
def find_params
#contact = Contact.find(params[:id])
end
def previous_query_string
session[:selected_group_id] ? { group_id: session[:selected_group_id] } : {}
end
end
Here's the part for the kaminari gem pagination:
<div class="card-footer">
<div class="pagination justify-content-center" id="paginator">
<%= paginate #contacts, remote: true %>
</div>
</div>
And here's where I am suppose to be rendering the contacts inside the views/contacts/index.html.erb:
<table class="table" id="contacts">
<%= render partial: "contact", object: #contacts, remote: true %>
</table>
you can't use j render outside .erb. Because j render is method of rails, your js just know it is string
what you need are use script tag inside your html.erb, or use rails ajax. It is working as expected
reference: How to render partial on the same page after clicking on link_to with AJAX
have you tried escape_javascript before calling render.
something like:
<%= escape_javascript render(my_partial) %>

rails create pages by hash with nested resources

image
as image, i'd like to make page with nested resources.
my plan is
register user
create tour(request input informations by params)
if #tour.tour_days(params) value is over 1
create day pages as much as the number of user put into tour_day params
so i want to create page like(if 1user create first tour and put 3 into tour_day)
localhost:3000/tours/1/days/1
localhost:3000/tours/1/days/2
localhost:3000/tours/1/days/3
(if 1user create second tour and put 2 into tour_day)
localhost:3000/tours/2/days/1
localhost:3000/tours/2/days/2
i'd like to create these pages by automatically when user put number into tour_day and click submit button from tour_view
i've done create nested resources and throw data but i can't make clearly.. ;(
the reason i nest resources and create additional controller(day_controller) is afterwords i'd like to add recommendation function(recommendation for whom couldn't make schedule)...
actually i'd like to make page like airbnb(creating room process)
(Am i doing right process??)
tour_controller
class ToursController < ApplicationController
before_action :set_tour, only:[:show, :edit, :update]
before_action :authenticate_user!, except:[:show]
def index
#tours = current_user.tours
end
def show
end
def new
#tour = current_user.tours.build
end
def create
#tour = current_user.tours.build(tour_params)
i = 0
if #tour.save
redirect_to new_tour_day_path(#tour.id, #day), notice: "Saved Your Tour Courses"
else
render :new
end
end
def edit
end
def update
if #tour.update(tour_params)
redirect_to edit_tour_day_path(#tour.id, #day), notice: "Updated Your Tour Courses"
else
render :edit
end
end
private
def set_tour
#tour = Tour.find(params[:id])
end
def tour_params
params.require(:tour).permit(:tour_title, :tour_theme, :tour_summary, :tour_language, :tour_day, :tour_member, :tour_car, :tour_camera, :price)
end
end
day_controller
class DaysController < ApplicationController
before_action :set_tour
before_action :set_day, only: [:show, :edit, :update]
before_action :authenticate_user!, except:[:show]
def index
end
def show
end
def new
#tour = current_user.tours.find(params[:tour_id])
#day = #tour.days.build
end
def create
#tour = current_user.tours.find(params[:tour_id])
#day = #tour.days.build(day_params)
if #day.save
redirect_to edit_tour_day_path(#tour.id, #day), notice: "Saved Your Tour Courses"
else
render :new, notice: "Errors"
end
end
def edit
end
def update
if #day.update(day_params)
redirect_to edit_tour_day_path(#day), notice: "Updated Your Tour Courses"
else
redner :edit
end
end
private
def set_tour
#tour = Tour.find(params[:tour_id])
end
def set_day
#day = Day.find(params[:id])
end
def day_params
params.require(:day).permit(:day_number)
end
end
tour_view(test page)
<div class="container">
<%= form_for #tour, :html => { multipart: true } do |f| %>
<div class="col-md-3 select">
<div class="form-group">
<label>Maximum Tour Day</label>
<%= f.select :tour_day, [["1",1], ["2",2], ["3",3], ["4",4]], prompt: "Select...", class: "form-control" %>
</div>
</div>
<div class="actions">
<%= f.submit "save", class: "btn btn-primary" %>
</div>
<% end %>
</div>
day_view(test page)
<div class="container">
<%= form_for [:tour, #day] do |f| %>
<label>day_number</label>
<%= f.text_field :schedule_, class: "form-control" %>
<div class="actions">
<%= f.submit "Save", class: "btn btn-primary"%>
</div>
<% end %>
</div>
** additionally
Inside of Day view
i'd like to create schedule like
image
when i click an icon, the form appear and make detail tour schedule with ajax.
how can i approach to create this function??
----
this is my first project of web programming, im so confusing
plz... give me some(lots of) help...
----
im using rails 5.0.0.1 and rails 2.3.1

Why does my Rails AJAX delete method only work if I refresh?

I just incorporated the DESTROY method for items in my school project. It worked fine, but now I must use AJAX to complete the action. After implementing this code, it only displays on the browser when I refresh the page, and not instantly when I delete an item. If I did not include enough information please let me know.
_item.html.erb
<% item.each do |i| %>
<p><%= i.name %> | <%= link_to "Complete", i, method: :delete, remote: true, class: 'glyphicon glyphicon-ok' %></p>
<% end %>
destroy.js.erb
<% if #item.destroyed? %>
$('#item-' +<%= #item.id %>).hide();
<% else %>
$('#item-' +<%= #item.id %>).prepend("<%= flash[:error] %>");
<% end %>
items_controller.rb
class ItemsController < ApplicationController
def index
#items = Item.all
end
def show
#item = Item.find(params[:id])
end
def new
#item = Item.new
end
def edit
#item = Item.find(params[:id])
end
def create
#item = Item.new(params.require(:item).permit(:name))
if #item.save
flash[:notice] = "The item was added to your list."
redirect_to current_user
else
flash[:error] = "There was a problem creating your item."
redirect_to current_user
end
end
def destroy
#item = Item.find(params[:id])
if #item.destroy
flash[:notice] = "\"#{#item.name}\" was completed and destroyed."
else
flash[:error] = "There was an error completing the item."
end
respond_to do |format|
format.html
format.js
end
end
end
Flash sets the message for the next request, this is why it works correctly in your create action (because you are redirecting). In your destroy action, you are rendering (which is not good, since it means delete request gets resent on page refresh, you should be redirecting here too) and setting flash, so it shows up on next request (refresh). If you want to send message for the response to the current request, you have to use flash.now :
flash.now[:notice] = "\"#{#item.name}\" was ..."
Again, you should use flash and redirect on success and use flash.now and render on request failure.
EDIT:
The above paragraphs only apply to html requests. I initially missed the point of the question! Thanks BroiSatse
Looking at your display code, you dont seem to be setting the "#item-#item.id", so your return js does nothing. Add it like this:
<p id="item-<%= i.id %>"><%= i.name %> | ...

How can I update an instance variable with each ajax request?

I have a long block of comments on a view of model Page. Instead of showing all the comments on page load, I'm trying to create a "view more" button that shows the next ten comments. The button sends an ajax request to the controller, which then renders this block using jquery:
_view_more.html.erb
<% comments.each_with_index do |comment, index|%>
<% if (( index > #start_number) && (index < #end_number) ) %>
<%= comment.text %>
<% end %>
Let's say I always want to show the next 10 comments. I would just set #start_number = #start_number + 10 and #end_number = #end_number + 10
in the controller, but instance variables get reset, so #start_number would be nil. How can I set a variable that increases by 10 upon every ajax request?
"view more" button
<%= link_to "view more", view_more_page_path, remote: true %>
pages_controller.rb
def view_more
#page = Page.find(params[:id])
respond_to do |format|
format.html { redirect_to root_path }
format.js
end
end
view_more
$("#comments-body").append("<%= escape_javascript(render 'view_more') %>");
I will use haml and coffee-script
When rendering comments you put an html5 data attribute with the id of the comment:
#view where to render comments
%div#comments-wrapper{:"data-pageid" => #page.id}
=render #comments
=link_to "View more", "#", id: "view-more-link"
The comment partial
#comments/_comment.html.haml
%p.single-comment{:"data-commentid" => comment.id}
=comment.body
application.coffee
$ ->
$("#view-more-link").on 'click', ->
last_comment_id = $("#comments-wrapper .single-comment").last().data('commentid')
page_id = $("#comments-wrapper").data("pageid")
$.ajax
url: "/comments/view_more"
dataType: "script"
data:
last_comment_id: last_comment_id
page_id: page_id
comments_controller
def view_more
#page = Page.find(params[:pageid])
if params[:last_comment_id]
#comments = #page.comments.where("comments.id > ?", params[:last_comment_id]).limit(10)
end
respond_to do |format|
format.js
end
end
comments/view_more.js.erb
$("#comments-wrapper").append("<%= escape_javascript(render #comments) %>");
Note: I don't how your routes were set up so I put the page.id as a data-attribute as well
I would use already implemented pagination gems kaminari or will_paginate. I'll create this example using will_paginate.
First of all, it's important to say that your approach is incorrect, because it loads all comments every view_more request. If you want to show 10 comments, makes sense select only they from database, right? The pagination gem will do it for you!
Let's to the code:
"view more" button
<%= link_to "view more", view_more_page_path, remote: true, id: 'view-more-btn' %>
pages_controller.rb
def view_more
#comments = Page.find(params[:id]).comments.paginate(page: params[:page], per_page: 10)
respond_to do |format|
format.html { redirect_to root_path }
format.js
end
end
_view_more.html.erb
<% #comments.each do |comment| %>
<%= comment.text %>
<% end %>
view_more.js.erb
$("#comments-wrapper").append("<%= escape_javascript(render 'view_more') %>");
# We need to update the 'view more' link with the next page number
$('#view-more-btn').attr('href', '<%= view_more_page_path((params[:page] || 0) + 1) %>')
is it not good to update an hidden variable before making ajax call with the count..?
var currentVal = parseInt($("[type=hidden]").val());
$("[type=hidden]").val( currentVal + 1 );
add the hidden field right at the begining of the comments section with default value as "0"
<input type="hidden" name="hiddenId" id="hiddenId" value="0">
Hope it will help
If you want a quick and dirty approach, you could save start_number and end_number inside a cookie.
But keeping track of what needs to be rendered next on client side would be a right thing to do.

Do I need Ajax or something to put different erb content in my tabs?

I want to have some tabs in an index page.
Each of the tabs should render a different category for the Food Model.
The tabs are working fine, but with static content.
I have 2 tables:
Foods, Categories
And I have a column :category_id in my Foods table.
My associations are: Food has_one category, and Categories has_many foods.
So, when clicking on one of the tabs, a category_id must be passed so that it knows what content to render.
I don't know how do I need to make my view (and maybe a scope?)...It's the first time I do this.
This is the code for one of the tabs:
<ul class="accordion-tabs">
<li class="tab-header-and-content">
Entrantes
<div class="tab-content row">
<% #foods.by_category(params[1]).each do |food| %>
<%= render "food_card" %>
<% end %>
</div>
</li>
I'm trying to pass params manually in each of the tabs, but I don't know if I need AJAX or something. Please help!
Update
My Foods Controller
def index
#addictfood = Addictfood.last
#food = Food.last
#foods = Food.all
#category = Category.find params[:id]
respond_to do |format|
format.html
format.json { render json: #category.foods.to_json }
end
end
My index view (Foods)
<ul class="accordion-tabs">
<div class="categories">
<li class="tab-header-and-content">
<% #categories.each do |tab| %>
<%= link_to tab.name, category_show_path(tab), class: "tab-link" %>
<% end %>
</li>
</div>
</ul>
</article>
<script>
$(".categories").on("click", "a", function() {
$.ajax({
url: $(this).attr("href"),
dataType: "json",
success: function(data) {
$(".category").html(data)
}
});
});
</script>
Error
Started GET "/foods" for 127.0.0.1 at 2014-07-02 20:41:53 +0200
Processing by FoodsController#index as HTML
Addictfood Load (0.3ms) SELECT "addictfoods".* FROM "addictfoods" ORDER BY "addictfoods"."id" DESC LIMIT 1
Food Load (0.3ms) SELECT "foods".* FROM "foods" ORDER BY "foods"."id" DESC LIMIT 1
Completed 404 Not Found in 4ms
ActiveRecord::RecordNotFound (Couldn't find Category without an ID):
app/controllers/foods_controller.rb:7:in `index'
If each food has one category only you should use the belongs_to category association.
In Category, you can leave it with has_many foods.
In the show view of the category controller you can pull the food like this:
<% #category.foods.each do |food| %>
<%= food.title %>
<% end %>
Params
You won't be able to pass params to your tabs if they are within a single request. Params are sent via HTTP in the URL, or behind the scenes, to give you variables in the backend of your app:
Params are there to give you the ability to manipulate the response after a request is made to the controller. In your case, with your files on the same page, you will need to use them in an ajax request to pull the relevant category content
--
Ajax
As mentioned by #mavis, you'll want to set up your models as such:
#app/models/food.rb
Class Food < ActiveRecord::Base
belongs_to :category
end
#app/models/category.rb
Class Category < ActiveRecord::Base
has_many :foods
end
This will give you the ability to list your tabs like this:
<div class="categories">
<% #categories.each do |tab| %>
<%= link_to tab.name, category_show_path(tab) %>
<% end %>
</div>
The problem you'll need to use ajax to overcome is to use it to populate your tabs, like this:
#app/assets/javascripts/application.js
$(".categories").on("click", "a", function() {
$.ajax({
url: $(this).attr("href"),
dataType: "json",
success: function(data) {
$(".category").html(data)
}
});
});
You'll then have to do this:
#app/controllers/categories_controller.rb
Class CategoriesController < ApplicationController
def show
#category = Category.find params[:id]
respond_to do |format|
format.html
format.json { render json: #category.foods.to_json }
end
end
end

Categories

Resources