Skip to content
This repository has been archived by the owner on Dec 17, 2021. It is now read-only.

Latest commit

 

History

History
144 lines (111 loc) · 4.85 KB

intercept.md

File metadata and controls

144 lines (111 loc) · 4.85 KB

Observer.intercept()

This method is used to intercept operations performed on an object or array with custom handlers. Requests like set, delete, get and has are trapped and forwarded to these custom handlers.

Syntax

// Intercept all operations or queries
Observer.intercept(obj, handler[, params = {}]);

// Intercept a specific operation
Observer.intercept(obj, type, handler[, params = {}]);

Parameters

  • obj - an object or array.
  • type - the operation to intercept.
  • handler - a function that handles the operation. This recieves:
    • event - an object containing details of the operation.
    • recieved - the return value of a previous trap in the list, if any.
    • next - a function that calls the next trap in the list, if any.
  • params - Additional parameters.

Return Value

An Interceptor instance.

Usage

let obj = {name: 'reflex',};
let getVersionNumberRemotely = () => '1.0.0';

Below, we're intercepting the "get" request and skipping all other requests using the next() function we receive in our handler. This trap will "lazy-load" the object's version value.

Observer.intercept(obj, (event, recieved, next) => {
    if (event.type === 'get') {
        let requestedKey = event.name;
        if (requestedKey === 'version' && !(requestedKey in obj)) {
            obj[requestedKey]; =  getVersionNumberRemotely();
        }
        return obj[requestedKey];
    }
    return next();
});

Now, let's see what we get for each property we access.

console.log(Observer.get(obj, 'name')); // 'reflex'
console.log(Observer.get(obj, 'version')); // '1.0.0'

In another case, we're intercepting a "set" operation to validate the incoming value for a specific property. We're using the type parameter to constrain the trap to just the "set" type.

Observer.intercept(obj, 'set', (event, recieved, next) => {
    let requestedKey = event.name;
    let requestedValue = event.value;
    if (requestedKey === 'url' && !requestedValue.startsWith('http')) {
        throw new Error('The url property only accepts a valid URL!');
    }
    obj[requestedKey] = requestedValue;
    // We return true here
    // and it's always good to still call next() with a return a value
    return next(true);
});

Now, let's attempt setting different URLs on our object. Remember that Observer.set() will also trigger observers that may be bound to the object.

console.log(Observer.set(obj, 'url', 'https://example.com')); // true
console.log(Observer.set(obj, 'url', 'example.com')); // Fatal Error

Tagging an Interceptor

The params.tags parameter can be used to tag a trap. Tags are an array of values (strings, numbers, objects, etc) that can be used to identify the trap for later use.

Observer.intercept(obj, handler, {tags:['#tag']});

Setting Multiple Interceptors

Multiple traps can rightly be set on an object. Each trap called will have the decision to call the next. A trap is called with the return value of the previous trap (or undefined where there is no previous trap) and a reference to the next trap (or a reference to the default handler where there is no next trap).

Below, we set an additional trap to handle setting the url property. But this time, we wouldn't bother if the previous trap has handled this.

Observer.intercept(obj, 'set', (event, recieved, next) => {
    If (received === true) {
        console.log('A previous handler has handled this!');
        return next(true);
    }
    // We could do the work here
    // or simply leave it to the default property setter
    return next();
});

The Returned Interceptor Instance

The Observer.intercept() method returns an Interceptor instance that gives us per-instance control.

// Obtain the Interceptor instance
let instance = Observer.intercept(obj, handler);

// Synthetically fire the handler
instance.fire({
    type:'set',
    name: 'propertyName'
    value:'...',
});

// Disconnect the trap
instance.disconnect();

Related Methods