# Events & Getters

xpress-mongo emits events on Create, Update & Delete queries ran by model instance functions.

NOTE: Only use if your version >=0.8.0

# Model

The snippet below shows a Model that has an update event registered to it.
Examples made on this page will make reference to this model.

const {is} = require('xpress-mongo');
const connection = require('path/to/your/connection');

class User extends connection.model('users') {
  
  static schema = {
    name: is.String().required(),
    avatar: is.String().required(),
    updatedAt: is.Date(),
    createdAt: is.Date().required()
  }
}

// Add on `update` event
User.on('update', (user) => {
  // set `updatedAt` to current date.
  user.set('updatedAt', new Date());
});

Note: The instance of the current model will be passed to your event function on execution.

# Events

Events tagged as Before events runs synchronously before the database call is made while After events runs asynchronously after the database call in the background.

# Before (sync)

# After (asynchronous)

# create

Runs synchronously before every new document is created.

/**
 * DECLARATION
 */
User.on('create', user => {
  // Set name to upper case
  user.set('name', user.data.name.toUpperCase());
  
  // Set Default avatar
  user.data.avatar = '/path/to/defualt_avatar.png';
});

/**
 * IMPLEMENTAION
 */
let user = {name: 'John Doe'};

user = await User.new(user); // Event is called
// OR
user = await new User().set(user).saveAndReturn(); // Event is called

/**
 User {
  name: "JOHN DOE",
  avatar: "/path/to/defualt_avatar.png",
  updatedAt: "2021-04-21T11:20:31.051Z",
  createdAt: "2021-04-21T11:20:31.051Z"
 }
 */

The create event also supports targeting fields using the dot operator.
if value returned !== undefined, xpress-mongo will set the value of the targeted field to the value returned.

/**
 * on every new document `avatar` will be set to '/images/default.png' if undefined.
 * because a defined value is returned.
 */
User.on('create.avatar', (user) => {
  if (!user.data.avatar) return '/images/default.png';
})

# created

This event fires in the background whenever you add a new document.

User.on("created", user => {
  console.log(`User: ${user.data.name} has just signed up!`)
})

# update

This event runs before a document is updated.

User.on('update', onUpdateFunction);

const user = await User.findById(ObjectId) // Returns instance.

await user.update({name: 'Paul Dean'}); // onUpdateFunction is called
// OR
await user.set({name: 'Paul Dean'}).save(); // onUpdateFunction is called

The update event also supports targeting fields using the dot operator.
if a !undefined value is returned, xpress-mongo will set the value of the targeted field to the value returned

/**
 * on every update and model has changes `updatedAt` date will be refreshed.
 * because a defined value is returned.
 */
User.on('update.updatedAt', (user) => {
  return user.hasChanges() ? new Date() : undefined
});

Notice the use of .hasChanges(), this is because all update event functions are called even when you have not made any real changes.

# deleted

This event runs in background after a document is deleted.
Note: The delete event does not support using dot operator on fields.

// This event deletes user avatar every time a user is deleted.
User.on('deleted', (user) => {
  try {
    fs.unlinkSync(user.data.avatar);
  } catch (e) {
    console.log(e)
  }
});

const user = await User.findById(ObjectId) // Returns instance.

await user.delete(); // event is called after delete

# watch

The watch event is a special event that occurs after a real change has been made to a field. Just Like the ** delete** event, it runs in the background.

Note: The watch event only supports the dot target for field names.

/** This event crops user avatar in the backround
 * every time the value of avatar is changed in any document. */
User.on('watch.avatar', user => {
  cropNewAvatar(user.data.avatar)
});

await user.update({avatar: 'new/avatar/file/path.png'}); // function is called
// OR
await user.set({avatar: 'new/avatar/file/path.png'}).save(); // function is called

# Getters

Since fields can be modified using update.fieldName events before saving to the database we can call them setters. but what about getters? This is where static append = [] model property comes in.

# append

For Example

const {is} = require("xpress-mongo");
const moment = require("moment");

class User extends XMongoModel {
  
  // Set Functions to be appended.
  static append = ["joinedTimeAgo"];
  
  // Schema
  static schema = {
    username: is.String().required(),
    joined: is.Date().required(),
  }
  
  // Convert joined field to TimeAgo String
  joinedTimeAgo() {
    return moment(this.data.joined).fromNow();
  }
}

The class above has the method joinedTimeAgo in it's append array.
This will automatically add the result of joinedTimeAgo to your Model instance results.

Using the above Model class

const user = await User.findOne({/* Query */});
// Or
const user = await User.findById(/* ID */);

/*{
  _id: MongoID
  username: 'testuser'
  joined: '2020-01-01T00:00:00.000Z'
  joinedTimeAgo: '1 year ago'
}*/

Note: The appended data will only be available on a model instance document.

# Multiple Results

When handling multiple results you won't get the append function unless you convert the array of results to model instances.

For Example