UJS, AJAX, Rails 4, form_for collection_select to pass value into method and return value back to form
Asked Answered
T

3

2

I'm very new to Rails, and as such am having lots of of confusion when dealing with AJAX, UJS and Rails together. I've looked at railscast, several SO answers, tried #rubyonrails IRC channel on freenode. Alas, I'm still stuck.

Anyway, here is my problem.

SO I have two Models, Building and Property. Property belongs_to Building and Building has_many Properties.

I've added the foreign key to Property as building_id.

Now, in my building model, I have a Method: self.search(search) and given the right address (example 999 Decarie), it will return the building_id from Building table in the database correctly.

def self.search(search)
    #search.instance_variables.map {|v| "#{v}: #{search.instance_variable_get(v)}\n"}.join
    if ((search.nil?) || (search == ""))
        nil
    else
        search = search.to_s
        d { search }
        split = search.split(' ', 2)
        stnum = split.first
        d { stnum }
        stname = split.last
        d { stname }
        Building.where("streetno = ?", stnum).where("streetname = ?", stname).pluck(:id).first
    end
end

In my Properties partial _form, I have a form_for loop that uses a collection_select to allow the users to pick any building address (ex. 999 Decarie), (so it renders as a select/option HTML drop-down list).

<div class="field" id="selection">
   <%= f.collection_select :buildinginfo, Building.all, :half_address, :half_address, {:label => "Building Info"}%>
</div>  

So, how do I, using unobtrusive javascript/ajax

A. Get the selected value of the collection select as soon as the user selects it in the form and pass it to the building model method mentioned above (self.search(search)), which returns the correct building ID.

B. immediately take the building ID returned by the method and store it in a hidden field on the form (which corresponds to building_id field in the Properties model). (in the code below I want replace the value 1 with the building ID)

 <div class="field" id="selection_id">
         <%= f.hidden_field :building_id, :value => 1 %>  
      </div>    

Thus, allowing my associations to work such that when I delete a building, all its related properties get deleted as well.

Let me know if you need more code, im using Rails 4, thank you so much!

Titan answered 6/6, 2014 at 3:38 Comment(0)
T
0

thanks for taking the time to explain this Rich.

While your approach makes sense, I used a different approach, because my end goal was simply to create associations between Building (has_many) and Property (belongs_to), and when I delete a Building I also want all Properties associated to it deleted as well.

So when creating/updating a property I need to add it to the Building.properties array, which automatically updates the property's building_id field.

here's my final code in the properties_controller:

def create
  @property = Property.new(property_params)

  **b_id = Building.search(@property.buildinginfo)**
  **Building.find_by(id: b_id).properties << @property**

  respond_to do |format|
    if @property.save
      format.html { redirect_to @property, notice: 'Property was successfully created.' }
      format.json { render :show, status: :created, location: @property }
    else
      format.html { render :new }
      format.json { render json: @property.errors, status: :unprocessable_entity }
    end
  end
end

def update
respond_to do |format|
  if @property.update(property_params)

    **b_id = Building.search(@property.buildinginfo)**
    **Building.find_by(id: b_id).properties << @property**

    format.html { redirect_to @property, notice: 'Property was successfully updated.' }
    format.json { render :show, status: :ok, location: @property }
  else
    format.html { render :edit }
    format.json { render json: @property.errors, status: :unprocessable_entity }
  end
end
end

The Building.search function (goes in the building Model):

    def self.search(search)

    if ((search.nil?) || (search == ""))
        nil
    else
        search = search.to_s
        split = search.split(' ', 2)
        stnum = split.first
        stname = split.last
        s = Building.where("streetno = ?", stnum).where("streetname = ?",  stname).pluck(:id).first
        s
    end
end
Titan answered 7/6, 2014 at 5:42 Comment(0)
P
1

Ajax

In Rails, Ajax works exactly the same as anywhere else on the web - you send an asynchronous request with Javascript, server processes & sends a response.

The trick with Rails is to keep your code as modular as possible, hence why the ujs stuff is so often used in apps. You'll be best reading up on Rails' ujs ajax capabilities here

As soon as the user selects it

Sounds like you need .on("change":

#app/assets/javascripts/application.js
$(document).on("change", function(){
   $.ajax({
       url: "your/path",
       data: {search: $(this).val()},
       success: function(data) {
           // ... do stuff here
       }
   });
});

This will basically send the request to your server, allowing you to process the response as you wish.

Immediately take the building ID returned by the method and store it in a hidden field

You'll want to do this:

#Ajax method
success: function(data) {
    $("#element").val(data);
}

This will need to be accompanied by the respond_to block in your controller like this:

#app/controllers/your_controller.rb
respond_to :js, :json
def search
   @search = Model.search params[:search]
   respond_with @search
end

--

Data

Interesting point - you can use .find_by instead of multiple where queries like this:

Model.find_by(name: value, name: value).pluck(:id)
Parapet answered 6/6, 2014 at 7:7 Comment(0)
T
0

thanks for taking the time to explain this Rich.

While your approach makes sense, I used a different approach, because my end goal was simply to create associations between Building (has_many) and Property (belongs_to), and when I delete a Building I also want all Properties associated to it deleted as well.

So when creating/updating a property I need to add it to the Building.properties array, which automatically updates the property's building_id field.

here's my final code in the properties_controller:

def create
  @property = Property.new(property_params)

  **b_id = Building.search(@property.buildinginfo)**
  **Building.find_by(id: b_id).properties << @property**

  respond_to do |format|
    if @property.save
      format.html { redirect_to @property, notice: 'Property was successfully created.' }
      format.json { render :show, status: :created, location: @property }
    else
      format.html { render :new }
      format.json { render json: @property.errors, status: :unprocessable_entity }
    end
  end
end

def update
respond_to do |format|
  if @property.update(property_params)

    **b_id = Building.search(@property.buildinginfo)**
    **Building.find_by(id: b_id).properties << @property**

    format.html { redirect_to @property, notice: 'Property was successfully updated.' }
    format.json { render :show, status: :ok, location: @property }
  else
    format.html { render :edit }
    format.json { render json: @property.errors, status: :unprocessable_entity }
  end
end
end

The Building.search function (goes in the building Model):

    def self.search(search)

    if ((search.nil?) || (search == ""))
        nil
    else
        search = search.to_s
        split = search.split(' ', 2)
        stnum = split.first
        stname = split.last
        s = Building.where("streetno = ?", stnum).where("streetname = ?",  stname).pluck(:id).first
        s
    end
end
Titan answered 7/6, 2014 at 5:42 Comment(0)
L
0

Here is an example related to this problem, hope this will be helpful to you. In this example we will validate the email.

Define a method in users_controller and set the routes at routes.rb

class UsersController < ApplicationController

def check_email
    @email = User.exists?(:email=> params[:email])  
    respond_to do |format|
        if @email
         format.json { render json: @email }
        else
         format.json { render json: @email }
        end
    end
end

routes.rb get "check_email" => "users#check_email"

Now come to the register.html.erb page and add javascript as:

$('#email').blur(function(){

var re = /[A-Z0-9._%+-]+@[A-Z0-9.-]+.[A-Z]{2,4}/igm;

if($('#email').val().trim()=="" || !re.test($('#email').val()))
{
    $("#lbl_email").text("*Please enter a valid email address");
    $('#lbl_email').show();      
    return false;           
}
else
{
    $('#lbl_email').hide();

    $.ajax({
        type: "GET",
        url: "/check_email",
        data:{ email: $(this).val() },
        dataType: "json",
        success:function(data){
          $('#lbl_email').show(); 
          $("#lbl_email").text("*Oppss! Email already exists");
          return false;

        },
        error: function() {
            $('#lbl_email').hide(); 
        }
    });
}
});
Luralurch answered 6/10, 2015 at 7:5 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.