Usando o Rails 3.1, onde você coloca o código JavaScript "específico da página"?

388

Para meu entendimento, todo o seu JavaScript é mesclado em um arquivo. O Rails faz isso por padrão quando adicionado //= require_tree .à parte inferior do seu application.jsarquivo de manifesto.

Isso soa como um salva-vidas real, mas estou um pouco preocupado com o código JavaScript específico da página. Esse código é executado em todas as páginas? A última coisa que quero é que todos os meus objetos sejam instanciados para todas as páginas quando forem necessários apenas em uma página.

Além disso, não há potencial para códigos que colidem também?

Ou você coloca uma pequena scripttag na parte inferior da página que apenas chama um método que executa o código javascript para a página?

Você não precisa mais do require.js?

obrigado

EDIT : Agradeço todas as respostas ... e não acho que elas estejam realmente entendendo o problema. Alguns deles são sobre estilo e não parecem se relacionar ... e outros apenas mencionam javascript_include_tag... o que eu sei que existe (obviamente ...), mas parece que o caminho a seguir do Rails 3.1 é encerrar tudo seu JavaScript em um arquivo, em vez de carregar o JavaScript individual na parte inferior de cada página.

A melhor solução que posso encontrar é agrupar certos recursos em divtags com ids ou classes. No código JavaScript, você apenas verifica se o idou classestá na página e, se estiver, executa o código JavaScript associado a ele. Dessa forma, se o elemento dinâmico não estiver na página, o código JavaScript não será executado - mesmo que tenha sido incluído no enorme application.jsarquivo compactado pelo Sprockets.

Minha solução acima tem o benefício de que, se uma caixa de pesquisa for incluída em 8 das 100 páginas, ela será executada apenas nessas 8 páginas. Você também não precisará incluir o mesmo código em 8 das páginas do site. Na verdade, você nunca mais precisará incluir tags de script manuais em seu site.

Eu acho que essa é a resposta real para minha pergunta.

Emblema de fogo
fonte
11
"o caminho a seguir do Rails 3.1 é agrupar todo o seu Javascript em um arquivo em vez de carregar o Javascript individual na parte inferior de cada página." - Apenas porque a equipe principal do Rails é, e sempre foi, muito ruim em saber como para gerenciar JavaScript. Arquivos pequenos geralmente são melhores (veja meus comentários em outro lugar). Quando se trata de JavaScript, o caminho do Rails raramente é o caminho certo (exceto para o pipeline de ativos, que funciona bem e o incentivo do CoffeeScript).
Marnen Laibow-Koser
Então você incluirá os arquivos js específicos da página em todas as páginas? Acho um desperdício, concordo mais com a resposta do ClosureCowboy.
gerky
11
Você deu uma olhada na resposta aceita para esta pergunta? stackoverflow.com/questions/6571753/…
rassom
11
@DutGRIFF Em outras palavras: não, não é melhor fazer as coisas da maneira Rails nesse caso (ou pelo menos não colocar tudo application.js), e na verdade a referência que você forneceu indica por que é assim: o download é o parte mais lenta do processo de execução do JS. Muitos arquivos pequenos têm mais cache do que um arquivo grande. O pessoal do Unholy Rails parece não perceber, então, que suas recomendações são inconsistentes com os princípios que estão tentando aderir e, portanto, suas recomendações não devem ser levadas a sério.
Marnen Laibow-Koser
11
@DutGRIFF Não, um arquivo JS grande normalmente não seria bom mesmo depois de armazenado em cache. Veja meus comentários em outras partes desta página: arquivos pequenos podem direcionar melhor para páginas específicas e podem ser armazenados em cache com uma granularidade mais fina. Não vejo qualquer caso bom uso para um único arquivo grande, a menos que não existe um código específico da página em tudo .
Marnen Laibow-Koser

Respostas:

157

Os documentos do Pipeline de ativos sugerem como executar JS específico do controlador:

Por exemplo, se um ProjectsControllerfor gerado, haverá um novo arquivo em app/assets/javascripts/projects.js.coffeee outro em app/assets/stylesheets/projects.css.scss. Você deve colocar qualquer JavaScript ou CSS exclusivo de um controlador em seus respectivos arquivos de ativos, pois esses arquivos podem ser carregados apenas para esses controladores com linhas como <%= javascript_include_tag params[:controller] %>ou <%= stylesheet_link_tag params[:controller] %>.

Link para: asset_pipeline

meleyal
fonte
50
Essa é a maneira mais elegante de fazer isso. Mas também, você precisará remover a linha // = require_tree. do application.js.coffee
zsljulius
2
Eu concordo totalmente com este método. Os outros métodos parecem muito desajeitados e ainda acabam carregando um arquivo js gigante. O projeto em que estou trabalhando tem quase 2 MB de arquivos JS / plugins etc. DEPOIS de serem combinados / minificados.
Bill Garrison
2
Sou bastante novo no Rails, mas me parece que esse deve ser o comportamento padrão.
Ross Hambrick
12
Para controle específico da ação, eu tenho isso no meu layout, pois nem todas as ações para cada controlador possuem JS específico. page_specific_js = "#{params[:controller]}_#{params[:action]}"e depois; javascript_include_tag page_specific_js if Rails.application.assets.find_asset page_specific_js
Sujimichi
2
As ações específicas do controlador ainda são reduzidas? Eles são adicionados ao arquivo js único criado por engrenagens ou isso leva a várias solicitações de arquivos de ativos?
23413 Jason
77

Para os js específicos da página, você pode usar a solução Garber-Irish .

Portanto, sua pasta javascripts do Rails pode ser assim para dois controladores - carros e usuários:

javascripts/
├── application.js
├── init.js
├── markup_based_js_execution
├── cars
   ├── init .js
   ├── index.js
   └── ...
└── users
    └── ...

E javascripts ficará assim:

// application.js

//= 
//= require init.js
//= require_tree cars
//= require_tree users

// init.js

SITENAME = new Object();
SITENAME.cars = new Object;
SITENAME.users = new Object;

SITENAME.common.init = function (){
  // Your js code for all pages here
}

// cars/init.js

SITENAME.cars.init = function (){
  // Your js code for the cars controller here
}

// cars/index.js

SITENAME.cars.index = function (){
  // Your js code for the index method of the cars controller
}

e markup_based_js_execution conterão código para o objeto UTIL e na execução do UTIL.init pronto para DOM.

E não esqueça de colocar isso no seu arquivo de layout:

<body data-controller="<%= controller_name %>" data-action="<%= action_name %>">

Eu também acho que é melhor usar classes em vez de data-*atributos, para um melhor CSS específico da página. Como Jason Garber mencionou: os seletores de CSS específicos da página podem ficar muito estranhos (quando você usa data-*atributos)

Eu espero que isso te ajude.

welldan97
fonte
4
E se você precisar de uma variável disponível para todas as ações no controlador de usuários, mas não disponível em outros controladores? Este método não possui alguns problemas de escopo?
tybro0103
@ tybro0103, acho que para implementar esse comportamento, você gostaria de escrever algo como window.varForOneController='val'nesta função init do controlador. Também o gon gem pode ajudar aqui ( github.com/gazay/gon ). Pode haver outras soluções alternativas.
welldan97
11
@ welldan97 Voto não para sua explicação - que é excelente - mas porque a estrutura garber-irlandesa é ruim. Carrega todas as suas JS em todas as páginas e depende de classes e IDs no elemento <body> para resolver as coisas. Esse é um sinal claro de combater o DOM: em circunstâncias normais, o elemento <body> não deve precisar de uma classe ou ID, pois existe apenas um em um documento. A maneira correta de fazer isso é simplesmente remover //= require_tree .e usar o JavaScript específico da página. Se você está tentando ativamente não fazer isso, está se esforçando para praticar mal.
Marnen Laibow-Koser
2
@ MarnenLaibow-Koser Pessoalmente, acredito que carregar todos os js em todas as páginas é bom para a maioria dos projetos quando você combina todos os js em um arquivo e o minimiza. Eu acredito que no geral funciona mais rápido para o usuário. Pelo menos é mais um conflito entre um arquivo js e muitos (por exemplo, veja stackoverflow.com/questions/555696/… ). Além disso, não há nada ruim em usar classes e IDs no corpo se isso torna o código mais simples e funciona para você. Modernizr ( modernizr.com ) faz isso, e algumas outras bibliotecas também.
welldan97
2
@ MarnenLaibow-Koser o pipeline de ativos de trilhos, para mim, parece ser um bom candidato para comparação com compilação. Um programador grava seu javascript em bons módulos dissociados e, em seguida, é agrupado, minificado e veiculado. Assim como no caso de linguagens compiladas, sempre haverá programadores que acham que estão um passo à frente do compilador ... mas acho que isso raramente é verdade.
perfil
65

Vejo que você respondeu sua própria pergunta, mas aqui está outra opção:

Basicamente, você está assumindo que

//= require_tree .

É necessário. Não é. Sinta-se livre para removê-lo. No meu aplicativo atual, o primeiro que estou fazendo com o 3.1.x, honestamente, criei três arquivos JS de nível superior diferentes. Meu application.jsarquivo tem apenas

//= require jquery
//= require jquery_ujs
//= require_directory .
//= require_directory ./api
//= require_directory ./admin

Dessa forma, posso criar subdiretórios, com seus próprios arquivos JS de nível superior, que incluem apenas o que eu preciso.

As chaves são:

  1. Você pode remover require_tree- o Rails permite alterar as suposições feitas
  2. Não há nada de especial no nome application.js- qualquer arquivo no assets/javascriptsubdiretório pode incluir diretivas de pré-processador com//=

Espero que ajude e adicione alguns detalhes à resposta do ClosureCowboy.

Sujal

sujal
fonte
8
+1 É ótimo saber para um novato como eu. Eu daria +2 se pudesse.
jrhorn424
5
@sujal Exatamente. A equipe principal do Rails é notória por um péssimo gerenciamento de JavaScript. Sinta-se à vontade para ignorar as sugestões e usar as partes boas do pipeline de ativos. :)
Marnen Laibow-Koser
11
Muito obrigado por este conselho. Não tenho vários arquivos JS de "nível superior", dependendo do módulo do meu aplicativo. Funciona bem.
elsurudo
11
+1 O ponto importante aqui para mim é que você pode substituir //= require_tree .por //= require_directory .para manter todos os arquivos existentes onde estão e criar novos diretórios para arquivos específicos da página.
Zelanix
41

Outra opção: para criar arquivos específicos de página ou modelo, você pode criar diretórios dentro de sua assets/javascripts/pasta.

assets/javascripts/global/
assets/javascripts/cupcakes
assets/javascripts/something_else_specific

Seu application.jsarquivo de manifesto principal pode ser configurado para carregar seus arquivos global/. Páginas ou grupos de páginas específicos podem ter seus próprios manifestos, que carregam arquivos de seus próprios diretórios específicos. O Sprockets combinará automaticamente os arquivos carregados application.jscom os arquivos específicos da página, o que permite que esta solução funcione.

Essa técnica também pode ser usada style_sheets/.

Encerramento
fonte
13
Você me fez desejar cupcakes agora .. Dangit!
Chuck Bergeron
Eu realmente gosto desta solução. O único problema que tenho com isso é que esses manifestos extras não são compactados / uglificados. Eles são compilados corretamente. Existe uma solução ou estou faltando alguma coisa?
Clt
11
isso significa que o navegador carrega um arquivo js, ​​que é uma combinação do arquivo específico global da página +?
Lulalala 6/03/12
Você poderia dar uma olhada na minha pergunta, se estiver disponível? stackoverflow.com/questions/17055213/…
Maximus S
11
@clst Eu acredito que esta é a resposta que você está procurando: guides.rubyonrails.org/asset_pipeline.html#precompiling-assets
FrontierPsycho
23

Agradeço todas as respostas ... e não acho que elas estejam realmente entendendo o problema. Alguns deles são sobre estilo e não parecem se relacionar ... e outros apenas mencionam javascript_include_tag... o que eu sei que existe (obviamente ...), mas parece que o caminho a seguir do Rails 3.1 é encerrar todos seu Javascript em um arquivo, em vez de carregar o Javascript individual na parte inferior de cada página.

A melhor solução que posso encontrar é agrupar certos recursos em divtags com ids ou classes. No código javascript. Em seguida, basta verificar se o idou classestá na página e, se estiver, você executa o código javascript associado a ele. Dessa forma, se o elemento dinâmico não estiver na página, o código javascript não será executado - mesmo que tenha sido incluído no enorme application.jsarquivo compactado pelo Sprockets.

Minha solução acima tem o benefício de que, se uma caixa de pesquisa for incluída em 8 das 100 páginas, ela será executada apenas nessas 8 páginas. Você também não precisará incluir o mesmo código em 8 das páginas do site. Na verdade, você nunca precisará incluir tags de script manuais em seu site em nenhum outro lugar - exceto para talvez pré-carregar dados.

Eu acho que essa é a resposta real para minha pergunta.

Emblema de fogo
fonte
Mas você realmente deseja essas <script>tags manuais . Sim, classes e IDs fazem parte da resposta, mas não faz sentido para o usuário carregar JavaScript que essa página específica não exige.
Marnen Laibow-Koser
4
@ MarnenLaibow-Koser a razão para não adicionar tags de script manuais a cada página exclusiva é que você precisa fazer o download desse conteúdo de script em todas as visualizações de página. Se você é capaz de empacotar tudo javascript em application.js usando o pipeline de ativos, em seguida, o usuário baixa esses scripts apenas uma vez, e puxa application.js do cache em todas as cargas de página subseqüentes
jakeonrails
@jakeonrails "a razão para não adicionar tags de script manuais a cada página exclusiva é que você precisa fazer o download desse conteúdo de script em todas as visualizações de páginas" - muito errado. O script será baixado uma vez e será buscado no cache do navegador em outras solicitações. "Se você conseguir empacotar todo o javascript no application.js usando o pipeline de ativos, o usuário fará o download desses scripts apenas uma vez" - é verdade, mas à custa de muitos códigos desnecessários. Se você pode estruturar seu JS em muitos arquivos pequenos, em vez de um grande, você obtém benefícios de armazenamento em cache sem código desnecessário.
Marnen Laibow-Koser
11
@ MarnenLaibow-Koser Acho que seria melhor dizer que, se você empacotar tudo em um script, seu usuário precisará baixar apenas 1 script para qualquer página do seu site. Se você possui vários scripts para diferentes partes do seu aplicativo, obviamente o usuário deve fazer o download de mais de um script. Ambos os métodos serão armazenados em cache, é claro, mas na maioria dos casos (aplicativos pequenos e médios), atender a um application.js por vez será mais eficiente para o download. A análise do JS pode ser outra história, dependendo do que você serve.
precisa saber é o seguinte
11
@Ziggy Além disso, se o arquivo pequeno é usado apenas em 8 de 100 páginas, por que o código fica no cache do usuário o tempo todo? Melhor deixar de lado as coisas que não são necessárias.
Marnen Laibow-Koser
16

Percebo que estou chegando a esta festa um pouco tarde, mas queria lançar uma solução que tenho usado ultimamente. No entanto, deixe-me mencionar primeiro ...

The Rails 3.1 / 3.2 Way (Não, senhor. Eu não gosto disso.)

Consulte: http://guides.rubyonrails.org/asset_pipeline.html#how-to-use-the-asset-pipeline

Estou incluindo o seguinte por uma questão de integridade nesta resposta, e porque não é uma solução inviável ... embora eu não me importe muito com isso.

O "Rails Way" é uma solução orientada a controladores, em vez de ser orientada a visualizações, conforme solicitado pelo autor original desta pergunta. Existem arquivos JS específicos do controlador com o nome de seus respectivos controladores. Todos esses arquivos são colocados em uma árvore de pastas que NÃO é incluída por padrão em nenhum aplicativo.js requer diretivas.

Para incluir código específico do controlador, o seguinte é adicionado a uma exibição.

<%= javascript_include_tag params[:controller] %>

Eu detesto essa solução, mas está lá e é rápida. Presumivelmente, você poderia chamar esses arquivos de algo como "people-index.js" e "people-show.js" e depois usar algo como "#{params[:controller]}-index"para obter uma solução orientada a exibição. Mais uma vez, solução rápida, mas não fica bem comigo.

Maneira do meu atributo de dados

Chame-me de louco, mas quero TODO o meu JS compilado e compactado no application.js quando implantar. Não quero me lembrar de incluir esses pequenos arquivos dispersos em todo o lugar.

Carrego todo o meu JS em um arquivo compacto e em breve em cache do navegador. Se uma certa parte do meu application.js precisar ser acionada em uma página, deixo o HTML informar, não o Rails.

Em vez de bloquear meu JS para IDs de elementos específicos ou desarrumar meu HTML com classes de marcadores, eu uso um atributo de dados personalizado chamado data-jstags.

<input name="search" data-jstag="auto-suggest hint" />

Em cada página, eu uso - insira aqui o método preferido da biblioteca JS - para executar o código quando o DOM terminar de carregar. Esse código de inicialização executa as seguintes ações:

  1. Iterar sobre todos os elementos no DOM marcados com data-jstag
  2. Para cada elemento, divida o valor do atributo no espaço, criando uma matriz de cadeias de tags.
  3. Para cada sequência de tags, faça uma pesquisa em um Hash para essa tag.
  4. Se uma chave correspondente for encontrada, execute a função que está associada a ela, passando o elemento como um parâmetro.

Digamos que eu tenha o seguinte definido em algum lugar no meu application.js:

function my_autosuggest_init(element) {
  /* Add events to watch input and make suggestions... */
}

function my_hint_init(element) {
  /* Add events to show a hint on change/blur when blank... */
  /* Yes, I know HTML 5 can do this natively with attributes. */
}

var JSTags = {
  'auto-suggest': my_autosuggest_init,
  'hint': my_hint_init
};

O evento de auto-inicialização aplicará as funções my_autosuggest_inite my_hint_inità entrada de pesquisa, transformando-a em uma entrada que exibe uma lista de sugestões enquanto o usuário digita, além de fornecer algum tipo de dica de entrada quando a entrada é deixada em branco e sem foco.

A menos que algum elemento seja marcado data-jstag="auto-suggest", o código de sugestão automática nunca é acionado. No entanto, ele está sempre lá, minificado e, eventualmente, armazenado em cache no meu application.js nos momentos em que eu preciso dele em uma página.

Se você precisar passar parâmetros adicionais para as funções JS marcadas, precisará aplicar alguma criatividade. Adicione atributos de parâmetro de dados, crie algum tipo de sintaxe de parâmetro ou use uma abordagem híbrida.

Mesmo que eu tenha algum fluxo de trabalho complicado que pareça específico do controlador, apenas crio um arquivo para ele na minha pasta lib, empacoto-o em application.js e o identifico com algo como 'assistente de coisa nova'. Quando meu bootstrap atingir essa tag, meu assistente bonito e sofisticado será instanciado e executado. É executado para a (s) visualização (ões) desse controlador quando necessário, mas não está acoplado ao controlador. De fato, se eu codificar meu assistente corretamente, talvez seja possível fornecer todos os dados de configuração nas visualizações e, portanto, reutilizá-lo posteriormente para qualquer outro controlador que precise dele.

De qualquer forma, é assim que implemento o JS específico da página há algum tempo e isso me serviu bem tanto para designs de sites simples quanto para aplicativos mais complexos / ricos. Espero que uma das duas soluções que apresentei aqui, do meu jeito ou do Rails, seja útil para quem se deparar com essa questão no futuro.

Ryan
fonte
6
Um pequeno detalhe: há uma noção na sua resposta de que, uma vez que o js é armazenado em cache no navegador, ele não tem impacto. Isso não é bem verdade. O navegador evita o download, se o arquivo js estiver armazenado em cache corretamente, mas ainda assim compila o código em todas as renderizações de páginas. Então, você precisa equilibrar as compensações. Se você possui muitas JS agregadas, mas apenas algumas são usadas por página, poderá melhorar o tempo da página dividindo a JS.
Sujal
Para saber mais sobre os efeitos práticos dessa etapa de compilação que eu estou falando, veja explicação de como pjax impactado Basecamp próximos 37 Signals': 37signals.com/svn/posts/...
Sujal
Esse é um argumento justo. Depois de ler o artigo e analisar os projetos em que usei a solução acima, percebo que escrevi essencialmente a mesma solução "enviar o HTML alterado" mencionada no artigo. A re-compilação frequente de JS não foi um problema em meus projetos por causa disso. A etapa de compilação é algo que terei em mente quando estiver trabalhando em sites menos orientados para "aplicativos de desktop".
18712 Ryan
2
Voto negativo para "Chame-me de louco, mas quero TODO meu JS compilado e compactado em application.js quando implantar." Você realmente não quer isso, pois faz com que o usuário carregue o JavaScript que ele não precisa e faz com que seus manipuladores procurem atributos que nem estarão lá. Ter tudo no app.js é tentador, e o Rails certamente facilita, mas a coisa certa a fazer é modularizar o JavaScript melhor.
Marnen Laibow-Koser
Você tem direito a uma opinião diferente ... e tecnicamente tem direito a voto negativo por uma diferença de opinião. No entanto, seria bom ver algumas justificativas para o motivo de um arquivo grande e armazenado em cache ser inferior a forçar várias solicitações HTTP para capturar JS modularizado. Além disso, você está enganado em relação ao procedimento de pesquisa do manipulador. Os valores da tag NÃO são pesquisados. Apenas uma pesquisa é executada e puxa todos os elementos que possuem um atributo data-jstag. Ele não pesquisa pelo nome da tag, apenas encontra todos os elementos que possuem tags e instancia apenas os objetos necessários.
21712 Ryan
7

Isso foi respondido e aceito há muito tempo, mas eu criei minha própria solução com base em algumas dessas respostas e na minha experiência com o Rails 3+.

O pipeline de ativos é bom. Use-o.

Primeiro, no seu application.jsarquivo, remova//= require_tree.

Em seguida, no seu application_controller.rbmétodo create a helper:

helper_method :javascript_include_view_js //Or something similar

def javascript_include_view_js
    if FileTest.exists? "app/assets/javascripts/"+params[:controller]+"/"+params[:action]+".js.erb"
        return '<script src="/assets/'+params[:controller]+'/'+params[:action]+'.js.erb" type="text/javascript"></script>'
    end
end

Em seguida, no seu application.html.erbarquivo de layout, adicione seu novo auxiliar entre as inclusões javascript existentes, prefixadas com o rawauxiliar:

<head>
    <title>Your Application</title>
    <%= stylesheet_link_tag "application", :media => "all" %>
    <%= javascript_include_tag "application" %>
    <%= raw javascript_include_view_js %>
</head>

Agora, agora você pode criar facilmente javascript específico de exibição usando a mesma estrutura de arquivo usada em qualquer outro lugar nos trilhos. Basta colar seus arquivos app/assets/:namespace/:controller/action.js.erb!

Espero que ajude alguém!

Mike A
fonte
11
Isso não causará problemas após a pré-compilação dos ativos e, em tempo de execução <%= raw ... %>, retornará um 404?
Nishant
Eu acho que o pipeline de ativos não é bom, pois cria um monte de arquivos que geralmente não devem ser usados. Então, para mim, contar com o pipeline de ativos está criando uma dependência de um sistema ineficiente.
Deborah
11
@DeborahSpeece Quando o pipeline de ativos cria arquivos que não devem ser usados? Você está confundindo o pipeline de ativos (bom) com require_tree /(ruim)?
Marnen Laibow-Koser
6

Você pode adicionar esta linha no seu arquivo de layout (por exemplo, application.html.erb) para carregar automaticamente o arquivo javascript específico do controlador (aquele que foi criado quando você gerou o controlador):

<%= javascript_include_tag params[:controller] %>

Você também pode adicionar uma linha para carregar automaticamente um arquivo de script por ação.

<%= javascript_include_tag params[:controller] + "/" + params[:action] %>

Basta colocar os scripts da página em um subdiretório nomeado após o nome do controlador. Nesses arquivos, você pode incluir outros scripts usando = require. Seria bom criar um auxiliar para incluir o arquivo apenas se ele existir, para evitar uma falha 404 no navegador.

mcubik
fonte
6
<%= javascript_include_tag params[:controller] %>
Sr. Bohr
fonte
2
Parece que ele pode responder à pergunta. Você poderia adicionar mais à resposta para realizá-la?
6

Talvez você encontre a jóia pluggable_js como solução adequada.

peresleguine
fonte
5

A gema LoadJS é outra opção:

O LoadJS fornece uma maneira de carregar o código Javascript específico da página em um aplicativo Rails sem perder a mágica fornecida pelo Sprockets. Todo o seu código Javascript continuará minificado em um arquivo Javascript, mas algumas partes dele serão executadas apenas para determinadas páginas.

https://github.com/guidomb/loadjs

meleyal
fonte
3

A resposta de Philip é muito boa. Aqui está o código para fazê-lo funcionar:

Em application.html.erb:

<body class="<%=params[:controller].parameterize%>">

Supondo que seu controlador se chame Projetos, isso gerará:

<body class="projects">

Em seguida, em projects.js.coffee:

jQuery ->
  if $('body.projects').length > 0  
     $('h1').click ->
       alert 'you clicked on an h1 in Projects'
Rahil Sondhi
fonte
Downvoting: qualquer solução que coloca uma aula sobre o <body>é ipso facto incorreta. Veja meus comentários em outras partes desta página.
Marnen Laibow-Koser
Não faça isso. O problema aqui é que, toda vez que você adiciona uma delas, você adiciona outra parte de js que precisa ser executada no carregamento da página. Definitivamente, isso pode causar degradação no desempenho à medida que o projeto cresce.
ifightcrime
2

Os JavaScripts são mesclados apenas quando você diz ao Rails (Sprockets, em vez disso) para mesclá-los.

yfeldblum
fonte
Claro. Acho que pergunto, porque os padrões do Rails incluem tudo na pasta ... o que significa que David pretende que você faça isso. Mas, como eu disse no outro comentário para @rubyprince, não tenho certeza sobre a execução quando é feita dessa maneira. Estou pensando que tenho que desativar //= require_tree .?
Fire Emblem
@FireEmblem Sim. require_tree .geralmente é uma má ideia.
Marnen Laibow-Koser
2

Foi assim que resolvi a questão do estilo: (desculpe o Haml)

%div{:id => "#{params[:controller].parameterize} #{params[:view]}"}
    = yield

Dessa forma, inicio todos os arquivos .css.sass específicos da página com:

#post
  /* Controller specific code here */
  &#index
    /* View specific code here */
  &#new
  &#edit
  &#show

Dessa forma, você pode evitar facilmente qualquer conflito. Quando se trata de arquivos .js.coffee, você pode apenas inicializar elementos como;

$('#post > #edit') ->
  $('form > h1').css('float', 'right')

Espero que isso tenha ajudado alguns.

Zeeraw
fonte
11
Leia o último bit novamente, por favor, para javascript, você pode aproveitar a mesma estrutura usada para folhas de estilo para inicializar funções específicas da visualização.
Zeeraw 30/05
Philip, $('#post > #edit') ->parece ser inválido. Como o escopo do jQuery funciona dentro de um escopo?
Ramon Tayag 24/09/11
2
Recentemente, comecei a carregar todos os scripts java e folhas de estilo específicos do controlador chamando isso em application.html.haml; = javascript_include_tag "application"e = javascript_include_tag params[:controller]dessa forma eu posso manter o código javascript confinado sem precisar especificar um escopo dentro do arquivo.
precisa saber é o seguinte
2

Concordo com sua resposta, para verificar se esse seletor está lá, use:

if ($(selector).length) {
    // Put the function that does not need to be executed every page
}

(não vi ninguém adicionar a solução real)

Kyle C
fonte
2

Não vejo uma resposta que realmente junte tudo e a exponha para você. Assim, tentarei colocar meleyal , sujal (à la ClosureCowboy ), a primeira parte da resposta de Ryan e até a ousada declaração de Gal sobre o Backbone.js ... todos juntos de uma maneira curta e clara. E, quem sabe, posso até atender aos requisitos de Marnen Laibow-Koser .

Edições de exemplo

assets / javascripts / application.js

//= require jquery
//= require jquery_ujs
//= require lodash.underscore.min
...


visualizações / layouts / application.html.erb

  ...
  </footer>

  <!-- Javascripts ================================================== -->
  <!-- Placed at the end of the document so the pages load faster -->
  <%= javascript_include_tag "application" %>
  <%= yield :javascript %>

</body>
</html>


visualizações / foo / index.html.erb

...
<% content_for :javascript do %>
  <%= javascript_include_tag params[:controller] %>
<% end %>


assets / javascripts / foo.js

//= require moment
//= require_tree ./foostuff


assets / javascripts / foostuff / foothis.js . café

alert "Hello world!"


Descrição breve

  • Remova //= require_tree .do application.js e liste apenas o JS que cada página compartilha.

  • As duas linhas mostradas acima em application.html.erb indicam à página onde incluir application.js e seu JS específico da página.

  • As três linhas mostradas acima em index.html.erb informam sua visualização para procurar por JS específico da página e incluí-lo em uma região de rendimento nomeada chamada ": javascript" (ou o que você quiser nomear). Neste exemplo, o controlador é "foo", portanto o Rails tentará incluir "foo.js" na região de rendimento: javascript no layout do aplicativo.

  • Liste sua JS específica da página em foo.js (ou qualquer que seja o nome do controlador). Liste bibliotecas comuns, uma árvore, diretórios, o que for.

  • Mantenha seu JS específico da página personalizado em algum lugar onde você possa referenciá-lo facilmente, além dos outros JS personalizados. Neste exemplo, o foo.js requer a árvore do foostuff; portanto, coloque seu JS personalizado lá, como foothis.js.coffee .

  • Não há regras rígidas aqui. Sinta-se à vontade para mover as coisas e, talvez, até criar várias regiões de rendimento de vários nomes em vários layouts, se necessário. Isso apenas mostra um possível primeiro passo à frente. (Eu não faço exatamente dessa maneira, considerando o uso do Backbone.js. Também posso optar por colocar o foo.js em uma pasta chamada foo, em vez de foostuff, mas ainda não decidimos isso.)

Notas

Você pode fazer coisas semelhantes com CSS, <%= stylesheet_link_tag params[:controller] %>mas isso está além do escopo da questão.

Se eu perdi uma prática recomendada flagrante aqui, envie-me uma nota e eu vou me adaptar. O Rails é relativamente novo para mim e, sinceramente, não estou muito impressionado até agora com o caos que ele traz por padrão para o desenvolvimento empresarial e todo o tráfego que o programa Rails médio gera.

juanitogan
fonte
isso parece o caminho a percorrer, vou ver se consigo implementá-lo em meu próprio aplicativo, obrigado pela resposta detalhada.
martincarlin87
1

Tenho outra solução, que apesar de primitiva funcionar bem para mim e não precisar de nenhuma estratégia de carregamento seletivo sofisticada. Coloque sua função pronta para documento nornal, mas teste a localização atual do Windows para ver se é a página para a qual seu javascript se destina:

$(document).ready(function() {
   if(window.location.pathname.indexOf('/yourpage') != -1) {
          // the javascript you want to execute
   }
}

Isso ainda permite que todos os js sejam carregados pelo Rails 3.x em um pacote pequeno, mas não gera muita sobrecarga ou conflito com páginas para as quais os js não se destinam.

Peter Abramowitsch
fonte
1

A resposta de ryguy é uma boa resposta, mesmo que tenha sido votada em pontos negativos.

Especialmente se você estiver usando algo como Backbone JS - cada página tem sua própria exibição de Backbone. Em seguida, o arquivo erb possui apenas uma única linha de javascript embutido que dispara a classe de exibição do backbone certo. Considero uma única linha de 'código de cola' e, portanto, o fato de que sua linha está correta. A vantagem é que você pode manter seu "require_tree", que permite ao navegador armazenar em cache todo o javascript.

em show.html.erb, você terá algo como:

<% provide :javascript do %>
  <%= javascript_include_tag do %>
    (new app.views.ProjectsView({el: 'body'})).render();
  <% end %>
<% end do %>

e no seu arquivo de layout, você precisará de:

<%= yield :javascript %>
Garota
fonte
Downvoting. O JavaScript embutido nunca é uma boa ideia. Mesmo que seja um código de cola, ele deve estar em um arquivo externo.
Marnen Laibow-Koser
1

Mova todos os seus arquivos JS comuns para uma subpasta como 'app / assets / javascript / global' e, em application.js, modifique a //= require_tree .linha para //= require_tree ./global.

Agora você pode colocar seu JS específico do controlador na raiz 'app / assets / javascript /' e eles não serão incluídos no JS compilado, sendo usados ​​apenas quando você os chamar = javascript_include_tagno seu controlador / visualização.

Gedean Dias
fonte
De jeito nenhum, isso é um upload de JavaScript para carregar em uma página. Nem importa se está em cache.
jackyalcine
1

Embora você tenha várias respostas aqui, acho que sua edição é provavelmente a melhor aposta. Um padrão de design que usamos em nossa equipe que recebemos do Gitlab é o padrão Dispatcher. Faz algo semelhante ao que você está falando, no entanto, o nome da página é definido na tag body pelos trilhos. Por exemplo, no seu arquivo de layout, inclua algo como (em HAML):

%body{'data-page' => "#{controller}:#{action}" }

Então, tenha apenas um encerramento e uma instrução switch no seu dispatcher.js.coffeearquivo na pasta javascripts da seguinte maneira:

$ ->
  new Dispatcher()

class Dispatcher
  constructor: ->
    page = $('body').attr('data-page')
    switch page
      when 'products:index'
        new Products() 
      when 'users:login'
        new Login()

Tudo o que você precisa fazer nos arquivos individuais (digamos products.js.coffeeou login.js.coffeepor exemplo) é incluí-los em uma classe e globalizar esse símbolo de classe para que você possa acessá-lo no expedidor:

class Products
  constructor: ->
    #do stuff
@Products = Products

O Gitlab tem vários exemplos disso com os quais você pode querer bisbilhotar caso esteja curioso :)

onetwopunch
fonte
1

Paloma projeto oferece uma abordagem interessante para gerenciar o código javascript específico da página.

Exemplo de uso de seus documentos:

var UsersController = Paloma.controller('Users');

// Executes when Rails User#new is executed.
UsersController.prototype.new = function(){
   alert('Hello Sexy User!' );
};
zavg
fonte
1

Passo 1. remover require_tree. em seu application.js e application.css.

Passo 2. Edite seu application.html.erb (por padrão do Rails) na pasta de layout. Adicione "params [: controller]" nas seguintes tags.

<%= stylesheet_link_tag    'application', params[:controller], media: 'all', 'data-turbolinks-track' => true %>

<%= javascript_include_tag 'application', params[:controller], 'data-turbolinks-track' => true %>

Etapa 3. Adicione um arquivo em config / initializers / assets.rb

%w( controller_one controller_two controller_three ).each do |controller|
  Rails.application.config.assets.precompile += ["#{controller}.js", "#{controller}.js.coffee", "#{controller}.css", "#{controller}.scss"]
end

referências: http://theflyingdeveloper.com/controller-specific-assets-with-rails-4/

etlds
fonte
Embora isso possa teoricamente responder à pergunta, seria preferível incluir aqui as partes essenciais da resposta e fornecer o link para referência.
Bhargav Rao
0

Eu não tentei isso, mas parece que o seguinte é verdade:

  • se você tiver um content_for que é javascript (por exemplo, com javascript real dentro dele), as rodas dentadas não saberiam disso e, portanto, isso funcionaria da mesma maneira que agora.

  • se você deseja excluir um arquivo do grande pacote de javascript, você deve entrar no arquivo config / sprockets.yml e modificar os arquivos de origem de acordo. Em seguida, inclua qualquer arquivo excluído, quando necessário.

Bill Eisenhauer
fonte
A exclusão de arquivos ou o uso de javascript personalizado na própria página é o "caminho certo" então? Foi assim que Davi pretendeu que as pessoas o usassem?
Fire Emblem
@ FireEmblem Eu não ligo muito para o que David pretendia, porque acho que ele não entende como organizar o JavaScript corretamente.
Marnen Laibow-Koser
0

Eu combinei algumas respostas em:

Auxiliar de aplicação:

module ApplicationHelper
  def js_page_specific_include
    page_specific_js = params[:controller] + '_' + params[:action]
    if Rails.application.assets.find_asset(page_specific_js).nil?
      javascript_include_tag 'application', 'data-turbolinks-track' => true
    else
      javascript_include_tag 'application', page_specific_js, 'data-turbolinks-track' => true
    end
  end
end

layouts / application.html.haml:

 <!DOCTYPE html>
%html{lang: 'uk'}
  %head   
    = stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => true
   bla-bla-bla
    = js_page_specific_include   
   bla-bla-bla  
kapellan
fonte
0

Primeiro: remove \\=require_treefrom application.js Segundo: todo o seu código JS deve ser alocado em /app/assets/javascritpte todo o seu código CSS deve ser alocado em/app/assets/stylesheets

Nicollas
fonte
-2

Seguindo a liderança de Ryan, aqui está o que eu fiz:

application.js.coffee

$ ->
    view_method_name = $("body").data("view") + "_onload"
    eval("#{view_method_name}()") if eval("typeof #{view_method_name} == 'function'")
    view_action_method_name = $("body").data("view") + "_"+$("body").data("action")+"_onload"
    eval("#{view_action_method_name}()") if eval("typeof #{view_action_method_name} == 'function'")

users.js.coffee (coffeescript específico do controlador, por exemplo, controller: users, action: dashboard)

window.users_dashboard_onload = () ->
    alert("controller action called")
window.users_onload = () ->
    alert("controller called")

application.html.haml

%body{:data=>{:view=>controller.controller_name, :action=>controller.action_name}}
Kruttik
fonte
Downvoting. Isso é ridiculamente complicado - para não mencionar inseguro (devido ao eval) se o seu HTML for comprometido por um servidor quebrado ou por um script de usuário mal-intencionado.
Marnen Laibow-Koser
-3

Veja como fazer isso, especialmente se você não precisar executar toneladas de bibliotecas para sua página específica, mas apenas executar algumas centenas de linhas de JS mais ou menos.

Como é perfeitamente adequado incorporar o código Javascript no HTML, basta criar no diretório app / views shared.js e colocar lá o código específico da sua página / páginas em my_cool_partial.html.erb

<script type="text/javascript"> 
<!--
  var your_code_goes_here = 0;
  function etc() {
     ...
  }
-->
</script>

Então agora, de onde você quiser, basta:

  = render :partial => 'shared.js/my_cool_partial'

E é isso, k?

valk
fonte
2
Downvoting. O JavaScript embutido nunca é aconselhável. O HTML deve conter apenas marcação. JS e CSS devem estar em arquivos reutilizáveis ​​separados.
Marnen Laibow-Koser