So, I just discovered select2. Awesome. Now I'm trying to figure out how to use it, server side with ajax / json. All of the examples I see, everywhere, show using select2 with JSONP to retrieve data from an external source. I feel like this should be even easier if calling from a local model, no? I'll get right to the nitty gritty. json returns a value, but the searchbox doesn't autocomplete, it stays blank.
view html:
<%= form_tag request_pal_path, remote: true do %>
<%= hidden_field_tag :email, nil, class: 'ui-corner-all' %>
<%= submit_tag "Send request", class: 'button' %>
<% end %>
and calling some js on it:
$(document).ready(function() {
$("#find_user #email").select2({
width: '400px',
placeholder: "Find user...",
minimumInputLength: 1,
multiple: false,
id: function(obj) {
return obj.id; // use slug field for id
},
ajax: { // instead of writing the function to execute the request we use Select2's convenient helper
url: "/users",
dataType: 'json',
data: function (term, page) {
return {
q: term, // search term
page_limit: 10
};
},
results: function (data, page) { // parse the results into the format expected by Select2.
// since we are using custom formatting functions we do not need to alter remote JSON data
return {results: data};
}
},
formatResult: FormatResult,
formatSelection: FormatSelection,
escapeMarkup: function (m) { return m; }
});
})
function FormatResult(user) {
return '<div>' + user.name + '</div>';
}
function FormatSelection(user) {
return user.name;
}
which goes to the controller, user index action:
def index
#find = User.where('name LIKE ?', "%#{params[:q]}%")
#users = #find.where('id NOT IN (?)', current_user.id).order('random()').page(params[:page]).per(100)
#title = "Potential pals"
respond_to do |format|
format.html
format.js {
#find = #find
#users = #users
}
format.json { #find }
end
end
and I made a .json file for it to respond to (not sure if this is even necessary):
<% #find.each do |user| %>
<%= user.name %>
<% end %>
So, the json is working, to an extent. If I look in the developer console, it shows a response coming from http://localhost:3000/users.json?q=tay, or whereever, and it returns a single value, for Taylor (in that instance). But when I search inside of the select2 search box, it just spins and spins, with no results. No console errors, so that's nice, ha. Thoughts? Thanks!
The select2 plugin expects JSON data in the following format:
[ { "text": "Taylor", "id": 1 }, { "text" : "Tailor", "id": 2 }, ...]
So you need to replace name with text in your user model when converting to JSON:
def as_json(*args)
super.tap { |hash| hash["text"] = hash.delete "name" }
end
and then in the index method:
def index
#find = User.where('name LIKE ?', "%#{params[:q]}%")
#users = #find.where('id NOT IN (?)', current_user.id).order('random()').page(params[:page]).per(100)
#title = "Potential pals"
respond_to do |format|
format.html
format.js {
#find = #find
#users = #users
}
format.json { render json: #find, :only => [:text, :id] } # might be :name here ?
end
end
and you don't need the view for JSON.
I guess the problem is in your .json file, since select2 needs json array or json object. Try to remove it and respond with format.json { render json: #find.to_json }. Other code seems ok to me.
Related
I use select2 and want to create new tags and then save them.
i have form for #cost and for select2 this
<%= f.collection_select :product_ids, Product.all,:id, :name ,{include_hidden: false},{ multiple: true} %>
for creation new product i have this js code
$(document).ready(function () {
$('#cost_product_ids').select2({
tags: true,
tokenSeparators: [",", " "],
createProduct: function (product) {
return {
id: product.term,
text: product.term,
isNew: true
};
}
}).on("change", function (e) {
var isNew = $(this).find('[data-select2-tag="true"]');
if (isNew.length) {
$.ajax({
type: "POST",
url: "/product_new",
data: {product: isNew.val()}
});
}
});
});
and controller method for save new product
def product_new
product = Product.find_by(name:params[:product])
Product.create(name:params[:product]) if !product
render json: :ok
end
cost create action
def create
#cost = Cost.new(costs_params)
if #cost.save
flash[:notice] = t('added')
if params[:add_more].present?
redirect_back(fallback_location: root_path)
else
redirect_to #cost
end
else
render action: 'edit'
end
end
def costs_params
params.require(:cost).permit(:day, :amount, :description, :source,:tag_list,:product_ids=>[])
end
it works ok, but when i want to save my #cost record with this newly created product i have received only name of my tag without id.
For example i have products water=>id:1,beer=>id:2,and create new juice tag in db it has id:3
on create in have params "product_ids"=>["1", "2", "juice"]
How to fix it?
you shouldn't use id: product.term,
but id: product.id,
I'm using the react-rails gem and have two models: Message and User. User has_many :messages.
In my message.js.jsx, I'd like to show the User of that message. In regular erb, it'd just be <%= message.user.name %>. How would I do this in the message.js.jsx component?
You could rename your component to message.js.jsx.erb and use ERB in it, but it will only be compiled once when Rails starts up.
A more React-ish way to handle is to AJAX load the user data in componentDidMount (or a Store, if using Flux).
message.js.jsx
getInitialState: function() {
return { user: { name: '' } };
},
componentDidMount: function() {
$.getJSON('/users/'+ this.props.id +'.json', function(userData) {
if (this.isMounted()) {
this.setState({ user: userData })
}
});
},
You can create a Rails endpoint to return userData as JSON something like this:
users_controller.rb
def show
#user = User.find(params[:id])
respond_to do |format|
format.html # default html response
format.json { render json: #user.to_json(only: [:id, :name]) }
end
end
See Facebook's page on this for more details
I agree with Unixmonkey that is the react way. You can also do it a few more ways.
#user = JSON.parse user.to_json(include: [:messages], only: [:id, :name])
As well as using componentDidMount to hit a JSON endpoint using jbuilder which you can put a timeout on if you want to update dynamically.
componentDidMount: function() {
$.getJSON('/users/'+ this.props.id +'.json', function(user) {
if (this.isMounted()) {
this.setState({ user: user })
}
});
},
Your show.json.jbuilder under user views would look something like this:
json.id #user.id
json.name #user.name
json.messages #user.messages do |message|
json.id message.id
json.content message.content
json.created_at message.created_at
end
My class Contribution belongs_to User. I have a form for a new contribution which includes a search field for for the user to whom the contribution will belong -
in /contributions/new.html.erb -
<%= text_field_tag :search, params[:search], id: "search" %>
<%= link_to "Search", search_contributions_path, id: "search_submit" %>
in application.js.erb
$(document).on("click", "a#search_submit", function(){
$.ajax({
url: $(this).attr("href"),
data: {query: $("#search").val() },
success: function(data) {
var user = $.parseJSON(json);
}
});
});
and in contribution_controller.rb -
def search
#users = User.search(params[:search])
render :users => #users.to_json
end
My controller is trying to render a page - how do I make it return the result I want to the view?
you could write:
render json: { users: #users.to_json }
Wouldn't a respond_to fix that? Something like
respond_to :html, :xml, :json
depending on the formats you want to "respond to", obviously.
Here's the source http://apidock.com/rails/ActionController/MimeResponds/ClassMethods/respond_to
You must use the following in your controller:
respond_to :html, :xml, :json
And You can return json as the following:
respond_to do |format|
format.json { render json: #users.result() }
end
Or as the following:
render json: { :users => #users.result().to_json }
I think you used ransak for searching, so you need use #users.result().to_json instead of #users.to_json
The issue is not related to json, your issue is related to javascript change your ajax call to be:
$(document).on("click", "a#search_submit", function(e){
e.preventDefault();
$.ajax({
url: $(this).attr("href"),
data: {query: $("#search").val() },
success: function(data) {
var user = $.parseJSON(json);
}
});
});
So I have been playing around with acts_as_taggable_on in active admin, and for the most part everything is working as expected.
However, whenever I search for tags, and add an existing tag to a model, it seems to save it as the ID, rather than as the name. Creation of new tags returns the name fine, and when I go to edit the object again the tags remain tagged by the name. But when I try and add another tag, one that already exists in the database, it returns the name in the form, and seems to save OK, but when I go back to edit the onject again the tag shows up as an ID, rather than the name.
In admin/gift.rb:
controller do
def autocomplete_gift_tags
#tags = ActsAsTaggableOn::Tag
.where("name LIKE ?", "#{params[:q]}%")
.order(:name)
respond_to do |format|
format.json { render json: #tags , only: [:id, :name], root: false }
end
end
end
In tag-autocomlete.js:
$(document).ready(function() {
$('.tagselect').each(function() {
var placeholder = $(this).data('placeholder');
var url = $(this).data('url');
var saved = $(this).data('saved');
$(this).select2({
tags: true,
placeholder: placeholder,
minimumInputLength: 1,
initSelection: function(element, callback) {
saved && callback(saved);
},
ajax: {
url: url,
dataType: 'json',
data: function(term) {
return {
q: term
};
},
results: function(data) {
return {
results: data
};
}
},
createSearchChoice: function(term, data) {
if ($(data).filter(function() {
return this.name.localeCompare(term) === 0;
}).length === 0) {
return {
id: term,
name: term
};
}
},
formatResult: function(item, page) {
return item.name;
},
formatSelection: function(item, page) {
return item.name;
}
});
});
});
And in my _gift_form.html.erb:
<%= f.input :tag_list, label: "Tags", input_html: { data: { placeholder: "Enter tags", saved: f.object.tags.map{|t| {id: t.name, name: t.name}}.to_json, url: autocomplete_gift_tags_path }, class: 'tagselect' } %>
Can't work out why the new ones are working, but the existing tags are not.
change this:
respond_to do |format|
format.json { render json: #tags , only: [:id, :name], root: false }
end
to this:
respond_to do |format|
format.json { render :json => #tags.collect{|t| {:id => t.name, :name => t.name }}}
end
So I'm following this outdated tutorial on using the jquery-ui slider with rails 3 beta since it's the only one I've found.
My view which has the script
<p>
<p>Showing all stocks between <span id="x_low_selected"><%= #price_range.first %></span> and <span id="x_high_selected"><%= #price_range.last %></span></p>
</p>
<div id="x_slider"></div>
<ul id="x_stock_list">
<%= render 'map' %>
</ul>
<script type="text/javascript">
$(function() {
$("#x_slider").slider( {
range: true,
step: 1,
max: <%= #price_range.last %>,
min: <%= #price_range.first %>,
values: [<%= #price_range.first %>, <%= #price_range.last %> ],
stop: function(event, ui) {
var prices = $('#x_slider').slider('option', 'values');
$('#x_low_selected').html(prices[0]);
$('#x_high_selected').html(prices[1]);
$.ajax({
url: 'http://localhost:3000/users',
type: "GET",
data: { low: prices[0], high: prices[1] },
dataType: 'json'
});
}
});
});
</script>
model method for prices
def self.low_high_prices
[User.minimum(:start), User.maximum(:end)]
end
and the index method in the controller which the ajax should call
def index
#users = User.all
unless params[:low] && params[:high]
#users = User.all
#json = User.all.to_gmaps4rails do |user, marker|
marker.infowindow "<a href=/users/#{user.id}> #{user.name} </a>"
marker.title user.name
end
else
#json = User.where("start >= params[:low] AND end <= params[:high]).to_gmaps4rails do |user, marker|
marker.infowindow "<a href=/users/#{user.id}> #{user.name} </a>"
marker.title user.name
end
end
#price_range = User.low_high_prices
respond_to do |format|
format.html # index.html.erb
format.json { render json: #users }
end
end
Does anyone know why the ajax call isn't being sent to the controller when the slider stops sliding? Not sure if I would be able to use :remote => true because the slider is generated inside the div.
Thanks
Also I am currently using rails 3.2 with ruby 1.9.7
I would guess that your call is going through to the controller, and the controller is returning JSON data.
The problem is that your $.ajax call is doing nothing with the JSON data received.
You need to add a callback function to handle the JSON data passed back in some way in the ajax call:
$.ajax({
url: 'http://localhost:3000/users',
type: "GET",
data: { low: prices[0], high: prices[1] },
dataType: 'json',
success: function (data) {
alert("Success: " + data);
//do something with the data here.
}
});
Also, looking at the controller code, it will only pass back the contents of #users in the JSON data, not the contents of #json or anything else - not sure if that was your intention or not.