# Displaying Pieces with Widgets

In the previous example, you learned how to create pieces. Now you'll see how to use display those pieces using widgets.

# Displaying Pieces

So you created pieces, but you don't have a way to display them on your site yet. For that you'll need to add a second module in app.js, this time a widget:

// app.js
// other modules, then...
  'people-widgets': {
    extend: 'apostrophe-pieces-widgets'
  }
// etc.

TIP

people-widgets will automatically figure out that its job is to display the pieces that come from the people module, by removing -widgets from its name. If you don't want to follow that pattern, you'll have to set the piecesModuleName option.

Already this is enough to let us add the new widget to any apos.area call in a page template, like your home.html or default.html template:

{# lib/modules/apostrophe-pages/views/default.html #}
apos.area(data.page, 'body', {
    widgets: {
      'apostrophe-rich-text': {},
      'apostrophe-images': {},
      'people': {}
    }
  }
)

Right off the bat, you can click the + anywhere in this area to add a people widget, and then decide whether to display all people or just hand-pick a few by entering their names. There's an autocomplete feature to complete names quickly.

Notice that people have to be published in order to be added to the widget.

# An important performance warning

Pieces widgets are great, but they are powered by joins, and joins can cause trouble because they load so many things... and the things they load may load other things, like widgets... until your site is slow, or Apostrophe refuses to load more widgets to prevent an infinite loop.

To solve that, you should always add a projection filter when configuring a subclass of apostrophe-pieces-widgets:

  // lib/modules/people-widgets/index.js
  module.exports = {
    extend: 'apostrophe-pieces-widgets',
    filters: {
      projection: {
        title: 1,
        phone: 1,
        thumbnail: 1,
        // Not a real database property, but including it in the projection
        // fetches everything needed to populate it
        _url: 1,
      }
    },
    // etc.
  }

This way, only the properties you really need are fetched for the widget. This can greatly speed up your site and prevent mysterious refusals to load any more data if things start joining back to themselves.

TIP

"Which properties do I need in my projection?" Just those you'll use in your widget.html template. However, if you try to guess on your own which properties are needed to populate _url correctly, you'll have a tough time. Instead, just say _url: 1 and Apostrophe will include those for you (slug, type and tags, by default).

# Custom templates for widgets

Your widget isn't very satisfying yet. It just displays full names. Let's improve it by creating your own lib/modules/people-widgets/views/widget.html file to provide a more detailed display:

{# lib/modules/people-widgets/views/widget.html #}
{% for piece in data.widget._pieces %}
  <h4>
    {% if piece._url %}<a href="{{ piece._url }}">{% endif %}
    {{ piece.title }}
    {% if piece._url %}</a>{% endif %}
    {% if piece.phone %}
      <a href="tel:{{ piece.phone }}">
        {{ piece.phone }}
      </a>
    {% endif %}
    {{
      apos.singleton(
        piece,
        'thumbnail',
        'apostrophe-images',
        {
          edit: false,
          size: 'one-sixth'
        }
      )
    }}
  </h4>
{% endfor %}

TIP

"Where do those piece._url links go?" Nowhere, yet. Read on to learn about apostrophe-pieces-pages, which provide a destination for those links.

The apostrophe-pieces-widgets module already has a widget.html file, but when you extend a widget and provide your own version of an existing template, your version gets rendered instead.

Notice that you can pass your piece to apos.singleton the same way you would pass a page. Both are Apostrophe docs, and both live in the aposDocs MongoDB collection. However, pages have a slug property that starts with a /, so they can be viewed at their own URLs. Pieces do not.