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