This post is to assist to create a select tag in a view (HTML) with a list of countries.
The solution presented here:
– The countries are stored in a table in database. If you don’t want to have a table with countries in DB, you can easily modify the code below to have countries as an array in code.
– Helper to show a list of countries in select tag in HTML
You can find the whole solution on github.
Data – list of countries
You have several options to get countries for your Rails application:
– create a table in your database and import data in raw SQL format.
You can find raw SQL file with countries and states here – http://27.org/isocountrylist/.
– create a migration to populate database with data
– have a list of countries in your code without creating a table in database.
Rails migration for a list of countries and states
– download this file with seed data
save this file to /db/seed/state_country_seeds.rb
if you want to generate your own data with countries you can start from this script:
https://github.com/wdblair/Rails-Seed-All-Countries-and-All-States
It has data in a txt-file as well as a PHP script to create a ruby code with seed data.
– generate models for countries and states:
[codesyntax lang=”rails”]
rails g model Country rails g model State
[/codesyntax]
– create a new migration file to create table structures:
[codesyntax lang=”rails”]
rails g migration CreateCountries
[/codesyntax]
the tables will be as follows:
[codesyntax lang="rails"]
countries: id name iso #An ISO abbreviation for the country states: id name country_id #the parent country's id, references an entry in countries. iso #An ISO abbreviation for the region.
[/codesyntax]
– edit generated migration file (in /db/migrate/XXX_load_countries.rb)
[codesyntax lang="rails"]
class Countries < ActiveRecord::Migration def up create_table :countries do |t| t.column :iso, :string, :size => 2 t.column :name, :string, :size => 80 end create_table :states do |t| t.column :name, :string, :size => 80 t.column :country_id, :integer t.column :iso, :string, :size=>2 end end def down drop_table :countries drop_table :states end end
[/codesyntax]
– create a new migration for loading data
[codesyntax lang=”rails”]
rails g migration LoadCountries
[/codesyntax]
– put this code in the file:
[codesyntax lang="rails"]
class LoadCountries < ActiveRecord::Migration def up # load require File.expand_path('../../seed/state_country_seeds.rb', __FILE__) end def down Country.delete_all State.delete_all end end
[/codesyntax]
- now we are ready to apply migrations.
[codesyntax lang="rails"]
rake db:migrate
[/codesyntax]
No table solution:
if you don’t want to create a table for countries then you can have them in code.
Create a new class Country and put this code from this file.
so it will look like this:
[codesyntax lang="rails"]
class Country
COUNTRIES_LIST = [{:iso => 'AF', :name => 'AFGHANISTAN', :printable_name => 'Afghanistan', :iso3 => 'AFG', :numcode => '004'}, {:iso => 'AL', :name => 'ALBANIA', :printable_name => 'Albania', :iso3 => 'ALB', :numcode => '008'}, {:iso => 'DZ', :name => 'ALGERIA', :printable_name => 'Algeria', :iso3 => 'DZA', :numcode => '012'},
...
{:iso => 'ZW', :name => 'ZIMBABWE', :printable_name => 'Zimbabwe', :iso3 => 'ZWE', :numcode => '716'}] def self.find_by(property, value) COUNTRIES_LIST.each do |country| return country if country[property.to_sym] == value end end end
[/codesyntax]
HTML – Helper to get HTML code with select list of countries
– add a column to store country to your model
generate a new migration:
[codesyntax lang="rails"]
rails g migration AddCountryToUser
[/codesyntax]
migration file:
[codesyntax lang="rails"]
class AddCountryToUser < ActiveRecord::Migration def change add_column :users, :country_id, :integer end end
[/codesyntax]
run migration
[codesyntax lang="rails"]
rake db:migrate
[/codesyntax]
– create a new helper file – /app/helpers/user_form_helper.rb with this code:
[codesyntax lang="rails"]
module UserFormHelper # attributes - general def make_attributes(hsh) unless hsh.nil? output = "" hsh.each {|key, val| output << "#{key}=\"#{val}\""} end output end def country_code_select(object, method, priority_countries=nil, options={}, html_options={}) countries = [Country.all.collect{|r| r.name}, Country.all.collect{|r| r.id}].transpose.sort if priority_countries.nil? output = select(object, method, countries, options, html_options) else output = "<select id='#{object}_#{method}' name='#{object}[#{method}]' #{make_attributes(html_options)}>\n" output << "<option value=\"0\">-- Select --</option>" if options[:include_blank] == true # get ids of priority_countries if needed unless priority_countries[0].respond_to?(:id) # get ids by an array of iso codes priority_countries_ids = Country.where(:iso=>priority_countries).all else priority_countries_ids = priority_countries end # priority_countries_ids.each do |pc| if options[:selected] == pc[:id] output << "<option value=\"#{pc[:id]}\" selected=\"selected\">#{pc[:name]}</option>" else output << "<option value=\"#{pc[:id]}\">#{pc[:name]}</option>" end end output << "<option value=\"0\" disabled=\"disabled\">----------------------------</option>" output << options_for_select(countries, options[:selected]) output << "</select>\n" end output.html_safe end
end
[/codesyntax]
- use helper in a view (HAML)
[codesyntax lang="rails"]
= country_code_select(:user, :country_id,
priority_countries=['US','GB'], {:selected=>@user.country_id, :include_blank=>false}, {:style=>''})
[/codesyntax]
W5540 says:
thanks for your sharing
but how to input states selection??