adamkdean

software engineering

Cloning objects in JavaScript

By Adam K Dean on

Just a quick snippet post today.

A great way to clone objects in JavaScript and break references is to serialize and deserialize the object. The process of converting it to a JSON string and back into an object severs any references. Unfortunately, it also breaks certain types such as Date and probably RegEx too.

var clone = JSON.parse(JSON.stringify(original));

A better way (I've found) to clone objects while retaining types is to use jQuery.extend.

// Shallow copy
var newObject = jQuery.extend({}, oldObject);

// Deep copy
var newObject = jQuery.extend(true, {}, oldObject);

There are other, quicker ways, but when you can run 120,000 deep copies a second, is that extra microsecond really worth it?

Invoke callbacks with unknown arguments

By Adam K Dean on

Whilst working on some instrumentation code, I had need to pass a function and an unknown number of arguments to another function which would then time the execution of that function. This turned out to be quite easy using fn.prototype.apply.

Let's say that we want to call a function but execute some other code, we'd do that here:

function invokeCallback(callback) {
  var params = Array.prototype.slice.call(arguments, 1);
  return callback.apply(null, params);
}

And here are our three varied functions:

function action() {
    console.log('action');
}

function greet(msg) {
    console.log(msg);
}

function nameage(name, age) {
    var msg = 'My name is ' + name + ' and my age is ' + age;
    console.log(msg);
}

And here is how we can call them dynamically without invokeCallback having to know anything about them:

invokeCallback(action);
invokeCallback(greet, 'Hello!');
invokeCallback(nameage, 'Bob', '20');

Amazing.

No MediaTypeFormatter is available

By Adam K Dean on

You may have come across the error message No MediaTypeFormatter is available to read an object of type T from content with media type 'text/plain'. during your adventures in ASP.NET Web API. Tonight it was my turn to be faced with this bothersome little 'issue'.

I was attempting to post {Username:"testuser", Password:"testpass"} to an API controller which received a ViewModel like so:

public class UserViewModel
{
    public string Username { get; set; }
    public string Password { get; set; }
}

public HttpResponseMessage Post(UserViewModel value)
{
    // do something
}

The only problem was that it just wasn't finding the controller action. Spitting out error messages and generally being counter-productive.

After going through lots of StackOverflow posts and dismissing them, it turned out the first one was actually relevant. I was using a Chrome extension (Postman) to help with REST calls and even though I had selected JSON, it was still sending requests as plain/text, and not application/json.

I set the Content-type header to application/json and it started working.

Enable AngularJS HTML5 Routing on IIS

By Adam K Dean on

AngularJS has the ability to use HTML5 routing, which means that rather than having your application using a hash (#) sign and then the route, it can instead get rid of the hash and still function as a Single Page Application. For example:

Without HTML5 routing: http://domain.com/#/user/15 -> http://domain.com/index.html
With HTML5 routing: http://domain.com/user/15 -> http://domain.com/index.html

For this to work though, you'll need to set up some sort of server-side url rewriting. To do this in IIS, you should first download and install the URL Rewrite module (Web Platform Installer will have it). After that, we need to set up some rules.

If you want the step by step version, follow this:

  1. Open up the site in question and go to URL Rewrite module.
  2. Add an inbound rule, name it "AngularJS Conditions". Set the pattern to match all directories you want to exclude, I use (app/.*|assets/.*|common/.*). Change the action to None, and click Apply.
  3. Add another inbound rule, name it "AngularJS Wildcard". Set the pattern to (.*), keep the action as Rewrite and put in the relative URL to your index.html, and click Apply.
  4. Test your site.
  5. If your css/js requests are being rewritten, you have a problem with your conditions rule.
  6. If you're getting 404.0 errors, you have a problem with your wildcard rule.
  7. Get back to work.

If you're impatient and just want the web.config, well, here you go:

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
  <system.webServer>
    <rewrite>
      <rules>
        <clear />
        <rule name="AngularJS Conditions" stopProcessing="true">
          <match url="(app/.*|assets/.*|common/.*|modules/.*|config/.*)" />
          <conditions logicalGrouping="MatchAll" trackAllCaptures="false" />
          <action type="None" />
        </rule>
        <rule name="AngularJS Wildcard" enabled="true">
          <match url="(.*)" />
          <conditions logicalGrouping="MatchAll" trackAllCaptures="false" />
          <action type="Rewrite" url="index.html" />
        </rule>
      </rules>
    </rewrite>
  </system.webServer>
</configuration>

You should now be set up for HTML5 routing.

Enable cross-origin resource sharing (CORS) in IIS

By Adam K Dean on

If you're hosting your API on a different domain to your app, such as api.domain.com, you may be hitting into some cross-origin request roadblocks. In IIS, this is pretty easy to fix -- or well -- disable.

In your WebAPI project's web.config, put in the following:

<httpProtocol>
    <customHeaders>
        <add name="Access-Control-Allow-Origin" value="*" />
        <add name="Access-Control-Allow-Headers" value="Origin, X-Requested-With, Content-Type, Accept" />
    </customHeaders>
</httpProtocol>

This will enable cross-origin resource sharing (CORS) and allow you to get back to work.