Like you said yourself, your example is obviously not going to work. Let me give you a short rundown why before we get into the solutions.
The issue is pretty simple, the server resolves all ERB code of .erb files before sending the file to the client. This means the client will never see the ERB code and cannot interact with it. You can complement your .js or .coffee files with ERB code, but the other way around isn't possible. The other issue you're having has to do with the asset digestions (which is returned on by default). The server knows these digested file paths, but the client doesn't unless the info is provided by the server.
The same goes for external resources (openweatherapi), they also don't know what the digestion of an asset is. So when interacting with an external resource from the JavaScript of your client you'll need some way to convert the returned asset paths by the external resource to the digested asset path.
The simplest solution is turning digestions off, but you can read here why they should normally be turned on.
#1 Provide asset paths up front
Let's say you know that the external API always request a certain assets. Maybe you host the images of the API on your own Rails server so you don't depend on some other server (for assets). Let's assume you store the images in the vendor/assets/images/openweatherapi
directory, since they third party. With the following JavaScript you'll provide the client a dictionary to translate normal asset names to digested asset names.
<% # 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 %>
Don't forget to include your JavaScript file in application.js, preferably before the inclusion of app JavaScript files so you can use the variable from the get go. Now to fetch the digested asset path from your JavaScript simply search for the path in the dictionary.
var icon = "1d0.png";
var $icon = $("<img />", {
class: "day-check-icon",
src: assets.openweatherapi[icon],
});
#2 Make AJAX calls to fetch asset paths
The other options is sending the asset name to the server in an AJAX call and let the server provide you the correct digested asset path. This is done pretty simple by doing the following:
# 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}));
}
Again, make sure the file is included in application.js preferably before any files that depend on the getAsset
function. Then you can do the following:
var icon = "1d0.png";
getAsset(icon).then(icon => {
var $icon = $("<img />", {
class: "day-check-icon",
src: icon.path,
});
});
Keep in mind you could further optimize the AJAX approach allowing you to fetch multiple icon paths with a single request. But this should give you the general idea. Also remember that AJAX calls add additional nesting and delay that can be avoided with the first approach.