Shawn Wilson
Shawn Wilson

Reputation: 1371

Issue with Nested Form Create - ActionController::UrlGenerationError in Calls#new

Hey all i'm building an app in Rails 4 Ruby 2.

I have recently nested a form In side of my Calls Show page I have added Ping (update) feature.

since I have completed the nesting... i can see the Ping in the show field the button to create a new Ping works, however i have since lost the ability to create a new call all together.

The Error I get is:

ActionController::UrlGenerationError in Calls#new
Showing /Users/TaurenLTD1/Desktop/TaurenLabs/PatrolProCAD/PatProCadApp/app/views/calls/_form.html.erb where line #459 raised:

No route matches {:action=>"new", :call_id=>nil, :controller=>"calls/pings"} missing required keys: [:call_id]
Extracted source (around line #459):
457
458
459
460
461
462

    </div>
    <div class="panel panel-info" id="update-pnl">
    <div class="panel-heading" id="update-top"><center><h4><strong id="update-txt">Live Updates - Found In View Call</strong> <%= link_to "Add Update", new_call_ping_path(@call), class: 'btn btn-primary btn-sm', :id => 'add-update' %> </h4></center></div>
    </div>
  <div class="new-wrapper">
    <div class="panel panel-warning" id="location-box">

Trace of template inclusion: app/views/calls/new.html.erb

My Routes rb file:

Rails.application.routes.draw do

  devise_for :users, controllers: { registrations: 'registrations' }

  devise_scope :user do
    authenticated :user do
      root 'calls#index', as: :authenticated_root
    end

    unauthenticated do
      root 'devise/sessions#new', as: :unauthenticated_root
    end
  end

  resources :sites

  resources :calls do 
    resources :pings, except: [:index], controller: 'calls/pings'

    collection do
      get 'history'
    end

    member do
      patch :update_unit_on_scene
      patch :update_unit_clear
      patch :update_unit2_os
      patch :update_unit2_cl
      patch :update_unit3_os
      patch :update_unit3_cl
      patch :update_unit4_os
      patch :update_unit4_cl
    end
  end

end

my calls/pings controller: (The update form accessed through the shot.html.erb page)

class Calls::PingsController < ApplicationController
  before_action :set_ping, only: [:show, :edit, :update, :destroy]

  # GET /pings
  # GET /pings.json
  def index
    @pings = Ping.all
  end

  # GET /pings/1
  # GET /pings/1.json
  def show

  end

  # GET /pings/new
  def new
    @call = Call.find(params[:call_id])
    @ping = Ping.new
  end

  # GET /pings/1/edit
  def edit

  end

  # POST /pings
  # POST /pings.json
  def create
    @call = Call.find(params[:call_id])
    @ping = Ping.new(ping_params)
    @ping.call = @call

    respond_to do |format|
      if @ping.save
        format.html { redirect_to @call, notice: 'Call update has been successfully added to call.' }
        format.json { render :show, status: :created, location: @call }
      else
        format.html { render :new }
        format.json { render json: @call.errors, status: :unprocessable_entity }
      end
    end
  end

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

  # DELETE /pings/1
  # DELETE /pings/1.json
  def destroy
    @ping.destroy
    respond_to do |format|
      format.html { redirect_to pings_url, notice: 'Ping was successfully destroyed.' }
      format.json { head :no_content }
    end
  end

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

    # Never trust parameters from the scary internet, only allow the white list through.
    def ping_params
      params.require(:ping).permit(:priority, :msg, :received, :call_id)
    end
end

and this is my calls controller (The original form)

class CallsController < ApplicationController
  before_action :set_call, only: [:show, :edit, :update, :destroy]

  # GET /calls
  # GET /calls.json
  def index
    @calls = Call.all
    @active_calls = @calls.select{|x| x.status == 'ACTIVE'}
    @pending_calls = @calls.select{|x| x.status == 'PENDING'}
    @clear_calls = @calls.select{|x| x.status == 'CLEAR'}
    @approved_calls = @calls.select{|x| x.status == "APPROVED"}
    @on_scene = @calls.select{|x| x.status == "ACTIVE"}
  end

  # GET /calls/1
  # GET /calls/1.json
  def show
    @unit_2 = @call.unit_2
    @unit_3 = @call.unit_3
    @unit_4 = @call.unit_4

    @call = Call.find(params[:id])
    @pings = @call.pings
  end

  # GET /calls/new
  def new
    @call = Call.new

    @unit_2 = @call.unit_2
    @unit_3 = @call.unit_3
    @unit_4 = @call.unit_4

    @pings = @call.pings
  end

  # GET /calls/1/edit
  def edit
    @call = Call.find(params[:id])
  end

  # POST /calls
  # POST /calls.json
  def create
    @call = Call.new(call_params)

    @call = Call.find(params[:call_id])
    @ping = Ping.new(ping_params)
    @ping.call = @call


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

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

  # DELETE /calls/1
  # DELETE /calls/1.json
  def destroy
    @call.destroy
    respond_to do |format|
      format.html { redirect_to calls_url, notice: 'Call was successfully destroyed.' }
      format.json { head :no_content }
    end
  end

### Custom View for Call History
  def history
    @approved_calls = Call.where(status: 'APPROVED')
  end

### Custom Calls for Unit 1-4 On Scene and Call Buttons ###
  def update_unit_on_scene
    @call = Call.find(params[:id])
    @call.unit_on_scene = DateTime.now
    @call.status = "ACTIVE"
    @call.save

    respond_to do |format|
      if @call.save
        format.html { redirect_to @call, notice: "On Scene Time Successfully Updated. - You Are Now Logged Out Of Service" }
      else
        format.html { render action: 'edit' }
      end
    end
  end

  def update_unit_clear
    @call = Call.find(params[:id])
    @call.unit_clear = DateTime.now
    @call.status = "CLEAR"
    @call.save

    respond_to do |format|
      if @call.save
        format.html { redirect_to @call, notice: "Scene Clear Time Successfully Updated. - You Are Now Logged In Service" }
      else
        format.html { render action: 'edit' }
      end
    end
  end

  def update_unit2_os
    @call = Call.find(params[:id])
    @call.unit2_os = DateTime.now
    @call.save

    respond_to do |format|
      if @call.save
        format.html { redirect_to @call, notice: "On Scene Time Successfully Updated. - You Are Now Logged Out Of Service" }
      else
        format.html { render action: 'edit' }
      end
    end
  end

  def update_unit2_cl
    @call = Call.find(params[:id])
    @call.unit2_cl = DateTime.now
    @call.save

    respond_to do |format|
      if @call.save
        format.html { redirect_to @call, notice: "Scene Clear Time Successfully Updated. - You Are Now Logged In Service" }
      else
        format.html { render action: 'edit' }
      end
    end
  end

  def update_unit3_os
    @call = Call.find(params[:id])
    @call.unit3_os = DateTime.now
    @call.save

    respond_to do |format|
      if @call.save
        format.html { redirect_to @call, notice: "On Scene Time Successfully Updated. - You Are Now Logged Out Of Service" }
      else
        format.html { render action: 'edit' }
      end
    end
  end

  def update_unit3_cl
    @call = Call.find(params[:id])
    @call.unit3_cl = DateTime.now
    @call.save

    respond_to do |format|
      if @call.save
        format.html { redirect_to @call, notice: "Scene Clear Time Successfully Updated. - You Are Now Logged In Service" }
      else
        format.html { render action: 'edit' }
      end
    end
  end

  def update_unit4_os
    @call = Call.find(params[:id])
    @call.unit4_os = DateTime.now
    @call.save

    respond_to do |format|
      if @call.save
        format.html { redirect_to @call, notice: "On Scene Time Successfully Updated. - You Are Now Logged Out Of Service" }
      else
        format.html { render action: 'edit' }
      end
    end
  end

  def update_unit4_cl
    @call = Call.find(params[:id])
    @call.unit4_cl = DateTime.now
    @call.save

    respond_to do |format|
      if @call.save
        format.html { redirect_to @call, notice: "Scene Clear Time Successfully Updated. - You Are Now Logged In Service" }
      else
        format.html { render action: 'edit' }
      end
    end
  end

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

    # Never trust parameters from the scary internet, only allow the white list through.
    def call_params
      params.require(:call).permit(:call_time, :status, :primary_type, :secondary_type, :site, :address, :unit_1, :unit_2, :unit_3, :unit_4, :call_details, :unit_on_scene, :unit_clear, :call_num, :site_id, :user_id, :unit2_os, :unit2_cl, :unit3_os, :unit3_cl, :unit4_os, :unit4_cl)
    end
end

I have no idea where i've gone wrong as i have never nested before and after going back trough a few tutorials on nesting i am still stuck.

Any help is greatly appreciated!

Thanks in advance!

EDIT #1:

Rake Routes Output for the affected scaffolds:

     Prefix Verb   URI Pattern                                Controller#Action

               call_pings POST   /calls/:call_id/pings(.:format)            calls/pings#create
             new_call_ping GET    /calls/:call_id/pings/new(.:format)        calls/pings#new
            edit_call_ping GET    /calls/:call_id/pings/:id/edit(.:format)   calls/pings#edit
                 call_ping GET    /calls/:call_id/pings/:id(.:format)        calls/pings#show
                           PATCH  /calls/:call_id/pings/:id(.:format)        calls/pings#update
                           PUT    /calls/:call_id/pings/:id(.:format)        calls/pings#update
                           DELETE /calls/:call_id/pings/:id(.:format)        calls/pings#destroy
             history_calls GET    /calls/history(.:format)                   calls#history
 update_unit_on_scene_call PATCH  /calls/:id/update_unit_on_scene(.:format)  calls#update_unit_on_scene
    update_unit_clear_call PATCH  /calls/:id/update_unit_clear(.:format)     calls#update_unit_clear
      update_unit2_os_call PATCH  /calls/:id/update_unit2_os(.:format)       calls#update_unit2_os
      update_unit2_cl_call PATCH  /calls/:id/update_unit2_cl(.:format)       calls#update_unit2_cl
      update_unit3_os_call PATCH  /calls/:id/update_unit3_os(.:format)       calls#update_unit3_os
      update_unit3_cl_call PATCH  /calls/:id/update_unit3_cl(.:format)       calls#update_unit3_cl
      update_unit4_os_call PATCH  /calls/:id/update_unit4_os(.:format)       calls#update_unit4_os
      update_unit4_cl_call PATCH  /calls/:id/update_unit4_cl(.:format)       calls#update_unit4_cl
update_primary_resp_1_call PATCH  /calls/:id/update_primary_resp_1(.:format) calls#update_primary_resp_1
                     calls GET    /calls(.:format)                           calls#index
                           POST   /calls(.:format)                           calls#create
                  new_call GET    /calls/new(.:format)                       calls#new
                 edit_call GET    /calls/:id/edit(.:format)                  calls#edit
                      call GET    /calls/:id(.:format)                       calls#show
                           PATCH  /calls/:id(.:format)                       calls#update
                           PUT    /calls/:id(.:format)                       calls#update
                           DELETE /calls/:id(.:format)                       calls#destroy

EDIT #2: Rails Server Output when clicking new call button:

tarted GET "/calls/new" for ::1 at 2015-11-22 23:26:21 -0700
Processing by CallsController#new as HTML
  User Load (0.2ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1  ORDER BY "users"."id" ASC LIMIT 1  [["id", 3]]
  Rendered calls/_form.html.erb (11.8ms)
  Rendered calls/new.html.erb within layouts/application (13.4ms)
Completed 500 Internal Server Error in 19ms (ActiveRecord: 0.2ms)

ActionView::Template::Error (No route matches {:action=>"new", :call_id=>nil, :controller=>"calls/pings"} missing required keys: [:call_id]):
    456:       </table>
    457:     </div>
    458:     <div class="panel panel-info" id="update-pnl">
    459:     <div class="panel-heading" id="update-top"><center><h4><strong id="update-txt">Live Updates - Found In View Call</strong> <%= link_to "Add Update", new_call_ping_path(@call), class: 'btn btn-primary btn-sm', :id => 'add-update' %> </h4></center></div>
    460:     </div>
    461:   <div class="new-wrapper">
    462:     <div class="panel panel-warning" id="location-box">
  app/views/calls/_form.html.erb:459:in `block in _app_views_calls__form_html_erb___1493490319796568156_70162629771600'
  app/views/calls/_form.html.erb:1:in `_app_views_calls__form_html_erb___1493490319796568156_70162629771600'
  app/views/calls/new.html.erb:3:in `_app_views_calls_new_html_erb___3696559492073097692_70162629705900'

Edit #3: Error when using new_call_ping_path

ActionController::UrlGenerationError in Calls#edit
Showing /Users/TaurenLTD1/Desktop/TaurenLabs/PatrolProCAD/PatProCadApp/app/views/calls/_form.html.erb where line #459 raised:

No route matches {:action=>"new", :controller=>"calls/pings", :id=>"3"} missing required keys: [:call_id]

</div>
    <div class="panel panel-info" id="update-pnl">
    <div class="panel-heading" id="update-top"><center><h4><strong id="update-txt">Live Updates - Found In View Call</strong> <%= link_to "Add Update", new_call_ping_path, class: 'btn btn-primary btn-sm', :id => 'add-update' %> </h4></center></div>
    </div>
  <div class="new-wrapper">
    <div class="panel panel-warning" id="location-box">

Upvotes: 0

Views: 91

Answers (3)

Shawn Wilson
Shawn Wilson

Reputation: 1371

So i solved this little problem.. It was actually a pretty ridiculous issue.. Whe i created the nested controller, I had the controller open when I dragged and dropped the file into the calls folder under controllers..

What happened was although i was pointing to the calls/pings controller and even though rake routes was showing the appropriate routes, rails was still looking at the original pings controller..

as soon as i deleted the duplicate controller, all fell into place and began to work just fine.

Thanks all for your had work on trying to resolve this!

Upvotes: 0

user3506853
user3506853

Reputation: 814

If you update pings for call in call controller then you have to do this:-

Model Call.rb

has_many :pings
accepts_nested_attributes_for :pings

Permit nested attributes in call_params method and change new and create method in calls_contoller.rb like:-

def new
    @call = Call.new
    @unit_2 = @call.unit_2
    @unit_3 = @call.unit_3
    @unit_4 = @call.unit_4
    @pings = @call.pings.build
end

def create
    @call = Call.new(call_params)
    respond_to do |format|
        if @call.save
            format.html { redirect_to @call, notice: 'Call was successfully created.' }
            format.json { render :show, status: :created, location: @call }
        else
            format.html { render :new }
            format.json { render json: @call.errors, status: :unprocessable_entity }
        end
end

private

def call_params
    params.require(:call).permit(:call_time, :status, :primary_type, :secondary_type, :site, :address, :unit_1, :unit_2, :unit_3, :unit_4, :call_details, :unit_on_scene, :unit_clear, :call_num, :site_id, :user_id, :unit2_os, :unit2_cl, :unit3_os, :unit3_cl, :unit4_os, :unit4_cl,
                                 pings_attributes: [:id, :priority, :msg, :received])
end

If you want to add pings for call from ping controller then you can add by doing this:-

def new
    @call = Call.find(params[:call_id])
    @pings = @call.pings.build
end

def create
    @call = Call.find(params[:call_id])
    respond_to do |format|
        if @call.update_attributes(call_params)
            format.html { redirect_to @call, notice: 'Call update has been successfully added to call.' }
            format.json { render :show, status: :created, location: @call }
        else
            format.html { render :new }
            format.json { render json: @call.errors, status: :unprocessable_entity }
        end
    end
end

private

def call_params
    params.require(:call).permit(pings_attributes: [:id, :priority, :msg, :received])
end

Upvotes: 1

BenNapp
BenNapp

Reputation: 261

I would try running rake routes from the command line and see if any routes rails generates from routes.rb match your error.

Posting relevant info from running rake routes may be useful.

EDIT:

Try just new_call_ping_path on line 459 instead of new_call_ping_path(@call).

EDIT 2:

Try new_call_ping_path(call_id: @call.id). The problem here is the URL generator is expecting the call_id since in routes.rb declares the format as follows: new_call_ping GET /calls/:call_id/pings/new(.:format).

Upvotes: 1

Related Questions