3

我有以下内容(其中 Venue 是 Actor 的 CTI 后代):

class Actor < ActiveRecord::Base
  has_one :profile, validate: true, autosave: true
end

class Venue < ActiveRecord::Base
...
  %w{description address website phone}.each do |attr|
    delegate attr.to_sym, "#{attr}=".to_sym, to: :profile!
  end

  def profile!
    actor.profile || actor.build_profile
  end
...
end

我将这些代表属性的字段直接包含在 Venue 表单中。当其中一个属性验证失败时,我看到的只是顶部的通知,而不是字段周围的包装。我想这一定是因为 Venue 实例的错误哈希中的键与属性名称不完全匹配,被设置为:"actor.profile.website"而不是只是:website.

有什么办法可以让这些错误正确显示吗?

编辑

这是表格:

<%= simple_form_for @venue do |f| %>
  <%= f.error_notification %>

  <%= f.input :name %>
  <%= f.input :address, required: true %>
  <%= f.input :phone %>
  <%= f.input :website %>
  <%= f.input :members, collection: [], class: "form_tag" %>
  <%= f.input :tag_list, as: :string, class: "form_tag", hint: t("misc.hint.tag"),
              input_html: { "data-pre" => @venue.tag_list.map {|t| { id: t, name: t }}.to_json } %>
  <%= f.input :description, as: :text, input_html: {rows: 6, cols: 53, class: "form_tag"} %>

  <div class="actions center">
<%= f.submit class: "btn btn-success" %>

4

4 回答 4

2

这是正确的。纠正的方法是使用这样的东西:

class Venue < ActiveRecord::Base
...
  after_validation do
    if errors.any?
      errors.messages.keys.each do |key|
        errors.messages[key.to_s.gsub(/actor.profile./, "").to_sym] = errors.messages.delete(key)
      end
    end
  end
...
end

更新:

如何使用 div class="field_with_error" 包装内容

注意:Rails 仅在基础对象有错误并且错误具有与属性名称(实际上是方法名称)相等的适当键时才包装字段。对于嵌套的关联属性,它根据关联序列使用前缀键(actor.profile.website)。

顺便说一下,常用的方法:

<%= field_error_proc.call(content) %>

# where content is any string/symbol stuff.

错误处理触发器:

<%= form_for... do |f| %>
  <% website_field = capture do %>
    <%= f.text_field :website %>
  <% end %>

  <% if f.object.errors[:"actor.profile.website"] %>
    <%= website_field %>
  <% else %>
    <%= field_error_proc.call(website_field) %>
  <% end %>
<% end %>

有点乏味吧?最好使用 Rails 的原生包装机制。

看下一个答案。

于 2012-12-22T23:47:25.280 回答
2
module OtherValidation
  extend ActiveSupport::Concern

  module ClassMethods
    def delegate_with_validations(*attr_names)
      options = attr_names.extract_options!
      delegate *attr_names, options
      attr_names.each {|a| validate_using(options[:to], a)}
    end      

    def validate_using(target, *args)
      options = args.extract_options!
      args.each do |attr_name|
        class_eval <<-EOV
          dup = #{target}._validators[:#{attr_name}].dup
          validate do
            dup.each do |v|
              validator = v.dup
              validator.attributes.delete(:#{attr_name})
              validator.attributes << :#{options[:to]||attr_name}
              validator.validate(self)
            end
          end
        EOV
      end
    end
  end

end

现在在场地模型中:

class Venue < ActiveRecord::Base
  include OtherValidation
  delegate_with_validations :website, :to => :profile!
end

# venue = Venue.new(:website => nil)
# venue.valid? # Profile validates presence of :website
#=> false
# venue.errors
#=> #<ActiveModel::Errors....., @messages={:website=>["can't be blank"]}>

更新:

对于任何自定义属性:

class Venue < ActiveRecord::Base
  include OtherValidation
  attr_accessor: title

  validate_using("Profile", :website, :to => :title)
end

# :website validation behavior constraints to :title attribute

# venue = Venue.new(:title => nil)
# venue.valid? # Profile validates presence of :website
#=> false
# venue.errors
#=> #<ActiveModel::Errors....., @messages={:title=>["can't be blank"]}>

config/initializers/other_delegation.rb

module OtherValidation
  ...
end

ActiveSupport.on_load :active_record do
  include OtherValidation
end
于 2012-12-23T16:03:32.563 回答
1

你可以用我的 gem 避免这些东西:

gem 'delegate_attributes'

class Blog < ActiveRecord::Base
  delegate_attributes :theme, :errors => :fit, :writer => true, :to => :category
end

选项:errors => :fit声明现在可以在下面定义错误消息的 i18n 翻译:

en:
  activerecord:
    errors:
      models:
        blog:
          attributes:
            theme:
              blank: "Can not be blank"

选项:writer => true委托编写器方法:.theme=

于 2013-01-14T19:33:42.567 回答
0

Valery的回答对我来说是有效的,但对于带前缀的代表来说并不适用。另外,我正在寻找一种方法来隔离解决方案,这样它只会影响有错误的表单字段的 HTML 生成。结束了这个(可能不是最干净的代码,但似乎可以完成这项工作):

initializers/delegate.rb

module MappedDelegation
  extend ActiveSupport::Concern

  included do
    cattr_reader :delegation_mappings
    @@delegation_mappings ||= {}

    def self.delegate_with_mappings(*methods)
      delegate_without_mappings(*methods)

      options = methods.pop
      prefix, to = options.values_at(:prefix, :to)
      method_prefix = \
        if prefix
          "#{prefix == true ? to : prefix}_"
        else
          ''
        end

      methods.each do |method|
        @@delegation_mappings["#{method_prefix}#{method}"] = to
      end
    end

    self.class_eval do
      class << self
        alias_method_chain :delegate, :mappings
      end
    end
  end
end

initializers/simple_form_delegate_errors.rb

module SimpleForm
  module Components
    module Errors
      def errors
        @errors ||= (errors_on_attribute + errors_on_delegate + errors_on_association).compact
      end

      def errors_on_delegate
        delegated_to = ( object.class.respond_to?(:delegation_mappings) ? object.class.delegation_mappings : {} )[attribute_name.to_s]
        delegated_to ? object.send(delegated_to).errors[attribute_name] : []
      end
    end
  end
end

app/models/venue.rb

class Venue < ActiveRecord::Base
  include MappedDelegation
  ...
于 2012-12-23T13:20:40.923 回答