Complex events routing

We alredy looked how to do some basic event routing. While that aproach works, it requires some boilerplates which we perfer to avoid. So in this tutorial we will look at the routing tools, that our react-form-generator provides.

We will use the same form's metadata, as in previous tutorial about routing:

    ...

    "fields": {
        "field1": {
            "renderer": "text",
            "validators": [
                {
                    "rule": "required",
                    "message": "Field is required"
                },
                {
                    "rule": "maxLength",
                    "value": 15,
                    "message": "Maximum length of field is 15 characters"
                }
            ]
        },
        "field2": {
            "renderer": "text",
            "defaultValue": "1",
            "validators": [
                {
                    "rule": "required",
                    "message": "Field is required"
                },
                {
                    "rule": "numbers",
                    "value": 5,
                    "message": "Only numbers allowed"
                }
            ]
        }, 
        "btnSend": {
            "renderer": "button",
            "rendererSpecific": {
                "text": "Send"
            }
        }
    }

All routing tools are located in the tools/routing module and can be accessed from FormGenerator.tools.

var FG            = window.FormGenerator
  , t             = window.FormGenerator.tools
  , GeneratedForm = FG({})
  , validateForm  = GeneratedForm.validateForm;

var App = React.createClass({
    // ======================== Life cycle ======================= //
    getInitialState: function () {
        return {
            value:  t.evalDefaults( meta ), 
            errors: {} 
        };
    },

    componentDidMount: function () {
        this._route = t.buildRouter(
            'btnSend:click', [ btnClickHandler ]
        );
    },

    // ======================== Handlers ========================= //
    handleFormChanged: function ( newValue, change, fldErrors ) {
        this.setState({ 
            value:   newValue,
            errors:  t.merge( this.state.errors, fldErrors ), 
        });
    },

    handleFormEvent: function ( fieldID, eventName, eventInfo ) {
        this._route( fieldID + ':' + eventName );
    },

    // ======================== Renders ========================== //
    render: function() {
        return (<GeneratedForm meta={meta}
                               value={this.state.value}
                               errors={this.state.errors}
                               onChange={this.handleFormChanged}/>);
    }
});


function btnClickHandler () {
    this.setState({
        errors: validateForm( meta, this.state.value )
    });
}

There are a few steps to setup our "embeded" routing. First, we must provide full set of event-to-handlers mapping. To do it, just call function buildRouter. This function accepts variadic number of arguments, where each odd argument is event mask, and each even argument is list of handlers for this event.

We use variadic function instead of function-with-one-big-object-argument, because of this:

this._route = t.buildRouter(
    'btnSend:click', [ btnClickHandler ],
    /btn.+:click/,   [ myLoggingFunction ]
);

Yes, you can use regular expression as event mask, not just hardcoded string names. This is very useful feature, and we will use it in dynamic form example (in progress).

buildRouter function builds repository of events' handlers and returns closure, that accepts event name.

When you call this closure, it executes each handler from list of handlers of specified event. Each handler will be called with closure's this context.

Here is interactive JSFiddle example for complex events routing.