Configuring and creating angular apps with Visual Studio is not straightforward as it requires some manual configuration settings and some addition of .config
files. It would be nice to have a ready-made template which would take this pain away and save the time. With Visual Studio 2017, a template is available for creating and running sample Angular application. The template is good enough to get you started with Angular. This post talks about how to create an Angular 4 App with Visual Studio 2017 and how to extend it with a simple example.
How to create an Angular 4 app with Visual Studio 2017
Let’s first set up the required software. You must install following software (if not installed).
- Node.js
- .NET Core 2.0
- Visual Studio 2017 Community. If already installed, then update it to version 15.4.2.
Open Visual Studio 2017. Hit File->New Project -> Select ASP.NET Core Web Application. Enter the project name and select the path where you wish to create this project. When you hit “OK”, you should see the following dialog box.
Select Angular from this dialog and Say “OK”. The Visual Studio will create an ASP.NET Core 2.0 based project with Angular 4 configured. You should see the following project structure.
The project structure looks familiar with Controller
, Views
and wwwroot
folder. The ClientApp
folder contains the angular app. You can also see webpack.config.js
at the root of the application, which tells you that webpack is being used by this project as module bundler. This file instructs webpack about how to bundle the client code.
If you want to learn all of Angular, I want to personally recommend ng-book as the single-best resource out there. You can get a copy here.
Before, diving into the sample code, it’s important to highlight a few things. This sample code uses JavaScriptServices
used for creating single page applications with ASP.NET Core. JavaScriptServices is a collection of client-side technologies for ASP.NET Core. Its goal is to position ASP.NET Core as developers’ preferred server-side platform for building SPAs. It includes support for the client-side frameworks – Angular, Aurelia, Knockout.js, React, React + Redux, and Vue.js.
Meet JavaScriptServices
This service comprises three distinct NuGet packages:
- Microsoft.AspNetCore.NodeServices (NodeServices): It allows you to execute JavaScript on the server within a Node.js environment. You can even consume NPM packages at runtime within your .NET Core application.
- Microsoft.AspNetCore.SpaServices (SpaServices): It offers a server-side platform to create for building SPAs. It has the necessary tools to handle server-side prerendering, webpack middleware support, Hot Module Replacement and routing helper to sync client-side routes with server-side route.
- Microsoft.AspNetCore.SpaTemplates (SpaTemplates): It offers single page application templates for ASP.NET Core.
All these packages are already included as they are part of single Microsoft.AspNetCore.All
package. This single meta package was introduced in ASP.NET Core 2.0.
You should read this official post to learn more about JavaScriptServices
.
Let’s visit some important lines of code to get a feeling how all this works. First, open Startup.cs
and look at configure()
method code. There are a couple of things to notice. First,
app.UseWebpackDevMiddleware(new WebpackDevMiddlewareOptions { HotModuleReplacement = true });
The above code registers WebpackDevMiddleware
into the HTTP request pipeline. As you know, webpack is a module bundler and used to build and bundle client-side resources. Normally, one would run the webpack at the command-line to bundle things, however, with this middleware this whole process is automated and you don’t have to worry now. Here, HotModuleReplacement
option is also set to true
.
“Hot Module Replacement” (HMR) is a feature to inject updated modules into the active runtime. So any changes made into the client side will get reflected instantly in the browser without even restarting the application or refreshing the browser.
The next piece of code is related to routing. We all are familiar with MapRoute()
to configure routing for ASP.NET Core MVC based application. The new thing here is, MapSpaFallbackRoute()
. This is required to handle client-side routing. If the request doesn’t match any static files and MVC controller, then it is meant for client-side app (not always though). In other words, this enables the server to handle any requests intended for the Angular router by returning the Angular application to the browser. Once the application is loaded in the browser, Angular takes over the URL routing.
app.UseStaticFiles(); app.UseMvc(routes => { routes.MapRoute( name: "default", template: "{controller=Home}/{action=Index}/{id?}"); routes.MapSpaFallbackRoute( name: "spa-fallback", defaults: new { controller = "Home", action = "Index" }); });
Here,MapSpaFallbackRoute
also specifies Index of HomeController
like the MapRoute()
. Both of them seem to be doing the same thing. Then what’s the difference here. Well, the MapRoute()
defines the default route for MVC application and the MapSpaFallbackRoute
always points to the HomeController
.
Next, open index.cshtml
present in the Home folder. You should see the following line of code.
<app asp-prerender-module="ClientApp/dist/main-server">Loading...</app>
The asp-prerender-module
tag helper is used to define which module to render (in this case ClientApp/dist/main-server). This enables server-side pre-rendering of client SPA applications. You should see main-server.js file at the ClientApp/dist location. This file is an entry point to the angular app and gets executed by Node.js. It is used to render the JS on the server before injecting it into the response.
Webpack defines an entry point alias of main-server with value “ClientApp/boot-server.ts” file. You can find this webpack.config.js
. The boot-server.ts
has createServerRenderer
function to configure server-side rendering and returning the HTML markup to the browser. You can check the Angular version in package.json
file. In this case it is pointing to 4.2.5.
"@angular/core": "4.2.5",
Lets run the application and you should see the following in your browser.
You can double-check the menu items with the angular app structure. Let’s extend this application a bit. Here, the fetch data component calls an API to get random weather forecasts. Like,
Earlier, I posted Bind Select DropDown List in Angular 2 and let’s add a dropdown on top of the grid to filter the data based on the selected value. Below is the API code to return summary list and weather forecasts. The new API added to the sample code is GetSummaries()
private static string[] Summaries = new[] { "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" }; [HttpGet("[action]")] public string[] GetSummaries() { return Summaries; }
Next is the updated code of fetchdata.component.ts
. The constructor now calls the GetSummaries
, along with WeatherForecasts
API. There is also a method defined filterForeCasts
which gets called on change event of the dropdown. This method filters the forecasts list based on the selected value and then returns the result.
import { Component, Inject } from '@angular/core'; import { Http } from '@angular/http'; @Component({ selector: 'fetchdata', templateUrl: './fetchdata.component.html' }) export class FetchDataComponent { public forecasts: WeatherForecast[]; public cacheForecasts: WeatherForecast[]; public summaries: any[]; constructor(http: Http, @Inject('BASE_URL') baseUrl: string) { http.get(baseUrl + 'api/SampleData/WeatherForecasts').subscribe(result => { this.forecasts = result.json() as WeatherForecast[]; this.cacheForecasts = this.forecasts; }, error => console.error(error)); http.get(baseUrl + 'api/SampleData/GetSummaries').subscribe(result => { this.summaries = result.json() as any[]; }, error => console.error(error)); } filterForeCasts(filterVal: any) { if (filterVal == "0") this.forecasts = this.cacheForecasts; else this.forecasts = this.cacheForecasts.filter((item) => item.summary == filterVal); } } interface WeatherForecast { dateFormatted: string; temperatureC: number; temperatureF: number; summary: string; } interface Summary { name: string; }
Lastly, put this HTML in fetchdata.component.html
to show the summary dropdown.
<div> <label>Summary: </label> <select (change)="filterForeCasts($event.target.value)"> <option value="0">--All--</option> <option *ngFor="let summary of summaries" value={{summary}}> {{summary}} </option> </select> </div>
Run the application and navigate to fetch data. You should see a drodown having list of all the summaries. Changing the selection will also update the weather forecast grid.
This Angular 4 app is upgraded to Angular 5. Read how to Upgrade Angular 4 app to Angular 5 with Visual Studio 2017. You can now also create Angular 5 app directly, Read Create an Angular 5 app in 5 steps using dotnet cli
Conclusion
This is a whole new web development experience with ASP.NET Core and Angular. This is backed by ASP.NET Core JavaScriptServices and Webpack for bundling. Rendering Angular on the server has advantages like better performance, optimized SEO and fast and safe delivery of the content to the browser. This post talks about different nuget packages of JavaScriptServices and how angular application pre-renders on the server. The WebpackDevMiddleware
and HMR inclusion is also great as it makes the webpack process automated.
Thank you for reading. Keep visiting this blog and share this in your network. Please put your thoughts and feedback in the comments section.
I’ve seen Angular 4 .NET Core App mixes all in a single project, WebAPI and Client Side. What about an external WebAPI? How Angular 4 consumes it?
Danilo,
It doesn’t make any difference in the approach/code for calling internal or external API, except the endpoint address. The difference with external APIs would be, authentication and authorization before making a call to external API. You may have to pass an access token along with your API request from Angular code.
Thanks
Hello,
I’ve read this document and understand that .net core is having a template to build angular App. So web API will be in controller and UI will be in angular. But I’ve following questions…
1. Application will be hosted to IIS as this is .net App. What about node? Because angular app needs node. Or we don’t need node any more? This app will work?
2. If we need to run node then IIS will internally manage all requests?? And how 2 different server behaves?
3. Can we create an app in conjunction of MVC pages as well as angular let’s say 10 MVC pages and 20 angular pages??
4. If yes then how routing and other things will work? Because node and mvc routing work separately.
5. I knew angular will be used to serve SPA only, but if we need to achieve a functionality where my current MVC pages works with new angular pages(SPA) then how to proceed or what will be workflow.
6. How to manage data sharing between both pages (web services will be there right?)
Pankaj,
I will try to answer all your questions.
1. If you’re deploying to an Azure web site, you don’t need to do anything here — Node.js is installed and available in the server environments. Otherwise you may need to install node.js on the server.
2. SPAs services uses Microsoft.AspNetCore.NodeServices (NodeServices) nuget package which allows you to execute JavaScript on the server within a Node.js environment.
3.Yes, we can mix angular and MVC.
4. Regarding routing, the ASP.nET Core will first check for MVC route. If not found, then it will be angular app route. Startup.cs class has code for both.
5. Didn’t get your question..
6.You can call the WEB API from Angular app and then store the data on the client app.
First of thank you very much for your reply appreciated!!!
Regards Q. 5. I knew angular will be used to serve SPA only, but if we need to achieve a functionality where my current MVC pages works with new angular pages(SPA) then how to proceed or what will be workflow.
Here i want to ask in my angular app if I want to load any MVC view or partial view (razor) then what will be approach?
Also please guide me if you’ve any link or sample code which have a sample app with 4-5 mvc razor pages and 4-5 angular pages in same app.
Great job. thanks and God bless U.
The ClientApp/dist folder will not be created persistently in the latest version of Angular CLI unless you do an ng build.
Also, when running the project in IE11 you will get an unhandled exception in vendor.js, line 33893.
Unhandled exception at line 33893, column 5 in http://localhost:6334/dist/vendor.js?v=RCvRrqPvM2Kc5BlkEQ045FeXR6gPMRIwfn51ludN14I
0x800a138f – JavaScript runtime error: Unable to get property ‘apply’ of undefined or null reference
You need to uncomment the IE9/10/11-related polyfills in .src/polyfills.ts to resolve this.
* BROWSER POLYFILLS
*/
/** IE9, IE10 and IE11 requires all of the following polyfills. **/
import ‘core-js/es6/symbol’;
import ‘core-js/es6/object’;
import ‘core-js/es6/function’;
import ‘core-js/es6/parse-int’;
import ‘core-js/es6/parse-float’;
import ‘core-js/es6/number’;
import ‘core-js/es6/math’;
import ‘core-js/es6/string’;
import ‘core-js/es6/date’;
import ‘core-js/es6/array’;
import ‘core-js/es6/regexp’;
import ‘core-js/es6/map’;
import ‘core-js/es6/weak-map’;
import ‘core-js/es6/set’;
How to setup Angular 4 with .NET MVC framework?
great article. my version of visual studio, vs17 15.6.1, did not automatically restore npm packages. i had to go out to the command line. also, this only worked with .net core selected as framework. With .net framework 4.6.1, the npm packages were not restored successfully, specifically @ngtools/webpack.
very nice article. Explained everything properly