Conditionally adding a link to a form -- how?

I've got two entities created by scaffolding: Expense & Vendor

In Expense#new there's a form with a Vendors-drop-down and a NewVendor- button. The latter button brings up Vendor#new. The Create button in Vendor#new brings up Vendor#show with Edit & Back links.

I want to append a third link conditionally to Vendor#show: if the Expense#new form led to the Vendor#show display, I want a "Return:Expense/New" link to accompany the Edit & Back links.

Is there a "Rails Way" to do this?

I've got two entities created by scaffolding: Expense & Vendor

In Expense#new there's a form with a Vendors-drop-down and a NewVendor- button. The latter button brings up Vendor#new. The Create button in Vendor#new brings up Vendor#show with Edit & Back links.

I want to append a third link conditionally to Vendor#show: if the Expense#new form led to the Vendor#show display, I want a "Return:Expense/New" link to accompany the Edit & Back links.

Is there a "Rails Way" to do this?

You can set a flag in the controller at the point you know that you want to show the link @show_return_link = true Then test this in the view to see whether to show the link or not <%= link_to .... if @show_return_link %>

Colin

Hi Colin,

Thank you for your quick response:

I got the link I wanted working perfectly, but not the conditional part, which would be "icing on the cake." Following are the gory details. If you can spot how I can be rescued from my fumbling, I'd be most appreciative.

Best wishes, Richard

The following in app\views\vendors\show.html.erb works perfectly, but it's displayed unconditionally: <%= link_to 'New Expense', '/expenses/new' %> (A verticule separator from preceding links also displays perfectly, by also is displayed unconditionally.)

The following produced no syntax error, but it did not display the link (Vendor::@showExpenseNew == true): <%= @showExpenseNew ? (link_to 'New Expense', '/expense/new') : "" %>

The following produced no syntax error, but did not display the link (Vendor::@showExpenseNew == true): <%= link_to 'New Expense', '/expense/new' if @showExpenseNew %>

The following produced no syntax error, but it did not display the link (Vendor::@showExpenseNew == true): <% if @showExpenseNew -%>     <%= link_to 'New Expense', '/expense/new' %> <% end -%>

The following produced a syntax error (even worse for lower-case "vendor") <% if Vendor/@showExpenseNew -%>     <%= link_to 'New Expense', '/expense/new' %> <% end -%>

Firstly it is conventional in rails to use underscore form for variables (show_expense_new), though this would not cause the problem you are seeing. If you have a look at the rails guide on debugging (http://guides.rubyonrails.org/) you will find techniques to help when you have this sort of problem. My favourite is to use ruby-debug to break into the code at the point where things are not working in order to inspect the data. I guess in this case that @showExpenseNew is not actually true as you think it is. The simplest technique is just to display the variable in the view and see what it's value is.

If you are getting nowhere post the code where you set it to true.

By the way it is generally considered good practice on the list to insert your comments at appropriate point in the preceding email as it makes it easier to follow the thread.

Colin

Hi Colin,

The following in app\views\vendors\show.html.erb works perfectly <%= link_to 'New Expense', '/expenses/new' %>

I have the following in app\controllers\vendors_controller.rb class VendorsController < ApplicationController     @show_new_expense_page = true [snip[

I now have in app\views\vendors\show.html.erb <%= link_to 'New Expense', '/expenses/new' if @show_new_expense_page %> The foregoing link fails to be displayed

I guess in this case that @showExpenseNew is not actually true as you think it is.

You are so right!! It is nil

look at http://guides.rubyonrails.org/

Excellent guidance; it's excellent

My favorite is to use ruby-debug

I did that. It took me days to get working. I won't bore you with my travails. But knew I needed to be able to debug but I didn't want to detour from getting version 1 of my app completed. But your suggestion persuaded me otherwise.

My goal is to have a variable that is persistent and which: 1. Is defined somewhere and initialized to false 2. Is set to true in app\views\expenses\new.html.erb when the <%= link_to 'New Vendor' ... is clicked 3. Is referenced in app\views\vendors\show.html.erb as a condition for whether to display some link

My current guess is to use a session variable, which I've got a lot of hope for. If that doesn't work, I'll start a new thread.

Best wishes, Richard

BTW, some of my failures are: My first attempt to see whether a shared value would work with @show_new_expense_page = true in app\controllers \vendors_controller.rb failed in app\views\vendors\show.html.erb because @show_new_expense_page was nil, which ruby_debug demonstated for me, thanks to you.

My second attempt changed to a class variable @show_new_expense_page in both places, which resulted in a syntax error.

My third attemp was to initialize @show_new_expense_page in app\controllers\application_controller.rb which led to NameError in Vendors#show uninitialized class variable @@show_new_expense_page in ActionView::Base::CompiledTemplates

Hi Colin,

The following in app\views\vendors\show.html.erb works perfectly <%= link_to 'New Expense', '/expenses/new' %>

I have the following in app\controllers\vendors_controller.rb class VendorsController < ApplicationController @show_new_expense_page = true [snip[

I now have in app\views\vendors\show.html.erb <%= link_to 'New Expense', '/expenses/new' if @show_new_expense_page %> The foregoing link fails to be displayed

I guess in this case that @showExpenseNew is not actually true as you think it is.

You are so right!! It is nil

look at http://guides.rubyonrails.org/

Excellent guidance; it's excellent

My favorite is to use ruby-debug

I did that. It took me days to get working. I won't bore you with my travails. But knew I needed to be able to debug but I didn't want to detour from getting version 1 of my app completed. But your suggestion persuaded me otherwise.

My goal is to have a variable that is persistent and which: 1. Is defined somewhere and initialized to false

You don't need to define it and set it to false, the code if variable is ok to use if variable is not defined - that is the same as false. So you only need to set it to true where you want it true.

2. Is set to true in app\views\expenses\new.html.erb when the <%= link_to 'New Vendor' ... is clicked

Sounds good

3. Is referenced in app\views\vendors\show.html.erb as a condition for whether to display some link

Also sounds good

My current guess is to use a session variable, which I've got a lot of hope for. If that doesn't work, I'll start a new thread.

There should not be any need for a session variable, just set it in the controller and test it in the view.

Best wishes, Richard

BTW, some of my failures are: My first attempt to see whether a shared value would work with @show_new_expense_page = true in app\controllers \vendors_controller.rb failed in app\views\vendors\show.html.erb because @show_new_expense_page was nil,

Why is it nil if you are setting in in the controller? Are you sure it is executing that line in the controller. Break there with ruby-debug and check.

which ruby_debug demonstated for me, thanks to you.

My second attempt changed to a class variable @show_new_expense_page in both places, which resulted in a syntax error.

Your first attempt just above is already a class variable, as it starts with @ so I don't follow you here.

My third attemp was to initialize @show_new_expense_page in app\controllers\application_controller.rb

No need for it in application_controller, only things that all controllers need go here.

Colin

Try using params instead of variables. The variables lose scope when changing between views/controllers and once that happens, the value becomes nil. Instead of @show_new_expanse_page = true try params['new_expense_page'] = true

and the value will persist until used. the test for this would be changed from

<%= link_to 'New Expense', '/expenses/new' if @show_new_expense_page %> to <%= link_to 'New Expense', '/expenses/new' if params['show_new_expense_page'] %>

Good luck

Bob

Try using params instead of variables. The variables lose scope when changing between views/controllers and once that happens, the value becomes nil. Instead of @show_new_expanse_page = true try params['new_expense_page'] = true

and the value will persist until used. the test for this would be changed from

<%= link_to 'New Expense', '/expenses/new' if @show_new_expense_page %> to <%= link_to 'New Expense', '/expenses/new' if params['show_new_expense_page'] %>

Bob, I don't understand, a variable @show_new_expense_page set in the controller will be available in the view.

Colin

Bob, I don't understand, a variable @show_new_expense_page set in the controller will be available in the view.

Colin

Set where in the controller? In a :before_filter or in show?

It doesn't matter, either will do. Assuming that 'show' is the action being run.

Colin

Hi Colin,

I had to work on adding authenticated users to my app, so I had to back-burner the conditional-link issue. I nevertheless would like to keep trying to learn how to do this. Maybe CSS is the way to make the link switch between visible or not depending on context. (Just my inspiration at the moment.)

Also, you mentioned that I needed have my switch initialized to false, because nil will result in the same thing, so I just commented it out in VendorController. Seems to clear up one problem.

Your first attempt just above is already a class variable, as it starts with @ so I don't follow you here.

I think @xxx defines an instance variables while @@xxx defines a class variable in Ruby. That's what my Ruby books tell me, as does http://www.rubyist.net/~slagell/ruby/instancevars.html.

It's nice that we got a couple of other respondents. Maybe we'll get this resolved shortly.

Best wishes, Richard

You're making this wildly overly complicated.

Stick this in a view file:

<% @foo = "bar" %> <% @state = false %> <%= @foo if @state %>

See what appears. Change @state to true and see what appears.

If the variable you're using to eval true/false gives you different and/or unexpected results, that variable is not what you think it is.

Use debug or inspect or logging to figure out why.

HTH,

Hi Colin,

I had to work on adding authenticated users to my app, so I had to back-burner the conditional-link issue. I nevertheless would like to keep trying to learn how to do this. Maybe CSS is the way to make the link switch between visible or not depending on context. (Just my inspiration at the moment.)

Also, you mentioned that I needed have my switch initialized to false, because nil will result in the same thing, so I just commented it out in VendorController. Seems to clear up one problem.

Your first attempt just above is already a class variable, as it starts with @ so I don't follow you here.

Yes you are right of course, forgot to engage brain. There is no need for a class variable, an instance variable is what you want.

Colin

Following up on what Bob Smith posted on 4/21 and Hassan Schroeder on 4/24, here's what I've got working:

======= app\views\vendors\show.html.erb ===== [snip] <%= link_to 'Edit', edit_vendor_path(@vendor) %> | <%# From scaffold - %> <%= link_to 'Back', vendors_path %> <%# From scaffold -%>

<%# params['show_new_expense_page'] = true %>

<% if params['show_new_expense_page'] -%>     >     <%= link_to 'New Expense', '/expenses/new' %> <% end -%>

<% params['show_new_expense_page'] = false %>

============== end ===================

With the "params ... = true" line commented out, I get two links: Edit | Back

Un-commented, I get three links as desired, and they all work: Edit | Back | New Expense

My intention is to have the "New Expense" link visible when a particular link was previously clicked in the following page:

======= app\views\expenses\new.html.erb ===== [snip]     <% @current_vendors = Vendor.find(:all, :order=>"nickname").collect { |v|       v.nickname + parens(v.qbname) } %>     <%= f.select :vendor, @current_vendors %>     <% params['new_expense_page'] = true -%>     <%= link_to 'New Vendor', :controller=>'vendors', :action=>'new' %> [snip] ================= end =================

But the "params ... true" setting is not conveyed to the app\views \vendors\show.html.erb page.

1. Can this be fixed? 2. If not, will using session rather than params work? 3. Or can I access the "previous page" value in the app\views\vendors \show.html.erb page and test whether the previous page was app\views \expenses\new.html.erb?

Again, thanks to all of you for your past responses. I'd be grateful for any additional ideas you may offer.

Following up on what Bob Smith posted on 4/21 and Hassan Schroeder on 4/24, here's what I've got working:

======= app\views\vendors\show.html.erb ===== [snip] <%= link_to 'Edit', edit_vendor_path(@vendor) %> | <%# From scaffold - %> <%= link_to 'Back', vendors_path %> <%# From scaffold -%>

<%# params['show_new_expense_page'] = true %>

<% if params['show_new_expense_page'] -%> > <%= link_to 'New Expense', '/expenses/new' %> <% end -%>

<% params['show_new_expense_page'] = false %>

============== end ===================

With the "params ... = true" line commented out, I get two links: Edit | Back

Un-commented, I get three links as desired, and they all work: Edit | Back | New Expense

My intention is to have the "New Expense" link visible when a particular link was previously clicked in the following page:

======= app\views\expenses\new.html.erb ===== [snip] <% @current_vendors = Vendor.find(:all, :order=>"nickname").collect { |v| v.nickname + parens(v.qbname) } %>

Nothing to do with your problem but you should definitely not be doing finds in the view, this should be in the controller.

<%= f.select :vendor, @current_vendors %> <% params['new_expense_page'] = true -%> <%= link_to 'New Vendor', :controller=>'vendors', :action=>'new' %> [snip] ================= end =================

But the "params ... true" setting is not conveyed to the app\views \vendors\show.html.erb page.

1. Can this be fixed? 2. If not, will using session rather than params work? 3. Or can I access the "previous page" value in the app\views\vendors \show.html.erb page and test whether the previous page was app\views \expenses\new.html.erb?

The solution has been suggested several times already in this thread, I will try again. In the _controller_ method that is rendering the page that has the conditional link displayed set a variable @show_new_expense_page = true at the point in the code where you know that you wish to show the link. In the view then just use if @show_new_expense_page on the link display.

Please try this and get back if you have problems, or ask for clarification if you do not understand it (if so which bit do you not understand). The approach you are trying to use is not the best way.

Colin

forgot to engage brain

The happens to me all the time. I retired a decade ago and I often can't remember what I did or intended five minutes ago. I've developed some techniques for combating this weakness, e.g.. when programming I log screen-shots of what I did and what I got as a result. It has a two-fold benefit: (1) if something goes awry, I can accurately recall what led up to the problem; and (2) if I can't remember how to do something I "know" how to do, I can search my log mechanically to get a clue.

Cheers, Richard

I think I see the cause of lack of communication between params in 1) app\views\expenses\new.html.erb; and 2) app\views\vendors\show.html.erb

The are two distinct params, viz: one in an instance of app\controllers\expenses_controller.rb; the other in instance of app\controllers\vendors_controller.rb;

And if I we overcome that problem, perhaps there's an even bigger problem: my app is intended to be installed on a server, includes the database. But the app is to be users of several classes on the network. Wouldn't the use of params fail in that case, but be ameliorated by use of sessions?

Am I all wet about this?

Thanks in advance, Richard

RichardOnRails wrote:

forgot to engage brain

The happens to me all the time. I retired a decade ago and I often can't remember what I did or intended five minutes ago. I've developed some techniques for combating this weakness, e.g.. when programming I log screen-shots of what I did and what I got as a result.

Can I suggest that you supplement this with automated tests and version control?

It has a two-fold benefit: (1) if something goes awry, I can accurately recall what led up to the problem; and (2) if I can't remember how to do something I "know" how to do, I can search my log mechanically to get a clue.

Cheers, Richard

Best,

Yes. I think you are totally missing how the Web works :slight_smile:

The params hash represents name/value pairs passed from a client (browser, typically) to your server, either as a GET request's query string, or as body parts of a POST request.

So the life of a params hash is one request. If you want something to persist beyond that, put it in session, store it in the DB or use a hidden field in a form.

Or copy it into an @ variable in the controller action to make it available in the rendered view. But I believe for the OP's problem he does not require to use params (or persistence) at all. See my previous post.

Colin