是否可以创建在语义相似但具有不同属性的表单中使用的部分?
解释这个想法的一个例子。
想象一下,我们有以下模型:
class UnitedStatesTemperature < ApplicationRecord
# higher_fahrenheit
# lower_fahrenheit
end
和
class BrazilTemperature < ApplicationRecord
# higher_celsius
# lower_celsius
end
和部分shared/_temperatures.html.erb
<%= form.label :higher_celsius, 'Higher' %>
<%= form.text_field :higher_celsius, id:
:brazil_temperature_higher_celsius %>
<%= form.label :lower_celsius, 'Lower' %>
<%= form.text_field :lower_celsius, id:
:brazil_temperature_lower_celsius %>
如果我们将此部分用于brazil_temperature
形式,则运行正常
<%= form_with(model: brazil_temperature, local: true) do |form| %>
<%= render 'shared/temperatures', form: form %>
<%= form.submit %>
<% end %>
如果我们在united_states_temperature
中使用相同的部分它不起作用(因为属性是不同的)。
我的问题是:
是否可以通用方式重写部分(shared/_temperatures.html.erb
),以便可以在两种形式(brazil_temperature
和united_states_temperature
)中使用?
答案 0 :(得分:2)
如果您将表单部分传递给属性名称,则可以使其更通用。
<% underscored_klass = form.object.class.name.underscore %>
<% form_id = ->(attr) { "#{underscored_klass}_#{attr}".to_sym } %>
<% # use the two lines below only if you want certain default attributes %>
<% lower_temp_attr = lower_temp_attr || :lower_celsius %>
<% higher_temp_attr = higher_temp_attr || :higher_celsius %>
<%= form.label higher_temp_attr, 'Higher' %>
<%= form.text_field higher_temp_attr, id: form_id[higher_temp_attr] %>
<%= form.label lower_temp_attr, 'Lower' %>
<%= form.text_field lower_temp_attr, id: form_id[lower_temp_attr] %>
然后按如下方式调用partial:
<%= form_with(model: brazil_temperature, local: true) do |form| %>
<%= render 'shared/temperatures', form: form, lower_temp_attr: :lower_celsius, higher_temp_attr: :higher_celsius %>
<%= form.submit %>
<% end %>
如果我没弄错,id会自动设置为model_name_attribute_name
。这意味着您可以省略<% form_id = ->(attr) { "#{underscored_klass}_#{attr}".to_sym } %>
和id: form_id[higher_temp_attr]
部分来清理代码。
变体的使用意味着稍微清理视图。这样,在为表单的不同变体呈现部分时,您不必传递lower_temp_attr: :lower_celsius, higher_temp_attr: :higher_celsius
。
您可以创建一个帮助程序,根据对象返回属性名称。
应用程序/助手/ temperature_helper.rb
module TemperatureHelper
def lower_temp_attr(temp) # variation #1
attribute_names = temp.attribute_names
return :lower_celsius if attribute_names.include?('lower_celsius')
return :lower_fahrenheit if attribute_names.include?('lower_fahrenheit')
raise ArgumentError,
'Passed object should have either "lower_celsius" or ' +
'"lower_fahrenheit" as attribute.'
end
def higher_temp_attr(temp) # variation #2
attribute_names = temp.attribute_names
options = %w[higher_celsius higher_fahrenheit]
attr_name = attribute_names.find { |attr_name| options.include?(attr_name) }
return attr_name.to_sym if attr_name
raise ArgumentError,
'Passed object should have either "lower_celsius" or ' +
'"lower_fahrenheit" as attribute.'
end
end
然后在您的部分中调用lower_temp_attr(form.object)
。
最后但并非最不重要的是,您可以省略帮助程序,并确保每个温度模型都响应返回属性名称的某个接口方法。
应用程序/模型/ generic_temperature.rb
class GenericTemperature < ApplicationRecord
self.abstract_class = true
def lower_temp_attr # variation #1
return :lower_celsius if attribute_names.include?('lower_celsius')
return :lower_fahrenheit if attribute_names.include?('lower_fahrenheit')
raise NotImplementedError
end
def higher_temp_attr # variation #2
options = %w[higher_celsius higher_fahrenheit]
attr_name = attribute_names.find { |attr_name| options.include?(attr_name) }
attr_name&.to_sym or raise NotImplementedError
end
end
让你的温度模型继承自 GenericTemperature 。
class UnitedStatesTemperature < GenericTemperature
end
并在您的部分内部致电form.object.lower_temp_attr
。
还有很多方法可以解决这个问题。例如,您也可以选择始终在NotImplementedError
中引发GenericTemperature
例外,并为每个模型实施该方法。在这种情况下,继承自GenericTemperature
除了确保如果未在模型中定义继承的NotImplementedError
异常时,不会执行任何操作。 UnitedStatesTemperature
。
如果由于某种原因你无法继承父模型(也许你将它用于不同的东西),你总是可以将代码放在一个模块中并包含它。
答案 1 :(得分:0)
首先定义一个返回温度标度的类方法
class UnitedStatesTemperature < ApplicationRecord
# higher_fahrenheit
# lower_fahrenheit
def self.scale
:fahrenheit
end
end
class BrazilTemperature < ApplicationRecord
# higher_celsius
# lower_celsius
def self.scale
:celsius
end
end
然后写下像shared/temperatures
<%
higher_scale = "higher_#{form.object.class.scale}".to_sym
lower_scale = "lower_#{form.object.class.scale}".to_sym
%>
<%= form.label higher_scale, 'Higher' %>
<%= form.text_field higher_scale, id:
"#{form.object.class.table_name}_#{higher_scale}" %>
<%# same for lower --%>