Us Zip Code
Simple gem to handle zipcode lookups and related functionality. rake zipcodes:update
will automatically download and update your local zipcode database. Generates three models for extended zipcode functionality.
Installation
Add the following line to your Gemfile:
gem 'us_zipcode'
Run:
bundle install
Generate the models and populate the data:
rails g us_zipcode:models
rake db:migrate
rake zipcodes:update
You should now have three new tables and three new models, Zipcode, State, County.
Usage
zipcode = Zipcode.find_by_code '66206'
zipcode.state.abbr # => 'KS'
zipcode.city # => 'Shawnee Mission'
zipcode.county.name # => 'Johnson'
zipcode.lat.to_s # => '38.959356', it is actually a BigDecimal object converted to_s for documentation.
zipcode.lon.to_s # => '-94.716155', ditto
zipcode.is_geocoded? # => true, most if not all should be pre-geocoded.
You can use State and County objects as follows:
state = State.find_by_abbr "MO"
state.cities.count # => 963
state.cities # gives you a sorted array of all cities for the state
state.zipcodes.count # => 1195
...
county = state.counties.first
county.cities.count # => 5
county.cities # gives you a sorted array of all cities for the county
county.zipcodes.count # => 5
Look Up City by State & zip code + county by city
First you have to select a state .On state selection , you can see the cities drop down which are associated with the selected state.
On city selection, you can see the corresponding county with its zip code.
Write the following code in your view:
<%= select_tag "state",options_from_collection_for_select(State.all, "id", "name") %>
<div id ="city"></div>
<div id ="county"></div>
<script>
$("#state").on("change", function () {
$.ajax({
url: "/mycontroller/get_cities_by_state",
dataType: "script",
method: "get",
data: {state_id: $(this).val()}
});
$("#county").html('');
});
</script>
Create mycontroller & paste the following code:
def get_cities_by_state
@cities = Zipcode.group(:city).where(state_id: params[:state_id])
end
def get_county_and_zip_by_city
@zipcodes = Zipcode.find_by_city(params[:city])
end
Create the following two js.erb files in app/views/mycontroller/
1.get_cities_by_state.js.erb & paste the following code:
$("#city").html('<%= escape_javascript(select_tag "city", options_from_collection_for_select(@cities, :id, :city)) %>');
$("#city").on("change", function(){
$.ajax({
url: "/mycontroller/get_county_and_zip_by_city",
dataType: "script",
method: "get",
data: {city: $("#city select option:selected"). text()}
});
});
2.get_county_and_zip_by_city.js.erb
$("#county").html('<%= escape_javascript(select_tag "county", options_from_collection_for_select(@zipcodes, :id, :county_and_zip)) %>');
In the last update your routes:
scope '/mycontroller' do
get 'get_cities_by_state' => 'mycontroller#get_cities_by_state'
get 'get_county_and_zip_by_city' => 'mycontroller#get_county_and_zip_by_city'
end
Automatic JQuery/AJAX lookup
You can have a user enter a zipcode and automatically lookup their city, state and county.
Put something like this in your view:
f.text_field :zip, :size => 5, :maxlength => 5, :class => 'zipcode_interactive'
f.text_field :city, :size => 20, :maxlength => 60, :readonly => true
f.text_field(:state, :size => 2, :maxlength => 2, :readonly => true)
f.text_field(:county, :size => 20, :maxlength => 60, :readonly => true)
Then add this to your application.js, but remember to replace [mycontrollername] with your own controller.
$(document).ready(function() {
// Interactive Zipcodes
$('input.zipcode_interactive').blur(function(data) {
var elem_id = $(this).attr("id");
var base_id = elem_id.substring(0, elem_id.lastIndexOf("_"));
$.get("/mycontrollername/get_zip_data/" + this.value, {},
function(data) {
var zipcode = $.parseJSON(data);
var city = $('#' + base_id + '_city');
var state = $('#' + base_id + '_state');
var county = $('#' + base_id + '_county');
if (zipcode.err) {
alert(zipcode.err);
} else {
city.val(zipcode.city);
state.val(zipcode.state)
county.val(zipcode.county)
}
})
});
});
You will also need a controller method similar to this, which will return the data to your form:
def get_zip_data
@zipcode = Zipcode.find_by_code(params[:code], :include => [:county, :state])
if @zipcode
@counties = County.find(:all, :conditions => [ "state_id = ?", @zipcode.county.state_id ])
data = {
'state' => @zipcode.state.abbr,
'county' => @zipcode.county.name,
'city' => @zipcode.city.titleize
}
render :text => data.to_json
else
if params[:code].blank?
return true
else
if params[:code].is_zipcode?
data = {
'err' => "Could not find Zipcode [#{params[:code]}]. If this is a valid zipcode please notify support <support@mydomain.com>, so we can update our database."
}
else
data = {
'err' => "[#{params[:code]}] is not a valid Zipcode."
}
end
render :text => data.to_json
end
end
end
And define a route for the AJAX function in routes.rb:
get 'mycontrollername/get_zip_data/:code', :controller => 'mycontrollername', :action => 'get_zip_data'
That’s about it.
Let me know if there are any errors. I cut and pasted the code above from a working application, but there may be some gotchas that I missed.