Handling a JS POST - ruby-on-rails

I am working on a ruby on rails website that also has javascript.
I have this javascript code, that I made, that makes a POST to the website and completes the action that I want it to.
function create_group_item(group_id, items_id){
$.ajax({
type: "POST",
url: '/group_items/create/',
data: { "group_id": group_id, "items_id": items_id },
success: function(data, textStatus, jqXHR){
},
error: function (jqXHR, textStatus, errorThrown){
console.log(errorThrown);
}
});
}
This works and is fine.
The issue I have is calling that same POST function from another rails controller instead of with javascript. I want to create a group_item from inside the item controller.
I have a variable in my item controller: group_item_info = {"group_id" => group_id.to_i, "item_id" => item_id}
Which, when I print it reads out: {"group_id"=>15, "item_id"=>754} in some instances. So, I have no issue getting the values of the group and item.
QUESTION
How do I call the group_items create function from the items controller.
Attempts so far
code such as:
#group.items << #item
and
def create_group_item
redirect_to url_for(:controller => :group_items_controller, :action => :create)
end
then: create_group_item({group_id: group_id, items_id: item_id})
have only given me errors.

Found a solution:
group_item_info = {"group_id" => group_id.to_i, "items_id" => item_id}
#group_item = GroupItem.new group_item_info
if #stand_item.save
flash[:notice] = "#{#item.name} has been added to your group."
else
flash[:alert] = "Something went wrong adding this item to your group, try again later."
end
One main thing which I had wrong in the other code included items_id vs item_id

Related

Load model association dynamically in Rails form before submit

So I'm not sure how to achieve this or if it's even possible and having a tough time searching for similar examples/answers. But is there a way to dynamically load a model association within a form before you submit it, like without knowing what the instance variable would be ahead of time?
I have models as follows:
Property
has_many :owners, inverse_of: :property
has_many :orders
Owners
belongs_to :property
Orders
belongs_to :property
has_many :owners, through: :property
In my orders.new form I have select boxes to choose a property to create the association for the new order (using Select2 plugin):
<%= bootstrap_form_for(#orders, ...) do |f| %>
...
<%= f.select :property_id, options_from_collection_for_select(Property.all, "id", "full_address"),
{ label: "Property:", include_blank: true }, { id: "orderPropSelect", data: { placeholder: "Select a Property"} } %>
...
<% end %>
So when someone SELECTS a property for the new order, is there a way I can load what owners that property already has associated to it before the form is submitted? Even just being able to see what owners are already there is okay (being able to edit them would be even better but I realize thats probably more complex)
The following code of course doesn't work but looking for something along the lines of:
<%= f.static_control label: "Property Owners" do %>
<% :property_id.owners.each do |owner| %>
<%= owner.name %>
<% end %>
<% end %>
I've tried variations of fields_for but I don't know how to tell the nested fields to be based off what is chosen in the above select (of course loading different owners based on what property is chosen. The errors that I get are undefined method owners for nil:NilClass which are appropriate because I know I'm not telling rails where to look correctly.
So is this possible and if so, how would I achieve this?
(I use bootstrap forms gem incase anyone is wondering about form syntax. I also have cocoon loaded for other forms so if there's a way to use that then I'm not opposed.)
Update Working code, slightly modified for extraneous conditions.
$("#orderPropSelect").off().on('change', function() {
var id = $(this).val();
console.log(id);
if (id !== '') {
$.ajax({
url: '/properties/' + id + '/owners',
dataType: "json",
success: function (data) {
owners_html = '';
$.each(data['owners'], function () {
owners_html += '<p>' + this + '</p>';
});
if (owners_html === '') {
$("#propOwnersShow").html('<p>No owner added yet.</p>');
} else {
$("#propOwnersShow").html($(owners_html));
}
},
error: function (XMLHttpRequest, textStatus, errorThrown) {
console.log(errorThrown);
}
});
} else {
$('#propOwnersShow').html('Please select a Property below to view its associated owners.')
}
});
You need to make sure it matches your underlying routes & stuff, and probably handle the case where there aren't owners, so to hide the owners div. If you want to do more complex stuff you could instead of .pluck build a better array/hash with id's that you can then also use to build elements that you can interact with (e.g. remove them from the list)
# your property controller
before_action :allowed, only [:owners]
def owners
owners = Property.find(params[:id]).owners.pluck(:name)
respond_to |format|
format.json { render json: { owners: owners, success: true } }
end
end
def allowed
# logic to define if this user is allowed to request this data, if you have devise, it could be
user_signed_in? && request.xhr?
end
# your routes.rb
get "properties/:id/owners", to: "properties#owners", as: "property_owners"
# or if you have resources
resources :properties do
member do
get :owners
end
end
# js file
$("#property_id").off().on('change', function() {
var id = $(this).val();
$.ajax({
url: '/properties/'+id+'/owners',
dataType: "json",
success: function(data) {
owners_html = '';
$.each(data['owners'], function() {
owners_html += '<p>'+this+'</p>';
});
$("selector_where_you_want_to_show_owners").html($(owners_html));
},
error: function(XMLHttpRequest, textStatus, errorThrown) {
console.log(errorThrown);
}
});
}
UPDATE:
You can prevent the issue with no Owners by using find_by instead and making sure that you always return an [], this way simplifying the logic on the front-side as well.
# on your controller instead use:
def owners
# .find_by will return "nil" if no properties are found, which
# then we can leverage with .try. .try will "try" calling those
# chained methods without raising an error. It usually returns
# "nil" if the parent is "nil" but .try with .pluck though will
# always return an [] no matter what the ancestors returned.
owners = Property.find_by(id: params[:id]).try(:owners).try(:pluck, :name]
# actually looking at your relationships it seems you could instead
# .where, since that will return an empty array, like so:
# owners = Owner.where(property_id: params[:id]).pluck(:name)
# this only does one database query where the other one does 2
# here we're simply making sure we add something to the array so
# that then on the front-end you can always deal with an array
# without worrying with the "no properties found". .blank? will
# return "true" if the value is nil, empty string, empty hash or
# empty array which works fine for this. So if there's no value in
# the array we add the "no owners" string.
owners << 'No owner added yet.' if owners.blank?
respond_to |format|
format.json { render json: { owners: owners, success: true } }
end
end
# And then since you'll always be returning an array you no longer
# have to worry about an empty array and your ajax function will
# look like this:
$.ajax({
url: '/properties/' + id + '/owners',
dataType: "json",
success: function (data) {
owners_html = '';
$.each(data['owners'], function () {
owners_html += '<p>' + this + '</p>';
});
$("#propOwnersShow").html($(owners_html));
},
error: function (XMLHttpRequest, textStatus, errorThrown) {
console.log(errorThrown);
}
});
Hope this helps

Rails ajax request response

I am using Rails CoffeeScript to call an action in my controller, which is fine, but I can not get the response to work.
I have a form with a list of budget lines. I want to allow the use to add a new line using CoffeeScript so I don't need to reload the question.
I have got the following in CoffeeScript:
$("button[id^='new_budget_line']").on 'click', (event) ->
category_id = $(this).attr('name')
child_economy_id = $('#child_economy_id').val()
$('#form_id').submit ->
valuesToSubmit = $(this).serialize()
$.ajax
type: 'POST'
url: $(this).attr('action')
data: valuesToSubmit
dataType: 'JSON'
$.ajax({
type: 'GET'
url: '../child_economy_lines/create_line'
data:
child_economy_id: child_economy_id
category_id: category_id
cost_type: 'direct'
dataType: JSON
}).done (response) ->
alert "Hey"
$('#test_append').html("Hey you now!!")
And the following in my controller
def create_line
logger.debug "Hejsa fra create line - category id #{params[:category_id]}"
#child_economy_line = #child_economy.child_economy_lines.build(:child_economy_category_id => params[:category_id], :cost_type => params[:cost_type])
if #child_economy_line.save
respond_to do |format|
format.js {render nothing: true}
format.json {render :json => "test"}
end
end
end
The action in the controller i called fine, and the new line is created, but I can not the actions after the ajax call to work. The part from .done ...
Can anybody help me identify where it is going wrong?

How can I check if a username is taken, while typing?

Many popular websites have a username field that, usually, turns red or blue as a user types in a new character, based on whether or not the characters entered correspond to an already-existing user.
Say I have the following username field:
<%= f.text_field :username, id:"username" %>
How could that functionality be added to this field?
Instead of checking the username and making request on every key, you can use the blur method to check the user name once the user leaves the username field, or else you need it on every key you can use keyup itself,
Your Javascript,
$( "#username" ).keyup(function() { # you can also try, $( "#username" ).blur(function() {
$.ajax({
url: '<%= check_duplicate_username_path %>', # your own route
type: "GET",
data: { username: $('#username').val() }, // This goes to Controller in params hash, i.e. params[:username]
complete: function() {},
success: function(data, textStatus, xhr) {
// do something with your returned data //
if (data.available == false)
{
$('#username').addClass("error"); // style the class with your required css
}
},
error: function() {
alert("Your Ajax error! message")
}
});
});
The route can be taken as,
get '/check_duplicate_username' => 'users#check_duplicate_username', as: :check_duplicate_username
The controller action can be something like,
def check_duplicate_username
#user = User.where('username = ?',params[:username]).first
if #user.present?
render json: {:success => 0, :message => 'User exists', :user_available => true}
else
render json: {:success => 1, :message => 'User Does not exist', :user_available => false}
end
end
You have to fire ajax request on textbox event.
write ajax function and add new function to you user_controller with GET http method and return suitable response for check availabilty of your username.
Trigger an ajax request while writing on the text box. Like:
$( "#username" ).keyup(function() {
$.ajax({
type: "GET",
url: '<%= username_availability_path %>', # replace by your route
data: {name: $('#username').prop('value')}
});
});
Create a new route on your routes.rb file with type GET. In the method access the typed name using params[:name] and then check if exists or not. Then do whatever you want.

Should Backbone collection 'create' add new model to collection with id from server included?

In my Backbone view here, I want to be able to immediately add my model to my collection with an 'id' attribute included in the newly created model. In other words, I need to have access to this 'id' right away. With the id available, my removeCategory method can work. To "solve" that problem, I tried to add the line this.collection.fetch() directly below this.collection.add(model);, but it gave me very bad UI (the new <li> wouldn't even render, etc), plus it seems like very bad coding practice anyway.
I'm relatively new with Backbone, so I'm not sure if create (save/add) should automatically add the model to the collection with an id or not. If it should, then there must be something wrong with the JSON data I'm receiving from my Rails RESTful API. If create does NOT do that, then how can I instantly get access to this specific newly created model, with an id (other than by refreshing the browser -- ugh!)? I need the id for <li data-id={{id}}></li> in my Handlebars template as soon as I invoke the "addCategory" method.
Also (& this might be a related problem), when I do model.fetch();, I get back the model's entire collection, plus that newly-created server-side model without an id. Any help on this would be much appreciated.
window.CategoriesView = Backbone.View.extend({
index_template: HandlebarsTemplates['categories/index'],
events: {
'click .delete': 'removeCategory',
'click .category_add': 'addCategory'
},
initialize: function() {
_.bindAll(this, 'render');
this.collection.fetch();
this.listenTo(this.collection, 'all', this.render);
},
show: function() {
$("#categorymodal").modal();
},
addCategory: function(e) {
e.preventDefault();
// I wrote the next three lines here explicitly rather than using 'create'
var model = new Category({ name: $(this.el).find('.category_name').val() });
model.save();
this.collection.add(model);
this.show();
},
removeCategory: function(e) {
e.preventDefault();
var id = $(e.target).parents('li').data('id');
var model = this.collection.where({ id: id })[0];
this.collection.remove(model);
if (model) { model.destroy(); }
this.render();
},
render: function() {
$(this.el).html(this.index_template());
this.collection.models.forEach(function(cat) {
var li_template = HandlebarsTemplates['categories/show']
$(this.el).find('.categories').append(li_template({
id: cat.toJSON().id,
name: cat.toJSON().name
}));
}, this);
return this;
}
});
Here is my #create action in Rails....
def create
#category = Category.create(category_params)
render :nothing => true
end
I am using jbuilder to provide the JSON from the Rails API.
Here is my Backbone model and collection...
window.Category = Backbone.Model.extend({
urlRoot: "categories"
});
window.Categories = Backbone.Collection.extend({
model: Category,
url : function() {
return 'categories';
}
});
I figured out my problem. It was in my Rails controller, not with Backbone create. I changed my #create action from this...
def create
#category = Category.create(category_params)
render :nothing => true
end
...to this....
def create
#category = Category.create(category_params)
redirect_to category_path(#category)
end
...with my show action being this...
def show
#category = Category.find(params[:id])
end
...that provided me with http://localhost:3000/categories/752.json. I've gotta make sure my backend is providing what I need JSON-wise!

Ajax call is returning error from action controller in rails

on change of a dropdown value i am trying to populate other dropdown list value.
Here i have added my new action in routes.rb :
resources :appointments do
collection do
get :getdata
end
end
This is my js code :
$("#appointment_department_id").change(function(){
//on change of department dropdown.
$.ajax({
url: "/appointment/getdata",
type: "GET",
data: {department_id: $(this).val()},
success: function(data){
alert(data);
}
error: function(data){
alert(data);
}
});
});
here is my action in controller file :
def getdata
#dept_id = params[:department_id]
department_name = #dept_id
#all_doctors = User.all; #will write my custom query later.
end
But on call to this action, it's returning error:
"NetworkError: 404 Not Found - http://localhost:3000/appointment/getdata?department_id=5"
(checked in firebug)
the error is in the ajax url, in the ajax request you are using 'appointment/getdata', but in routes you have defined appointments,
so use
$("#appointment_department_id").change(function(){
//on change of department dropdown.
$.ajax({
**url: "/appointments/getdata",**
type: "GET",
data: {department_id: $(this).val()},
success: function(data){
alert(data);
}
error: function(data){
alert(data);
}
});
});
Where's your respond_to in your controller?
If you're sending an Ajax request, you'll have to either define respond_to "JS" or "JSON" like this:
def getdata
respond_to do |format|
format.js
end
end
You could also do it like this:
Class Controller
respond_to :html,:js, :json
def getdata
respond_with(#custom_vars)
end
end
i think you forget "s" of "appointment" word in url: "/appointment/getdata", so try to add "s" like this :
$.ajax({
url: "/appointments/getdata",
...
...

Resources