Friday, July 18, 2014

Angular and ASP.NET MVC: when to use which golden hammer

In my previous post, I outlined how to use ASP.NET MVC and Angular together, making certain views pure MVC and others Angular.

When I first used Angular this way, I was so happy, I went the full Angular way making requests from Angular for my data. Essentially, something like this:
$http({
    method: 'GET',
    url: '/api/rest/'
}).success(function (data, status, headers, config) {
    vm.names = data;
}).error(function (data, status, headers, config) {
              
});

This is code inside my customers.js. It's just a call to my RestController.cs (a WebAPI controller). In my customers.html template, I have a simple:
<li ng-repeat="name in vm.names">{{name}}</li>

But now, we're making two requests for something that the server could have added to our HTML anyway. This would be different if we could add a customer on the overview page, but because adding is done on a separate page, this data is actually static (from the viewpoint of the browser).

So can we keep using Angular, but let the server dynamically add anything to the page that it can? Luckily yes, and this is where, to me, we hit the sweet spot of SPAs: making the web application feel responsive thanks to Angular, but making it performant and limiting request thanks to ASP.NET.

Here's how.

Add a new (MVC) controller and a view. Make the controller return the view with the necessary data, taking care to add Angular attributes where necessary. Something like this:
@model IEnumerable<string>

<section id="dashboard-view" class="mainbar" data-ng-controller="customersmvc as vm">
    <h1>{{vm.title}}</h1>

    <a href="/Angular/#/customers/create">Create new customer</a>

    <p>
        The title of this view is rendered via Angular, whereas the customer names are added to
        the HTML on the server.
    </p>

    <div>
        <ul>
            @foreach (var name in Model)
            {
                <li>@name</li>
            }
        </ul>
    </div>

</section>

This page now combines Angular and ASP.NET MVC. This can be handy for data that is "dynamic" in the sense that it comes from a database, differs per user, etc but won't change once it's rendered in the browser.

For data that will change in the browser, you can still leverage Angular.

One final thing to take a look at is that ASP.NET MVC will set the Layout of the view you are loading. This is done in the _ViewStart.cshtml file. Of course, you want to keep this for pages loaded normally via ASP.NET MVC, but not for the ones loaded by Angular.

If we would keep this for the pages loaded by Angular, we would get our navigation, footer, etc. twice. You can easily avoid this by adding the following to the view:

@{Layout = null;}

So that's basically how I've combined ASP.NET MVC and Angular. Let me know if you've found a better or different way, or if you have any comments or questions.

Wednesday, July 16, 2014

Angular and ASP.NET MVC: now I've got two golden hammers!

Angular is a great tool, but it took me some time to find a way to combine it elegantly with ASP.NET MVC. This is basically how I did it.

First, create a new ASP.NET MVC application. Next, install the Angular package via NuGet. Now for the customization.

The objective is to use the normal ASP.NET MVC navigation, unless for certain URLs, when we'll let Angular take over.

So http://www.example.com/Account/Login would be handled by ASP.NET ("ASP.NET-mode"), but http://www.example.com/#/Customers would be handled by Angular ("Angular-mode"). Of course, it's ASP.NET serving us the Customers page, but after that, we want to use Angular for data-binding, navigation, routing, the forms, etc.

Add a new Controller with one method, Index(), that returns View(). Standard ASP.NET up until now. I named mine AngularController.

Next, add a View in the corresponding folder (in my case: /Angular/Index.cshtml). In this view, set up your main Angular view. Something like:
@{
    ViewBag.Title = "Index";
}

<div ng-app="app">
    <div ng-controller="main as vm">
        <div ng-view class="shuffle-animation"></div>
    </div>
</div>

@section scripts {
    @Scripts.Render("~/bundles/angular")
}

So when we're in "Angular-mode", we want ASP.NET MVC to include our Angular scripts. The angular bundle looks something like this (in /App_Start/BundleConfig.cs):
bundles.Add(new Bundle("~/bundles/angular").Include(
                      "~/Scripts/angular.js",
                      "~/Scripts/angular-animate.js",
                      "~/Scripts/angular-route.js",
                      "~/Scripts/angular-sanitize.js",
                      "~/Scripts/app/app.js",
                      "~/Scripts/app/config.js",
                      "~/Scripts/app/main.js",
                      "~/Scripts/app/customers/customers.js"));

The reason I'm not using a ScriptBundle is because we don't want ASP.NET to minify the Angular scripts. This causes errors because Angular sometimes depends on function arguments to be specific strings. You can read more on that here.

For now, minification isn't important, but in a production-environment, you would want to use the minified Angular scripts.

In app.js, config.js and main.js, I've put the necessary code to get Angular running. The most important part is the getRoutes function in config.js:
function getRoutes() {
    return [
        {
            url: '/customers',
            templateUrl: '/Scripts/app/customers/customers.html'
        }
    ];
}

Finally, the customers.html and customers.js contain my Angular logic and HTML markup for this specific page. This now allows you to navigate to http://localhost:1748/Angular/#/ (your portnumber may vary of course).

There you have it. ASP.NET MVC is serving the HTML page that includes references to Angular scripts and templates, the browser downloads all that, and then Angular wires it all together!

(Of course, you might want to configure ASP.NET to use a different URL for the AngularController)

Adding this to your navigation is as simple as adding this tag to your _Layout.cshtml file:
<li><a href="https://www.blogger.com/Angular/#/customers">Customers</a></li>

Don't forget the hash.

Now lets add a second page. This will make the difference between what I've been calling "ASP.NET-mode" and "Angular-mode" more clear.

Add a new html file and a new javascript file to the /Scripts/app/customers/ folder, add the route to config.js and add the javascript file to the Angular bundle in BundleConfig.cs. The link in my case would now be:
<a href="https://www.blogger.com/Angular/#/customers/create">Create new customer</a>

Now, when you run the app, navigating from /Angular/#/customers to, say, /Account/Login will load the entire new page. But navigating from /Angular/#/customers to /Anguler/#/customers/create stays within Angular, and just loads the new template, "staying inside" your SPA. You can sort of notice it because loading a new page "inside" the SPA feels faster.

So we've effectively combined classic ASP.NET MVC with Angular, allowing us to choose where we want/need which.

You can check out the source code in my GitHub repository. I've taken care to make every commit a logical step in the process.

Monday, June 16, 2014

Techorama 2014: Nik Molnar on GitHub

At Techorama, some sessions really sprung out. I took some notes and pictures (tip for the Windows Phoners: Office Lens) so I could share what I learnt.

Nik Molnar gave a great session on extending GitHub (see his slides on GitHub). He showed some of the great tools that add functionality to GitHub. If you don't already have a GitHub account or work with Git, I cannot over-recommend getting started with it.

Here are some things he shared, though very incomplete. Be sure to check out the rest or, even better, attend one of his talks if you can.

Printing

Gitprint is a simple idea that could be of great use. Go to a markdown file on GitHub and change the 'hub' in the url to 'print'. Presto! A printable PDF version of the file. You can also do this from the repository root and Gitprint will take the README.MD file.

Markdown

Speaking of Markdown. If you haven't already learned it, do so right away. It's increasingly the markup language of developers collaborating.
A handy tool for Markdown is MarkdownPad. It allows you to see the results on the fly and it offers a choice of the different Markdown flavors.
Something I learnt about Markdown is that apart from the usual readme.md file, GitHub also recognizes a contributing.md file. The readme file will be displayed in your repository root, but the contributing file will be shown to anyone that wants to create an issue. An example of such a file can be found in the Angular repository. Notice the link on the 'guidelines for contributing' when you want to create an issue.
And finally, if you're into Markdown and mindmapping, check out Memofon.com.

HuBoard

HuBoard is a task board that lists the issues of a GitHub repository and syncs between GitHub and HuBoard. When you drag a task to the done column, the issue is closed. Quite neat.

5 minute fork

5minfork.com is similar to gitprint.com. If your repository has an index.html file in the root, replace https://github.com/ with http://5minfork.com (lose the -s in the protocol) and a fork will be created and hosted. For example, https://github.com/petermorlion/pwa has an index.html. Go to http://5minfork.com/petermorlion/pwa to see it live. Of course, this is a simple example. But for more complex applications, this could be handy to see how it works when not on your local machine (think Angular, Durandal, etc).

etc etc etc

There was lots more: Emoji Cheat Sheet, Expandinizr, userscripts.org, Octokit, asciiflow, Web Sequence Diagrams, and more. Once again, check out the session in its entirety.


Wednesday, June 11, 2014

Techorama 2014: databases (Grant Fritchey)

I'm not a database guy. In fact, it's one of the aspects of programming I least like. So it surprised some of my colleagues when I told them I was attending a session at Techorama aimed at DBAs.

But the description struck a chord with me. Grant Fritchey gave a session named "Solving the Database Deployment Problem".

In my short career, I've only seen one place where database deployment was done correctly. And it was my first employer. Every change to the SQL database had to be put in a .sql file. The file went into source control. All these scripts could be executed multiple times on our local development sandbox (our own PC, no connections to servers). Essentially, you could rebuild the database time and again and start messing about from scratch.

These scripts were also executed on the build server, staging server, and finally production servers. We used RoundhouseE for database versioning and it all went more or less automatic and it was a blast!

Other companies seem to struggle with their database (although they won't admit the arcane and tedious wizardry that is easy-once-you-know-how-it's-done is struggling). Scripts need to be executed manually, versioning is done inside the script file, database compares are done, etc.

That's why I took a picture of what I believe was Grant's best slide:



First of all, treat it as code. That means (among others) putting it in source control. Too often, databases are still managed like we did in the nineties (well not we, as I was still on my skateboard back then, oblivious of the coolness of programming).

And automate! Don't waste developer's or DBA's precious time with tedious monkey jobs. Invest in an automated solution.


One question I asked him was if he had experience with NoSQL databases and automating that. NoSQL databases don't always have an easy way of creating scripts for updates and putting that into source control.

While Grant did have experience with NoSQL databases, they had a product (can't remember which) that did support scripts.

Although it should be feasable to have your application run some code on startup, and check/update the database if necessary. Sort of like Code First Migrations and MigrateDatabaseToLatestVersion in Entity Framework.

Not part of the Techorama session, but this slide deck Grant recently uploaded covers the session in more detail. And be sure to check out this extensive resource on the subject.


Tuesday, June 10, 2014

Use CDNs, but have a fallback mechanism

At Techorama 2014, Nik Molnar mentioned very shortly that you should use CDNs. CDNs, or Content Delivery Networks (or Distribution), are public repositories of javascript libraries, css files, images, etc that anyone can reference.

There are lots of CDNs out there. A popular one is Google Hosted Libraries. You'll find Angular, jQuery, jQuery UI, etc.

The main advantage of using a CDN is performance. There's a good chance that people who visit your site will already have visited a site referencing the same libraries/files. If these were hosted on a the same CDN that you are referencing, your browser will fetch the file from its own cache. That's one less HTTP request, and increased speed in loading the file.

An added bonus is that it doesn't use up your bandwidth :)

One thing Nik mentioned very briefly was to have a fallback mechanism in case the CDN doesn't respond. However, he didn't go into how this works.

A CDN that is down shouldn't happen (often), but knowing the internet, it will. If your site is a low profile, small throwaway test-site, there's no problem. If your site is more crucial and has lots of visitors, it pays to have this fallback, as it's very simple.

Instead of just referencing the CDN like this:

<script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>


You can check for the existence of a variable the library defines. If it doesn't exist, load the local version of the file:

<script>
    window.jQuery 
    || document.write('<script src="js/libs/jquery-2.1.1.min.js"><script>')
</script>

Wednesday, June 4, 2014

Techorama 2014

I've written about learning in the past, and how I've given up a bit on user group sessions. In my case, I learn more when I get behind my computer and write some code myself. But when Team4Talent offered me to attend Techorama, I didn't turn it down.

I know people have opinions on some of the 'celebrity' developers in Belgium. But say what you want, organizing something like Techorama is a feat not everyone can pull off. So congrats to all the people that helped make it happen (and thanks for the vegetarian food!).

In the past, I attended TechDays every two years. The reason was that I found this more than enough. Most sessions were so-so and quite a lot felt like a Microsoft marketing announcement (with all due respect to the speakers).

This wasn't the case with the Community Days, and neither was it the case with Techorama. One funny example of this was the fact that at TechDays, speakers used Internet Explorer. At Techorama, it was all Chrome and Firefox.

But the sessions were also more technical and independant. Of course, it was mostly about Microsoft technology. But I found the sessions I attended to be more satisfying for me as developer. At TechDays, sometimes you just got a brief introduction to something new, without digging in deeper.

Notable speakers for me were:

I will try to post some interesting slides and stuff I learnt. But for now, know that Techorama was worth attending. And apart from learning, it's a great place to have fun and meet interesting people.

Update (5/06/2014): They just uploaded most of the slides to OneDrive.

Wednesday, April 16, 2014

A (JSONP) REST service with your existing WCF service

I'm mainly doing javascript in my free time, but at work, it's still classic .NET. Yesterday, I had to quickly set up a REST service via WCF to test something. In the spirit of saving my keystrokes, and for my own later reference, I'm posting how to do it here, instead of emailing it.

I'm assuming you have an existing WCF service.

Just add the following to your ServiceContract (you'll need a reference to System.ServiceModel.Web):

[OperationContract]
[WebGet(UriTemplate = "{id}", ResponseFormat = WebMessageFormat.Json)]
Person GetPerson(string id);

For REST to work, your parameters will have to be a string and you will have to parse it in the implementation of your ServiceContract.

I want JSONP, so I've added the response format and need some extra stuff in my config:

The service:
<endpoint address="People" 
    binding="webHttpBinding"
    bindingConfiguration="Default" 
    behaviorConfiguration="jsonBehavior" 
    contract="My.Contracts.IPersonService" />

A behavior:
<endpointBehaviors>
  <behavior name="jsonBehavior">
    <webHttp />
  </behavior>
</endpointBehaviors>

Finally, the binding:
<webHttpBinding>
  <binding name="Default" crossDomainScriptAccessEnabled="true" />
</webHttpBinding>

If you need other REST methods (POST for example), have a look at WebInvoke.

Now you can navigate to your url, for example: http://www.example.com/People/3, or use this url for AJAX calls.