Get Going with the Angular and ASP.NET CLIs

Angular and ASP.NET CLIs Header

When learning Angular and ASP.NET Core, it's oftentimes helpful to start with an empty project. However, there are some integration steps required to have Angular and ASP.NET Core work together.

This tutorial shows how to create an Angular, ASP.NET Core project using the command line interfaces (CLI) for both Angular and .NET Core. It demonstrates how to host and debug an ASP.NET Core project in Visual Studio Code.

Prerequisites

The following must be installed to complete this tutorial:

Note: The commands in this article work on a Windows machine with PowerShell. Other platforms may require modifications.

Configure Angular

  1. Install Angular CLI globally to make the commands available in any directory

    npm install -g @angular/cli
    
  2. Use the newly installed Angular CLI to scaffold a new project. This command creates a new directory to house your application. It may take a couple minutes to complete because the utility installs the npm packages for Angular, the build, and testing.

    ng new my-directory
    
  3. Navigate into the repository and verify the application by running it with the Angular CLI. Use the --open argument to open the application in the default browser.

    cd my-directory
    ng serve --open
    

    The Angular CLI runs the application on a Node.js web server and you will see the stub page.

    Angular CLI Stub Page

  4. Terminate the running web server with the key combination Ctrl + C to continue in the command window.

Configure ASP.NET Core

  1. In the repository, use the .NET Core CLI to scaffold an empty ASP.NET Core project.

    dotnet new web
    

    Note: The dotnet new command scaffolds the .NET project within the current working directory whereas the Angular CLI command ng new creates a new directory to contain the generated Angular code.

  2. Install the ASP.NET static file support middleware.

    dotnet add package Microsoft.AspNetCore.StaticFiles
    
  3. Open the repository in Visual Studio Code

    code .
    
  4. Visual Studio Code prompts you to configure the application and to restore .NET packages. Press Yes and Restore respectively. The editor generates a .vscode folder containing configuration files and downloads the .NET Core dependencies.

    .NET Core Configuration Prompt

    Tip: If the prompt doesn't appear initially, open a C# file in the editor.

  5. Configure the Angular CLI to output the Angular build to the wwwroot folder. Open the .angular-cli.json configuration file and change "outDir": "dist" to "outDir": "wwwroot".

    {
    // ...
    "apps": [
     {
        "root": "src",
        "outDir": "wwwroot",
        // ...
    }
    }
    
  6. Configure static file support in the Startup.cs file. The Configure method should have the following code.

    public void Configure(IApplicationBuilder app,
      IHostingEnvironment env, ILoggerFactory loggerFactory)
    {
      loggerFactory.AddConsole();
    
      if (env.IsDevelopment())
      {
          app.UseDeveloperExceptionPage();
      }
    
      app.UseDefaultFiles();
      app.UseStaticFiles();
    }
    
  7. Add a build step to the .csproj file to build the Angular application with the Angular CLI.

    <Target Name="AngularBuild" AfterTargets="Build">
      <Exec Command="npm run build" />
    </Target>
    
  8. Press the F5 key to run the ASP.NET application with Visual Studio Code's debugger. The Angular application is now hosted from ASP.NET Core instead of Node.js. Notice how ASP.NET Core uses port 5000 by default as opposed to 4200 with the Angular CLI.

    Angular CLI Stub Page on ASP.NET Core

    Completed Startup.cs Code:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Threading.Tasks;
    using Microsoft.AspNetCore.Builder;
    using Microsoft.AspNetCore.Hosting;
    using Microsoft.AspNetCore.Http;
    using Microsoft.Extensions.DependencyInjection;
    using Microsoft.Extensions.Logging;
    
    namespace my_directory
    {
      public class Startup
      {
          // This method gets called by the runtime. Use this method to add  services to the container.
          // For more information on how to configure your application, visit  https://go.microsoft.com/fwlink/?LinkID=398940
          public void ConfigureServices(IServiceCollection services)
          {
          }
    
          // This method gets called by the runtime. Use this method to  configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app,
            IHostingEnvironment env, ILoggerFactory loggerFactory)
        {
            loggerFactory.AddConsole();
    
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
    
            app.UseDefaultFiles();
            app.UseStaticFiles();
        }
      }
    }
    

    Completed my-directory.csproj Code:

    <Project Sdk="Microsoft.NET.Sdk.Web">
    <PropertyGroup>
      <TargetFramework>netcoreapp1.1</TargetFramework>
    </PropertyGroup>
    
    <ItemGroup>
      <Folder Include="wwwroot\" />
    </ItemGroup>
    
    <ItemGroup>
      <PackageReference Include="Microsoft.AspNetCore" Version="1.1.1" />
      <PackageReference Include="Microsoft.AspNetCore.StaticFiles"  Version="1.1.1" />
    </ItemGroup>
    
    <Target Name="AngularBuild" AfterTargets="Build">
      <Exec Command="npm run build" />
    </Target>
    </Project>
    

Conclusion

The tooling has finally come together for both Angular and ASP.NET so that anyone can easily create an application using the command line.

Use the steps here to build the foundation and then move to integrate other features like routing and authentication. In fact, learn to integrate routing right here.

Have you been using the CLI for Angular or ASP.NET Core? What do you like or dislike about them? Please leave a note in the comments.

Angular Build with Webpack from Scratch – Part 2

Angular Build with Webpack Hero

Derived from photo by Yann Caradec / flickr.com, CC BY-SA

This is the second post in a two-part series getting started with webpack from scratch. The first part covered how to configure the Angular-specific portion of a webpack build. This post walks through how to configure the HTML, CSS, and development server. It builds upon the configuration started in part one.

If you want to jump straight to the code, the completed demo is on GitHub.

Webpack Context

As webpack begins to handle more of the application, it becomes necessary to configure multiple entry points for the build. Part one configures the full path for the single entry file. You could follow the same pattern with any new entry files. You could instead set the context configuration property.

The context property sets a directory as the location where file paths resolve. Instead of combining the paths of each entry file, you configure the base path and then specify the individual file names. This is how part one configured the single entry for webpack:

const path = require('path');
const source = path.resolve(__dirname, 'src', 'index.ts');

module.exports = {
    entry: source,
    // ...
};

To prepare for multiple entries, refactor the entry to use the context property as well:

const path = require('path');
const source = path.resolve(__dirname, 'src');

module.exports = {
    context: source,
    entry: [
        './index.ts'
    ],
    // ...
};

Note: The __dirname variable is a string from Node.js providing the current module's directory.

Using the context property is optional but it helps to remove repetitive path combining logic from your configuration.

HTML Build

Single-page applications are named this way because they typically contain one HTML document that loads the scripts to bootstrap the application. Angular is no exception and there is a handy plugin for webpack called HtmlWebpackPlugin to dynamically configure the HTML page.

The initial configuration for HtmlWebpackPlugin is straightforward. Import the module from its npm package, html-webpack-plugin, and then add it to the plugins property of the webpack configuration.

const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
    // ...
    plugins: [
        new HtmlWebpackPlugin()
    ]
};

By default, the plug-in gives you a boilerplate HTML file for your application. You can however, supply it with a template through the module's constructor arguments. Keep in mind that when using a template, the HtmlWebpackPlugin automatically adds a script element referencing the output of the webpack build so ensure you haven't added the script reference multiple times. This is how to specify the template for HtmlWebpackPlugin:

module.exports = {
    // ...
    plugins: [
        new HtmlWebpackPlugin({
            template: './index.html'
        })
    ]
};

Now you have a webpack build that includes the JavaScript and HTML for your application where the bundle is automatically referenced in the output index.html file. Try it by running npm run build at the command line and inspect the output in the dist directory. The HtmlWebpackPlugin has many great extensions. You can find some of them listed in the project's README file.

CSS Build

The final portion of the Tour of Heroes to address is the global CSS. In the first part of this series, you configured webpack to load any CSS files that are referenced by Angular components. However, the application has global CSS defined in the styles.css file. This file includes style rules relevant to the overall look and feel of the application so it makes sense to keep this outside of the individual components` CSS.

The configuration already processes CSS for the components but this configuration won't work for the global CSS. The first thing is to define the boundary between when webpack should process CSS for components and when webpack should process the global CSS. Looking at the application, everything in the directory .\src\app\ needs to be configured for the angular2-template-loader where other CSS in the .\src\ directory should be bundled and included as a linkelement in the index.html file.

Define a variable for the .\src\app\ path and use the include property on the raw-loader configuration:

const source = path.resolve(__dirname, 'src');
const appDirectory = path.resolve(source, 'app');

module.exports = {
    // ...
    module: {
        rules: [
            // ...
            {
                test: /\.(css|html)$/, 
                include: appDirectory,
                loader: 'raw-loader'
            },
            // ...
        ]
    },
    // ...
};

To process the non-component CSS, use the ExtractTextPlugin. This plugin is essentially a wrapper that provides bundling functionality. It leverages other loaders' import logic to load the CSS content. In the end, you get a combined CSS file. Import the plugin from its npm package, extract-text-webpack-plugin. You also use the style-loader and css-loader npm packages for their respective CSS loaders.

Add the following to the rules array of the webpack configuration:

const ExtractTextPlugin = require('extract-text-webpack-plugin');

const source = path.resolve(__dirname, 'src');
const appDirectory = path.resolve(source, 'app');

module.exports = {
    // ...
    module: {
        rules: [
            // ...
            {
                test: /\.css$/,
                exclude: appDirectory,
                loader: ExtractTextPlugin.extract({ 
                    fallback: 'style-loader', 
                    use: 'css-loader?sourceMap' 
                })
            },
            // ...
        ]
    },
    // ...
};

You see this configuration loads files ending in the .css extension and excludes the appDirectory CSS. Remember, the appDirectory path contains your Angular code and undergoes a different build process than global styles.

The ExtractTextPlugin is configured as both a loader and a plug-in. To configure the plug-in portion of ExtractTextPlugin, add it to the plugins array. Provide a name for the final output bundle, for example styles.css.

module.exports = {
    // ...
    plugins: [
        // ...
        new ExtractTextPlugin('styles.css')
    ]    
};

Note: This tutorial uses styles.css as both the name of the input file and the output file. Feel free to customize this to help ensure you understand which piece of the configuration is related to inputs and which is related to outputs.

Finally, webpack needs to know about the existing Tour of Heroes styles.css file so add it as an entry in the configuration:

module.exports = {
    context: source,
    entry: [
        './index.ts',
        './styles.css'
    ],
    // ...
);

Now that the CSS is auto-generated, the HtmlWebpackPlugin and ExtractTextPlugin work together to add a link to the index.html template file. Remove any existing links to styles.css from the index.html file.

The ExtractTextPlugin has several different options and uses so it's worth taking a look at its documentation.

At this point, your webpack build is complete. Try it with npm run build to see the output. The final HTML template and webpack configuration should have the following contents:

<html>
<head>
  <base href="/">
  <title>Angular 2 Tour of Heroes</title>
  <meta name="viewport" content="width=device-width, initial-scale=1">
</head>

<body>
  <my-app>Loading...</my-app>
</body>

</html>
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const ExtractTextPlugin = require('extract-text-webpack-plugin');

const source = path.resolve(__dirname, 'src');
const destination = path.resolve(__dirname, 'dist');
const appDirectory = path.resolve(source, 'app');

module.exports = {
    context: source,
    entry: [
        './index.ts',
        './styles.css'
    ],
    output: {
        filename: 'index.js',
        path: destination
    },
    resolve: {
        extensions: ['.ts', '.js']
    },
    module: {
        rules: [
            {
                test: /\.ts$/,
                loaders: [
                    'awesome-typescript-loader',
                    'angular2-template-loader'
                ]
            }, 
            {
                test: /\.ts$/,
                loader: 'webpack-replace',
                query: {
                    search: 'moduleId: module.id,',
                    replace: ''
                }
            },
            {
                test: /\.css$/,
                exclude: appDirectory,
                loader: ExtractTextPlugin.extract({ 
                    fallback: 'style-loader', 
                    use: 'css-loader?sourceMap' 
                })
            },
            {
                test: /\.(css|html)$/, 
                include: appDirectory,
                loader: 'raw-loader'
            },
        ]
    },
    plugins: [        
        new HtmlWebpackPlugin({
            template: './index.html'
        }),
        new ExtractTextPlugin('styles.css')
    ]
};

Development Server

The webpack development server integrates with webpack to provide many great features to aid your development. This functionality is installed with the webpack-dev-server npm package. The server automatically watches for changed files and refreshes the browser once a new compilation is complete. To improve performance, the webpack development server keeps the generated contents in memory as opposed to writing it to disk.

To setup a project for the webpack development server, create an npm script. For this tutorial, use the npm start command. There are several command line options available to configure the development server. Use the --open option to have the development server automatically open the default browser to the server's port on localhost:

"scripts": {
    "start": "webpack-dev-server --open",
    ...
}

While the development server has many configuration options, this tutorial covers two that are relevant to Angular developers: devtool and historyApiFallback. The devtool property specifies a source map type to aid in debugging at runtime. Go ahead and set the property to 'source-map'.

Then under the devTool configuration property, set the historyApiFallback property to true. This property tells the server to always return the index.html contents when a route is not available on the server. This is very useful when using client-side routing so that the server defers routing responsibility to Angular's router. Define these properties in your webpack configuration:

// ...
module.exports = {
    // ...
    devtool: 'source-map',
    devServer: {
        historyApiFallback: true,
    }
};

Save your configuration and type npm start at the command line to see webpack build and serve the application.

Final Thoughts

Now you have a starting point for your webpack build and more importantly, you understand how these pieces work together. Many of the more advanced webpack examples you see online use these pieces as their foundation so you can more easily add additional features to the build as you need them.

If you want to see this build in action, look at the repository on GitHub.

What are the next steps on your webpack journey? Which aspects of the build are still causing issues for you? Please sound off in the comments.

Philly.NET Code Camp 2017 Talk

Philly.NET Code Camp Header

I had the privilege to speak at the Philly.NET Code Camp this past weekend. I presented Angular, ASP.NET Core, and Visual Studio Code – Oh My! which proposes a roadmap for getting started with these technologies. The talk touches on a breadth of features and hones in on a specific set of steps for getting started.

With recent updates to both ASP.NET Core and Angular CLI, creating new projects has gotten easier. I'm making the slides available now to those interested and will write up a tutorial on the blog to match the demonstration I gave during the talk.

I attended this code camp for the first time nine years ago and always enjoy catching up with the friends I made along the way. I look forward to seeing you next year!