Domenico Garofoli Domenico Garofoli - 2 months ago 38
Javascript Question

Rails wicked wizard with javascript

I want to create a wizard in js.

steps :first_step,
:second_step


In my 'controller_step'

def show
case step
when :first_step
@r = R.new
when :second_step

end
render_wizard
end

def update
case step
when :first_step
@r = R.new(r_params)
when :second_step

end

render_wizard @r
end


I have problems after the update of the first step. I'm receive the following error message:


"Missing template controller_step/second_step, application/second_step
with {:locale=>[:en], :formats=>[:html], :variants=>[],
:handlers=>[:erb, :builder, :raw, :ruby, :coffee, :jbuilder]}. ".


How can I force loading of js templete? I would load "second_step.js.erb".

I tried to change the update method:

respond_to do |format|
format.js { render :js => ( render_wizard @r ) }
end


Of course I get the following error:


"AbstractController::DoubleRenderError in ...Controller#update Render
and/or redirect were called multiple times in this action. Please note
that you may only call render OR redirect, and at most once per
action. Also note that neither redirect nor render terminate execution
of the action, so if you want to exit an action after redirecting, you
need to do something like "redirect_to(...) and return"."


I also tried to change the code (in update):

respond_to do |format|
format.js { render :js => ( render_wizard @room_types and return ) }
end


I'm get the same error ( ... application/second_step with {:locale=>[:en], :formats=>[:html] .... )

P.S.

In view of the first step:

<%= form_for(@r, url: wizard_path, method: :put, remote: true) do |f| %>
....
<%= f.submit "Submit", class: "btn btn-default" %>
<% end %>


How do I fix ? thanks in advance

Answer

The #render_wizard method defined in the Wicked::Controller::Concerns::RenderRedirect is a wrapper method around the ActionController::Base#render method. It accepts a hash of options and passes it down to the controller's regular #render method.

This is the source code from the Wicked library:

def render_wizard(resource = nil, options = {})
  ...

  if @skip_to
    ...
  else
    render_step wizard_value(step), options
  end
end

def render_step(the_step, options = {})
  if the_step.nil? || the_step.to_s == Wicked::FINISH_STEP
    ...
  else
    render the_step, options #<-- Here
  end
end

Your code:

respond_to do |format|
  format.js { render :js => ( render_wizard @r ) }  
end

is basically doing:

respond_to do |format|
  format.js { render :js => ( render @r ) }  
end

which is in fact calling the render method twice.

As it is searching for a .html template rather than a .js.erb one, try adding a formats: 'js' option to the render_wizard method. It should prepend ['js'] to the :formats=>[:html] we see in the Missing template error message.

respond_to do |format|
  format.js { render_wizard(@r, formats: 'js') }
end

also, make sure the template's filename follows the rails convention and start with a _. (ie: _second_step.js.erb)


About the double render error, you are correct. You must return from the controller #show or #update method and prevent further code from calling the #render method a second time. You seem to have fixed that problem already.

EDIT#1

It seems like you may be able to call this method directly in your controller.. :

def render_step(the_step, options = {})
  # Wicked::FINISH_STEP = "wicked_finish"
  if the_step.nil? || the_step.to_s == Wicked::FINISH_STEP
    redirect_to_finish_wizard options
  else
    render the_step, options
  end
end

I believe the_step will be the partial's name. I think you should be able to call the #render_step method from your controller.

You may be able to do:

def show
  respond_to do |f|
    f.js do
      case step
      when :first_step
          @r  = R.new
          render_step(step) and return
      when :second_step
        ...
      end
    end
  end
end
Comments