如何使用 Rails 助手:引导轮播演示

已发表: 2022-03-11

在所有 Rails 内置结构中,最被滥用、误解和忽视的一种是视图助手。 位于你的app/helpers目录中,并且在每个新的 Rails 项目中默认生成,帮助程序经常因为成为整个应用程序视图层中使用的一次性方法的垃圾场而声名狼藉。 不幸的是,Rails 本身通过默认情况下将所有助手包含到每个视图中来鼓励这种缺乏结构和糟糕的组织,从而创建了一个被污染的全局命名空间。

但是,如果您的助手可以更具语义化、组织性更好,甚至可以跨项目重用呢? 如果它们不仅仅是散布在整个视图中的一次性函数,而是可以轻松生成复杂标记的强大方法,让您的视图摆脱条件逻辑和代码呢?

让我们看看在构建图像轮播时如何做到这一点,使用熟悉的 Twitter Bootstrap 框架和一些好的老式面向对象编程。

何时使用 Rails 助手

在 Rails 的视图层中可以使用许多不同的设计模式:presenters、decorator、partials 以及 helpers,仅举几例。 我的简单经验法则是,当您想要生成需要特定结构、特定 CSS 类、条件逻辑或跨不同页面重用的 HTML 标记时,帮助程序非常有用。

展示 Rails 助手功能的最佳示例是FormBuilder及其用于生成输入字段、选择标签、标签和其他 HTML 结构的所有相关方法。 这些有用的方法使用正确设置的所有相关属性为您生成标记。 像这样的便利性是我们一开始就爱上 Rails 的原因。

使用精心设计的帮助程序的好处与任何编写良好、干净的代码相同:封装、减少代码重复 (DRY) 以及将逻辑排除在视图之外。

Twitter Bootstrap 轮播的剖析

Twitter Bootstrap 是一个广泛使用的前端框架,它内置了对常见组件的支持,例如模式、选项卡和图像轮播。 这些 Bootstrap 组件是自定义助手的一个很好的用例,因为标记是高度结构化的,需要正确设置某些类、ID 和数据属性才能使 JavaScript 工作,并且设置这些属性需要一些条件逻辑。

Bootstrap 3 轮播具有以下标记:

 <div class="carousel slide" data-ride="carousel"> <!-- Indicators --> <ol class="carousel-indicators"> <li data-target="#carousel-example-generic" data-slide-to="0" class="active"></li> <li data-target="#carousel-example-generic" data-slide-to="1"></li> <li data-target="#carousel-example-generic" data-slide-to="2"></li> </ol> <!-- Wrapper for slides --> <div class="carousel-inner"> <div class="item active"> <img src="..." alt="..."> </div> <div class="item"> <img src="..." alt="..."> </div> ... </div> <!-- Controls --> <a class="left carousel-control" href="#carousel-example-generic" data-slide="prev"> <span class="glyphicon glyphicon-chevron-left"></span> </a> <a class="right carousel-control" href="#carousel-example-generic" data-slide="next"> <span class="glyphicon glyphicon-chevron-right"></span> </a> </div>

如您所见,有三个主要结构:(1)指示器(2)图像幻灯片(3)滑动控件。

蓝图显示了一个居中的宽屏比例矩形(幻灯片),底部附近有三个小圆圈(指示器)。它的两侧分别是两个带有左右箭头的细矩形(控件)。
Bootstrap 轮播的部件。

目标是能够构建一个单独的辅助方法,该方法获取一组图像并呈现整个轮播组件,确保数据、 idhref属性和 CSS 类都设置正确。

帮手

让我们从助手的基本轮廓开始:

 # app/helpers/carousel_helper.rb module CarouselHelper def carousel_for(images) Carousel.new(self, images).html end class Carousel def initialize(view, images) @view, @images = view, images end def html # TO FILL IN end private attr_accessor :view, :images end end

辅助方法carousel_for将返回给定图像 URL 的完整轮播标记。 与其构建一套单独的方法来渲染轮播的每个部分(这将需要我们将图像集合和其他状态信息传递给每个方法),我们将创建一个新的普通的旧 Ruby 类,称为Carousel以表示轮播数据。 此类将公开一个html方法,该方法返回完全呈现的标记。 我们使用图像 URL images的集合和视图上下文view对其进行初始化。

请注意, view参数是ActionView的一个实例,所有 Rails 助手都混入其中。 我们将它传递给我们的对象实例,以便访问 Rails 的内置辅助方法,例如link_tocontent_tagimage_tagsafe_join ,我们将使用它们在类中构建标记。 我们还将添加delegate宏,因此我们可以直接调用这些方法,而无需引用view

 def html content = view.safe_join([indicators, slides, controls]) view.content_tag(:div, content, class: 'carousel slide') end private attr_accessor :view, :images delegate :link_to, :content_tag, :image_tag, :safe_join, to: :view def indicators # TO FILL IN end def slides # TO FILL IN end def controls # TO FILL IN end

我们知道轮播由三个独立的组件组成,所以让我们将最终为每个组件提供标记的方法存根,然后让html方法将它们连接到容器div标记中,为轮播本身应用必要的 Bootstrap 类。

safe_join是一种方便的内置方法,它将字符串集合连接在一起并在结果上调用html_safe 。 请记住,我们可以通过创建实例时传入的view参数访问这些方法。

我们将首先构建指标:

 def indicators items = images.count.times.map { |index| indicator_tag(index) } content_tag(:ol, safe_join(items), class: 'carousel-indicators') end def indicator_tag(index) options = { class: (index.zero? ? 'active' : ''), data: { target: uid, slide_to: index } } content_tag(:li, '', options) end

指示符是一个简单的有序列表ol ,其中集合中的每个图像都有一个列表项li元素。 当前活动的图像指示器需要active的 CSS 类,因此我们将确保为我们创建的第一个指示器设置它。 这是一个很好的逻辑示例,通常必须在视图本身中。

请注意,指标需要引用包含轮播元素的唯一id (如果页面上有多个轮播)。 我们可以在初始化程序中轻松生成此id ,并在整个类的其余部分(特别是在指标和控件中)使用它。 在辅助方法中以编程方式执行此操作可确保id在轮播元素之间保持一致。 很多时候,一个小错字或在一个地方更改id而不是其他地方会导致轮播中断; 这不会在这里发生,因为所有元素都会自动引用相同的id

 def initialize(view, images) # ... @uid = SecureRandom.hex(6) end attr_accessor :uid

接下来是图片幻灯片:

 def slides items = images.map.with_index { |image, index| slide_tag(image, index.zero?) } content_tag(:div, safe_join(items), class: 'carousel-inner') end def slide_tag(image, is_active) options = { class: (is_active ? 'item active' : 'item'), } content_tag(:div, image_tag(image), options) end

我们简单地遍历传递给Carousel实例的每个图像并创建正确的标记:使用item CSS 类包装在div中的图像标签,再次确保将active类添加到我们创建的第一个类中。

最后,我们需要上一个/下一个控件:

 def controls safe_join([control_tag('left'), control_tag('right')]) end def control_tag(direction) options = { class: "#{direction} carousel-control", data: { slide: direction == 'left' ? 'prev' : 'next' } } icon = content_tag(:i, nil, class: "glyphicon glyphicon-chevron-#{direction}") control = link_to(icon, "##{uid}", options) end

我们创建控制轮播在图像之间来回移动的链接。 再次注意uid的使用; 无需担心在轮播结构中的所有不同位置都没有使用正确的 ID,它会自动保持一致且唯一。

成品:

至此,我们的轮播助手就完成了。 这是它的全部内容:

 # app/helpers/carousel_helper.rb module CarouselHelper def carousel_for(images) Carousel.new(self, images).html end class Carousel def initialize(view, images) @view, @images = view, images @uid = SecureRandom.hex(6) end def html content = safe_join([indicators, slides, controls]) content_tag(:div, content, id: uid, class: 'carousel slide') end private attr_accessor :view, :images, :uid delegate :link_to, :content_tag, :image_tag, :safe_join, to: :view def indicators items = images.count.times.map { |index| indicator_tag(index) } content_tag(:ol, safe_join(items), class: 'carousel-indicators') end def indicator_tag(index) options = { class: (index.zero? ? 'active' : ''), data: { target: uid, slide_to: index } } content_tag(:li, '', options) end def slides items = images.map.with_index { |image, index| slide_tag(image, index.zero?) } content_tag(:div, safe_join(items), class: 'carousel-inner') end def slide_tag(image, is_active) options = { class: (is_active ? 'item active' : 'item'), } content_tag(:div, image_tag(image), options) end def controls safe_join([control_tag('left'), control_tag('right')]) end def control_tag(direction) options = { class: "#{direction} carousel-control", data: { slide: direction == 'left' ? 'prev' : 'next' } } icon = content_tag(:i, '', class: "glyphicon glyphicon-chevron-#{direction}") control = link_to(icon, "##{uid}", options) end end end

帮手在行动:

最后,为了强调重点,让我们看一个简单的例子,说明这个助手如何让我们的生活更轻松。 假设我们正在建立一个出租公寓列表的网站。 每个Apartment对象都有一个图像 URL 列表:

 class Apartment def image_urls # ... end end

使用我们的 carousel 助手,我们可以通过一次调用carousel_for来渲染整个 Bootstrap 轮播,从视图中完全移除相当复杂的逻辑:

 <% apartment = Apartment.new %> # ... <%= carousel_for(apartment.image_urls) %> 

不确定何时使用 Rails 视图助手? 这是一个演示。

鸣叫

结论

使用这种简单但功能强大的技术,我们将大量标记和逻辑从视图层移到了一个辅助函数中,该函数可用于在任何地方渲染轮播组件,只需调用carousel_for(some_images) . 每当您使用 Twitter Bootstrap 时,都可以在所有 Rails 项目中使用这个通用助手。 最重要的是,您的工具包中现在有了一个新工具,您也可以将它用于特定于项目的组件。

因此,下次当您发现自己键入和重新键入相同类型的标记并将条件​​逻辑嵌入到您的视图中时,请查看是否有一个辅助函数正在等待编写以使您的生活更轻松。