2008-09-29 9 views
12

Tengo una aplicación Ruby/Rails que tiene dos o tres "secciones" principales. Cuando un usuario visita esa sección, deseo mostrar algo de navegación secundaria. Las tres secciones usan el mismo diseño, por lo que no puedo "codificar" la navegación en el diseño.¿Cómo implemento la navegación específica de sección en Ruby on Rails?

Puedo pensar en algunos métodos diferentes para hacer esto. Supongo que para ayudar a las personas a votar las incluiré como respuestas.

¿Alguna otra idea? ¿O por qué votas?

Respuesta

9

Puede hacerlo fácilmente utilizando parciales, suponiendo que cada sección tiene su propio controlador.

Digamos que usted tiene tres secciones llamadas Mensajes, Usuarios y administración, cada uno con su propio controlador: PostsController, UsersController y AdminController.

En cada directorio views correspondiente, se declara un parcial _subnav.html.erb:

 
/app/views/users/_subnav.html.erb 
/app/views/posts/_subnav.html.erb 
/app/views/admin/_subnav.html.erb 

En cada uno de estos parciales subnav se declara las opciones específicas para esa sección, por lo /users/_subnav.html.erb podría contener:

<ul id="subnav"> 
    <li><%= link_to 'All Users', users_path %></li> 
    <li><%= link_to 'New User', new_user_path %></li> 
</ul> 

Mientras que /posts/_subnav.html.erb puede contener:

<ul id="subnav"> 
    <li><%= link_to 'All Posts', posts_path %></li> 
    <li><%= link_to 'New Post', new_post_path %></li> 
</ul> 

Por último, una vez que haya hecho esto, sólo tiene que incluir la subnav parcial en el diseño:

<div id="header">...</div>  
<%= render :partial => "subnav" %> 
<div id="content"><%= yield %></div> 
<div id="footer">...</div> 
7
  1. Representación parcial. Esto es muy similar al método de ayuda excepto tal vez la disposición tendría cierta si las declaraciones, o pasar de que fuera a un ayudante ...
+0

como en render: parcial => 'navegación', ¿verdad? – webmat

+0

sí, algo así como render: partial => 'section1_subnav' –

1

Le sugiero que use parciales. Hay algunas maneras en que puede hacerlo. Cuando creo parciales que son un poco quisquillosos porque necesitan variables específicas, también creo un método de ayuda para ello.

module RenderHelper 
    #options: a nested array of menu names and their corresponding url 
    def render_submenu(menu_items=[[]]) 
    render :partial => 'shared/submenu', :locals => {:menu_items => menu_items} 
    end 
end 

Ahora el parcial tiene una variable local denominada menu_items sobre la cual puede iterar para crear su submenú. Tenga en cuenta que sugiero un conjunto anidado en lugar de un hash porque el orden de un hash es impredecible.

Tenga en cuenta que la lógica de decidir qué elementos se deben mostrar en el menú también podría estar dentro de render_submenu si tiene más sentido para usted.

7

En cuanto al contenido de sus submenús, puede hacerlo de manera declarativa en cada controlador.

class PostsController < ApplicationController 
#... 
protected 
    helper_method :menu_items 
    def menu_items 
    [ 
     ['Submenu 1', url_for(me)], 
     ['Submenu 2', url_for(you)] 
    ] 
    end 
end 

Ahora cada vez que llame menu_items desde un punto de vista, usted tendrá la lista de la derecha para repetir para el controlador específico.

Esto me parece una solución más limpia que poner esta lógica dentro de las plantillas de vista.

Tenga en cuenta que también puede declarar un menú de menú predeterminado (¿vacío?) Dentro de ApplicationController también.

+1

¿No sería mejor tener el texto para las opciones del submenú en la Vista en lugar del Controlador? – Olly

+1

Este es el mejor enfoque que podría elegir. Como se indicó en el comentario anterior, rompe MVC por completo. – maurycy

3

Advertencia: ¡Trucos avanzados por delante!

Render them all. Oculte los que no necesita con CSS/Javascript, que pueden inicializarse trivialmente de muchas maneras. (Javascript puede leer la URL utilizada, los parámetros de consulta, algo en una cookie, etc.). Esto tiene la ventaja de poder jugar mucho mejor con su caché (¿por qué almacenar en caché tres vistas y luego caducar todas simultáneamente cuando puede caché uno? ?), y se puede utilizar para presentar una mejor experiencia de usuario.

Por ejemplo, imaginemos que tiene una interfaz de barra de pestañas común con navegación secundaria. Si representa el contenido de las tres pestañas (es decir, está escrito en el HTML) y esconde dos de ellas, cambiar entre dos pestañas es trivial. Javascript y ni siquiera llegan a su servidor. ¡Gran victoria!Sin latencia para el usuario. Sin carga de servidor para ti

¿Quiere otra gran victoria? Puede usar una variación de esta técnica para hacer trampa en páginas que podrían ser 99% comunes en todos los usuarios pero que aún contienen el estado del usuario. Por ejemplo, puede tener una página de inicio de un sitio que es relativamente común para todos los usuarios, pero diga "Hiya Bob" cuando están conectados. Coloque la parte no común ("Hiya, Bob") en una cookie. Haga que esa parte de la página se lea en Javascript leyendo la cookie. Almacenar en caché toda la página para todos los usuarios independientemente del estado de inicio de sesión en el almacenamiento en caché de página. Esto es literalmente capaz de cortar el 70% de los accesos de toda la pila de Rails en algunos sitios.

quién le importa si los carriles se puede escalar o no cuando su sitio es realmente Nginx servir activos estáticos con nuevas páginas HTML en ocasiones conseguir entregados por algunos de Ruby se ejecuta en cada acceso milésima o menos;)

+0

Voto a favor de la originalidad del pensamiento. :-) – maurycy

+0

Tenga en cuenta que esta respuesta * fallará * para algunos usuarios si primero no prueba (o asume) JavaScript o Cookies, respectivamente. Una mejor respuesta sería usar JavaScript con HTML parcial o Varnish + ESI en su lugar. –

1

hay otra manera posible de hacer esto: Diseños anidados

No recuerdo donde encontré este código para disculpas al autor original.

crear un archivo llamado nested_layouts.rb en la carpeta lib e incluir el siguiente código:

module NestedLayouts 
    def render(options = nil, &block) 
    if options 
     if options[:layout].is_a?(Array) 
     layouts = options.delete(:layout) 
     options[:layout] = layouts.pop 
     inner_layout = layouts.shift 
     options[:text] = layouts.inject(render_to_string(options.merge({:layout=>inner_layout}))) do |output,layout| 
      render_to_string(options.merge({:text => output, :layout => layout})) 
     end 
     end 
    end 
    super 
    end 
end 

a continuación, crear sus varios diseños en la carpeta de diseños, (por ejemplo, 'admin.rhtml' y 'aplicación .rhtml ').

Ahora en sus controladores de añadir esta justo dentro de la clase:

include NestedLayouts 

Y, por último, al final de sus acciones hacer esto:

def show 
    ... 
    render :layout => ['admin','application'] 
end 

el orden de los diseños de la matriz es importante . El diseño del administrador se representará dentro del diseño de la aplicación dondequiera que esté el 'yeild'.

este método puede funcionar muy bien dependiendo del diseño del sitio y cómo se organizan los diversos elementos. por ejemplo, uno de los diseños incluidos podría contener una serie de divs que contienen el contenido que debe mostrarse para una acción en particular, y el CSS en un diseño más alto podría controlar dónde están posicionados.

0

Hay pocas aproximaciones a este problema.

Es posible que desee utilizar diferentes diseños para cada sección.

Es posible que desee utilizar un parcial incluido por todas las vistas en un directorio determinado.

Es posible que desee utilizar content_for que se completa mediante una vista o un parcial, y se llama en el diseño global, si tiene uno.

Personalmente, creo que debe evitar más abstracción en este caso.