The purpose of this post is to establish ourselves with a working environment geared towards development on ASP.NET Core 1.0 through Visual Studio 2015. I will walk through creating an Angular2 Single Page Application with MVC 6, Web API 2 and TypeScript. The sample application is available here on my github page.
As a community of early adopters it is gratifying to see collaborations betwixt Microsoft and Google, finally "we can all get along".
These collaborations are exciting and certainly influential - my only hope is that we see more of these collaborations and other large industry leaders follow their example!
There are several things that you'll need before we dive into writing code. We should take a moment to familiarize ourselves with some of the new core concepts as well as ensuring that we have our IDE correctly installed and configured.
It all starts with your favorite IDE, mine being VS (if that wasn't obvious). You can download the community version of it here. The nice thing about the community version is that it is entirely free! Once you have it installed you're ready to begin setting up your web solution. Finally, as a prerequisite you should at the very least be familiar with .NET MVC.
The project.json is the replacement for the <inlinecode>*.csproj<inlinecode> file. The main difference is the file format - you've probably been noticing the shift away from XML and the movement to the preferred JSON format as it is superior.
The wwwroot folder now acts as the directory in which your web site will be served from. Instead of your entire solution directory acting as the file structure, now this single folder and its sub directories act as the root of the application.
While Node.js and its corresponding package manager, namely "npm" are part of the install for Visual Studio 2015 - it installs with <inlinecode>1.4.9<inlinecode> which is unfortunately an outdated version. Angular2 might play nicer with a later version of npm.
"Node.js" package ecosystem, npm, is the largest ecosystem of open source libraries in the world!
Let's address this now, so that we do not run into issues once we start writing code. Install the latest version of Node.js here. Once installed navigate to the following directory:
C:\Program Files (x86)\Microsoft Visual Studio 14.0\Common7\IDE\Extensions\Microsoft\Web Tools\External\
Open the npm.cmd file in your favorite text editor (running as _Administrator_) and replace the contents of this file with the following:
@"C:\Program Files\nodejs\node.exe" "C:\Program Files\nodejs\node_modules\npm\bin\npm-cli.js" %*
Now Visual Studio 2015 will utilize the npm version that was installed from the aforementioned steps.
At this point you are probably wondering, “what is ASP.NET Core 1.0 and what happened to ASP.NET 5?”. These are both great questions, and to answer them ASP.NET 5 has been renamed more appropriately. Since it was completely rewritten from the ground up, why not rename and version it? For the remainder of this tutorial, if you happen to see reference to ASP.NET 5 think of it as ASP.NET Core 1.0 and use them interchangeably for now. But don’t take my word for it, check out Scott Hanselman’s post "ASP.NET 5 is dead".
ASP.NET 5 is a new open-source and cross-platform framework for building modern cloud-based Web applications using .NET.
With .NET 4.6 you get C# 6. C# 6 is something that I have personally been looking forward to. I'm not sure I share the same level of excitement as I did with .NET 4.5 and its asynchronous programming model support, but excited nonetheless! Additionally, we are now going to start harnessing ASP.NET Core 1.0.
ASP.NET 5 applications are built/ran using the new .NET Execution Environment (DNX), and every ASP.NET 5 project is a DNX project.
DNX is an acronym that stands for .NET Execution Environment. It is basically just an SDK within the context of Visual Studio, but also acts as the runtime environment in which your ASP.NET Core 1.0 applications execute. Accompanying DNX is the DNU and DNVM CLI's.
For more details on these command line interfaces please visit here.
We should be familiar enough with some of the core concepts such that we can finally begin by creating a new ASP.NET Core 1.0 web application. From within Visual Studio 2015, select<inlinecode> File→New→Project<inlinecode>. Then from within the New Project dialog navigate and select <inlinecode>Installed→Templates→Visual C#→Web→ASP.NET Web Application<inlinecode> – select “Ok”.
Visual Studio 2015 now groups templates by .NET Framework version, notice ASP.NET 5 applications are separated and beneath .NET Framework 4.6.1. Additionally, notice when you select the ASP.NET 5 templates that the MVC and Web API check-boxes are disabled – the reason being that they are now unified in ASP.NET 5. Personally, I’m thrilled that there is no longer a need to have Web API controllers inherit ApiController and MVC controllers inherit Controller – more about this later…
There can be only one.
Yes, that is a Highlander reference! But it’s actually appropriate because there should be only one <inlinecode>Controller<inlinecode> – not to mention the seemingly countless other duplicate classes that overlapped with <inlinecode>System.Web.Http<inlinecode> and <inlinecode>System.Web.Mvc<inlinecode>.
Select the “Empty” template, and let’s continue – your solution should now resemble the figure below:
We can delete the <inlinecode>Project_Readme.html<inlinecode> file as it is not needed. Now let’s add some standard MVC scaffolding, you should have the following for our starting point:
All that is needed from the previous step was the folder structure and several built-in templates.
Of the multiple items we have added at this point, there are only three that we need to alter from the default template provided. First the <inlinecode>_ViewImports.cshtml<inlinecode> should resemble the content below – and simply enables tag helpers.
Finally, the <inlinecode>Index.cshtml<inlinecode> which is rendered as the body of the layout and introduces an appelement that will be detailed later.
Now let’s get our webroot structure solidified, expand the wwwroot folder and add the folders depicted below. We will add to these later.
Open the <inlinecode>project.json<inlinecode> file and let’s paste in the following </inlinecode></inlinecode>JSON. This will pull in the required dependencies we need for our starter application. The ‘dependencies’ are simply NuGet package references – be sure to paste everything defined below.
If you’re familiar with OWIN then the next steps will be very straightforward. Open the <inlinecode>Startup.cs<inlinecode> file., and let’s paste in some more source and discuss its purpose.
You might have noticed that in addition to a <inlinecode>public void Configure<inlinecode> method there is now a <inlinecode>public void ConfigureServices<inlinecode> method as well. Additionally, the <inlinecode>Configure<inlinecode> method now expects two new parameters, <inlinecode>IHostingEnvironment<inlinecode> and <inlinecode>ILoggerFactory<inlinecode>.
The typical usage pattern is that you invoke an <inlinecode>Add*<inlinecode> extension method in the <inlinecode>ConfigureServices<inlinecode> operation to add a service for consumption, while in the Configure operation you invoke the corresponding <inlinecode>Use*<inlinecode> extension method.
The pattern becomes obvious when you study the <inlinecode>ConfigureServices<inlinecode> source, notice how we are adding Glimpse and MVC to the <inlinecode>IServiceCollection<inlinecode>.
Glimpse provides real time diagnostics & insights to the fingertips of hundreds of thousands of developers daily.
As you see below, within the Configure method now invokes the two corresponding methods – UseGlimpse and UseMvc.
In other words, pay attention to what it is that you’re doing. A lot of what we have done thus far was simple MVC related and server-side, let’s now shift the attention to the client-side and how Angular2, TypeScript, Task Runners and npm will play a role.
Right click on project and select <inlinecode>Add→New Item<inlinecode>. From the Add New Item dialog select <inlinecode>Installed→Client-side→NPM<inlinecode> Configuration File. This will add the <inlinecode>package.json file<inlinecode>, replace its contents with the following:
Note the different types of dependencies from the highlighted source above, one set of dependencies is explicitly for development only – meaning it is not part of what is served to clients in response to a web request. Among these development dependencies you’ll notice gulp a lot.
With the introduction of Visual Studio 2015 the notion of Task Runners is now supported. The Task Runner Explorer is a new window in which your task runner file tasks can be bound to IDE events.
Gulp is one of two task runners now fully integrated and supported in the IDE. Gulp is a pre-processor for content delivery and if you’re familiar with the concept of MVC bundling, this is pretty much the same thing. Multiple files are input and the files are checked for errors, minified and delivered as a single file to the client.
Let’s add the following <inlinecode>gulpfile.js<inlinecode> to our project, <inlinecode>Add→New Item<inlinecode>. From the Add New Item dialog select <inlinecode>Installed→Client-side→Gulp Configuration File<inlinecode>.
Most (if not all) of the Gulp source code should be easy to read and comprehend. We are simply defining several tasks that can be bound to IDE events. We require several JS libraries that were referenced in our <inlinecode>devDependencies<inlinecode>, then we simply author the desired functionality into each named task by utilizing the fluent API that Gulp provides for chaining methods together.
There are four events that you can tether tasks to, three of which are technically build-events and the other is not.
We have bound our default task to the “Before Build” event. These tasks copy over the dependencies our application relies on to run.
Angular2 is simply the second version of the AngularJS.
Angular is a development platform for building mobile and desktop web applications.
In our <inlinecode>wwwroot<inlinecode> we expect the following folder structure, where the <inlinecode>wwwroot\app<inlinecode> is the only place in which we manually manipulate source code. The other folders should be left alone as our **Gulp** task will copy over our dependencies as needed into the other directories.`
+-- app [ working directory ]
¦ +-- components
Let’s start by adding our <inlinecode>boot.ts<inlinecode> file. This file will bootstrap our main entry point for Angular2 to initialize and act on. Additionally this will define our Router and Http Providers. The <inlinecode>MVC _Layout.cshtml<inlinecode> has a script tag that invokes this file.
We will now need to define our AppComponent, add a new TypeScript file namely app.component.ts to the wwwroot\app directory. It should contain the following:
With our AppComponent created, we’ll need to create our <inlinecode>app.html<inlinecode>. This will serve as the template for the menu structure and view-port if you will of our application. There is a direct correlation that I need to call attention to, for each <inlinecode>RouteDefinition<inlinecode> there is a menu item in our HTML that is rendered. When a route-link is clicked Angular2 loads the component declared in the route definition and renders its HTML in place of the route-outlet tag.
For this sample application, I will demonstrate how to utilize static <inlinecode>.html<inlinecode> and MVC partial view pre-processing for our Angular2 component “templateUrl’s”. Additionally, I’ll demonstrate a simply Web API 2 call as well. You may have noticed that we have several components that we have references to within the a<inlinecode>pp.component<inlinecode> that we have yet to create, let’s do that now.
First add <inlinecode>static.component.ts<inlinecode> and <inlinecode>static.html<inlinecode> under the <inlinecode>wwwroot\app\components directory<inlinecode>.
Notice the <inlinecode>selector<inlinecode> and <inlinecode>templateUrl<inlinecode>? The selector is actually the CSS selector <inlinecode>Angular2<inlinecode> uses to find the DOM element in which the templateUrl will replace once rendered. Here is the .html, as you can see very simple.
We can utilize the power of MVC’s pre-processing capabilities and have a <inlinecode>Controller<inlinecode> who is responsible for serving up partial views. This is great since we can then utilize MVC templating in conjunction with Angular2 templating. Add yet another new TypeScript file named <inlinecode>mvc.component.ts<inlinecode>.
Notice that our syntax has changed ever so slightly for the <inlinecode>templateUrl<inlinecode>, this is actually pointing to an MVC partial view that we’re going to create right now. Create a new folder under <inlinecode>Views<inlinecode>, name it “Partial”. Then add a new <inlinecode>MVC View Page<inlinecode>, naming it <inlinecode>Message.cshtml<inlinecode>.
The markup is nearly identical, but the implementation is vastly different. We’ll need the corresponding <inlinecode>Controller<inlinecode> to serve up these requests. Add a <inlinecode>PartialController<inlinecode> with two entry points, the second of which will become obvious in our next step.
I love the one-liner syntax that a simple lambda expression provides with C# 6!
Adding another <inlinecode>MVC View Page<inlinecode> to our newly created <inlinecode>Views\Partial<inlinecode> directory, let’s call it <inlinecode>Numbers.cshtml<inlinecode>. The markup has several key bindings to an Angular2 component that we will need to define.
Back over in our <inlinecode>wwwroot\app\components<inlinecode>, we will need to obviously add our <inlinecode>api.component.ts<inlinecode> but also an <inlinecode>api.service.ts<inlinecode> that will encapsulate our Web API 2 call via Angular2’s Http library.
Our <inlinecode>ApiComponent<inlinecode> gets injected with an instance of our <inlinecode>ApiService<inlinecode> and defines a few simple members that are bound to the <inlinecode>Numbers.cshtml<inlinecode> markup.
I’m simply ecstatic that there was a change to move away from the <inlinecode>Promise<inlinecode> model to the <inlinecode>Observable<inlinecode> model instead. Anytime I get to work with </inlinecode></inlinecode></inlinecode></inlinecode>Reactive Programming paradigm, I learn something new. Angular2’s Http library is based on the observable programming model and relies on RxJS. I invite you to read how Angular2 works with <inlinecode>observables<inlinecode> here but simply put, you subscribe to the Observable passing it the <inlinecode>observerOrNext<inlinecode> (and optionally <inlinecode>error<inlinecode> and <inlinecode>complete<inlinecode>) functions.
All we need now is a Web API 2 controller that will serve up the <inlinecode>HttpGet<inlinecode> that <inlinecode>ApiService<inlinecode> expects at “api/random”. Fortunately for us this has been dramatically simplified with </inlinecode></inlinecode></inlinecode></inlinecode>ASP.NET 5. No more woes with <inlinecode>System.Web.Mvc<inlinecode> vs. <inlinecode>System.Web.Http<inlinecode>.
Now, you can create a single web application that handles the Web UI and data services without needing to reconcile differences in these programming frameworks.
Notice that there is no <inlinecode>ApiController<inlinecode> inheritance, and both the MVC and Web API controllers are simply inheriting from <inlinecode>Controller<inlinecode>.
You should be able to clone the repository locally, build it and play around. I encourage you to do so and hope that you find the sample source fun and the tutorial surrounding it useful. Today we discussed some of the capabilities of ASP.NET 5, MVC 6, Web API 2, Angular2 and TypeScript. I leave you with one question, “tomorrow, what will you do with it?”.