14 Mar

AngularJS URLs missing trailing slash

I ran across this problem trying to deploy an Asp.Net MVC website with an AngularJS front end.  Everything worked fine as long as the site was deployed as it’s own website within IIS, but when we deployed to an Application folder within an existing site things started going wrong.

The problem was that the URLs were not getting their trailing slashes properly.  IIS adds a trailing slash to URLs when the last segment of the address refers to a folder rather than a file.

http://somesite.com/AngularApp should have been converted to http://somesite.com/AngularApp/

Since it wasn’t getting converted I was getting http://somesite.com/AngularApp#/ rather than http://somesite.com/AngularApp/#/

The fix I settled on was to check the URL of the request when it came in and if it matched the root url, but didn’t have the trailing slash, add the trailing slash.  I just added the following code to the Global.asax

protected void Application_BeginRequest()
    if (Request.ApplicationPath != "/" 
            && Request.ApplicationPath.Equals(Request.Path, StringComparison.CurrentCultureIgnoreCase))
        var redirectUrl = VirtualPathUtility.AppendTrailingSlash(Request.ApplicationPath);
30 Nov

Error: The Key is Already Attached from Breeze.js

First let me say if you’re reading this and you haven’t checked out Breeze.js, you should.  It’s a nice little library.  I’m using it in an Angular.js based application and it has greatly simplified data access.  Now on to the show.

I’m using Breeze.js for data access and as part of that I am registering some constructors like so.

var AddressCtor = function () {
    this.CompanyId = document.getElementById('CompanyId').value;

var NoteCtor = function () {
    this.CompanyId = document.getElementById('CompanyId').value;

metadataStore.registerEntityTypeCtor('Address', AddressCtor);
metadataStore.registerEntityTypeCtor('Note', NoteCtor);

Everything was working fine then I tried to do a little code cleanup.  The constructors for Note and Address were identical.   So, of course I, re-factored them into a single generically named function.

var CompanyEntity = function () {
    this.CompanyId = document.getElementById('CompanyId').value;

metadataStore.registerEntityTypeCtor('Address', CompanyEntity);
metadataStore.registerEntityTypeCtor('Note', CompanyEntity);

Well, it appears that you cannot register 2 constructors that call the same function like that.  If you do you the error “The Key is Already Attached: Note:Webapplication1″, and no data loaded in the Angular app.

When I finally found the cause, I couldn’t believe it.  The fix that I came up with was to pass null as the constructor parameter and move that CompanyId assignment into a function that is passed as the init parameter.  This has the added bonus that entities that need to do a series of things AND set the CompanyId can just call the companyEntityInit function from inside their initializers.

function companyEntityInit(entity) {
    entity.CompanyId = document.getElementById('CompanyId').value;

metadataStore.registerEntityTypeCtor('Address', null, companyEntityInit);
metadataStore.registerEntityTypeCtor('Note', null, companyEntityInit);