Sammi Sinno

.Net Development - Off The Beaten Path

A .Net Development blog that explores everything .Net and puts an emphasis on lesser known community driven projects.

Home

Fluently Building Javascript Objects

Wednesday, June 7, 2017

One thing all developers strive for is keeping your code base clean and understandable. One way to do this is to utilize a fluent syntax when building out objects. In this case I'm building out an object that will be posted back to the server to dynamically setup queries.

Here is an example of what it may look like to set this object up without using fluent syntax:

      var data = {
                conditions: [
                    {
                        searchType: 'equals',
                        field: 'countryId',
                        value: this.countryId
                    }
                ],
                page: parseInt(page),
                pageSize: this.getPageSize()
            };

Not too bad right? But what if we wanted to add more conditions to the array...

      var data = {
                conditions: [
                    {
                        searchType: 'equals',
                        field: 'countryId',
                        value: this.countryId
                    },
										{
                        searchType: 'greaterthan',
                        field: 'population',
                        value: 2000
                    },
										{
                        searchType: 'contains',
                        field: 'cityIds',
                        value: 2
                    }
                ],
                page: parseInt(page),
                pageSize: this.getPageSize()
            };

As you can see, the more conditions we add to the array, the more unweildy the data object will get, and if this is something that you are using consistently, it can muddy up your code base pretty quickly.

Enter in fluent syntax, with this method we can now easily create a data object like this in just a few lines:

var data =
	this.data(1, this.getPageSize())
		.condition('countryId', 'equals', this.countryId)
		.condition('population', 'greaterthan', 2000)
		.condition('cityIds', 'contains', 2)
		.result;

Pretty nice huh? Its easier to read and takes much less lines of code to write. Although, I do believe there is a trade off. Lets take a look at the code that is used to implement this fluent syntax:

var DataMixin = {
        data: function (page, pageSize) {
            var data = {};
            data.conditions = [];
            data.groupJoins = [];

            if (!_.isUndefined(page) && !_.isUndefined(pageSize)) {
                data.page = page;
                data.pageSize = pageSize;
            }

            var returnObj = {
                result: data
            };

            var condition =
                function (field, searchType, value, options) {
                    var condition = {
                        field: field,
                        searchType: searchType,
                        value: value
                    };

                    if (!_.isUndefined(options)) {
                        data.conditions.push(_.extend(condition, options));
                    } else {
                        data.conditions.push(condition);
                    }

                    return returnObj;
                };

            var conjunction =
                function (group1, conj, group2) {
                    data.groupJoins.push(group1);
                    data.groupJoins.push(conj);
                    data.groupJoins.push(group2);

                    return returnObj;
                };

            returnObj.condition = condition;
            returnObj.conjunction = conjunction;

            return returnObj;
        }

The trick is to pass around the same object that contains the functions you want to execute in your chain. In this case that is the returnObj. The 'returnObj' contains two methods (condition and conjunction) and a result property which is used to access the data object at the end of the chain.

As far as the trade off goes, implementing your own fluent api does get more complicated than if you wanted to simply build the objects. If you believe you can create a fluent implementation that is both maintainable and understandable then this could be a solution for you, otherwise you may want to stick with your current method.