Extending the pieces editor modal

← Previous: Adding new batch operations for pieces

Let's look at adding a custom button for use when editing a particular subclass of apostrophe-pieces.

If all you want is an extra field in the form, there's a much easier way. Just use addFields to add more schema fields to your subclass of pieces. See the tutorials!

For starters, you'll want to override editorModal.html for your module.

{# in lib/modules/mymodulename/views/editorModal.html #}

{% extends "editorBase.html" %}
{% import 'apostrophe-ui:components/buttons.html' as buttons with context %}

{%- block controls -%}
  {# The standard controls #}
  {{ super() }}
  {# Custom button #}
  {{ buttons.major('My Label', { action: 'mything' }) }}
{%- endblock -%}

You will want to do exactly the same thing for createModal.html if your button also makes sense when creating new pieces.

Next check out editor-modal.js, where the pieces module overrides beforeShow to add a bunch of jquery click handlers.

self.link('mything', function() { ... }) calls that function when a data-mything attribute is found in the context of self.$el (the modal).

You can extend this by creating your own editor-modal.js file in your own module's public/js folder. Since you are extending apostrophe-pieces, which already pushes editor-modal.js, your own version automatically gets pushed to the browser too.

To extend beforeShow at project level you'll want to follow the "super pattern":

// lib/modules/mymodule/public/js/editor-modal.js

apos.define('mymodule-editor-modal', {
  extend: 'apostrophe-pieces-editor-modal',
  construct: function(self, options) {
    var superBeforeShow = self.beforeShow;
    self.beforeShow = function(callback) {
      self.link('mything', self.doMyThing);
      return superBeforeShow(callback);
    self.doMyThing = function() {
      ... up to you! ...

If you want to talk to an API route provided by the same module you can call:

self.api('apiname', { POST object for req.body }, function(result) {
   ... do something with result ...

self.api will invoke the URL /modules/modulename/apiname as a POST and submit the data object as req.body, using the JSON content type.

On the server side, you'll need to extend your module to implement the API:

// In lib/modules/mymodulename/index.js

module.exports = {
  construct: function(self, options) {
    self.route('apiname', function(req, res) {
      // Validate things with the launder module
      var name = self.apos.launder.string(req.body.name);
      ... Do more with that ...
      // Deliver a JSON resposne
      return res.send({ status: 'ok', moreInfo: 'something' });

Calling self.route('apiname'...) is similar to calling self.apos.app.post('/modules/mymodulename/apiname', ...), but it's a lot less work, and you can call again with the same apiname to override it, which you can't do with app.post.

Next: Responsive images →