Earlier, I posted about creating an angular 6 based app with VS 2017. It’s a pure Angular 6 app and doesn’t use features comes with the ASP.NET Core SPA template. At the time of writing this post, default ASP.NET Core SPA template for angular points to angular 5. The SPA template uses angular on the client side and ASP.NET Core as back-end. It uses a package Microsoft.AspNetCore.SpaServices
as a middleware to provide different configurable options for your application such as HMR (Hot Module Replacement), Routing Helper, SSR (Server Side Rendering) etc.. In this post, let’s find out how to create an angular 6 app with ASP.NET Core 2.1 and implement ASP.NET Core SPA template features in the same app.
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.
Implement ASP.NET Core SPA template features in an Angular 6 app
Before we begin, please do the following installation (ignore if already done).
- .NET Core 2.1 RC1 SDK — includes the runtime
- Install Visual Studio 2017 Update 7 or Visual Studio for Mac 7.5. You can also use latest version of Visual Studio 2017 Preview. Visual Studio and Visual Studio “Preview” can be installed side-by-side on the same device. It will have no impact on your current stable VS installation.
- Node.js and npm latest version
- Angular CLI
Open Visual Studio 2017, hit Ctrl+Shift+N and select the ASP.NET Core Web Application (.NET Core) project type from the templates. When you click Ok, you will get the following prompt. Select ASP.NET Core 2.1 and choose the API template.
The Visual Studio will create an ASP.NET Core 2.1 based Web API project with a controller named ValuesController
. Just run the app to make sure it is running successfully.
Now, let’s add Angular 6 app to this project through Angular CLI. To do that, open command prompt and navigate to the project folder directory. First, install/update the Angular CLI using the following command.
npm install -g @angular/cli
To create angular 6 based app, navigate to the ASP.NET Core project root folder (where the project’s .csproj file is present) and run the following command.
ng new ClientApp
This will take some time to create the app. Once created, the project structure will look like the following.
To configure SPA template features, we need to add support for Microsoft.AspNetCore.SpaServices
middleware. To do that, open Startup.cs
and registers the SPA service that can provide static files to be served for a Single Page Application (SPA). The ConfigureServices
method looks like:
public void ConfigureServices(IServiceCollection services) { services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1); services.AddSpaStaticFiles(c => { c.RootPath = "ClientApp/dist"; }); }
Here, the published scripts/files/images will be copied to the dist folder inside the ClientApp, hence configuration.RootPath = "ClientApp/dist"
.
Next, we need to add SPA middleware to the pipeline. We can do that inside the Configure
method. So, the Configure
method looks like:
public void Configure(IApplicationBuilder app, IHostingEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } else { app.UseHsts(); } app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseSpaStaticFiles(); app.UseMvc(routes => { routes.MapRoute(name: "default", template: "{controller}/{action=index}/{id}"); }); app.UseSpa(spa => { // To learn more about options for serving an Angular SPA from ASP.NET Core, // see https://go.microsoft.com/fwlink/?linkid=864501 spa.Options.SourcePath = "ClientApp"; if (env.IsDevelopment()) { spa.UseAngularCliServer(npmScript: "start"); } }); }
Finally, delete "launchUrl": "api/values"
from the Properties/launchSettings.json file. Build the app and run it. You should see the following.
Let’s us extend this application and call the ValuesController
API from the angular app. To do that, open app.component.ts
and add code to call the API using HTTPClient.
import { Component } from '@angular/core'; import { HttpClient } from '@angular/common/http'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent { title = 'app'; public values: string[]; constructor(private http: HttpClient) { this.http.get('/api/values').subscribe(result => { this.values = result as string[]; }, error => console.error(error)); } }
Next, import HTTPClient in app.module.ts
.
import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; import { HttpClientModule } from '@angular/common/http'; import { AppComponent } from './app.component'; @NgModule({ declarations: [ AppComponent ], imports: [ BrowserModule, HttpClientModule ], providers: [], bootstrap: [AppComponent] }) export class AppModule { }
Finally, add the following HTML to app.component.html
to display the values returned by the API.
<h3>Data coming from server values API</h3> <ul> <li *ngFor="let value of values"> {{ value }} </li> </ul>
Run the app and you should see the values returned from the API.
That’s it.
Thank you for reading. Keep visiting this blog and share this in your network. Please put your thoughts and feedback in the comments section.
Thanks for this post. I was missing `app.UseSpaStaticFiles()` which threw off the entire prod environment.
My Properties.launchSettings.json is marked as ignored in Solution Explorer, as is the “dist” folder under ClientApp.
I suspect these were added as part of the default project template when I decided to upgrade Angular to version 7.1.2 and copy ClientApp manually into the core 2.1 solution.
I have also added Identity Hosting as described here:
https://docs.microsoft.com/en-us/aspnet/core/security/authentication/scaffold-identity?view=aspnetcore-2.1&tabs=visual-studio
because I want to evolve my asp.net core 2.1 web api into something big with secure sign-in password account sign-up, etc, etc.
So, should the page that is displayed when running the API be the index.html in the ClientApp or should it be the Areas.Identity.Pages.Account.Login.cshtml.cs ?
If so, how do I make sure the first page hit is the login or sign-up, etc when I type npm start in the command prompt and then run IIS express in the API ?
Is the fact that I have the Identity Scaffolding causing those folders to be marked as ignored ?
I am using VS 2017.
Please help (or point me in the right direction thanks)
I am getting this error “Cannot GET /” . Same spa path only apply spa.Options.SourcePath = “ClientApp”;. Can you check and how to resolve the error.
And Also I am implement angular 6 HttpClient. that time my code can’t work properly.
return this._http.get(this.myAppUrl + ‘api/Employee/Index’)
.pipe(
map((response: Response) => response.json()),
catchError(this.errorHandler)
)
I am sorry to bother you with another question, but I am having trouble publishing my application to my hosting site. I would appear that the angular pages are not being packed and output when I am publishing. The src dir does not contain any of my pages and when I try to run the stie, I am getting error:
he SPA default page middleware could not return the default page ‘/index.html’ because it was not found, and no other middleware handled the request.
Your application is running in Production mode, so make sure it has been published, or that you have built your SPA manually. Alternatively you may wish to switch to the Development environment.
Do I have to do something special to make sure these my angular pages are published?
Hi Bob,
you could try the following. Create an Angular 5 .net core app via the template from VS.
There is a section in the csproj called , copy and paste that to your A6 csproj and try to publish.
The section name is ‘Target Name=”PublishRunWebpack”‘. This somehow got lost in my last comment.
Thanks. That worked great.
The first tutorial had “Here, the angular app name must be same as your project name.” but this one is different with “ng new ClientApp”. If you do a “ng new Angular6SPAApp” as per first tutorial and use “c.RootPath = “ClientApp/dist”;” code from second tutorial you get “directory is invalid” npm exception.
My previous comment should have been paired with this change to angular.json:
“options”: {
“outputPath”: “wwwroot”,
That way, when I mentioned the following, the Angular “ng build” process will put the files where the Visual Studio 2017 Publish command will expect them to reside, which will result in a successful deployment (to Azure Web Services, for example).
// In production, the Angular files will be served from this directory
services.AddSpaStaticFiles(configuration =>
{
configuration.RootPath = “wwwroot”;
});
Correct me if I misunderstand, but instead of pointing to ClientApp/dist, shouldn’t the ConfigureServices method of Startup.cs point to wwwroot? If you publish the application to Azure Web Services, isn’t wwwroot the ONLY place it will look for files?
// In production, the Angular files will be served from this directory
services.AddSpaStaticFiles(configuration =>
{
configuration.RootPath = “wwwroot”;
});
Hi people,
I have a big problem with tha implementation.
With that crazy arquitecture, is dificult to understant how ASP.NET core is using Angular.
Now iam trying to call an API from the Angular inside de ASP.NET core, but, I got this error:
“Response to preflight request doesn’t pass access control check: No ‘Access-Control-Allow-Origin’ header is present on the requested resource. Origin ‘https://localhost:5001’ is therefore not allowed access.”
(I use wappalyzer and it says i’am usin Node.js with node.expres.js??????????????????????????????????????????????????)
I already defined the httpoption, new httpHeaders inside Angular like:
const httpOptions = {
headers: new HttpHeaders({
‘Content-Type’: ‘application/json’,
‘Access-Control-Allow-Origin’:’*’
})
.
.
.
and the same in my services at startup.cs like:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
services.AddSpaStaticFiles(c =>
{
c.RootPath = “ClientApp/dist”;
});
services.AddTransient(_ => new AppDb(Configuration[“ConnectionStrings:DefaultConnection”]));
// ********************
// Setup CORS
// ********************
var corsBuilder = new CorsPolicyBuilder();
corsBuilder.AllowAnyHeader();
corsBuilder.AllowAnyMethod();
corsBuilder.AllowAnyOrigin(); // For anyone access.
corsBuilder.WithOrigins(“https://localhost:5001”); // for a specific url. Don’t add a forward slash on the end!
corsBuilder.AllowCredentials();
services.AddCors(options =>
{
options.AddPolicy(“SiteCorsPolicy”, corsBuilder.Build());
});
}
Can any one generate a grat post to explain explicitly how Angular is running inside ASP.Net core?????
I think all new ASP.Net Core users will apreciatte it a lot
Thank you a lot in advance
Noop
Thank you, awesome work. I got you a coffee.
Publishing however does not handle the web content properly. (the dist folder is missing from the output)
I guess this is because we did not use the angular template.
Could you write an article or enhance this one how to publis everything?
Stefan,
Thank you for showing your support. Let me take a look at the publishing issue and will update either this post or create a new post very soon.
Hi bro,,,
is there any update at the publishing issue >>>
Hi Ethan,
were you able to resolve the publishing issue?
Awesome work. I got you a coffe.
Unfortunatelly when I try to publish my application the web content is missing.
I guess this is because we don’t use the angular SPA template where the dist files are copied.
Could you create a post where this is described or enhance this one?
Hi. Thanks for great post. But How can I implement SASS with the new Angular 6 core structure which webpack.config file has gone.
When generating the ClientApp, use the following to have the default styling file extension changed from .css to .scss. Tested with Angular-CLI 6.0.8 and Angular 6.0.4.
ng new ClientApp –style=scss
Hi Friends ,
My Project running fine where i Publish code and host on IIS is not running because index page is not getting
Thanks for the great post. However, the app is failed to render the new changes. I mean, when I change the TS or HTML files, it should render automatically. How can I do this?
Try to run the app from angular cli using “ng serve” command.
If I run the app using “ng serve”, the app would be running on port number http://localhost:4200/. The problem is, I can’t access web API with this host. Please advise.
Yes, I see. You need to enable HMR in the SPA templates. Read ALEXANDER comments in the same post. He has done the same.
The angular app at http://localhost:4200/ got failed to access the VS web project, says http://localhost:3737/, is because of the browser’s same origin policy. It is a normal safety measure. Web browser treats the difference in port numbers as two different domain/origin and thus making ajax call from :4200 to :3737 (and any other port numbers) will always fail.
But there is way to “bypass” this safety measure (during development only!) — simply by adding a HTTP header “Access-Control-Allow-Origin: http://localhost:4200” at all (ajax) response returned by server.
We can achieved it by adding below code at “Startup.cs”:
#code#
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment()) // only add such header at dev env!
{
// To favour development and testing with angular app. Remember NOT to add a trailing slash
// Ref: https://docs.microsoft.com/en-us/aspnet/core/security/cors?view=aspnetcore-2.1#enabling-cors-with-middleware
app.UseCors(builder =>
builder.WithOrigins(“http://localhost:4200”) // 4200 is the port number consumed by angular app from “ng serve”
);
}
}
#/code#
Thank you, very useful.
Only that I’ve made this project and thought It were fine to add a support real HMR (now there’s reload a whole page “on save” and this is not very good).
After a short search I’ve found the page:
https://github.com/angular/angular-cli/wiki/stories-configure-hmr
But here there are some moments that need to be corrected.
1. In angular.js:
“build”: {
….
“configurations”: {
“production”: {
………………
},
“hmr”: {
“fileReplacements”: [
{
“replace”: “src/environments/environment.ts”,
“with”: “src/environments/environment.hmr.ts”
}
]
}
}
}
….
“serve”: {
“configurations”: {
“production”: {
………
}
“hmr”: {
“hmr”: true,
“browserTarget”: “[APP_NAME]:build:hmr” (правильно, [APP_NAME] заменить на имя своего приложения)
}
}
},
2. In the file ClientApp/src/tsconfig.app.json delete the line:
“types”: []
3. Int the file ClientApp/src/main.ts replace the line
hmrBootstrap( module, bootstrap );
by
hmrBootstrap( module, bootstrap );
4. And, of course, in Startup.cs replace a name of the called script:
spa.UseAngularCliServer( npmScript: “hmr” );
I would be happy if I could help someone.
(правильно, [APP_NAME] заменить на имя своего приложения) – ignore.
In 3) in the second “hmrBootstrap( module, bootstrap )”; just ytbefore “bootsrap” it is necessary to put expression !any!, where instead of the sign “!” use angle brackets (they disappeared in my previous comment on the dispatch).
Alexander, Is it possible for you to share this code on github? I would like to create a post out of it.
thank you for sharing 😉
very helpful!
Awesome post! Exactly what I was looking for and very simple to follow. Thank you.
Great post! This is essentially the “V2” template, but nice to see it broken down in this manner.
When I build the application I got the below error
Severity Code Description Project File Line Suppression State
Error CS1061 ‘ISpaBuilder’ does not contain a definition for ‘UseAngularCliServer’ and no extension method ‘UseAngularCliServer’ accepting a first argument of type ‘ISpaBuilder’ could be found (are you missing a using directive or an assembly reference?) Web2018 C:Web2018Web2018Startup.cs 63 Active
I got error when I write below code
spa.UseAngularCliServer(npmScript: “start”);
I already install the “Install-Package Microsoft.AspNetCore.SpaServices -Version 2.1.0”
Can you please tell me what was problem.
Include following line in your Startup.cs,
using Microsoft.AspNetCore.SpaServices.AngularCli;
Also install Microsoft.AspNetCore.SpaServices.Extension nuget package, if not installed. Although it should be installed.
Thanks It’s build now :-).
Now when I run the application through IISExpress (F5) in Visual Studio 2017 it give error
https://pasteboard.co/HnHhb13.png
NPM (Node package manager) is not installed. Please check the post for list of installation required. If it is installed, then make sure the entry is there in Environment PATH variables.
I am getting the same error, and I have NPM installed and it is in in path.
Bob,
There is something wrong either with NPM or Angular CLI. Make sure both are installed and available in PATH variables. Angular CLI should be installed globally as Visual Studio will start the angular CLI server to run the app.
What is weird is this worked fine until I added the the Spa stuff. I had followed your last tutorial and everything ran fine. Once I added the SPA items from this blog, it blew up. I will try re-installing Angular. I have already re-installed Node but that did not help.
That’s weird. As I followed the same steps and it worked fine. There is one change compare to last tutorial. The angular app needs to be created at ASP.NET Core project root folder (where the project’s .csproj file is present).