首页 > 解决方案 > 如何在javascript中使用带有railsasset_path的动态值?

问题描述

基于从 JavaScript 获取资产路径,我尝试捕获src我的图像:

var icon = "1d0"
// ...
// some logic to get the right icon
// ...
var $icon = $("<img />", {
  class: "day-check-icon",
  src: "<%= asset_path('" + icon + ".png') %>"
})

显然,它不会起作用,但它给出了我想要实现的目标。


假设其(图像)名称存储在 JavaScript 变量中,我如何动态渲染src指向 my的图像?app/assets/images/

标签: javascriptruby-on-rails

解决方案


就像你自己说的那样,你的例子显然是行不通的。在我们讨论解决方案之前,让我简要介绍一下原因。

问题很简单,服务器在将文件发送到客户端之前解析.erb文件的所有 ERB 代码。这意味着客户端永远不会看到 ERB 代码,也无法与之交互。您可以使用 ERB 代码补充您的.js.coffee文件,但反过来是不可能的。您遇到的另一个问题与资产消化有关(默认情况下会返回)。服务器知道这些摘要文件路径,但客户端不知道,除非服务器提供信息。

外部资源(openweatherapi)也是如此,他们也不知道资产的消化是什么。因此,当您从客户端的 JavaScript 与外部资源交互时,您需要某种方法将外部资源返回的资产路径转换为摘要资产路径。

最简单的解决方案是关闭消解,但您可以在此处阅读为什么通常应该打开消解。

#1 预先提供资产路径

假设您知道外部 API 总是请求某些资产。也许您将 API 的图像托管在您自己的 Rails 服务器上,这样您就不必依赖其他服务器(用于资产)。假设您将图像存储在vendor/assets/images/openweatherapi目录中,因为它们是第三方的。使用以下 JavaScript,您将为客户端提供一个字典,以将正常资产名称转换为摘要资产名称。

<% # lib/assets/javascripts/openweatherapi_assets.js.erb %>

<% # create a global variable assets if it doesn't exist already %>
var assets = assets || {};
assets.openweatherapi = assets.openweatherapi || {};

<% # go to path vendor/assets/images/openweather/* select all children that %>
<% # are a file and loop through each of them %>
<% Dir['vendor/assets/images/openweatherapi/*'].select(&File.method(:file?)).each do |path| %>
  <% # the asset path doesn't include the (app|lib|vendor)/assets/images/ part %>
  <% asset_path = path.sub('vendor/assets/images/', '') %>
  <% file_name = File.basename(path) %>

<% # create the dictionary %>
assets.openweatherapi['<%= file_name %>'] = '<%= asset_path(asset_path) %>';

<% end %>

不要忘记在application.js中包含您的 JavaScript 文件,最好在包含应用程序JavaScript 文件之前,这样您就可以从一开始就使用该变量。现在要从 JavaScript 中获取摘要的资产路径,只需在字典中搜索路径即可。

var icon = "1d0.png";
var $icon = $("<img />", {
  class: "day-check-icon",
  src: assets.openweatherapi[icon],
});

#2 进行 AJAX 调用以获取资产路径

其他选项是在 AJAX 调用中将资产名称发送到服务器,并让服务器为您提供正确的摘要资产路径。通过执行以下操作非常简单:

# config/routes.rb
Rails.application.routes.draw do
  resources :assets, only: :index
end

# app/controllers/assets_controller.rb
class AssetsController < ApplicationController
  def index
    @name = params.require(:name)
  end
end

# app/views/assets/index.json.jbuilder
json.name @name
json.path asset_path(@name)

<% # app/assets/javascripts/assets.js.erb %>
<% routes = Rails.application.routes.url_helpers %>
function getAsset(name) {
  return Promise.resolve($.getJSON('<%= routes.assets_path %>', {name: name}));
}

同样,确保该文件包含在application.jsgetAsset中,最好是在依赖于该函数的任何文件之前。然后您可以执行以下操作:

var icon = "1d0.png";
getAsset(icon).then(icon => {
  var $icon = $("<img />", {
    class: "day-check-icon",
    src: icon.path,
  });
});

请记住,您可以进一步优化 AJAX 方法,允许您通过单个请求获取多个图标路径。但这应该给你一个大致的想法。还要记住,AJAX 调用会增加额外的嵌套和延迟,而第一种方法可以避免这些。


推荐阅读