Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Wizard paths with object IDs #7

Closed
emptyflask opened this issue Apr 30, 2012 · 23 comments
Closed

Wizard paths with object IDs #7

emptyflask opened this issue Apr 30, 2012 · 23 comments

Comments

@emptyflask
Copy link

I'm not sure how to use Wicked with paths like:

/controller/:id/wizard_step

The situation is that I want to create a new product:

/products/new

Save a new object and redirect to the wizard to populate its data:

/products/1/step_1
/products/1/step_2
etc.
@emptyflask
Copy link
Author

Looks like this should work, ignoring the update requests for now:

resources :products, :except => :show do
  get ':id' => 'products#show'
end

@schneems
Copy link
Member

You will want to have the url something like 'products/:product_id/:id`. Wicked Hijacks the :id attribute, so you'll need to use :product_id. Similar to this example https://github.com/schneems/wicked/wiki/Partial-Validation-of-Active-Record-Objects

I'm getting ready to jump on a plane, let me know if you need more help.

@schneems
Copy link
Member

schneems commented May 3, 2012

I updated this example https://github.com/schneems/wicked/wiki/Partial-Validation-of-Active-Record-Objects

tldr; you should use a seperate controller and nested routing.

resources :products do
  resources :build
end

@brewpoo
Copy link

brewpoo commented Jul 5, 2012

Sorry to resurrect this one. But using the nested routing method how would you go about creating a new object. The show action expects a product_id.

@jonandersen
Copy link

Im wondering this as well...

The create route is:

/products/:product_id/build(.:format)

And since the product is created in create how would we go about arriving to the create method?

@gbatta
Copy link

gbatta commented Jul 27, 2012

Also hate to pile on, but I'm running into exactly the same problem when working with the partial validation infrastructure, even when nesting 'build' within 'products' in config/routes.rb and specifying the controller as 'reports/build'.

Specifically, for the Partial Validation example code given: If you were outside the wizard, and you wanted to point to the first step of the show action in the wizard to begin the Product object creation process, what route would you use?

When I rake routes, I see that all of the product_build routes require a product_id, which isn't generated until the object is saved. But we can't save the object without employing the partial validation steps in the wizard.

@schneems schneems reopened this Jul 27, 2012
@schneems
Copy link
Member

You have to create the product first. You have to have a product id before you can go into the wizard. That is where the partial validation comes in, we are saying that even if the product is saved to the database it's not a "published" or "active" or whatever you want to call it. We have to store that data somewhere, the database is the best place.

So.

Step one create a product in a controller that is not the wizard controller, step two redirect to the wizard controller, use partial validation to incrementally build a product. When it is completely change the status.

Perhaps i'm misunderstanding the problem, or perhaps the documentation isn't very clear. Either way, if all of you have the same problem, I want to understand it better. Let me know if this helps, though I feel like i'm not saying anything new.

If you're still confused, try walking me through step by step what is happening in your code with controller names and routes and everything, and I can try to shed some light on how to do this.

@gbatta
Copy link

gbatta commented Jul 27, 2012

Richard,

Thanks so much for promptly responding!! I really appreciate the work you put into Wicked.

What you're describing is I believe what I've tried to do: I saved a new object outside of the wizard controller, and then attempted to redirect to the show action of the wizard controller. However, in order to save the object and pass validations, I had to attach a condition to the validation in the model, which is where the problem may sit.

Specifically, in my not-wizard controller for my Report object (this would be 'Product' in your example), these are my new and create actions...I'm a newbie, so I'm sure this can be heavily refactored:

def new
@account = current_account
@report = @account.reports.build
end

def create
@account = current_account
@report=@account.reports.build(params[:report])
@report.status='initial'
@report = @report.save
if @report.save
redirect_to account_report_build_path(@account,@report,:first)
else
render 'new'
end
end

What I did was change the status to 'initial' before saving and attached a conditional validation in the model. The initial save just adds the 'full_name' and 'nickname' attributes to the new Reports object:

In models/reports.rb:

validates_presence_of :full_name, :nickname, :if=>:active_or_initial?

def active?
status=="active"
end
def active_or_initial?
status.include?('initial') || active?
end

The problem is, I keep getting 'undefined method `save' for true:TrueClass', and when I inspect params, it seems that report_id is updating as equal to 'true' for some reason. I would have expected a new report_id number to be automatically assigned upon saving, rather than it equaling 'true'.

Thanks much!
George

@gbatta
Copy link

gbatta commented Jul 27, 2012

Grrr, I think I get it now. I don't need the conditional validations for the initial set of object attributes added at the first save. I only need them for the to-be-added (via Partial Validation) attributes.

@jonandersen
Copy link

Yea thanks for clearing it up. What confused me was the following:

def create
    @product = Product.create
    redirect_to wizard_path(steps.first, :product_id => @product.id)
  end

Since this is in the WizardController I cant figure out how to call this, since you need the product already created. This method does create an product that we already should be using. As of now I simply redirect from another controller (after creating the product) to the new method in the partial validation controller and start the wizard from there. If there is a nicer way to do it let me know.

And thanks for the awesome gem!

@schneems
Copy link
Member

Gotcha, since we're not using the :product_id in that route it could be anything like

post ' /products/make_me_a_sammich/build'

or

post ' /products/foo/build'

I think the example makes more sense if we make the product in another controller and then redirect. I can update the docs, do you think that makes more sense?

@jonandersen
Copy link

Well that does make more sense to me :)

@schneems schneems reopened this Jul 30, 2012
@gbatta
Copy link

gbatta commented Jul 30, 2012

Yes!! I agree that specifying that the product is made in another controller and redirected would make it clearer.

That's implicit in the Partial Validation, but I think what drove me to think, initially, that the product was first created in the wizard was a) the 'def create...end' code that jonanderson pointed out and b) the fact that a vital attribute like product.name was first added in the wizard.

@schneems
Copy link
Member

I added some docs to the wiki that hopefully make this a bit clearer: https://github.com/schneems/wicked/wiki/Building-Partial-Objects-Step-by-Step if one of you gets a chance can you look over it and let me know if you have comments.

@gbatta
Copy link

gbatta commented Aug 25, 2012

Richard,

Yes, this sentence does indeed clarify things. So the
/products/building/build route will hit the create action exactly as you
constructed it in the BuildController example?

Best,
George

On Wed, Aug 22, 2012 at 7:52 AM, Richard Schneeman <notifications@github.com

wrote:

I added some docs to the wiki that hopefully make this a bit clearer:
https://github.com/schneems/wicked/wiki/Building-Partial-Objects-Step-by-Stepif one of you gets a chance can you look over it and let me know if you
have comments.


Reply to this email directly or view it on GitHubhttps://github.com//issues/7#issuecomment-7936709.

@tetherit
Copy link

I'm still confused with this, I have this:

  def update
    steps_logic
    @activation.attributes = current_params.to_h
    render_wizard(@activation, activation_id: @activation.id)
  end

In my logs I see this:

15:23:32 log.1     | 2016-05-30 15:23:32.134454 I [51931:70239350409860] (203.1ms) ActivationsController -- Completed #update -- {:controller=>"ActivationsController", :action=>"update", :params=>{"utf8"=>"✓", "authenticity_token"=>"uQff3b5EzMSU1SqiI3LHHu36sNsdfJqLKhIHawazUC3g79mAuCrJDeC/z8kGoUAotlTrzaBBVK964Kf3kN/6TA==", "activation"=>{"timebox_serial"=>"unactivated1", "terms"=>"1"}, "commit"=>"Next", "id"=>"validate"}, :format=>"HTML", :method=>"PUT", :path=>"/activations/validate", :status=>302, :view_runtime=>0.0, :status_message=>"Found"}
15:23:32 log.1     | 2016-05-30 15:23:32.289774 I [51931:70239350409860] (101.2ms) ActivationsController -- Completed #show -- {:controller=>"ActivationsController", :action=>"show", :params=>{"id"=>"user"}, :format=>"HTML", :method=>"GET", :path=>"/activations/user", :status=>302, :view_runtime=>0.0, :status_message=>"Found"}
15:23:32 log.1     | 2016-05-30 15:23:32.656819 I [51931:70239350409860] (316.8ms) ActivationsController -- Completed #show -- {:controller=>"ActivationsController", :action=>"show", :params=>{"id"=>"timeline"}, :format=>"HTML", :method=>"GET", :path=>"/activations/timeline", :status=>200, :view_runtime=>247.19, :status_message=>"OK"}

So it looks like render_wizard does the correct thing and updates the activation, but then it redirects to the show action without passing activation_id, how do I get it to pass activation_id?

@schneems
Copy link
Member

schneems commented Jun 7, 2016

If you did your routes right, you won't need to manually pass the ID through. render_wizard does not pass arguments to redirect like that.

If your route is something like activations/:activation_id/products/<step_name> then the activation_id will be inferred by Rails automatically.

@tetherit
Copy link

tetherit commented Jun 7, 2016

Thank you for your reply. My routes is just "resources :activations"

I ended up doing this instead:

def update
  ... 
  session[:activation_id] = @activation.id if @activation
  ...
end

And when the activation is successful:

def show
  ...
  session.delete(:activation_id)
  ...
end

@schneems
Copy link
Member

schneems commented Jun 8, 2016

Sounds good, just be wary not to put any info in the session that you don't mind losing. If you imagine they'll be going through the wizard all in one go, then you're probably fine. If you think they might stop, email themselves a link to the URL they are on and resume, you might want to make sure the ID is in the URL.

@Blandph88
Copy link

Hi Richard,

The wicked wizard is exactly what I'm looking for, so thanks for creating.
I'm still having bit of trouble though and haven't cracked it yet.
I'm trying to create a teampage object.

Currently receiving the following error:

undefined local variable or method `wizard_path' for #TeamBuildingTeampagesController:0x007fc8242dc4b0

I've got the following:

team_building_teampage_steps_controller.rb

class TeamBuildingTeampageStepsController < ApplicationController
include Wicked::Wizard

steps :team_building_team_types, .....

def show
@team_building_teampage = TeamBuildingTeampage.find(params[:team_building_teampage_id])
render_wizard
end

def update
@team_building_teampage = TeamBuildingTeampage.find(params[:team_building_teampage_id])
@team_building_teampage.update_attributes(params[:team_building_teampage])
render_wizard @team_building_teampage
end

team_building_teampages_controller.rb

class TeamBuildingTeampagesController < ApplicationController
before_action :authenticate_user!, except: [:index, :show]

def create
@team_building_teampage = current_user.team_building_teampages.create
if @team_building_teampage.save
redirect_to wizard_path
else
render :new
end
end

private

def team_building_teampage_params
  params.require(:team_building_teampage).permit(:title,.....)
end

end

config/routes.rb

resources :team_building_teampage_steps

post "/team_building_teampages/team_building_teampage_id/team_building_teampage_steps/team_building_team_types", to: "team_building_teampage_steps#show", as: "wizard_path"

and this is the form_for I've got in each of my step views:

<%= form_for (@team_building_teampage, :url => wizard_path, :method => :put) do |f| %>

......

<% end %>

@Blandph88
Copy link

Also receiving this error when I try to go to the views directly:

Couldn't find TeamBuildingTeampage with 'id'=

def show
@team_building_teampage = TeamBuildingTeampage.find(params[:team_building_teampage_id])
render_wizard
end

Bit of a mess...

@apextemple
Copy link

@Blandph88 did you find a solution? I'm also having a similar problem. The first step is working but when I want to get to the second step, I'm getting the following error: Couldn't find Product withoun an ID

@Blandph88
Copy link

Blandph88 commented Apr 19, 2019

@apextemple Ended up following Ben Awad’s YouTube tutorial for Airbnb clone where he covers making a multi step form. Look it up. 👌🏻

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants