在视图中包含带有 ERB 的 Coffeescript 文件

Posted

技术标签:

【中文标题】在视图中包含带有 ERB 的 Coffeescript 文件【英文标题】:Include a Coffeescript file with ERB in a view 【发布时间】:2013-11-12 02:53:56 【问题描述】:

这让我很头疼……

所以我有一个应用程序,它需要一个边栏来列出与用户播放器有关的各种信息。此侧边栏的一部分是朋友列表。现在,当玩家 A 向玩家 B 发送好友请求时,该请求应该会自动记录在 B 的侧边栏中,我打算使用 WebSockets 来做到这一点。

这是我的cp.js.coffe.erb 文件(目前只有几个 ERB 的 sn-ps;会有更多负载,我宁愿先让它工作):

$ ->
  $("#cp").accordion()

  if `"WebSocket" in window`
    socket = new WebSocket("ws://localhost:8080") 

  socket.onopen = =>
    console.log("Connection Open")
    init = 
      sender: "cp"
      action: "init"
      user:   <%= @user.id %>
      token:  <%= cookies["remember_token"] %>
    

    socket.send(init.to_json)

  socket.onerror = (e)=>
    console.log(e)

  socket.onclose = =>
    console.log("Closed")

  socket.onmessage = (m)=>
    console.log("Recieved: #m.data")
    msg = m.data.JSON.parse
    switch msg.action
      when "ret_init"

      when "friend_udt"
        refreshFriend()

refreshFriend() ->
  html = "<%= j render 'layouts/friends' %>"
  $('#friends').empty()
  $('#friends').add(html)

理论上,代码本身可以正常工作,问题是 Rails 不允许您在资产管道中使用 ERB,因此该文件必须位于 app/views/layouts该文件无法访问控制器中声明的变量或使用render 方法(或大多数其他 ERB 方法)。

事情是这样的:我不能在我的 application.html.erb 文件中包含所述文件,我考虑使用 AJAX 请求该文件,但据我了解,它将立即执行一次 javascript,我需要这里的方法可以持续更新侧边栏。

是否有任何方法可以包含此文件,以便它与 ERB 和 CoffeScript 一起使用,从而使页面持续可用?我是否误解了整个 AJAX 请求方法?


感谢@nzifnab 对 JS 的帮助。现在我的朋友部分看起来像这样:
<ul id="friendlist">
    <% if Relation.find_by(owner: @user.id, type: "freq") != nil %>
        <% Relation.find_by(owner: @user.id, type: "freq").each do |r| %>
            <li class="friend-request-cp"><%= link_to "/#User.find(r.character).name" %></li>
        <% end %>
    <% end %>
    <% if Relation.find_by(owner: @user.id, type: "friend") != nil %>
        <% Relation.find_by(owner: @user.id, type: "friend").each do |r| %>
            <li class="friend-cp"><%= link_to "/#User.find(r.character).name" %></li>
        <% end %>
    <% end %>
</ul>

我需要为每个项目应用两种不同的样式,因此我在这里使用 ERB。这很好用,因为它是在第一次导航到页面时加载的,但是我的代码应该在每次通知通过任何新交互时重新呈现该部分。然后它将再次使用数据库中的数据重新填充列表。有没有更有效的方法来做到这一点?我还能用你给我看的hamlcoffeeassets gem 做这个吗?


随之而来的是轻微的切线: 顺便说一句,我在 Windows 7 上使用 Ruby 2.0.0-p247 和 Rails 4。我觉得有必要将其包括在内,因为与 Ubuntu 有很大不同的 gem 存在一些主要的兼容性问题。我不得不从 Ubuntu 迁移到 Windows,因为从 13.04 更新到 13.10 破坏了该操作系统上的所有 Ruby Gem。我不需要找到解决办法:我实际上只有四天的时间来构建这个应用程序。

【问题讨论】:

顺便说一句,您可能想多花一点时间在 CoffeeScript 文档上:if 'WebSocket' of windowif window.WebSocket? 比反引号原始 JavaScript 更好。 感谢@mu-is-too-short ,我正在寻找Coffee 的实现:) html = "&lt;%= j render 'layouts/friends' %&gt;" 作为 AJAX 调用或某种客户端模板会更好吗? (Java|Coffee)Script 中的 ERB(设置常量等除外)总是让我怀疑。 'layouts/friends 也包含 ERB,所以我不确定它会如何发挥作用。 Ajax 真的让我很烦,我希望这可以节省我的时间,因为我必须在星期二提交。 【参考方案1】:

这真的应该去app/assets/javascripts/cp.js.coffee.erb,你可以在资产管道中使用erb就好了(见here)确保你拼写正确的coffee扩展名!

这样做,您应该可以毫无问题地通过 ajax 调用它,路径是 /assets/cp.js

【讨论】:

第一次对我来说没有用,很多其他问题也说了同样的话,但我想我只是做错了什么。 ERB 现在可以工作,但我遇到了一个问题:找不到@user.id 啊,我的问题有点错误:render 在管道内不起作用,因此需要在views 中。我的错,我的解释不清楚。【参考方案2】:

你可以在资产管道中使用erb,但你必须记住,它只会被渲染一次,永远,而不是为每个用户渲染一次,所以即使有一个@user变量(它赢了'不是),它永远不会改变。您可以在咖啡文件中使用erb 来处理路由路径和环境变量等内容,但不能用于特定于用户的配置和对 JS 的动态更改等内容。无论如何,这是不好的做法。

您真正应该做的是使用 javascript 库来读取 cookie,而不是尝试使用 rails 来完成(这将使您能够访问您似乎正在尝试做的一些事情)。当您需要更多动态行为时,您应该将数据属性或其他值呈现到 html DOM 本身并使用 javascript 来读取。

看看这个 cookie 库:https://github.com/carhartl/jquery-cookie

通过快速谷歌搜索还有很多其他内容可供查看。

socket.onopen = =>
  console.log("Connection Open")
  init = 
    sender: "cp"
    action: "init"
    user:  $.cookie('user_id')
    token:  $.cookie('remember_token')
  

有几种方法可以使用 JS 为您的视图呈现新标记。一种方法是使用 js 模板。我是hamlcoffeeassets 库的忠实粉丝:https://github.com/netzpirat/haml_coffee_assets 虽然它使用 haml 来显示视图,而不是 ERB。也有 ERB 变体。

您可以像这样向app/assets/templates/friend.jst.hamlc 添加一些标记:

%p This is my friend markup #@friend.name

然后你可以像这样从你的 JS 渲染它:

$('#friends').append(JST['templates/friend'](friend: name: 'Bob'))

这会将您的模板中的标记附加到您传入的值中。在我的示例中,您最终会在 #friends 容器中使用此标记:

<p>This is my friend markup Bob</p>

或者,您可以通过 rails 将您想要的部分呈现为 JSON 响应,作为一个字符串,然后将其插入到您的文档中...

所以你的 JS 可能看起来像这样:

socket.onmessage = (m)=>
  console.log("Recieved: #m.data")
  msg = m.data.JSON.parse
  switch msg.action
    when "ret_init"

    when "friend_udt"
      refreshFriend(msg.friendHTML)

refreshFriend(html) ->
  $('#friends').html(html)

更新

关于您的 ERB 问题...首先,您的部分是非常低效的,每次渲染时对数据库进行四次类似的调用。 haml_coffee_assets 用于haml 标记语言(我更喜欢ERB),如果你想要ERB,请使用eco:https://github.com/sstephenson/eco

如果你想在 JS 中呈现这个,那么你需要通过通知数据响应将这个“朋友关系”数据作为 JSON 发送,在呈现 javascript 时你无法访问活动记录或任何控制器方法或实例变量部分 - 他们不回击服务器,他们只使用您的 javascript 当时可以访问的内容。

【讨论】:

谢谢@nzifnab,你能看到我上面的编辑吗?它不适合这个框。【参考方案3】:

试试这个宝石:'coffeebeans'

    将您的咖啡文件命名为“some_file.html.erb”

    <%= coffeescript_tag_do %> 
        # your coffee script here ...
    <% end %>
    

    在另一个 erb 文件中:

    <%= render file: '.../some_file' %>
    

【讨论】:

以上是关于在视图中包含带有 ERB 的 Coffeescript 文件的主要内容,如果未能解决你的问题,请参考以下文章

轨在一个视图中的多个表单

我将如何在 application.html.erb 的 Rails 中显示不同的标题?

为啥 ERB 不在 Rails 视图规范中执行?

如何从 js.erb 视图中选择这个 div 元素

Ruby on Rails 在带有静态脚本文件的视图中渲染

带有视图的 Rails3 本地化?