How to use Angular CLI with Visual Studio 2017

How to use Angular CLI with Visual Studio 2017

This article will show how to create an Angular web application using Angular CLI to manage the build process and dependency management, using WebPack. The latest version of Angular at this time of this article is 4.0.1. Using this technique you can integrate Angular into a new or existing Asp.Net MVC web application. Your application can either be a full Angular SPA-only application or it may also include some MVC routed pages. This sample will also use Asp.Net MVC Web API to create the REST layer data endpoints called by the Angular typescript services. All of this will be hosted in a single web application that you can deploy to any IIS-based server or to an Azure cloud service role.

These directions have also been verified with Visual Studio 2015 Community Edition.

Why have both Angular and Asp.Net MVC in one application?

You can certainly have an all Angular application, and there is nothing wrong with that. But there are plenty of reasons to have both in a single application. Many websites have a number of static pages and those pages may have common headers and footers easily managed with master layouts in MVC. Those layouts may have data rendered on the server, or cached in a CDN. Some projects manage this by putting the Angular app in a virtual directory sub-path of the primary domain.

Maybe you want a way to manage multiple separate SPA applications under one deployed application. You can do this by combining multiple Angular CLI SPA apps into one MVC app at build time and then deploying the combined app as one.

The most common scenario may be that you already have an Asp.Net MVC web application now, and you want to phase in an Angular CLI SPA application over time, moving a page at a time from an MVC route into the SPA application.

You can also easily accomplish this using SystemJS by following my previous Angular article series.

The Problems with SystemJS

In my last Angular series the sample project used SystemJS in an Asp.Net MVC web application project. The series discovered some annoyances with the structure of SystemJS. First you had to remember to add new dependencies to the bootrapping code in the main cshtml razor view hosting your Angular app; and second your templateUrl and CssUrl file references in components had to take into account where the view template would be at run-time (dist folder) instead of where its relative location during development.

Differences in Angular CLI

Angular CLI (command line interface) manages your Angular code build for development and the AOT (ahead of time) production build, manages registering all dependencies on the page automatically, helps you follow organizational guidelines with the generator CLI commands and a lot more.

What is the downside? Its a great tool for WebStorm or VS Code, but its not meant to be part of an Asp.Net MVC web application. We need to do a couple things to integrate it into the Visual Studio ecosystem. Hopefully this will be smoother in future versions, and for now this article will show you how to do the initial setup to smooth out this hopefully temporary rough edge.

The root of the problem is that Angular CLI wants to handle the entire build process and serving the app inside Express web server when in development mode. The Visual Studio ‘run’ button also does this for web applications and class libraries needed by your web application. CLI cannot manage the build of your server side C#.Net code. Visual Studio runs in IIS Express or full IIS, while Angular development mode wants to run in Express.

Integrating Visual Studio MVC web apps and Angular CLI apps

As noted earlier, Angular and Visual Studio both want to manage their own build process. So our solution is to let them, and combine the results mid-way. We’re going to build the Angular CLI project first, then copy the few build output files into a folder of the Asp.Net MVC web application and then Visual Studio can perform it’s build as usual and then run the project. Simple.

Creating the project structures

Ok, so the concept is easy enough, but now lets get into the detail of what kind of projects you should create. If you already have an Asp.Net MVC application, you can skip ahead to the creation of the Angular CLI project.

Create your solution and class library

My first step is to create a new solution with a class library project. Be sure to keep “Create directory for solution” checked. Add it to Git source control, and build the business layer. I’m not going to walk through this step in any more detail, but you can check out the sample project.

Create your Asp.Net MVC web application

This step will be nothing new for anyone currently developing with Asp.Net MVC. You should add a new project to the solution, I recommend template “Asp.Net Web Application (.Net Framework)”.

On the second step of the project wizard, select your type of authentication you want included with the app, and also check the box to add “Web API”, since in this example that will serve as the REST service used by the Angular CLI project.

Creating your Angular CLI project

This step uses the CLI — command line interface. The best way I’ve found to do this is via a normal windows command prompt, NOT the Visual Studio 2017 command prompt.” The Visual Studio variety does not respect your system PATH setting and thus ‘npm’ won’t be available to you.

Start by creating a Visual Studio project to hold the CLI generated project. Use ‘Add Project’ and pick ‘Asp.Net Web Application’ just like the MVC project first step, but on step 2 just select the ‘Empty’ template (not MVC or Web API). We will fill this project folder with the CLI generated content. The sample project is named ‘candor-sample-ng-cli’.

Prerequisite alert: For the next step you need to download and install NodeJS / NPM if you don’t have it already. Version 6.9 or later should work, but I have the latest Node 6.10.2 / NPM 3.10.10 at the time of this writing. I used the Windows MSI installer. Go get it here:
https://nodejs.org/en/download/

Once Node is installed, make sure you have the Angular CLI installed globally. This is done once per developer machine, not a per project step. I ran it from my solution folder, but it could be from any folder. After running this command you’ll see NPM output with dependency packages being installed

C:\Users\you\Documents\GitHub\sample-ng2-mvc> npm install -g @angular/cli@latest 

Verify the CLI is installed by checking the CLI help command. After running this command you’ll see all the types of code you can generate and the options for each one.

C:\Users\you\Documents\GitHub\sample-ng2-mvc> ng --help
...
ng new<options...>
  Creates a new directory and a new Angular app.
  --dry-run (Boolean) (Default: false) Run through without making any changes.
    aliases: -d, --dryRun
  --verbose (Boolean) (Default: false) Adds more details to output logging.
    aliases: -v, --verbose
  --link-cli (Boolean) (Default: false) Automatically link the `@angular/cli` package.
    aliases: -lc, --linkCli
  --skip-install (Boolean) (Default: false) Skip installing packages.
    aliases: -si, --skipInstall
  --skip-git (Boolean) (Default: false) Skip initializing a git repository.
    aliases: -sg, --skipGit
  --skip-tests (Boolean) (Default: false) Skip creating spec files.
    aliases: -st, --skipTests
  --skip-commit (Boolean) (Default: false) Skip committing the first commit to git.
    aliases: -sc, --skipCommit
  --directory (String) The directory name to create the app in.
    aliases: -dir <value>, --directory <value>
  --source-dir (String) (Default: src) The name of the source directory.
    aliases: -sd <value>, --sourceDir <value>
  --style (String) (Default: css) The style file default extension.
    aliases: --style <value>
  --prefix (String) (Default: app) The prefix to use for all component selectors.
    aliases: -p <value>, --prefix <value>
  --routing (Boolean) (Default: false) Generate a routing module.
    aliases: --routing
  --inline-style (Boolean) (Default: false) Should have an inline style.
    aliases: -is, --inlineStyle
  --inline-template (Boolean) (Default: false) Should have an inline template.
    aliases: -it, --inlineTemplate
... rest clipped for brevity

Now to create a new CLI project in your solution, run the ‘new’ command. This will fill in the Visual Studio generated project folder with the CLI structure.

C:\Users\you\Documents\GitHub\sample-ng2-mvc>ng new candor-sample-ng-cli --routing --skip-git --directory candor-sample-ng-cli

Since our project solution is already under Git source control, we can use the skip-git flag to tell the CLI not to generate a new Git repo for the project. The directory option will generate the CLI project in our existing Visual Studio project folder since the name of that folder is supplied.

The output shows the files created relative to the output directory specified.

installing ng
  create .editorconfig
  create README.md
  create src\app\app-routing.module.ts
  create src\app\app.component.css
  create src\app\app.component.html
  create src\app\app.component.spec.ts
  create src\app\app.component.ts
  create src\app\app.module.ts
  create src\assets\.gitkeep
  create src\environments\environment.prod.ts
  create src\environments\environment.ts
  create src\favicon.ico
  create src\index.html
  create src\main.ts
  create src\polyfills.ts
  create src\styles.css
  create src\test.ts
  create src\tsconfig.app.json
  create src\tsconfig.spec.json
  create src\typings.d.ts
  create .angular-cli.json
  create e2e\app.e2e-spec.ts
  create e2e\app.po.ts
  create e2e\tsconfig.e2e.json
  create karma.conf.js
  create package.json
  create protractor.conf.js
  create tsconfig.json
  create tslint.json
Installing packages for tooling via npm.
Installed packages for tooling via npm.
Project 'candor-sample-ng-cli' successfully created.

The last 2 lines of the output take some time since they are downloading lots of packages over the internet, so just be patient and wait for it to complete. Your experience may vary if you have ‘Yarn’ package manager installed. If you wish the CLI to install packages via Yarn and configure Yarn at project setup, then you need to tell the CLI to do so by default on creation of new packages. If you wish to do that the command is as follows (to be run before ng new), and Yarn must be installed first.

C:\Users\you\Documents\GitHub\sample-ng2-mvc>ng set --global packageManager=yarn

Now you can switch over to Visual studio and include all of these generated files into the project. But do not include the node_modules folder, since those are restored by other developers using the ‘npm install’ command or by using Visual Studio’s package restore menu item.

Setup the build process

Build and Test the ng project

You should now ‘serve’ the CLI project and make sure it runs.

C:\Users\you\Documents\GitHub\sample-ng2-mvc>cd candor-sample-ng-cli

C:\Users\you\Documents\GitHub\sample-ng2-mvc\candor-sample-ng-cli>ng serve
** NG Live Development Server is running on http://localhost:4200 **
Hash: bec42c6bef317950e4ef
Time: 8356ms
chunk    {0} polyfills.bundle.js, polyfills.bundle.js.map (polyfills) 158 kB {4} [initial] [rendered]
chunk    {1} main.bundle.js, main.bundle.js.map (main) 4.92 kB {3} [initial] [rendered]
chunk    {2} styles.bundle.js, styles.bundle.js.map (styles) 9.77 kB {4} [initial] [rendered]
chunk    {3} vendor.bundle.js, vendor.bundle.js.map (vendor) 2.64 MB [initial] [rendered]
chunk    {4} inline.bundle.js, inline.bundle.js.map (inline) 0 bytes [entry] [rendered]
webpack: Compiled successfully.

As the instructions note, the app is now running and you can open it at http://localhost:4200/. Open it and you will see “Loading…” and then “app works.” To quit the server to enter further commands, press CTRL-C. You’ll be asked if you want to terminate batch job. Type ‘Y’ and press enter to confirm.

The serve command does not generate a build, rather you need to run the build command to do this.

C:\Users\you\Documents\GitHub\sample-ng2-mvc\candor-sample-ng-cli>ng build
Hash: 7125a86fd8201d4b0de4
Time: 7496ms
chunk    {0} polyfills.bundle.js, polyfills.bundle.js.map (polyfills) 158 kB {4} [initial] [rendered]
chunk    {1} main.bundle.js, main.bundle.js.map (main) 4.9 kB {3} [initial] [rendered]
chunk    {2} styles.bundle.js, styles.bundle.js.map (styles) 9.77 kB {4} [initial] [rendered]
chunk    {3} vendor.bundle.js, vendor.bundle.js.map (vendor) 2.34 MB [initial] [rendered]
chunk    {4} inline.bundle.js, inline.bundle.js.map (inline) 0 bytes [entry] [rendered]

You will now have a dist folder in your project containing an index.html and a few script files with source maps. This is all you need to run the application. Don’t check in the ‘dist’ folder into source control and don’t include it in the Visual Studio project.

Build CLI output in the MVC web application – Option 1

Now that we have the CLI project, the next step is to get the build output and package it into the main MVC application.

Open your project properties to the ‘Build Events’ tab. For the “Pre-build event command line” enter an xcopy command.

xcopy /I /E /Y "$(SolutionDir)candor-sample-ng-cli\dist" "$(ProjectDir)Scripts\NgApp" 

This is a good start, but it only copies files, and doesn’t rebuild the Angular CLI project each time. Let’s add the ‘ng build’ command before the copy, and some echo statements so we know what happened afterwards.

echo "cd $(SolutionDir)candor-sample-ng-cli" &&^
cd "$(SolutionDir)candor-sample-ng-cli" &&^
echo "building candor-sample-ng-cli" &&^
ng build &&^
echo 'copy files' &&^
xcopy /I /E /Y "$(SolutionDir)candor-sample-ng-cli\dist" "$(ProjectDir)Scripts\NgApp"

This will produce output in the output window of Visual Studio each time you build the project. To be clear, this build script goes in the build process of the MVC web project and not the CLI container MVC app. This makes more sense since that is the web application to run in Visual Studio.

Note, if you have an error here about not finding the path specified, you may have your solution (.sln) file in the wrong location. In the sample project I had to move the solution file to the root, out of the Candor.Sample\ directory in order for this script to work. Normally solution files are at the root below all the project folders and this will work fine. Be sure when creating a brand new project that you keep “Create directory for solution” checked.

1>------ Build started: Project: Candor.Sample.MvcWeb, Configuration: Debug Any CPU ------
1>  "cd C:\Users\mlang\Documents\GitHub\sample-ng2-mvc\candor-sample-ng-cli" 
1>  "building candor-sample-ng-cli" 
1>  Hash: [1m7125a86fd8201d4b0de4[39m[22m
1>  Time: [1m7325[39m[22mms
1>  chunk    {[1m[33m0[39m[22m} [1m[32mpolyfills.bundle.js, polyfills.bundle.js.map[39m[22m (polyfills) 158 kB {[1m[33m4[39m[22m}[1m[33m [initial][39m[22m[1m[32m [rendered][39m[22m
1>  chunk    {[1m[33m1[39m[22m} [1m[32mmain.bundle.js, main.bundle.js.map[39m[22m (main) 4.9 kB {[1m[33m3[39m[22m}[1m[33m [initial][39m[22m[1m[32m [rendered][39m[22m
1>  chunk    {[1m[33m2[39m[22m} [1m[32mstyles.bundle.js, styles.bundle.js.map[39m[22m (styles) 9.77 kB {[1m[33m4[39m[22m}[1m[33m [initial][39m[22m[1m[32m [rendered][39m[22m
1>  chunk    {[1m[33m3[39m[22m} [1m[32mvendor.bundle.js, vendor.bundle.js.map[39m[22m (vendor) 2.34 MB[1m[33m [initial][39m[22m[1m[32m [rendered][39m[22m
1>  chunk    {[1m[33m4[39m[22m} [1m[32minline.bundle.js, inline.bundle.js.map[39m[22m (inline) 0 bytes[1m[33m [entry][39m[22m[1m[32m [rendered][39m[22m
1>  'copy files' 
1>  C:\Users\mlang\Documents\GitHub\sample-ng2-mvc\candor-sample-ng-cli\dist\favicon.ico
1>  C:\Users\mlang\Documents\GitHub\sample-ng2-mvc\candor-sample-ng-cli\dist\index.html
1>  C:\Users\mlang\Documents\GitHub\sample-ng2-mvc\candor-sample-ng-cli\dist\inline.bundle.js
1>  C:\Users\mlang\Documents\GitHub\sample-ng2-mvc\candor-sample-ng-cli\dist\inline.bundle.js.map
1>  C:\Users\mlang\Documents\GitHub\sample-ng2-mvc\candor-sample-ng-cli\dist\main.bundle.js
1>  C:\Users\mlang\Documents\GitHub\sample-ng2-mvc\candor-sample-ng-cli\dist\main.bundle.js.map
1>  C:\Users\mlang\Documents\GitHub\sample-ng2-mvc\candor-sample-ng-cli\dist\polyfills.bundle.js
1>  C:\Users\mlang\Documents\GitHub\sample-ng2-mvc\candor-sample-ng-cli\dist\polyfills.bundle.js.map
1>  C:\Users\mlang\Documents\GitHub\sample-ng2-mvc\candor-sample-ng-cli\dist\styles.bundle.js
1>  C:\Users\mlang\Documents\GitHub\sample-ng2-mvc\candor-sample-ng-cli\dist\styles.bundle.js.map
1>  C:\Users\mlang\Documents\GitHub\sample-ng2-mvc\candor-sample-ng-cli\dist\vendor.bundle.js
1>  C:\Users\mlang\Documents\GitHub\sample-ng2-mvc\candor-sample-ng-cli\dist\vendor.bundle.js.map
1>  12 File(s) copied
1>    0% compiling 10% building modules 0/1 modules 1 active ...-mvc\candor-sample-ng-cli\src\main.ts ...            
1>  Candor.Sample.MvcWeb -> C:\Users\mlang\Documents\GitHub\sample-ng2-mvc\Candor.Sample.MvcWeb\bin\Candor.Sample.MvcWeb.dll
========== Build: 1 succeeded, 0 failed, 1 up-to-date, 0 skipped ==========
Build CLI output and copy into the web application – Option 2

–Update May 3– A problem with xcopy in the MVC web application is that any changes to the CLI project while the MVC project is running won’t be reflected in the MVC project. Depending on your team workflow this may or may not be a problem.

You can move the xcopy command to the CLI project in the packages.json file. Create a new task for copy, and another to distribute a build to the MVC app.

  "name": "candor-sample-ng-cli",
  "version": "0.0.0",
  "license": "MIT",
  "scripts": {
    "ng": "ng",
    "start": "ng serve",
    "build": "ng build",
    "test": "ng test",
    "lint": "ng lint",
    "e2e": "ng e2e",
    "copy": "xcopy \"dist\" \"..\\Candor.Sample.MvcWeb\\Scripts\\NgApp\" /i /s /r /y /c",
    "dist": "npm run build && npm run copy"
  },

You can run this on the command line as:

C:\Users\you\Documents\GitHub\sample-ng2-mvc>npm run dist

> candor-sample-ng-cli@0.0.0 dist C:\Users\mlang\Documents\GitHub\sample-ng2-mvc\candor-sample-ng-cli
> npm run build && npm run copy


> candor-sample-ng-cli@0.0.0 build C:\Users\mlang\Documents\GitHub\sample-ng2-mvc\candor-sample-ng-cli
> ng build

Hash: ba16068dcc4018b1b749
Time: 13856ms
chunk    {0} polyfills.bundle.js, polyfills.bundle.js.map (polyfills) 158 kB {4} [initial] [rendered]
chunk    {1} main.bundle.js, main.bundle.js.map (main) 27 kB {3} [initial] [rendered]
chunk    {2} styles.bundle.js, styles.bundle.js.map (styles) 60.1 kB {4} [initial] [rendered]
chunk    {3} vendor.bundle.js, vendor.bundle.js.map (vendor) 3.55 MB [initial] [rendered]
chunk    {4} inline.bundle.js, inline.bundle.js.map (inline) 0 bytes [entry] [rendered]

> candor-sample-ng-cli@0.0.0 copy C:\Users\mlang\Documents\GitHub\sample-ng2-mvc\candor-sample-ng-cli
> xcopy "dist" "..\Candor.Sample.MvcWeb\Scripts\NgApp" /i /s /r /y /c

dist\favicon.ico
dist\index.html
dist\inline.bundle.js
dist\inline.bundle.js.map
dist\main.bundle.js
dist\main.bundle.js.map
dist\polyfills.bundle.js
dist\polyfills.bundle.js.map
dist\styles.bundle.js
dist\styles.bundle.js.map
dist\vendor.bundle.js
dist\vendor.bundle.js.map
dist\assets\logo.png
13 File(s) copied

This is much faster than stopping Visual Studio, rebuilding and then run again and waiting for the same build and xcopy time. Its not instantaneous, since it takes a little time for the CLI build to run.

Use the copied CLI application on a page

Now that the CLI build is available in the MVC web application, you can now reference it on a page. Lets do this by creating a new MVC view and controller action. The view is a copy of the generated index.html with a small tweak to file paths.

// /Controllers/HomeController.cs
//Your typical default HomeController.cs with attribute routing and a few new routes
[RoutePrefix("")]
public class HomeController : Controller
{
    [Route("")]
    public ActionResult Index()
    {
        return View("NgApp");
    }
    [Route("trip")]
    [Route("location")]
    [Route("person")]
    public ActionResult AppBookmarkableRoutes()
    {
        return View("NgApp");
    }

    [Route("about")]
    public ActionResult About()
    {
        ViewBag.Message = "Your application description page.";

        return View();
    }

    [Route("contact")]
    public ActionResult Contact()
    {
        ViewBag.Message = "Your contact page.";

        return View();
    }
}

The index route and the new routes to be used in the Angular app all reference the same new view to render the Angular app. If your routing isn’t working, check that your RouteConfig has enabled attribute routing.

// /AppStart/RouteConfig.cs
public static void RegisterRoutes(RouteCollection routes)
{
    routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

    routes.MapMvcAttributeRoutes();

    routes.MapRoute(
        name: "Default",
        url: "{controller}/{action}/{id}",
        defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
    );
}

The new view file is a copy of the index.html with only the paths of the script files changed to reflect where we copied them.

<!-- /Views/Home/NgApp.cshtml -->
{
    Layout = null;
}
<!doctype html>
<html>
<head>
    <meta charset="utf-8">
    <title>CandorSampleNgCli</title>
    <base href="/">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="icon" type="image/x-icon" href="favicon.ico">
</head>
<body>
    <app-root>Loading...</app-root>
    <script type="text/javascript" src="~/Scripts/NgApp/inline.bundle.js"></script>
    <script type="text/javascript" src="~/Scripts/NgApp/polyfills.bundle.js"></script>
    <script type="text/javascript" src="~/Scripts/NgApp/styles.bundle.js"></script>
    <script type="text/javascript" src="~/Scripts/NgApp/vendor.bundle.js"></script>
    <script type="text/javascript" src="~/Scripts/NgApp/main.bundle.js"></script>
</body>
</html>

You should be able to run the MVC application now and see your app.



Moving forward

The app content and features still need to be developed. But you can now edit your app in the CLI project using Visual Studio and press the ‘Run’ button in VS and see your app run. If you prefer you can also run ‘ng serve’ on the command line to run the CLI project directly such as for debugging any routing issues.

The biggest issue remaining would be creation of the production build. When Angular CLI builds for production it doesn’t generate as many script files and it embeds some more content in the index.html file. Instead of hand-jamming the production bits into place when you deploy, you may want to update the script that copies the index.html contents into the NgApp.cshtml file, or maybe just copy the files into the root folder of the MVC web app and then the contents don’t need to be modified when moving into the NGApp.cshtml.

See the followup to this project, How To Create Dynamic Menu And Page Title With Angular Material and CLI

How to create dynamic menu and page title with Angular Material and CLI

References

Full source of the Sample project:
https://github.com/michael-lang/sample-ng2-mvc/

Angular CLI
https://github.com/angular/angular-cli

Post/Pre build events in Visual Studio to copy files
http://stackoverflow.com/questions/11001822/copy-files-from-one-project-to-another-using-post-build-event-vs2010

Alternative: “Running Multiple Websites in a Windows Azure Web Role”
http://www.wadewegner.com/2011/02/running-multiple-websites-in-a-windows-azure-web-role/

Prerequisite: Node Installation
https://nodejs.org/en/download/

About the Author

Michael Lang

Co-Founder and CTO of Watchdog Creative, business development, technology vision, and more since 2013, Developer, and Mentor since 1999. See the about page for more details.

56 Comments

  • By Saeid Reply

    It’s possible to change the `dist` folder location in the `.angular-cli.json` file that will eliminate the need of xcopy.

    • Michael Lang By Michael Lang Reply

      Good idea, Have you tried this?

      • By Guilhaume Bordiau Reply

        I tried. Way easier, no need for the build script.

        • By Sarthak Reply

          Hello, Guilhaume
          Can you please provide the source code for angular 2 CLI with MVC project where we get rid of xcopy command.

  • By Gavin Raine Reply

    Great article and really helpful. Thanks Michael !

    At first, I thought I had failed to get this right somehow, but it worked perfectly once I stopped trying to use Internet Explorer. (For reference – I saw the error “Unable to get property ‘apply’ of undefined or null reference”.)

    It looks this is an issue that several people have had with Angular CLI & IE 11, so I’m off to look at “polyfills.ts” now.

    • Michael Lang By Michael Lang Reply

      I’m glad to know it helps!

  • By Sevensnake Reply

    Thanks that would be great. I did not know you can have mvc and Angular2 application as 2 projects and you can still work your application. Does this affect angular2 routing using the Candor.Sample.MvcWeb.?

    • Michael Lang By Michael Lang Reply

      Angular routing using ui-router doesn’t post back to your server to load routed components, so once a SPA application is up and you are navigating between routes in the app, MVC on the server won’t interfere. If you load a url from the SPA page (not a route), then it will get it from the server.

      If you have bookmarkable routes that should all load the same Angular app, add routes to the controller to make them all load that SPA view. I did this in the HomeController when I set multiple Route attributes on the AppBookmarkableRoutes() controller action.

      I will be fleshing out this sample app further and will write more articles as it progresses.

  • By Robert Bernstein Reply

    This article is excellent! I was able to get my Angular + WebAPI environment working when previous attempts had gone very poorly. Thanks for sharing!

    • Michael Lang By Michael Lang Reply

      That’s great to know this is useful to others! Thanks for sharing your success.

    • By Taylor Waldron Reply

      What did you need to do differently to get it to work with webAPI? Exactly the same but with webAPI instead of mvc?

  • By Matthew Copeland Reply

    The second problem with SystemJS is no longer an issue. They now have a file called systemjs-angular-loader.js which you can include in your SystemJS configuration. It supports component relative templateUrls and styleUrls.

  • By Kshitiz Reply

    Could you provide any suggestion regarding handling lazy module loading feature of angular/routes using this approach?

    • Michael Lang By Michael Lang Reply

      I recognize that may be an issue since the lazy loader may expect those scripts at the ‘baseUrl’ in your html head. I see two scenarios.

      You may have a single CLI project being deployed this way and it may be very large requiring lazy loading for best performance. In this scenario the CLI build (or via xcopy) should go to the root of your MVC project to solve the lazy load issue.

      Or you may have multiple small CLI projects that don’t need lazy loading because they are already small. In this scenario, deploying to a sub-folder won’t be a problem.

  • By Gerald Reply

    This is great! May I know if typescript debugging will work while running in visual studio?

  • By Tony Lugg Reply

    Good article. I’m not using MVC but my main project is a web application with Web API. The problem is, the build process does not take place in the Web API project unless I make a change in that project or explicitly tell it to rebuild, i.e. F5 sees no changes in the Web API project and so the build does not happen, and consequently no pre-build to build and copy my Angular CLI app. Is there any way to hook the build and copy into the debug process?

    • Michael Lang By Michael Lang Reply

      Good point Tony. This brings me to consider when running the MVC app and you make a change in the CLI app and want to see it reflected without stopping and restarting. The answer is to move the copy task into the CLI project. I’ve updated the sample git repo with an updated build.

      Add a couple NPM tasks to package.json

      “copy”: “xcopy \”dist\” \”..\\Candor.Sample.MvcWeb\\Scripts\\NgApp\” /i /s /r /y /c”,
      “dist”: “npm run build && npm run copy”

      Then update visual studio build task to run ‘npm run dist’ instead of the ng build and xcopy. now on run it will always trigger a new build. Also when you change code in the CLI project, then on the command line type “npm run dist” to see the running MVC app update with the changes.

      Note the xcopy command above is enclosed in a string, containing some escape characters. The same command if run in a command prompt would be:
      xcopy “dist” “..\Candor.Sample.MvcWeb\Scripts\NgApp” /i /s /r /y /c
      (run this from within the cli project root folder)

  • By Lance Reply

    Thanks very much for your excellent article. I have it all working but it is painfully slow to build and now I have to do it after each and every change. SystemJs seemed to do it so much faster and also I was able to make changes to the ts and html files while debugging in VS, all that was required was an F5 on the browser. Now each change requires a rebuild (so the new files are in NgApp) which slows down development. Maybe I’m doing something wrong or you have some suggestions to speed up the build.

    • Michael Lang By Michael Lang Reply

      The change in my last comment should speed things up. See above.

      Add/move the copy command to the CLI project. Then you don’t need to stop the project and rebuild to get changes in the CLI project.

      I’ve updated the article to show this option.

      • By Lance Reply

        Thanks for your advice. I made a change based on Saeid’s suggestion above. In the webpack.config.js file I changed the value of path to “path”: path.join(process.cwd(), “../Scripts/NgApp”). This eliminates the need for XCopy which helps a little. I then can run “npm run build” while my VS project is running to get the changes. This is better but the build is still very slow. SystemJS was much faster and I didn’t have to run “npm run build” after every change. Is there any way to speed up the Webpack build?

  • By Daniel McFarland Reply

    Great work, Michael. I haven’t tried your steps yet, but I had mucked around with a different approach and concluded something along the lines of what you’ve written was the best bet. I’ll give it a shot here shortly, but from what I’ve read and what others have posted, it seems like you’ve hit the nail on the head–at least until the ng-cli is enhanced a bit to allow a custom server, among other things.

    • Michael Lang By Michael Lang Reply

      Thanks Daniel. I’m also looking forward to a more integrated experience or interconnectability between Angular CLI and Visual Studio.

  • By FoxMalder133 Reply

    Visual Studio 2017. Angular cli
    After this steps you can run your Asp.net MVC +Angular project also for prod. Because index.html will change links of scripts.

    Structure of steps.
    1.File for change
    2. Description what need to do with file in the first point.
    3. new code example

    1. YourProjectName\.angular-cli.json
    2. to do: change
    3.
    “outDir”: “./Scripts/app/”,
    “deployUrl”: “./Scripts/app/”,

    1. YourProjectName\package.json
    2. to do: Add new code to package.json
    3.
    “scripts”: {
    “ng”: “ng”,
    “start”: “ng serve”,
    “build”: “ng build”,
    “test”: “ng test”,
    “lint”: “ng lint”,
    “e2e”: “ng e2e”,
    “copy”: “xcopy \”Scripts\\app\\index.html\” \”Views\\Home\\NgApp.cshtml\” /i /s /r /y /c”,
    “dist”: “npm run build && npm run copy”,
    “distProd”: “ng build –prod && npm run copy”
    },

    1. YourProjectName\src\index.html
    2. to do: add “layout” as first statement at the beginning. I know this step is looked strange, but it will be dynamicaly View for Asp.net MVC. ))) package.json will copy this file.
    3.
    @{
    Layout = null;
    }

    1. Specify Pre-Build Events
    2. to do: Right click on YourProjectName -> Properties -> build events -> Pre-build event command line box
    And add text
    3.
    if $(ConfigurationName) == Debug (
    npm run dist
    ) ELSE (
    npm run distProd
    )

  • By FoxMalder133 Reply

    I forgot a little step for [1. Specify Pre-Build Events]
    Close and open Visual Studio after changing [Pre-build event command line box]. It’s important for apply changes.

  • By Stephen Reply

    So it looks as though images are not being loaded in the MVC project after the Angular application has been build and copied to the NgApp folder. Images that are used in CSS for example, as background images work fine. Those images are bundled by webpack and the load OK. I’m noticing that images associated with img tags in the html are not bundled and MVC is unable to serve them up. In the sample application {{appName}} the logo.png gives a 404.

    I really like this approach and the image problem so far is my only blocker.

    Thanks!

    Stephen

    • By JP Reply

      Are your images in /src/assets in your CLI project? Is your MVC project expecting content to be served from /Content? If yes and yes, just add another script to your package.json, e.g. “copyAssets”: “xcopy \”.\\src\assets\” \”..\\YourMVCProject\\Content\” /i /s /r /y /c

      I had the same problem—literally if you just copypasta the “copy” script and modify it to copy over images, you’ll be straight!

  • By Houda MEZNI Reply

    Great work, Michael! PlZ i have a problem in routes “http://localhost:4200/home ” it works even i refrech the page but when i move to “http://localhost:16408/home” and i refrech the page i had a problem “Server error in application ‘/’. The resource can not be found” PLZ how i can solve it ??

    • Michael Lang By Michael Lang Reply

      The error in application ‘/’ error message is a generic broad based .Net exception message. There is no single solution to that error.

      localhost:4200 is the default port to access the cli app after running “ng serve” on the command line. None of your MVC app routes will be accessible at that port.

      localhost:16408 (or other 5 digit port) is running your MVC app and the exact port number varies for every project. After following this article, both your MVC app routes and your angular CLI app routes will be accessible, if you’ve told MVC to route the CLI routes to your CLI container view.

      If you are using attribute routing per the article, check the HomeController and make sure it has a route to load the CLI app for all the routes in your CLI app (code sample in article above). Check which MVC controller route loads the “home’ route in your MVC app and put a breakpoint in it to see it get hit.. Whatever code is in the controller action or the view rendered by it is your problem. In my sample, ‘home’ was not a route I sent to the angular app, so it may be unrelated to the angular app.

    • Michael Lang By Michael Lang Reply

      On further review I think you mean ‘home’ is one of your intended angular app routes? If so, you may not have added the AppBookmarkableRoutes() controller action with an attribute route to your angular app. If you added it then a breakpoint in that controller action, if hit, will verify the Mvc routing rules is setup properly. If the breakpoint is hit and the rendered view generates the error, then some step wasn’t followed correctly in setup of the rendered view, or you have a configuration problem in the MVC app.

      If you are starting with an existing angular app and MVC app, there are too many possible problems to diagnose via a comment. Put your existing already working Angular app in a new MVC app with these instructions and it should work. Then you’ll have to backtrack what other problems you have in your existing MVC app.

  • By Justin N Reply

    Excellent article helped a lot! I am confused on one thing though, how is the mvc project able to pick up the component directive in the NgApp cshtml file? When i try to use another directive from a new component i have created it does not show. Do i need to do something with the routing in the home controller?

    • Michael Lang By Michael Lang Reply

      The angular project is built by webpack (angular cli) into a distributable and is then loaded into the page as long as you update the paths per the article.

      The MVC project doesn’t automatically pick up any routes from the Angular project. The MVC app does hand them off to NgApp.cstml via the HomeController.AppBookmarkableRoutes() controller action (see the article sample code). Notice it has an attribute route for every route your Angular app needs, in this app “location”, “trip”, and “person”. So as you add new routes, add an attribute value here to match. If you forget this step, MVC will try to route it within the MVC app and show a 404 if no match is found.

      [Route(“trip”)]
      [Route(“location”)]
      [Route(“person”)]
      public ActionResult AppBookmarkableRoutes()
      {
      return View(“NgApp”);
      }

      • Michael Lang By Michael Lang Reply

        Note, I called this AppBookmarkableRoutes, because other routes not in this list will still work on the Angular page after it is loaded, but on request to the server to load a url not in this list, MVC won’t see a match (and thus a bookmark to it wouldn’t work). But if you load the angular app at a route configured by MVC, then navigate to another route outlet in the Angular app not configured in MVC, it still works since that request isn’t sent to the server.

      • By Justin N Reply

        Thank you for the response, I should have been more clear in my question but this was very good information to know in case i cannot solve my issue. So i understand that your response was for routing done through the angular app, how how to get the mvc side to recognize and not through a 404. I’m trying to do the opposite. I’m working with a preexisting .net mvc project that used angularjs and I am rewriting it to use angular 4. My mentor wants to keep the views as they were, in the mvc project as cshtml under the views folder. What i wanted to do is within those cshtml files use a component directive, , like you did with in the NgApp.cshtml file. I’m beginning to think this is impossible as the NgApp file is the only part talking to the angular components, correct?

        • Michael Lang By Michael Lang Reply

          This project sample uses Angular 4.0.1. Calling Angular 2+ from AngularJs requires use of ngUpgrade. I recommend you learn ngUpgrade from Victor Savkin, previously part of the Angular team. ngUpgrade does not make you rewrite your angularJs code, it lets you run AngularJs inside an Angular2+ app, or run Angular2 components within an AngularJs app.

          https://blog.nrwl.io/ngupgrade-in-depth-436a52298a00

          • By haroon

            Sir i have created both mvc and angular/cli project after that i am totally confused what xcopy and outdir i am not getting anything why don’t u make one video for that,otherwise give me solution for that without xcopy

            here is my folder structure

            solutoion name:
            1)angularcli project
            2)mvc with webapi project

            please help me

          • Michael Lang By Michael Lang

            @haroon I would recommend you start from a fresh project, or inspect the linked working project and find what you have done differently. Otherwise, keep an eye out for my next article in the coming weeks that is an evolution of this process.

  • By Azeem Reply

    Hy
    I m using angular with vs code it is working on localhost:3000 it works when open it works but not working class component plz anybody can help to solve this problem?

  • By Rafael Reply

    Still getting the SystemJS error, ReferenceError: System is not defined when running SampleAngular2Mvc from your git repository https://github.com/michael-lang/sample-ng2-mvc/

  • By Saad Zulfiqar Reply

    Thumbs up. Thanks

  • By Peter Reply

    Nice try and indeed I’m thinking more or less on the same direction also. Thus one thing I would like to point out, in addition to the index.chtml twist, is that if bootstrap is included in the Angular project, the glyphicon will get mess up as they are generated into the dist folder, with other script and/or generated css assuming the relative location of glyphicon font set is at the root directory of the site from the point of view of Angular..

  • By Alex Millan Reply

    Great great post!! One question … I’m trying to deploy the application to IIS. I publish through a folder profile, then move the folder to a server path, generate my application pool with “No managed code”, open the application in the browser but nothing appears. Install the “.NET Core Windows Server Hosting Bundle” but it still does not display anything. What am I missing ??

  • By Rafael Reply

    I ended up with 3 projects, the mocking project (library) the MVC and the AngularCLI. But your repo states 4 projects, how come?

    How to deploy to azure? since both technologies have their own way of building the app

    Thanks in advance,

    • Michael Lang By Michael Lang Reply

      The extra project demonstrates the SystemJs/Angular (non-CLI) option mentioned in some of my other Angular articles. You can ignore it for the sake of this article.

      Alex Millan and Rafael:
      This project doesn’t pick between hosted IIS, Amazon, Azure or any other deployment target. Deploy as you would any other .Net web application project. Neither the Angular front end nor the MVC Web API should change based on how you deploy. Your database calls should be wrapped in a data-layer that will care about the database type used (Oracle, Sql, Azure storage or others), with the connection details abstracted out into the web.config.

  • By Guilherme Reply

    Hi Michael,
    really good tutorial. But I still having a problem.

    I am trying to use this layout ( https://github.com/bonnici/light-bootstrap-dashboard-angularcli ) but Icouldn’t put it to work. It is always having some problems. Could you help me to setup it?

  • By Randal Reply

    @Michael – Wondering if you could instead have everything in the MVC project, and have 2 folders – one for the CLI based code and another the target of the outDir (normally ‘dist’) in angular-cli.json? The only issue with that way is then the CLI folder would get deployed when you publish the mvc app. A way around that would be to manually edit the project file and use the “ExcludeFoldersFromDeployment ” setting see reference: https://stackoverflow.com/questions/6104818/ms-visual-studio-how-to-exclude-certain-project-folders-from-publishing. This is what Im trying now. Also, supposedly this process of using CLI gets a little easier with .net core, but thats next on my list.

    • Michael Lang By Michael Lang Reply

      You can put the folders where you want. They key is that you run ‘ng build’ and use the output in what is deployed, not the source files. This time around I used an MVC project to contain the Angular CLI source files so that I could use Visual Studio to edit the typescript files.

      I am putting together another option now where I use VSCode to edit the Angular CLI based project source, along with mocks to simulate a backend, then just import that build output to the Visual Studio MVC project for inclusion and usage of the Web API endpoints as an end to end integration. Stay tuned to future posts for more detail.

  • By Michael Nysteen Reply

    I’ve looked through the existing comments but didn’t see anyone suggesting the following approach (if I simply overlooked it, then I apologize for reposting).
    I have setup the two projects as explained, but instead of using an “npm run dist” command everytime I change files in the Angular project I instead simply set the “outDir” of the .angular-cli.json file to “../MyMVCApp/Scripts/NgApp”. This way whenever “ng build” runs it simply builds directly into my MVC app, eliminating the need for xcopy. Also, it lets me use “ng build –watch” so it automatically builds the newest files into my MVC app whenver I change the source files (without running “npm run dist”) without having to set up gulp-watch or similar.

    I have only tested it in a simple dummy example. Have anyone else tried it on a larger scale or have some feedback as to whether there might be some caveats i have overlooked when developing a real app that goes through CI and eventually goes to production?

    • Michael Lang By Michael Lang Reply

      Sounds like a good idea.

  • By parth patel Reply

    What if I want to use NPM package manager instead of Yarn package manager?

    • Michael Lang By Michael Lang Reply

      You can use NPM or yarn, both work equally well. This article only mentions NPM, but the sample repo does also configure yarn.

  • By Parth Patel Reply

    I have tried your demo but in this, If I make any changes in Angular file then my browser is not auto refresh and also the changes are not reflected in the browser when I manually refresh it and one more question, Can I call MVC controller from angular service?

  • By houda Reply

    Hello, Thank you for the awesome tutorial !! I want to ask sometimes this function didn’t work ( “copy”: “xcopy \”dist\” \”..\\Candor.Sample.MvcWeb\\Scripts\\NgApp\” /i /s /r /y /c”,
    “dist”: “npm run build && npm run copy”)
    At the beginning, it works very well and then after 2 weeks for example if I change something in Angular project and copy to Asp.net project I can’t find what I changed.
    Do you any idea, please

Leave a Comment

Your email address will not be published.