chainselect ruby on rails It does not work for me

0

I try to make a select with a form, but when I'm going to save this I get this, and I do not know what I have wrong.

  

undefined method 'map' for nil: NilClass

     

around line # 17
<%= select_tag "pais", options_from_collection_for_select(@paises, "id", "name" ), class: "form-control", :include_blank => "Seleccione Pais" %>

     

& lt ;% = select_tag "country", options_from_collection_for_select (@paises, "id", "name"), class: "form-control",: include_blank = > "Select Country"% >

Form

<%= simple_form_for(@persona) do |f| %>
  <%= f.error_notification %>

  <div class="form-inputs">

    <%= f.input :identificacion %>

    <%= f.input :nombre %>
    <%= f.input :email %>

  </div>

 <div class="row">
  <div class="col-md-4">
       <label>Pais</label>
    <%= select_tag "pais", options_from_collection_for_select(@paises, "id", "name" ), class: "form-control", :include_blank => "Seleccione Pais" %>

  </div>
  <div class="col-md-3">
       <label>Departamento</label>
       <%= select_tag "departamento", "<option value="">Seleccione departamento</option>".html_safe, class: "form-control" %>
  </div>
  <div class="col-md-2">
       <label>Municipio</label>
       <%= select_tag "municipio", "<option value="">Seleccione Municipio</option>".html_safe, class: "form-control" %>
  </div>

</div>
<div class="form-actions">
    <%= f.button :submit %>
  </div>
</div>
<% end %>

JavaScript

$(document).on('turbolinks:load', function(){

   // Empieza codigo para select anidados
   $("#pais").change(function(event, data) {

    var id_pais = $('select#pais :selected').val();
    $.ajax({
    url: '/personas/select_departamento',
    dataType: "JSON",
    type: 'GET',
    data: { idpais: id_pais },
    success: function(data) {
        var $select = $('#departamento');
        $select.empty().append('<option value="">Seleccione Departamento</option>');
        $(data).each(function (index, o) {    
        var $option = $("<option/>").attr("value", o.id).text(o.name);
        $select.append($option);
        });      
    }
    });
    // inicializar los selects
    var $select = $('#municipio');
    $select.empty().append('<option value="">Seleccione Municipio</option>');
  });

  $("#departamento").change(function(event, data) {
    var id_departamento =  $('select#departamento :selected').val();
    $.ajax({
    url: '/personas/select_municipio',
    dataType: "JSON",
    type: 'GET',
    data: { iddepartamento: id_departamento },
    success: function(data) {
        var $select = $('#municipio');
        $select.empty().append('<option value="">Seleccione Municipio</option>');
        $(data).each(function (index, o) {    
        var $option = $("<option/>").attr("value", o.id).text(o.name);
        $select.append($option);
        });      
    }
    });

  });

  // Termina codigo para select anidados  

});

On routes routes.rb

Rails.application.routes.draw do

  resources :municipios
  resources :departamentos
  resources :paises
  get 'static_pages/home'

  get 'static_pages/help'

  root :to => 'personas#index'
  resources :personas do
    get :select_departamento, on: :collection
    get :select_municipio, on: :collection
  end

end

In Controller

class PersonasController < ApplicationController
  before_action :set_persona, only: [:show, :edit, :update, :destroy]

  # GET /personas
  # GET /personas.json
  def index
    @personas = Persona.all
    @paises   = Pais.all
  end

 # select anidados
  def select_departamento
    rs = Departamento.where(:pais_id => params[:idpais]).order('name').all
    respond_to do |format|
    format.json {render json: rs }
    format.html
    end
  end

  def select_municipio
    rs = Municipio.where(:departamento_id => params[:iddepartamento]).order('name').all
    respond_to do |format|
    format.json {render json: rs }
    format.html
    end
  end

  # GET /personas/1
  # GET /personas/1.json
  def show
  end

  # GET /personas/new
  def new
    @persona = Persona.new
    @paises   = Pais.all
  end

  # GET /personas/1/edit
  def edit
  end

  # POST /personas
  # POST /personas.json
  def create
    @persona = Persona.new(persona_params)
    respond_to do |format|
      if @persona.save
        format.html { redirect_to @persona, notice: 'Persona was successfully created.' }
        format.json { render :show, status: :created, location: @persona }
      else
        format.html { render :new }
        format.json { render json: @persona.errors, status: :unprocessable_entity }
      end
    end
  end

  # PATCH/PUT /personas/1
  # PATCH/PUT /personas/1.json
  def update
    respond_to do |format|
      if @persona.update(persona_params)
        format.html { redirect_to @persona, notice: 'Persona was successfully updated.' }
        format.json { render :show, status: :ok, location: @persona }
      else
        format.html { render :edit }
        format.json { render json: @persona.errors, status: :unprocessable_entity }
      end
    end
  end

  # DELETE /personas/1
  # DELETE /personas/1.json
  def destroy
    @persona.destroy
    respond_to do |format|
      format.html { redirect_to personas_url, notice: 'Persona was successfully destroyed.' }
      format.json { head :no_content }
    end
  end

  private
    # Use callbacks to share common setup or constraints between actions.
    def set_persona
      @persona = Persona.find(params[:id])
    end

    # Never trust parameters from the scary internet, only allow the white list through.
    def persona_params
      params.require(:persona).permit(:identificacion, :nombre, :email, :municipio_id)
    end
end

In the database

ActiveRecord::Schema.define(version: 20170221123516) do

  create_table "departamentos", force: :cascade do |t|
    t.string   "name"
    t.integer  "pais_id"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.index ["pais_id"], name: "index_departamentos_on_pais_id"
  end

  create_table "municipios", force: :cascade do |t|
    t.string   "name"
    t.integer  "departamento_id"
    t.datetime "created_at",      null: false
    t.datetime "updated_at",      null: false
    t.index ["departamento_id"], name: "index_municipios_on_departamento_id"
  end

  create_table "paises", force: :cascade do |t|
    t.string   "name"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
  end

  create_table "personas", force: :cascade do |t|
    t.string   "identificacion"
    t.string   "nombre"
    t.string   "email"
    t.integer  "municipio_id"
    t.datetime "created_at",     null: false
    t.datetime "updated_at",     null: false
    t.index ["municipio_id"], name: "index_personas_on_municipio_id"
  end

end

The select works:

Model

class Persona < ApplicationRecord
  belongs_to :municipio
end

This was an error that came up after adding save!

  

Validation failed: Municipality must exist

    
asked by MIGUEL ANGEL GIL RODRIGUEZ 05.06.2017 в 08:00
source

1 answer

0

The problem is that when saving @persona in your action create some validation fails and, therefore, it is not saved; and according to the logic established in that action, you should show your view new ( render :new ):

if @persona.save
  format.html { redirect_to @persona, notice: 'Persona was successfully created.' }
  format.json { render :show, status: :created, location: @persona }
else
  format.html { render :new }
  format.json { render json: @persona.errors, status: :unprocessable_entity }
end

But in your action create no there is the variable @paises , which is necessary to generate the select within new.html.erb , in:

options_from_collection_for_select(@paises, "id", "name" )

The error is generated because @paises is null or nil (the helper options_from_collection_for_select calls the map method).

To fix it simply add @paises as you do in your action new :

if @persona.save
  format.html { redirect_to @persona, notice: 'Persona was successfully created.' }
  format.json { render :show, status: :created, location: @persona }
else
  @paises = Pais.all
  format.html { render :new }
  format.json { render json: @persona.errors, status: :unprocessable_entity }
end

Regarding the second error, it seems that you are providing an invalid value for municipio_id ; however, I recommend using save! instead of save temporarily to identify the error faster.

Considering the above, this would be your action create :

def create
  @persona = Persona.new(persona_params)
  respond_to do |format|
    if @persona.save!   # << Aquí está el cambio
      format.html { redirect_to @persona, notice: 'Persona was successfully created.' }
      format.json { render :show, status: :created, location: @persona }
    else
      @paises = Pais.all
      format.html { render :new }
      format.json { render json: @persona.errors, status: :unprocessable_entity }
    end
  end
end

Using save! 1 will show another error screen with the description of the validation that failed and prevented the record from being saved successfully.

1 It is very important that you return your code to save once you identify the error, otherwise you will have an error screen every time a user puts a wrong data.

The error (after adding save! ) is very clear:

  

Validation failed: Municipality must exist

In the form you are sending a id of Municipio that does not exist; therefore you must make sure that

  • your table of municipios have information and
  • the municipality you select in your form exists in the municipios table.
  • answered by 05.06.2017 в 15:26