1

Assume I have a model like this:

class Item < ApplicationRecord
end

And in items#new or items#edit, in app/views/items/_form.html.erb:

<%= f.submit %>

This will generate a submit button named "Create Item" or "Update Item" depending on the route.

Now I want to use an alternative name for the generated button text (and only here!), like "Create Article" and "Update Article", while keeping the name Item everywhere else in the code, so I can still use items_url, ItemsHelper::some_method etc.

Using <%= f.submit "Text" %> is not an option since it won't keep the difference between Create %{model} and Update %{model} when the same form is rendered in different views.

I have made the following attempts without success:

# 1 - no difference observed
class Item < ApplicationRecord
  def display_name
    "Object"
  end
end

# 2 - undefined method `name' for "Artlcle":String
class Item < ApplicationRecord
  def model_name
    "Object"
  end
end

# 3 - undefined method `object_path'
class Item < ApplicationRecord
  def model_name
    ActiveModel::Name.new Item, nil, 'Object'
  end
end

This isn't a good solution, either, as it defeats Rails' DRY principle.

<% if determine_controller %>
  <%= f.submit "Some text" %>
<% else %>
  <%= f.submit "Some other text" %>
<% end %>

How do I achieve this goal? Is it possible without rails-i18n?

Additional question: Does the solution in your answer also work on Rails 5?

iBug
  • 35,554
  • 7
  • 89
  • 134
  • Is there a reason you can't do `<%= f.submit "My Submit Text" %>` as suggested in [this answer](https://stackoverflow.com/questions/4769483/rails-how-to-change-the-text-on-the-submit-button-in-a-rails-form) – lacostenycoder Jun 10 '20 at 13:17
  • @lacostenycoder That would *not* retain the difference between `Create %{model}` and `Update %{model}`. – iBug Jun 10 '20 at 13:19
  • would it kill you to do this with javascript? – lacostenycoder Jun 10 '20 at 13:30
  • @lacostenycoder There are much better workarounds than using JS. – iBug Jun 10 '20 at 13:34
  • @AbM The code you linked clearly shows `object.model_name.human`, which in my case is ``undefined method `name' for "Artlcle":String``. – iBug Jun 10 '20 at 13:35
  • 1
    I agree that it could be much easier to accomplish that with erb, but to answer your question, can you try changing the `model_name` method to `ActiveModel::Name.new(Item, nil, 'Object')` instead of just `"Object"` – AbM Jun 10 '20 at 13:35
  • @AbM The exception is now ``undefined method `object_path'``. No luck. – iBug Jun 10 '20 at 13:37
  • @iBug yes, this is the expected behaviour based on [this](https://github.com/rails/rails/blob/186115180e671e1a222f368600189df155446260/activemodel/lib/active_model/naming.rb#L179). If you change the name, it will change the routes, I18n keys, routes naming, etc... – AbM Jun 10 '20 at 13:44

1 Answers1

0

Perhaps a bit too hackish, but monkey-patching does the job (thanks to @AbM for a link to Rails source):

class Item < ApplicationRecord
  def model_name.human
    'Article'
  end
end

To make life easier, I wrote a "helper method" in the base class:

class ApplicationRecord < ActiveRecord::Base
  def self.display_name(name)
    self.model_name.define_singleton_method :human do
      name
    end
  end
end

And then I could do this:

class Item < ApplicationRecord
  display_name 'Article'
end
iBug
  • 35,554
  • 7
  • 89
  • 134