1

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

sirion1987
  • 111
  • 4
  • 13

2 Answers2

1

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
Shiyason
  • 759
  • 7
  • 16
  • You have missed a crucial step in render_redirect.rb: `def render_wizard(resource = nil, options = {}) process_resource!(resource)` .... When passing a parameter to the method render_wizard , performs process_resource ... `def process_resource!(resource) if resource if resource.save @skip_to ||= @next_step` ... In this case ' if @skip_to' in render_wizard is true .... and execute '`redirect_to wizard_path(@skip_to, @wicked_redirect_params || {}), options'` ... – sirion1987 Sep 07 '16 at 13:05
  • I followed your suggestion, but it continues to give the same mistake. The step execute when you pass a params to render_wizard is : `render_step wizard_value(step), options` ( in render_redirect.rb ) because @skip_to is true – sirion1987 Sep 07 '16 at 13:11
  • There you go. By passing a resource you were getting redirected to the resource's path! I should have caught that. Let me know once you find a solution! It seems like you would have to bypass the `#render_wizard` method and use the `#render` method so you can specify the partial.. – Shiyason Sep 07 '16 at 13:19
  • Ok. If I execute `render :second_step, :formats => 'js'` when I render second step I get this : `
    `. The wizard stay still to the first step ...
    – sirion1987 Sep 07 '16 at 14:01
1

I solved in this way:

in r model:

validates   :x, presence: true

in step controller :

...
steps   :first_step,
        :second_step,
        :finish_step
...

def show
    case step
        when :first_step
            @room_types  = R.new
        end

    render_wizard       
end

def update
   case step
      when :view_new_room_type_step
         @r = R.create(r_params)
      when :view_desc_step
         @r = R.find(params[:r][:id])
         @r.update(r_params)
      end

    respond_to do |format|
       if !@r.errors.any?
          format.js { !next_step?(:finish_step) ? ( render next_wizard_path ) : ( redirect_to_finish_wizard ) }
       else
          format.js { render wizard_path, r: @r }
       end
    end
end 

private
        ...

        def redirect_to_finish_wizard
            redirect_to r_index_path , notice: "Ok"
        end

in first_step.js.erb

$("#modal").html("<%= escape_javascript(render 'form_first_step') %>")

in _form_first_step.html.erb:

<% if @r.errors.any? %>
   <div class="alert fade in alert-danger alert-dismissable"><button aria-hidden="true" class="close" data-dismiss="alert" type="button">&#215;</button>
      <ul>
         <% @r.errors.full_messages.each do |msg| %>
            <%= content_tag :li, msg, :id => "error_#{msg}" if msg.is_a?(String) %>
          <% end %>
      </ul>
    </div>
<% end %>

<%= form_for(@room_types, url: wizard_path, method: :put, remote: true) do |f| %>
   ...
<% end %>

in second form ( of second step ):

<%= form_for(@room_types, url: wizard_path(:second_step), method: :put, remote: true) do |f| %>
    ....
    <%= f.hidden_field :id %>
<% end %>

Idea: to validate the data in the steps you can use jquery-form-validator-rails

sirion1987
  • 111
  • 4
  • 13