# Custom columns and sortable columns for the "manage" modal

By default, the "manage" modal displays just a few columns: "title," "last updated," and "published." The "title" and "updated at" columns support sorting on that column, by clicking on the column heading.

You can extend this list and even specify your own sortable columns. Here's how to do that:

// lib/modules/your-piece/index.js
module.exports = {
  // Other configuration options, then...
  addColumns: [
    {
      name: 'myCustomField',
      label: 'Custom Field',
      sort: {
        // Sort on this property. The `1` is required
        myCustomField: 1
      }
    }
  ]
}

TIP

Notice that for sort you specify an object exactly like what you'd pass to MongoDB's sort() method, or Apostrophe's sort() cursor filter. In particular, the actual property you sort on does not have to match the property name displayed in the column. For example, when working with people's names you might sort on { lastName: 1, firstName: 1 } rather than title.

In some cases, the field you are adding in a new column may not display well as it is in the database. Dates are a good example of this. You can use a custom Nunjucks template to output the value instead. We can use the default updatedAt field as an example of this. updatedAt is a standard column and we would not suggest adding it twice, but it serves as an example since the referenced template is in the code base for you to see.

beforeConstruct: function(self, options) {
  options.addColumns = [
    {
      name: 'updatedAt',
      label: 'Last Updated',
      sort: {
        // Sort on this property. The `1` is required
        updatedAt: 1
      },
      // The value of the property shows up as `data.value` in the template.
      partial: function(value) {
        if (!value) {
          // Don't crash if `updatedAt` is missing
          return '';
        }
        return self.partial('manageUpdatedAt.html', { value: value });
      }
    }
  ]
}

To display the title or another field from a join in a column, include the partial method in the addColumns options:

  // Other configuration options, then...
  addColumns: [
  {
    name: '_myJoinedField',
    label: 'Joined Field',
    partial: function(_myJoinedField) {
      if (!_myJoinedField) {
        return 'None';
      } else {
        return _myJoinedField.title;
      }
    }
  }

It's important to note that this is done in beforeConstruct. The self object is not available for the self.partial method in the simple addColumns option (see the previous example).

TIP

If you want to change one of the standard columns, override defaultColumns rather than setting addColumns.

Joins are one of Apostrophe's best features:

addFields: [
  {
    name: '_pages',
    type: 'joinByArray',
    withType: 'apostrophe-page',
    label: 'Pages',
    idsField: 'pageIds'
  }
]

But if you try to access the _children property of those pages, you'll be disappointed at first.

Child pages get fetched only if the children() filter is called on the cursor that fetches those docs. This takes extra time and does extra work, and most joins don't need them. So by default, they are not fetched.

Fortunately, you can turn on extra cursor filters yourself in any join:

addFields: [
  {
    name: '_pages',
    type: 'joinByArray',
    withType: 'apostrophe-page',
    label: 'Pages',
    idsField: 'pageIds',
    filters: {
      children: true
    }
  }
]