Skip to content

The power of mixins #1264

@fabien

Description

@fabien

I'm just throwing this in here to fuel some much needed discussion regarding the state of mixins, and what they could do for Loopback. I'm already using them extensively in my projects, and they allow distinct traits to be easily shared amongst models, and to be configured as needed.

When I see something like this #1260 - I see a problem that just begs for a Mixin to be implemented. Given the right infrastructure like the new operation hooks, there's a lot you can do.

Most of this has been available since loopbackio/loopback-datasource-juggler#201 ... but there's a pending discussion here: strongloop/loopback-boot#79

This is a bit from an model I've been using in one of my projects, post.js:

function setup(app) {
    this.defineProperty('info', 'MetaData');

    this.hasMany('Note', { as: 'notes', scope: { order: 'createdAt' } });
    this.referencesMany('Employee', { as: 'authors' });

    this.scope('galleries', { where: { type: 'Gallery' } });

    this.mixin('ObjectId');
    this.mixin('Protect', { properties: ['slug', 'type'] });
    this.mixin('Alias', { property: 'slug', from: 'title' });
    this.mixin('Transform', { property: 'type', filters: ['urlify', 'classify'] });
    this.mixin('TimeStamps');
    this.mixin('Storage', { as: 'attachments', references: true, relation: { 
        foreignKey: 'attachmentIds', scope: { order: 'filename' } } 
    });
    this.mixin('Includes');
    this.mixin('Reorder', { rel: 'attachments' });
    this.mixin('Constraint', { rel: 'notes', action: 'delete' });
    this.mixin('Versions', { attributes: ['title', 'body'], locale: true });
    this.mixin('Search', { index: 'main' });
    this.mixin('Expose', { scope: { order: 'slug' } });

    this.mixin('Render');
    this.mixin('Forms', { exclude: ['createdAt', 'updatedAt'] });

    this.mixin('Virtuals', { methods: {
        summary: function() {
            return 'Summary: ' + this.title;
        }
    } });
};

module.exports = function(Post) {
    Post.on('attached', setup.bind(Post));
};

And the post.json:

{
  "name": "Post",
  "base": "PersistedModel",
  "properties": {
    "type": {
      "type": "string",
      "required": true,
      "default": "Post",
      "search": {
        "index": "not_analyzed"
      }
    },
    "title": {
      "type": "string",
      "required": true,
      "search": {
        "raw": true
      }
    },
    "body": {
      "type": "string",
      "required": true
    },
    "active": {
      "type": "boolean",
      "default": true
    }
  },
  "validations": [],
  "relations": {
    "sections": {
      "type": "embedsMany",
      "model": "Section"
    }
  },
  "acls": [],
  "methods": []
}

Note that there's no pseudo code in there - this all works, and has been tested extensively.

Alternatively, you can declare mixins in your post.json under the mixins key like this:

{
  "name": "Post",
  "base": "PersistedModel",
  "properties": { ... },
  "mixins": {
    "Protect": { "properties": ["slug", "type"] },
    "Transform": [
       { "property": "name", "filters": ["capitalize"] },
       { "property": "type", "filters": ["urlify", "classify"] }
    ]
  }
}

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions