【问题标题】:Avoiding nil in Rails views在 Rails 视图中避免 nil
【发布时间】:2011-04-02 01:06:07
【问题描述】:

我确定已经有人问过这个问题,但我找不到答案。

我有一个 Project 模型,它与我的 Client 模型具有 belongs_to 关系。客户有名字,但项目不一定有客户。

在我看来,我有这样的代码:

<%=h project.client && project.client.name %>

因为如果项目没有客户端,那么尝试访问 project.client.name 会导致 NoMethodError(nil 没有名为 name 的方法)。

问题是,在视图中检查这种 nil 是否可以接受,还是我应该寻找另一种解决方法?

【问题讨论】:

    标签: ruby-on-rails model-view-controller null


    【解决方案1】:

    你可以在你的Project 类中使用delegate,这样你就会尊重Law of demeter,它说你应该“只和你的直接朋友交谈”。

    项目.rb

    class Project
      delegate :name, to: :client, prefix: true, allow_nil: true    
    end
    

    这样项目对象就会知道在哪里询问客户的姓名:

    #You can now call
    project.client_name
    

    Rails documentation.中查看更多关于delegate的信息

    【讨论】:

      【解决方案2】:

      我认为这些检查通常可以通过一些思考来消除。这有利于保持视图代码更简洁,更重要的是,将逻辑排除在视图层之外,这是最佳实践。一些模板引擎不允许视图中有任何逻辑。

      至少有几种情况。假设您有一个依赖于实例变量的 show 操作。我会说如果找不到记录,控制器不应该通过重定向或其他方式呈现 html。如果数组视图中有一个循环,请使用@array.each do |a| end,这样它就不会评估数组是否为空。如果您真的希望视图中的应用程序默认值,请尝试从配置文件中加载它,例如@page_title || #{@APP_CONFIG['page_title']}(见Railscasts #85)。请记住,您可能希望稍后更改这些字符串,例如翻译 UI。

      这些是可以避免存在检查和使用try 的几种情况。如果可能的话,我会尽量避免它们。如果您无法避免它们,我会将条件检查放在视图助手中,并为其添加一个助手单元测试以验证(并记录)两个代码路径。

      【讨论】:

        【解决方案3】:

        我经常遇到这种情况,是的,这很烦人。即使不应该有零,我继承的脏数据有时也会触发它。

        您的解决方案是一种处理方式。您还可以向 Project 添加一个名为 client_name 的方法,该方法会显示客户端名称(如果存在),但是您将模型链接在一起的次数比某些人推荐的要多。

        def client_name
          client && client.name
        end
        

        您也可以创建一个辅助方法来执行此操作,但最终您可能会编写很多方法。 :)

        正如下面 Skilldrick 所提到的,这对于添加默认字符串也很有用:

        def client_name
          client ? client.name : "no client"
        end
        

        【讨论】:

        • 这在某些情况下绝对有用(例如,如果您想要一个默认名称,如“no client”)。
        • 另一个不使用三元运算符的默认字符串实现:client.name || "no client"
        • 当然,有人可能会争辩说,由于最初的问题是讨论使用纯 mvc 模型,因此通过在模型中放置默认显示字符串,您可能会在您的模型中注入更多视图逻辑型号?这就是为什么我会像提问者那样做。但是,这都是风格,这也有效:)。我只是不想假设我创建的每个视图都需要相同的默认字符串,你知道吗?
        • 是否有 gem 或其他东西可以自动处理这种情况?
        【解决方案4】:

        我的 hacky 解决方案是生成一个块并挽救错误。许多人会说使用救援作为逻辑是非常糟糕的形式。只是不要在你真正需要知道什么时候是 nil 并且不应该是 nil 的地方使用它。

        在 application_helper.rb 中:

          def none_on_fail
              begin
                  return yield
              rescue
                  return "(none entered)"
              end
          end
        

        然后在视图中:

        <%= none_on_fail { project.client.name }  %>
        

        然后可以根据需要将方法链接到任意深度,并且可以在任何方法上使用它,但它会掩盖模型/关系/方法(如果存在)的其他潜在问题。我会把它等同于用火焰喷射器取出碎片。如果使用不当,非常有效,但会带来痛苦的后果。

        【讨论】:

          【解决方案5】:

          随便用

          project.client.try(:name)
          

          【讨论】:

          • 我忘了那个... :) 但是,当您深入遍历 5-6 个模型时,它仍然会变得很麻烦。 :(
          • @Tass 你对得墨忒耳定律说的很对,但我认为这不是正确的实现方式,请看我下面的帖子。
          • @dombesz 但是您的方法使本地化变得更加困难。如果您想尊重 demeter,请发出警告或类似的警告。
          【解决方案6】:

          我认为它完全可以接受 - 这是视图逻辑,您或多或少地根据是否有数据来决定是否显示视图的一部分。

          【讨论】:

            猜你喜欢
            • 2010-09-21
            • 2011-03-29
            • 2011-01-18
            • 2011-09-23
            • 2019-02-18
            • 1970-01-01
            • 2011-06-12
            • 2011-02-14
            • 2012-04-10
            相关资源
            最近更新 更多