Подтвердить что ты не робот

Перемещение виртуальных атрибутов с несколькими входами в компонент пользовательского ввода SimpleForm

Высота сохраняется в базе данных в дюймах.

Однако ноги и дюймы нуждаются в собственных индивидуальных входах в форме:

Height: [_______] feet [_______] inches

Итак, я использовал виртуальные атрибуты и заработал. Вот упрощенная версия моей модели:

class Client < ActiveRecord::Base
  attr_accessible :name, :height_feet, :height_inches

  before_save :combine_height

    def height_feet
      height.floor/12 if height
    end

    def height_feet=(feet)
      @feet = feet
    end

    def height_inches
      if height && height%12 != 0
        height%12
      end
    end

    def height_inches=(inches) #on save?
      @inches = inches
    end

  def combine_height
    self.height = @feet.to_d*12 + @inches.to_d #if @feet.present?
  end

end

И частичное использование _form с помощью simple_form:

<%= simple_form_for(@client) do |f| %>
  <ul>
    <%= f.error_notification %>
    <%= f.input :name %>
    <%= f.input :weight %>
    <li>
      <%= f.input :height_feet, :label => 'Height', :wrapper => false %>
      <span>feet</span>
      <%= f.input :height_inches, :label => false, :wrapper => false %>
      <span>inches</span>
    </li>
    <%= f.error :base %>
  </ul>
    <%= f.button :submit %>
<% end %>

Это работает. Но это не идеально.

Я бы хотел сделать это и создать пользовательский компонент ввода, чтобы добавить высоту в форму с помощью <%= f.input :height, as: :feet_and_inch %> - и, следовательно, любой другой вход, который следует такой же схеме, как <%= f.input :wingspan, as: :feet_and_inch %>.

Я экспериментировал с пользовательскими компонентами, но я не могу получить два входа для отображения - и я не уверен, где лучше всего поставить логику "преобразования" с ног и дюймов в дюймы (и аналогично из дюймы назад до футов и дюймов).

4b9b3361

Ответ 1

Насколько я знаю, вы не можете ничего переместить, кроме рендеринга на пользовательский ввод. SimpleForm не вызывается после отправки формы, поэтому он никак не может помешать значениям. Я хотел бы ошибаться в этом, поскольку мне это нужно и в прошлом. Во всяком случае, здесь версия, которая поддерживает логику преобразования в модели.

Пользовательский ввод SimpleForm:

# app/inputs/feet_and_inch_input.rb
class FeetAndInchInput < SimpleForm::Inputs::Base
  def input
    output           = ""
    label            = @options.fetch(:label) { @attribute_name.to_s.capitalize }

    feet_attribute   = "#{attribute_name}_feet".to_sym
    inches_attribute = "#{attribute_name}_inches".to_sym

    output << @builder.input(feet_attribute, wrapper: false, label: label)
    output << template.content_tag(:span, " feet ")
    output << @builder.input(inches_attribute, wrapper: false, label: false)
    output << template.content_tag(:span, " inches ")

    output.html_safe
  end

  def label
    ""
  end
end

Форма. Обратите внимание, что я не помещал теги <li> внутри пользовательского ввода, я думаю, что он более гибкий, но не стесняйтесь его изменять.

# app/views/clients/_form.html.erb
<li>
  <%= f.input :height, as: :feet_and_inch %>
</li>

Все это зависит от того, что для каждого атрибута height у вас также есть атрибуты height_feet и height_inches.

Теперь для модели я не честно уверен, что если это так, может быть, кто-то может найти лучшее решение, НО вот он:

class Client < ActiveRecord::Base
  attr_accessible :name

  ["height", "weight"].each do |attribute|
    attr_accessible "#{attribute}_feet".to_sym
    attr_accessible "#{attribute}_inches".to_sym

    before_save do
      feet  = instance_variable_get("@#{attribute}_feet_ins_var").to_d
      inches = instance_variable_get("@#{attribute}_inches_ins_var").to_d

      self.send("#{attribute}=", feet*12 + inches)
    end

    define_method "#{attribute}_feet" do
      value = self.send(attribute)
      value.floor / 12 if value
    end

    define_method "#{attribute}_feet=" do |feet|
      instance_variable_set("@#{attribute}_feet_ins_var", feet)
    end

    define_method "#{attribute}_inches=" do |inches|
      instance_variable_set("@#{attribute}_inches_ins_var", inches)
    end

    define_method "#{attribute}_inches" do
      value = self.send(attribute)
      value % 12 if value && value % 12 != 0
    end
  end
end

В основном он делает то же самое, но определяет методы динамически. Вы можете увидеть вверху список атрибутов, для которых вы хотите, чтобы эти методы были сгенерированы.

Обратите внимание, что все это на самом деле не полностью проверено и может убить вашу кошку, но, надеюсь, может дать вам некоторые идеи.

Ответ 2

Мое скромное мнение заключается в том, что вы бы дали лучший пользовательский опыт, если пользователь вводит данные только в одном поле. Вот мои проблемы:

Предполагая, что вы используете высоты в ограниченном диапазоне (вероятно, человеческий рост), вы можете написать подтверждение, которое определяет, что вводит пользователь - дюймы или футы. Затем вы можете сделать ссылку для проверки (или лучше кнопку), спрашивая, является ли вход таким, каким он должен был быть (дюймы или ноги обнаружены).

Все это (включая преобразование размеров, в то время как оно всего в дюймах → футы) может быть выполнено в javascript, вы можете получить текущие измерения по вызову Ajax и не перезагружать весь код страницы.

EDIT: я нашел интересную точку зрения, связанную со сложными вводами. Другой полезный ресурс об пользовательском взаимодействии в форме заполнения с ногами и дюймами.

Ваш вопрос действительно интересный, и мне бы хотелось увидеть решение, которое вы выбрали.