Okay, one thing I hate in JavaScript code is all the code around AJAX requests.

jQuery has a wrapper. And AngularJS has a wrapper. And RxJS has a wrapper. And...

Why don't we just use XMLHttpRequest? Then our services, which are often fairly UI-templating-agnostic already, can live anywhere we want.

There are at least two good reasons... The first is to handle JSONP. I'm ignoring that for now.

The second is to handle async code in a manner that's conventional for each templating engine. $http in AngularJS, for instance, uses $q in place of Promises to ensure changes are communicated correctly to AngularJS. RxJS returns things as Observables (RxJS specific, give or take) rather than Promises (which is a standard), though this is easy to work around.

It's probably worth saying that, well, first that I stole the framework for this code from a SO answer, and second that this is a great example of how to hand-roll a Promise. Just call resolve or reject when you're ready and poof, you've got a Promise.

For the most part, however, you can pass the data event back into the templating system's change detection scheme fairly easily.

So I wanted to write a quick XMLHttpRequest library one could use for the vast majority of CRUD operations. I've only got CR below (give or take. Let's not get into POST vs PUT for the time being), but you get the picture.

I'll try to edit this as I make changes. Comments welcome.

The bottom line, though, is that this is not difficult code. Why we thought we needed wrappers to make AJAX calls I'll never know. The longer you can resist context-specific code, like library-specific wrappers, the longer your original code can live.

(See Exhibit VanillaJS (or the more useful, but strangely mascoted, competing Vanilla JS project).)

Yes, this is, in contrast to my original goals, in TypeScript. It's a pretty easy port. But seriously, you should be using TypeScript.

export default class BaseService {
    getOneUntyped(url: string, id?: string): Promise<any> {
        return new Promise((resolve, reject) => {
            let request = new XMLHttpRequest();

            request.onerror = function () {
                reject(`No response was given.`);
            };

            request.onreadystatechange = function () {
                console.log(request.readyState, request.status);

                // 4 === DONE
                if (request.readyState === 4) {
                    // 200 === OK.
                    if (request.status === 200) {
                        resolve(JSON.parse(request.response));
                    } else {
                        // https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/status
                        // Before the request completes, the value of status is 0.
                        // Browsers also report a status of 0 in case of XMLHttpRequest errors.
                        if (request.status !== 0) {
                            reject(`${request.response} ${request.statusText}`);
                        }
                    }
                }
            };

            let urlToUse = url + (id ? id : "");
            request.open("GET", urlToUse, true);
            request.send();
        });
    }

    postOneTyped<T>(url: string, payload: T): Promise<boolean> {
        return new Promise((resolve, reject) => {
            var request = new XMLHttpRequest(); // new HttpRequest instance
            request.open("POST", url);
            request.setRequestHeader("Content-Type", "application/json");
            request.send(JSON.stringify(payload));

            request.onerror = function () {
                reject(`No response was given.`);
            };

            request.onreadystatechange = function () {
                console.log(request.readyState, request.status);

                // 4 === DONE
                if (request.readyState === 4) {
                    // 201 === Created.
                    if (request.status === 201) {
                        resolve(true);
                    } else {
                        // https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/status
                        // Before the request completes, the value of status is 0.
                        // Browsers also report a status of 0 in case of XMLHttpRequest errors.
                        if (request.status !== 0) {
                            reject(`${request.response} ${request.statusText}`);
                        }
                    }
                }
            };
        });
    }
}

Labels: , , ,