Creating a custom image widget in Apostrophe CMS

2019-07-23 03:35发布

问题:

I am having trouble creating a custom image widget in Apostrophe CMS. The current apostrophe-images-widgets works fine for more images, but I need a special case for the website logo as it needs specific css and a specific location on the website.

I tried following the tutorial here for making a custom widget with a manager, but I have no gotten the results I would like. Here is my logo-image module

module.exports = {
    extend: 'apostrophe-pieces',
    name: 'logo-image',
    label: 'Website Logo',
    addFields: [
        {
            name: 'companyName',
            label: 'Company Name',
            type: 'string',
            required: true
        },
        {
            name: 'logo',
            label: 'Logo',
            type: 'singleton',
            widgetType: 'apostrophe-images',
            options: {
                limit: 1,
                minSize: [ 200, 200 ]
            }
        }
    ],
    construct: function(self, options) {
        self.beforeSave = function(req, piece, options, callback) {
            piece.companyName = 'Freedom Mutts';
            return callback();
        };
    }
};

which I registered in app.js like so

modules: {
    'logo-image': {}
}

Then, I have a logo-image-widgets module with an index.js like so

module.exports = {
      extend: 'apostrophe-pieces-widgets',
      label: 'Website Logo',
      filters: {
          projection: {
          slug: 1,
          title: 1,
          type: 1,
          tags: 1
      }
   }
};

and a modules/logo-image-widgets/views/widget.html like so

<a class="navbar-brand" href="/">{{
  apos.singleton(
    piece,
    'logo',
    'apostrophe-images',
    {
      edit: false
    }
  )
}}</a>

and a base layout like so

{% extends data.outerLayout %}
<div class="apos-refreshable" data-apos-refreshable>
    {% block beforeMain %}
        <nav class="navbar navbar-default navbar-fixed-top nav-back">
            <div class="container">
                <div class="navbar-header">
                    <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false"
                    aria-controls="navbar">
                        <span class="sr-only">Toggle navigation</span>
                        <span class="icon-bar"></span>
                        <span class="icon-bar"></span>
                        <span class="icon-bar"></span>
                    </button>
                    {{
                        apos.area(data.page, 'logo', {
                            widgets: {
                                'logo-image': {}
                            }
                        })
                    }}
                </div>
                <div id="navbar" class="navbar-collapse collapse">
                    <ul class="nav navbar-nav">
                        <li class="active"><a href="#">Home</a></li>
                        <li><a href="#about">Our Pets</a></li>
                        <li><a href="#about">About</a></li>
                        <li><a href="#contact">Contact</a></li>
                    </ul>
                    <ul class="nav navbar-nav navbar-right">
                        <li><button class="btn header-btn">Donate <span class="sr-only">(current)</span></a></li>
                    </ul>
                </div><!--/.nav-collapse -->
            </div>
        </nav>
    {% endblock %}
    {% block main %}
        Main code here...
    {% endblock %}
    {% block afterMain %}
        layout code here...
    {% endblock %}
</div>

This leaves me with a couple issues, 1) I cannot add the logo image to the website itself, only through the menu bar and 2) I don't think it is actually what I want. I want a custom <img /> tag rendered with my own css classes.

I wasn't too sure where to ask this question, so any advice would be great. I am having a lot of trouble creating more complex custom widgets.

Any help is appreciated. Thanks!

回答1:

I'm the lead developer of Apostrophe at P'unk Avenue.

What you've done here shows a solid grasp of how pieces and pieces-widgets work, although our convention would be to always give the module itself a plural name (it manages many things) while the name property remains singular (it's what we call one instance of the thing in the database). That's just a style nitpick though, the code is fine.

The advice you really need: there's a much easier way to get where you're going for a single project. Just copy the lib/modules/apostrophe-images-widgets/views/widgetBase.html file of the original apostrophe-images-widgets module to the file lib/modules/apostrophe-images-widgets/views/widget.html at project level and hack it up to your heart's content.

(You could just copy widget.html and override various template blocks in widgetBase, but it sounds like your needs wouldn't be met by those blocks alone.)

Since you don't want this behavior change for every single widget, use an if statement in your template to vary the output based on data.options.foo, where foo is any option you're passing to the widget in your home page template. Look at how data.options.size is handled now, for instance.

If you need extra JavaScript, copy lib/modules/apostrophe-images-widgets/public/js/always.js to project level as well; it's very short, consisting of a play method that invokes our jquery-projector plugin. Your play method can do anything it wants to instead.

Having said that, if your goals are more ambitious, take a look at the apostrophe-images and apostrophe-images-widgets modules themselves. They use the attachment field type, which you can use too, including using it directly in a widget module if you are not interested in having a browseable repository of images for reuse. So you can bypass the "widgets display stuff drawn from a repository of pieces" pattern entirely.