JSON Rendering: How to include the opening bracket description? - ruby-on-rails

I have some Ruby code that is putting out JSON like this:
def nearby
##bathrooms = Bathroom.geo_scope(:origin =>[params[:lat],[params[:lon]]], :within => 5)
lat = params[:lat]
lon = params[:lon]
##bathrooms = Bathroom.geo_scope(:within => 5, :origin => [45.580639,-122.677682], :order=>'distance')
#bathrooms = Bathroom.geo_scope(:within => 3, :origin => [lat,lon], :order=>'distance')
respond_to do |format|
format.json { render :json => #bathrooms }
format.js { render :nothing => true }
end
end
The result is something like this:
[{"ID":129,"access":"1","avail":"0","bathroomtype":"0","city":"PORTLAND","comment":"","country":"US","created":"0000-00-00 00:00:00","directions":"The two bathrooms are in the long hallway at the back of the restaurant, past the bar.","distance":"2.94986114636676","lat":"45.539217","lon":"-122.661795","modifed":"0000-00-00","name":"Echo","postal":"97212-3727","slug":"echo-portland170","source":"","state":"OR","street":"2225 NE Martin Luther King Jr. Blvd"},
What I don't know how to do is to name the set? For example, after the first enclosing bracket I would like to name the set bathrooms. How can I do this is Rails?

Try:
render json: { bathrooms: #bathrooms }

It looks like #bathrooms is a list, so you can't assign keys to elements in lists. You'll need to make a map first. For example, if you only want the first element:
format.json { render :json => {:bathrooms => #bathrooms[0]} }
To set all list elements:
format.json { render :json => {:bathrooms => #bathrooms} }

Related

Rendering Two JSON Items In Rails

This seems like a duplicate question but the answers on the others posts don't seem to work for my issue here.
I'm needing to render two JSON items here within my index method in my controller:
def index
#user = User.all
#libraries = Library.all.order(:created_at)
user_cards = current_user.libraries
render :json => #libraries, :include => :user
render :json => user_cards
end
I attempted to do it this way (failed with a 500 error):
render :json => #libraries, user_cards, :include => :user
And I also attempted to do it this way (also failed with a 500 error): render :json => #libraries :include => [:user, :user_cards]
UPDATE
This is the most recent attempt as rendering the json properly:
def index
#user = User.all
#libraries = Library.all.order(:created_at)
user_cards = current_user.libraries
render json: {
user_cards: user_cards,
libraries: #libraries.as_json(include: [:user])
}
end
The issue with this is that I am now getting an error on libraries throughout my application as it stands. If I simply just render json like I originally had it (render :json => #libraries, :include => :user), I do not get this error. So, I'm assuming the way I have it is still not correct. The exact error on libraries is being called within one of my React components where I use filter:
Error: Uncaught TypeError: this.props.librarys.filter is not a function
Error Location:
let filteredCards = this.props.librarys.filter(
(library) => {
return library.title.toLowerCase().indexOf(this.state.search.toLowerCase()) !== -1 || library.desc.toLowerCase().indexOf(this.state.search.toLowerCase()) !== -1
}
)
Controller can only return one response, you can achieve this with combining this two returns into one:
respond_to do |format|
format.json { render json: { user_cards: user_cards,
libraries: #libraries } }
end

How to access the updated object in a javascript callback (instead of the old object)

Building on the helpful and working solution presented here, I'm trying to fix my update callback as well.
Problem is, the specific unit that I'm trying to extract data from is always the old cached version, even though this callback is triggered by a successful update action.
// callback triggered by the update action
$('.best_in_place').bind("ajax:success", function () {
...
console.log(unit.duration);
// which is exactly the same as
console.log(<%= Unit.find(unit.id).unit_users.pluck(:duration).sum %>);
// and both print the OLD duration val instead of the updated val which is in the database
});
and the unit_users_controller code...
def update
#unit = #unituser.unit
respond_to do |format|
if #unituser.update(unit_user_params)
#unit.reload
logger.info('-----------------------------------------------------------------')
logger.info('#unit.duration in the controller is ' + #unit.duration.to_s) # which is the correct value
logger.info('-----------------------------------------------------------------')
gon.unit_duration = #unit.duration # an experiment which didn't work for me
format.json {respond_with_bip(#unituser) }
else
# format.html { render :action => 'edit' }
format.json { respond_with_bip(#unituser) }
end
end
end
I've tried several versions of unit.reload, and nothing helps. Maybe I was putting it in the wrong place?
I did this one sometime ago here is my code, maybe it will help you:
Javascript:
$(document).ready(function() {
$('.price_bind').bind("ajax:success", function (event, data, status, xhr) {
var parsed_data = jQuery.parseJSON(data);
$(this).text(parsed_data.newprice);
$(this).parentsUntil('body').find(".totalpricep span").text(parsed_data.totalprice);
});
}
View:
<%= best_in_place detail, :price, :classes => 'price_bind', :path => purchase_detail_path(#purchase, detail)%>
Controller:
def update
respond_to do |format|
if #detail.update_attributes(params[:detail])
#n=#detail.mk_bal
#r=false
if #detail.purchase != nil
#p=#detail.purchase.totalprice
if params[:detail]['status'] && #purchase.step==1
#remdet = #purchase.details.where(:step => 1, :status => false)
if #remdet.empty?
#purchase.update_attribute(:step, 2)
#r=true
end
end
else
#p=nil
end
format.html { redirect_to #detail, notice: 'Detail was successfully updated.' }
format.json { render :json => {:newprice => #n, :totalprice => #p, :newstatus => #detail.status, :refresh => #r}}
else
format.html { render action: "edit" }
format.json { render json: #detail.errors, status: :unprocessable_entity }
end
end
end
This isn't about caching. Your Ruby code is evaluated server-side, before the JavaScript is ever send to the client, and it's only evaluated once, long before the AJAX request can happen.
The client never sees this line:
console.log(<%= Unit.find(unit.id).unit_users.pluck(:duration).sum %>);
All the client will see is something like:
console.log(32); // or whatever the sum is
You cannot use <%= %> here. That will always give you the original value. Instead, you need to send the new value to the client in response to the AJAX request.

param is missing or the value is empty: user_club - rails

I want to call create action of controller user_clubs and I did this way:
View Clubs
<button>
<%= link_to "Join Club", user_clubs_path(:user_id => current_user.id, :club_id => #club.id, :join_date => Date.current), :method => :post %>
</button>
Controller user_clubs
def create
#user_club = UserClub.new(user_club_params)
respond_to do |format|
if #user_club.save
format.html { redirect_to #user_club, notice: 'User club was successfully created.' }
format.json { render :show, status: :created, location: #user_club }
else
format.html { render :new }
format.json { render json: #user_club.errors, status: :unprocessable_entity }
end
end
end
def user_club_params
params.require(:user_club).permit(:user_id, :club_id, :join_date) --->**Error here**
end
Error information
app/controllers/user_clubs_controller.rb:75:in user_club_params'
app/controllers/user_clubs_controller.rb:28:increate'
Request
Parameters:
{"_method"=>"post",
"authenticity_token"=>"5Grhb+LIGt9B8XbnEcvg7BZQlDE935KO/aeikZoqxYs=",
"club_id"=>"1",
"join_date"=>"2014-11-17",
"user_id"=>"2"
}
Clubs and UserClubs are different. Club is a model that represents a team of people and user_clubs is the model that represents the many-to-many relationship between Users and Clubs.
First, can someone explain me how the call to user_clubs_path followed by the arguments know that has to go to the action create of user_clubs controller?
In second, the objective problem, why is this an error?
First question
Because of your routes definition, type into a terminal:
rake routes
And you'll see all generated routes and its associated helpers. First column (rake output) references the named helper: user_clubs => user_clubs_path):
Second question
You should add the parameters into user_club key, because you're requiring (by strong_parameters) this "scope" params.require(:user_club):
user_clubs_path(:user_club => {:user_id => current_user.id, :club_id => #club.id, :join_date => Date.current})
You'll receive in the controller:
{
"_method" => "post",
"authenticity_token" => "...",
"user_club" => {
"club_id" => "1",
"join_date"=> "2014-11-17",
"user_id"=> "2"
}
}
The parameters need to be nested under the user_club key. Try this instead:
user_clubs_path(:user_club => {:user_id => current_user.id, :club_id => #club.id, :join_date => Date.current})

Remove blank element from array

When I'm saving multiple select from a ruby on rails form it appears to be adding a blank element at the front. How do I remove it? The field is selected_player.
{"utf8"=>"✓",
"authenticity_token"=>"H8W7qPBezubyeU0adnTGZ4oJqYErin1QNz5oK0QV6WY=",
"schedule"=>{"event"=>"1",
"result_id"=>"",
"time"=>"26/10/2012",
"duration"=>"15",
"arrival_time"=>"14",
"location_id"=>"25",
"selected_players"=>["", "38", "41"],
"team_id"=>"1",
"opponent_id"=>"7",
"home_or_away"=>"Home"},
"commit"=>"Save Event"}
controller
def update
#schedule = Schedule.find(params[:id])
#user = User.find(current_user)
#players = User.where(:team_id => current_user[:team_id]).all
respond_to do |format|
if #schedule.update_attributes(params[:schedule])
Notifier.event_added(#user,#schedule).deliver
format.html { redirect_to(#schedule,
:notice => "#{event_display_c(#schedule.event)} vs #{#schedule.opponent.name} was successfully updated.") }
format.json { head :no_content }
else
format.html { render :action => "edit" }
format.json { render :json => #schedule.errors, :status => :unprocessable_entity }
end
end
end
This works for empty strings:
array.delete_if(&:empty?)
To filter out empty strings and nil values use:
array.delete_if(&:blank?)
Example:
>> a = ["A", "B", "", nil]
=> ["A", "B", "", nil]
>> a.delete_if(&:blank?)
=> ["A", "B"]
Ref reject! of Array class
params["schedule"]["selected_players"] = ["", "38", "41"]
params["schedule"]["selected_players"].reject!{|a| a==""} #gives params["selected_players"] = ["38", "41"]
This should work as well.
params["schedule"]["selected_players"].reject!(&:blank?)
Something like:
params["selected_players"].select!{|val| !val.empty?}
should work
What is "selected_players"? Is it something like "collection_singular_ids" of the collection associations? If so, you can leave it as it is, because ActiveRecord will remove the blank elements from the array with following code:
ids = Array.wrap(ids).reject { |id| id.blank? }
If you want to handle this in the model rather than the controller you can add a setter method like this
def selected_players=(param_array)
write_attribute(:selected_players, param_array.reject(&:blank?))
end
I think params["selected_players"].compact is the most succinct.
Docs are here: http://www.ruby-doc.org/core-1.9.3/Array.html#method-i-compact

responding with multiple JSON renders. (Ruby/Rails)

This is a relatively simple one and I'm pretty sure its just syntax.
Im trying to render multiple objects as json as a response in a controller. So something like this:
def info
#allWebsites = Website.all
#allPages = Page.all
#allElementTypes = ElementType.all
#allElementData = ElementData.all
respond_to do |format|
format.json{render :json => #allWebsites}
format.json{render :json =>#allPages}
format.json{render :json =>#allElementTypes}
format.json{render :json =>#allElementData}
end
end
end
Problem is I'm only getting a single json back and its always the top one. Is there any way to render multiple objects this way?
Or should I create a new object made up of other objects.to_json?
you could actually do it like so:
format.json {
render :json => {
:websites => #allWebsites,
:pages => #allPages,
:element_types => #AllElementTypes,
:element_data => #AllElementData
}
}
in case you use jquery you will need to do something like:
data = $.parseJSON( xhr.responseText );
data.websites #=> #allWebsites data from your controller
data.pages #=> #allPages data from your controller
and so on
EDIT:
answering your question, you don't necessarily have to parse the response, it's just what I usually do. There's a number of functions that do it for you right away, for example:
$.getJSON('/info', function(data) {
var websites = data.websites,
pages = data.pages,
...
});

Resources