Module: Alchemy::ElementsHelper

Includes:
ElementsBlockHelper, EssencesHelper, UrlHelper
Included in:
PagesHelper
Defined in:
app/helpers/alchemy/elements_helper.rb

Overview

This helpers are useful to render elements from pages.

The most important helper for frontend developers is the #render_elements helper.

Instance Method Summary collapse

Methods included from ElementsBlockHelper

#element_editor_for, #element_view_for

Methods included from UrlHelper

#download_alchemy_attachment_path, #download_alchemy_attachment_url, #full_url_for_element, #show_alchemy_page_path, #show_alchemy_page_url, #show_page_path_params

Methods included from EssencesHelper

#render_essence, #render_essence_view, #render_essence_view_by_name

Instance Method Details

#element_dom_id(element) ⇒ Object

Returns a string for the id attribute of a html element for the given element



196
197
198
199
# File 'app/helpers/alchemy/elements_helper.rb', line 196

def element_dom_id(element)
  return "" if element.nil?
  "#{element.name}_#{element.id}".html_safe
end

#element_preview_code(element) ⇒ Object

Renders the HTML tag attributes required for preview mode.



202
203
204
205
206
207
208
209
# File 'app/helpers/alchemy/elements_helper.rb', line 202

def element_preview_code(element)
  if respond_to?(:tag_options)
    tag_options(element_preview_code_attributes(element))
  else
    # Rails 5.1 uses TagBuilder
    tag_builder.tag_options(element_preview_code_attributes(element))
  end
end

#element_preview_code_attributes(element) ⇒ Object

Returns a hash containing the HTML tag attributes required for preview mode.



212
213
214
215
# File 'app/helpers/alchemy/elements_helper.rb', line 212

def element_preview_code_attributes(element)
  return {} unless element.present? && @preview_mode && element.page == @page
  { 'data-alchemy-element' => element.id }
end

#element_tags(element, options = {}) ⇒ String

Returns the element’s tags information as a string. Parameters and options are equivalent to #element_tags_attributes.

Returns:

  • (String)

    HTML tag attributes containing the element’s tag information.

See Also:



225
226
227
228
229
230
231
232
# File 'app/helpers/alchemy/elements_helper.rb', line 225

def element_tags(element, options = {})
  if respond_to?(:tag_options)
    tag_options(element_tags_attributes(element, options))
  else
    # Rails 5.1 uses TagBuilder
    tag_builder.tag_options(element_tags_attributes(element, options))
  end
end

#element_tags_attributes(element, options = {}) ⇒ Hash

Returns the element’s tags information as an attribute hash.

Parameters:

Options Hash (options):

  • :formatter (Proc) — default: 'lambda { |tags| tags.join(' ') }'

    Lambda converting array of tags to a string.

Returns:

  • (Hash)

    HTML tag attributes containing the element’s tag information.



245
246
247
248
249
250
251
252
# File 'app/helpers/alchemy/elements_helper.rb', line 245

def element_tags_attributes(element, options = {})
  options = {
    formatter: lambda { |tags| tags.join(' ') }
  }.merge(options)

  return {} if !element.taggable? || element.tag_list.blank?
  { 'data-element-tags' => options[:formatter].call(element.tag_list) }
end

#render_element(*args) ⇒ Object

Note:

If the view partial is not found alchemy/elements/_view_not_found.html.erb gets rendered.

This helper renders a Alchemy::Element view partial.

A element view partial is the html snippet presented to the website visitor.

The partial is located in app/views/alchemy/elements.

View partial naming

The partial has to be named after the name of the element as defined in the elements.yml file and has to be suffixed with _view.

Example

Given a headline element

# elements.yml
- name: headline
  contents:
  - name: text
    type: EssenceText

Then your element view partial has to be named like:

app/views/alchemy/elements/_headline_view.html.{erb|haml|slim}

Element partials generator

You can use this handy generator to let Alchemy generate the partials for you:

$ rails generate alchemy:elements --skip

Usage

<%= render_element(Alchemy::Element.published.named(:headline).first) %>

Parameters:

  • element (Alchemy::Element)

    The element you want to render the view for

  • options (Hash)

    Additional options

  • counter (Number)

    a counter



162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
# File 'app/helpers/alchemy/elements_helper.rb', line 162

def render_element(*args)
  if args.length == 4
    element, _part, options, counter = *args
    Alchemy::Deprecation.warn "passing a `part` parameter as second argument to `render_element` has been removed without replacement. " \
      "You can safely remove it."
  else
    element, options, counter = *args
  end

  options ||= {}
  counter ||= 1

  if element.nil?
    warning('Element is nil')
    render "alchemy/elements/view_not_found", {name: 'nil'}
    return
  end

  element.store_page(@page)

  render element, {
    element: element,
    counter: counter,
    options: options
  }.merge(options.delete(:locals) || {})
rescue ActionView::MissingTemplate => e
  warning(%(
    Element view partial not found for #{element.name}.\n
    #{e}
  ))
  render "alchemy/elements/view_not_found", name: element.name
end

#render_elements(options = {}) ⇒ Object

Renders elements from given page

Examples:

Render only certain elements:

<header>
  <%= render_elements only: ['header', 'claim'] %>
</header>
<section id="content">
  <%= render_elements except: ['header', 'claim'] %>
</section>

Render elements from global page:

<footer>
  <%= render_elements from_page: 'footer' %>
</footer>

Fallback to elements from global page:

You can use the fallback option as an override for elements that are stored on another page. So you can take elements from a global page and only if the user adds an element on current page the local one gets rendered.

  1. You have to pass the the name of the element the fallback is for as for key.

  2. You have to pass a page_layout name or Page from where the fallback elements is taken from as from key.

  3. You can pass the name of element to fallback with as with key. This is optional (the element name from the for key is taken as default).

<%= render_elements(fallback: {
  for: 'contact_teaser',
  from: 'sidebar',
  with: 'contact_teaser'
}) %>

Custom elements finder:

Having a custom element finder class:

class MyCustomNewsArchive
  def elements(page:)
    news_page.elements.named('news').order(created_at: :desc)
  end

  private

  def news_page
    Alchemy::Page.where(page_layout: 'news-archive')
  end
end

In your view:

<div class="news-archive">
  <%= render_elements finder: MyCustomNewsArchive.new %>
</div>

Parameters:

  • options (Hash) (defaults to: {})

    a customizable set of options

Options Hash (options):

  • :from_page (Alchemy::Page|String) — default: @page

    The page the elements are rendered from. You can pass a page_layout String or a Page object.

  • :only (Array<String>|String)

    A list of element names only to be rendered.

  • :except (Array<String>|String)

    A list of element names not to be rendered.

  • :count (Number)

    The amount of elements to be rendered (begins with first element found)

  • :offset (Number)

    The offset to begin loading elements from

  • :fallback (Hash)

    Define elements that are rendered from another page.

  • :random (Boolean) — default: false

    Randomize the output of elements

  • :reverse (Boolean) — default: false

    Reverse the rendering order

  • :separator (String)

    A string that will be used to join the element partials.

  • :finder (Class) — default: Alchemy::ElementsFinder

    A class instance that will return elements that get rendered. Use this for your custom element loading logic in views.



92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# File 'app/helpers/alchemy/elements_helper.rb', line 92

def render_elements(options = {})
  options = {
    from_page: @page,
    render_format: 'html'
  }.update(options)

  if options[:sort_by]
    Alchemy::Deprecation.warn "options[:sort_by] has been removed without replacement. " \
      "Please implement your own element sorting by passing a custom finder instance to options[:finder]."
  end

  if options[:from_cell]
    Alchemy::Deprecation.warn "options[:from_cell] has been removed without replacement. " \
      "Please `render element.nested_elements` instead."
  end

  finder = options[:finder] || Alchemy::ElementsFinder.new(options)
  elements = finder.elements(page: options[:from_page])

  buff = []
  elements.each_with_index do |element, i|
    buff << render_element(element, options, i + 1)
  end
  buff.join(options[:separator]).html_safe
end

#sort_elements_by_content(elements, content_name, reverse = false) ⇒ Array

Deprecated.

Sort given elements by content.

Parameters:

  • elements (Array)
    • The elements you want to sort

  • content_name (String)
    • The name of the content you want to sort by

  • reverse (Boolean) (defaults to: false)
    • Reverse the sorted elements order

Returns:

  • (Array)


261
262
263
264
265
266
267
268
269
# File 'app/helpers/alchemy/elements_helper.rb', line 261

def sort_elements_by_content(elements, content_name, reverse = false)
  Alchemy::Deprecation.warn "options[:sort_by] is deprecated. Please implement your own element sorting."
  sorted_elements = elements.sort_by do |element|
    content = element.content_by_name(content_name)
    content ? content.ingredient.to_s : ''
  end

  reverse ? sorted_elements.reverse : sorted_elements
end