---
title: no content for you!
teaser: Conditional display of application components.
tags: web,rails
author: Jared Carroll
published_on: 2007-07-02
---

Recently we had a feature request in an app: 'show the login box on every page
if you're not logged in except the sign-up/registration page'.

Since the login box was going to be on just about every page we decided to put
it in our application wide layout:

In `app/views/layouts/application.rhtml`:

```erb
<div id="wrapper">

  <div id="sidebar">
    <%= render :partial => 'login_box' -%>
  </div> <!-- end #sidebar -->

  <!-- etc... -->

</div> <!-- end #wrapper -->
```

We also decided to put it in a partial in `app/views/layouts`:

In `app/views/layouts/_login_box.rhtml`:

```erb
<% form_tag login_url do -%>

    <label for="email">Email</label>
    <%= text_field_tag :email %>

    <label for="password">Password</label>
    <%= password_field_tag :password %>

    <%= submit_tag 'Login' %>

<% end -%>
```

In this application, user sign-up/registration took place at another site so
adding the ability for a user to sign-up/register in this app was a new feature.

Here's the first attempt at keeping the login box out of the
sign-up/registration page (user sign-up/registration was happening in
UsersController#new and UsersController#create):

In `app/views/layouts/_login_box.rhtml`:

```erb
<% if ! (controller.controller_name == 'users' &&
          controller.action_name == 'new') ||
        (controller.controller_name == 'users' &&
          controller.action_name == 'create')
  <% form_tag login_url do -%>
      <label for="email">Email</label>
      <%= text_field_tag :email %>
      <label for="password">Password</label>
      <%= password_field_tag :password %>
      <%= submit_tag 'Login' %>
  <% end -%>
<% end -%>
```

That's terrible.

First off, any use of the controller and/or action name in a view is not
allowed.  If you're doing it, you haven't found the right design.

Let's go back to the application wide layout:

In`app/views/layouts/application.rhtml`:

```erb
<div id="wrapper">

  <div id="sidebar">
    <% content_for :login_box do -%>
      <%= render :partial => 'login_box' -%>
    <% end %>

    <%= yield :login_box %>
  </div> <!-- end #sidebar -->

  <!-- etc... -->

</div> <!-- end #wrapper -->
```

Here I modified it to use #content_for to provide some default <abbr
title="HyperText Markup Language">HTML</abbr> for the :login_box symbol that's
outputted by the call to #yield.  Now if I can just override that on my user
sign-up/registration page I can keep the login box off those pages.

Here we go:

In `app/views/users/new.rhtml`:

```erb
<% content_for :login_box do -%>
<% end -%>

<!-- user sign-up/registration form... -->
```

Nope, didn't work.

In Rails we know views are rendered before their corresponding layout.  Now what
happens when using `#content_for` is that each `#content_for` for a specific
symbol simply appends to the output of the previous `#content_for` call for that
same symbol.  So what the above code results in is an empty space and then the
login box <abbr title="HyperText Markup Language">HTML</abbr> appearing on
`users/new.rhtml`.

How about some conditional logic in our application wide layout?

In `app/views/layouts/application.rhtml`:

```erb
<div id="wrapper">

  <div id="sidebar">
    <%= yield(:login_box) || render(:partial => 'login_box') %>
  </div> <!-- end #sidebar -->

  <!-- etc... -->

</div> <!-- end #wrapper -->
```

Now it works.

So if we get something when `#yield`'ing the `:login_box` symbol our login box
partial won't be rendered.  And if we get nothing our login box will be
rendered.  Just what we wanted.

Back to our sign-up/registration form view.

In `app/views/users/new.rhtml`:

```erb
<% content_for :login_box do -%>
<% end -%>

<!-- user sign-up/registration form... -->
```

If I saw that in a view I'd probabaly rip it out.  I mean why is there an empty
`#content_for` block?

Let's wrap it up in a helper method.  I'll throw it in `application_helper.rb`
because that's where it belongs.

In `app/helpers/application_helper.rb`:

```erb
def no_content_for(symbol)
  content_for(symbol) { '' }
end
```

The block that returns an empty String hack is there because if you don't return
anything you get a error from Rails complaining about calling #+ on nil.
Apparently, an empty `#content_for` block in a view must return at least an
empty String.

And our final view:

In `app/views/users/new.rhtml`:

```erb
<% no_content_for :login_box -%>
<!-- user sign-up/registration form... -->
```

Next!
