Refactoring to Angular Pipes

Refactoring to Angular Pipes

Derived from photo by Bodey Marcoccia / flickr.com, CC BY-SA

Angular pipes are often overlooked in their simplicity. If you are familiar with filters in Angular 1.x, they are conceptually similar. At their core, they convert a set of inputs to display text in the UI. And while there are other ways to accomplish this, you may find that by using pipes you can reduce the amount of code in your components.

This is one of a series of posts detailing the process of building an application with Angular, Visual Studio, ASP.NET Core, and Azure. See the complete list of posts here.

The prior post demonstrates how to create a component using Angular Material. The component class contains a TODO comment to convert mapping logic into a pipe. This post shows how to create the pipe:

// TODO: move to own pipe or directive
private mapToDisplayDateText(date: Date): string {
    // TODO: Make today's date mockable, convert to UTC
    const todaysDate = new Date();
    const isToday = areSameDate(todaysDate, date);
    const yesterdaysDate = addDays(new Date(), -1);
    const isYesterday = areSameDate(yesterdaysDate, date);

    const dayText = isToday ? 'Today' : isYesterday ? 'Yesterday' : formatDate(date, 'dddd, MMMM, D, YYYY');
    const lastCompletedDate = `${dayText}, ${formatDate(date, 'h:mm A')}`;

    return lastCompletedDate;
}

Why Use Pipes

Admittedly, I'm a little torn on using pipes. In their pure form, the concept is straightforward: take a set of inputs and return the display output as a string. A pure JavaScript function can also accomplish the same result in a component. I sometimes view pipes as conceptual overhead that you don't necessarily need to learn when starting with Angular. The concept count is already so high for many developers. Why add pipes to the mix?

This post explores how encapsulating this mapping logic in its own UI-based wrapper, a pipe, reduces component complexity in two ways. First when mapping in the component, you are responsible for determining if a value has changed or not before mapping (or you decide not to check this at all). When using a pipe, Angular's change detection evaluates the value to determine if mapping is required.

Second, using a pipe can reduce model complexity. For instance, this example formats a date in a custom way. This date may be rendered in different ways depending on where it is shown. When mapping in the component, each component is responsible for mapping this date into the correct string format and the component is responsible for two pieces of state, the original value and the display value (or multiple display values). When this logic is encapsulated in a pipe, the view declaratively defines how the data should display and the component is only responsible for one piece of state, the date object.

Initially, I recommend sticking with pure pipes. They are more performant out of the box because they adhere to the default (and fast) Angular change detection behavior where only values and object references are compared. If you find the need to detect changes at a deeper level of an object, there are probably better ways to handle this outside the scope of a pipe – and this article.

This post isn't attempting to recreate the documentation, please read that here. Rather, this is more of a case study.

Creating the Pipe

In the initial component implementation, the pipe formats the date and replaces the day of the week text when the day is either today or yesterday. This is how the component class is written with the formatting logic in the component:

import { Component, OnInit } from '@angular/core';
import { formatDate, areSameDate, addDays } from '../utility';
import { Routine, RoutinesViewModel, RoutineViewModel } from './models';

@Component({
  selector: 'app-routines',
  templateUrl: './routines.component.html',
  styleUrls: ['./routines.component.css']
})
export class RoutinesComponent implements OnInit {
  private routines: Routine[] = [
    {
      name: 'Morning',
      lastCompletedDate: new Date()
    },
    {
      name: 'Social Media',
      lastCompletedDate: new Date(2017, 8, 23, 19, 55)
    },
    {
      name: 'Bedtime',
      lastCompletedDate: new Date(2017, 8, 5, 21, 16)
    }
  ];

  viewModel: RoutinesViewModel = {
    routines: []
  };

  ngOnInit() {
    this.render();
  }

  trackRoutine(routine: RoutineViewModel) {
    return routine.name;
  }

  // TODO: move to own pipe or directive
  private mapToDisplayDateText(date: Date): string {
    // TODO: Make today's date mockable, convert to UTC
    const todaysDate = new Date();
    const isToday = areSameDate(todaysDate, date);
    const yesterdaysDate = addDays(new Date(), -1);
    const isYesterday = areSameDate(yesterdaysDate, date);

    const dayText = isToday ? 'Today' : isYesterday ? 'Yesterday' : formatDate(date, 'dddd, MMMM, D, YYYY');
    const lastCompletedDate = `${dayText}, ${formatDate(date, 'h:mm A')}`;

    return lastCompletedDate;
  }

  private render() {
    const viewModel = {
      routines: this.routines.map(routine => {
        const routineViewModel: RoutineViewModel = {
          lastCompletedDate: this.mapToDisplayDateText(routine.lastCompletedDate),
          name: routine.name
        };

        return routineViewModel;
      })
    };

    this.viewModel = viewModel;
  }
}
<md-toolbar color="primary">
  <span>Routines</span>
</md-toolbar>

<md-nav-list>
  <md-list-item *ngFor="let routine of viewModel.routines; index as i; trackBy: trackRoutine">
    <a>
      <div md-line>
        <span class="mat-title">{{ routine.name }}</span>
      </div>
      <div md-line>
        <span class="mat-subheading-2">{{ routine.lastCompletedDate }}</span>
      </div>
    </a>
  </md-list-item>
</md-nav-list>

<a md-fab class="add-routine-button"><md-icon>add</md-icon></a>

This is the pipe and the component after refactoring:

import { Pipe, PipeTransform } from '@angular/core';
import { formatDate, areSameDate, addDays } from '../utility';

@Pipe({ name: 'todayAwareDate' })
export class TodayAwareDatePipe implements PipeTransform {
  transform(date: Date, todayText = 'Today', yesterdayText = 'Yesterday') {
    // TODO: Make today's date mockable, convert to UTC
    const todaysDate = new Date();
    const isToday = areSameDate(todaysDate, date);
    const yesterdaysDate = addDays(new Date(), -1);
    const isYesterday = areSameDate(yesterdaysDate, date);

    const dayText = isToday ? todayText : isYesterday ? yesterdayText : formatDate(date, 'dddd, MMMM, D, YYYY');
    const lastCompletedDate = `${dayText}, ${formatDate(date, 'h:mm A')}`;

    return lastCompletedDate;
  }
}
import { Component, OnInit } from '@angular/core';
import { Routine, RoutinesViewModel } from './models';

@Component({
  selector: 'app-routines',
  templateUrl: './routines.component.html',
  styleUrls: ['./routines.component.css']
})
export class RoutinesComponent implements OnInit {
  private routines: Routine[] = [
    {
      name: 'Morning',
      lastCompletedDate: new Date()
    },
    {
      name: 'Social Media',
      lastCompletedDate: new Date(2017, 8, 23, 19, 55)
    },
    {
      name: 'Bedtime',
      lastCompletedDate: new Date(2017, 8, 5, 21, 16)
    }
  ];

  viewModel: RoutinesViewModel = {
    routines: []
  };

  ngOnInit() {
    this.render();
  }

  trackRoutine(routine: Routine) {
    return routine.name;
  }

  private render() {
    this.viewModel = {
      routines: this.routines
    };
  }
}
<md-toolbar color="primary">
  <span>Routines</span>
</md-toolbar>

<md-nav-list>
  <md-list-item *ngFor="let routine of viewModel.routines; index as i; trackBy: trackRoutine">
    <a>
      <div md-line>
        <span class="mat-title">{{ routine.name }}</span>
      </div>
      <div md-line>
        <span class="mat-subheading-2">{{ routine.lastCompletedDate | todayAwareDate }}</span>
      </div>
    </a>
  </md-list-item>
</md-nav-list>

<a md-fab class="add-routine-button"><md-icon>add</md-icon></a>

There are a couple points to note. First, notice how much less code is in the component. Component code bases can grow very large. If possible, moving mapping logic out of a component generally improves the component's maintainability over time. Second, there used to be two models, one represented the 'entity' or server-side data model and the other represented the model for the view or 'view model'. This distinction may come back later but for now notice that the component no longer must keep track of the date object and the displayed date string. There is just one model, Routine with one date property.

Like how Angular Material creates an NgModule per component, the same convention can be followed for pipes. This is the NgModule for the TodayAwareDatePipe:

import { NgModule } from '@angular/core';

import { TodayAwareDatePipe } from './today-aware-date.pipe';

@NgModule({
  exports: [TodayAwareDatePipe],
  declarations: [TodayAwareDatePipe]
})
export class TodayAwareDateModule { }

It is imported into the RoutinesModule:

// other imports ...
import { TodayAwareDateModule } from '../pipes/today-aware-date.module';

@NgModule({
  declarations: [
    RoutinesComponent
  ],
  imports: [
    CommonModule,
    MdToolbarModule,
    MdListModule,
    MdIconModule,
    MdButtonModule,
    TodayAwareDateModule
  ],
  exports: [
    RoutinesComponent
  ]
})
export class RoutinesModule { }

Tip: Use the Angular v4 TypeScript Snippets Visual Studio Code extension to quickly create both pipes and NgModules.

To view the full repository, check out the branch for this post in the Bebop Routines project on GitHub.

Conclusion

In this example, you saw how to refactor a component's mapping logic to a re-usable pipe. This helps the component's maintainability by reducing both logic and state. Also, the pipe leverages Angular change detection to ensure the mapping logic is only executed when necessary.

Are you using pipes today? How have you found them to be the most useful? Let me know your thoughts and questions in the comments.

Creating the First Screen with Angular Material

Creating the First Screen with Angular Material

Derived from photo by Andy Melton / flickr.com, CC BY-SA

The last post showed how to prototype the Routines App UI using Adobe XD. This post focuses on building the first screen using Angular Material. Before working on the UI, generate a new project using the Angular CLI. If not familiar with the CLI, this is a good resource with which to get started.

This is one of a series of posts detailing the process of building an application with Angular, Visual Studio, ASP.NET Core, and Azure. See the complete list of posts here.

This is a summary of decisions made and lessons learned while creating this screen:

  • Angular Material documentation has everything needed but it took me some time to get the whole mental model, hopefully this post will help anyone else getting started
  • Unlike Bootstrap, Angular Material's CSS doesn't attempt to canvas the whole screen with default CSS and it's more likely you will have to explicitly opt-in than opt-out of the Angular Material theming
  • While the Angular Material setup takes some time, using the components is straightforward by following the examples in the documentation

Adding Angular Material Dependencies

Getting started with Angular Material requires new npm packages, referencing CSS themes, and referencing fonts for Roboto typography and Material icons. First, install the required npm packages for Angular Material:

npm install @angular/cdk && @angular/material

The @angular/material package is somewhat self-explanatory and contains the modules and CSS required to use Angular Material components. The @angular/cdk is interesting. The CDK stands for component developer kit. This is a set of primitive components with defined functionality and API. Using these, anyone can apply custom visual design and extend with custom functionality. Some of the Angular Material themed components are built on top of CDK counterparts. For more information, see the documentation for the CDK data-table.

Next, add references for the Roboto font and Material icons font. These assets are common to the Material design language and Google makes them available on their CDN. Add the following lines in the <head> element of the src/index.html file:

  <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
  <link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500" rel="stylesheet">

Angular Material currently includes four pre-built theme options for its components:

  • deeppurple-amber.css
  • indigo-pink.css
  • pink-bluegrey.css
  • purple-green.css

To create a custom theme, Angular Material provides a set of SCSS variables and mix-ins. For now, I'm choosing a pre-built theme. To apply, simply include the link to the pre-built CSS file in the src/styles.css file:

@import '~@angular/material/prebuilt-themes/deeppurple-amber.css';

By referencing the pre-built theme in the styles.css file, the Angular CLI bundles this and other referenced CSS together in one file.

If familiar with CSS frameworks such as Bootstrap, you may be used to the framework applying an extensive set of global styles to the page. Angular Material very deliberately tries to isolate its styles to the Angular Material components. For instance, if you add a regular HTML button to the screen without adding any Angular Material specific markup to it, it renders as the browser would.

Similarly, this also means that CSS used to reset browser-default margins are not applied. The first thing I noticed when starting to add the components to the UI is that I had a default padding around my body element. I found the most straight-forward way to handle this is to apply CSS resets as needed in the styles.css file. At this point, I have only needed to remove the default <body> margin and padding:

/* resets */
body {
  margin: 0;
  padding: 0;
}
/* end resets */

Adding Components

Essentially, this screen has three components: a header, list, and a button. Each of the components in Angular Material is contained within its own NgModule so each of the respective NgModules must be included as an import in the NgModule utilizing the components.

In addition, some of the Angular Material components depend on Angular animations. These are imported using the BrowserAnimationsModule. While I haven't seen any issues using the BrowserAnimationsModule in the component's NgModule versus the application's NgModule (or even removing it altogether), I decided to keep it in the application's NgModule to align with the documentation for now. This is the code for the app.module.ts file:

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';

import { AppComponent } from './app.component';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    BrowserAnimationsModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

For the RoutinesComponent, create a new folder called routines. In this folder are five files:

  • model.ts – type definitions for model objects
  • routines.component.css – the RoutinesComponent CSS
  • routines.component.html – the RoutinesComponent template HTML
  • routines.component.ts – the RoutinesComponent class
  • routines.module.ts – the NgModule to export the RoutinesComponent

Starting with the RoutinesComponent class, there are several things that happen here to create a static UI. First, take a look at the code:

import { Component, OnInit } from '@angular/core';
import { formatDate, areSameDate, addDays } from '../utility';
import { Routine, RoutinesViewModel, RoutineViewModel } from './models';

@Component({
  selector: 'app-routines',
  templateUrl: './routines.component.html',
  styleUrls: ['./routines.component.css']
})
export class RoutinesComponent implements OnInit {
  private routines: Routine[] = [
    {
      name: 'Morning',
      lastCompletedDate: new Date()
    },
    {
      name: 'Social Media',
      lastCompletedDate: new Date(2017, 8, 23, 19, 55)
    },
    {
      name: 'Bedtime',
      lastCompletedDate: new Date(2017, 8, 5, 21, 16)
    }
  ];

  viewModel: RoutinesViewModel = {
    routines: []
  };

  ngOnInit() {
    this.render();
  }

  trackRoutine(routine: RoutineViewModel) {
    return routine.name;
  }

  // TODO: move to own Pipe or Directive
  private mapToDisplayDateText(date: Date): string {
    // TODO: Make today's date mockable, convert to UTC
    const todaysDate = new Date();
    const isToday = areSameDate(todaysDate, date);
    const yesterdaysDate = addDays(new Date(), -1);
    const isYesterday = areSameDate(yesterdaysDate, date);

    const dayText = isToday ? 'Today' : isYesterday ? 'Yesterday' : formatDate(date, 'dddd, MMMM, D, YYYY');
    const lastCompletedDate = `${dayText}, ${formatDate(date, 'h:mm A')}`;

    return lastCompletedDate;
  }

  private render() {
    const viewModel = {
      routines: this.routines.map(routine => {
        const routineViewModel: RoutineViewModel = {
          lastCompletedDate: this.mapToDisplayDateText(routine.lastCompletedDate),
          name: routine.name
        };

        return routineViewModel;
      })
    };

    this.viewModel = viewModel;
  }
}

The component references several models from models.ts. There is a data structure for Routine which I consider more of a domain entity that represents the 'real-world' routine object. Also, there are a several models that represent the data structure of the UI or 'view models'.

The RoutinesComponent has a render method that maps the domain entity to the component's viewModel property. The viewModel property is the main data structure with which to bind the template. To track the items in the list, the class has a trackRoutine function.

The mapToDisplayDateText method should be moved but it's temporary purpose is to create a custom display format for the date. You will also notice some helper functions for Date objects imported from a utility folder.

This component and the required Angular Material NgModules are configured with the RoutinesModule in the routines.module.ts file:

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { MdToolbarModule, MdListModule, MdButtonModule, MdIconModule } from '@angular/material';

import { RoutinesComponent } from './routines.component';

@NgModule({
  declarations: [
    RoutinesComponent
  ],
  imports: [
    CommonModule,
    MdToolbarModule,
    MdListModule,
    MdIconModule,
    MdButtonModule
  ],
  exports: [
    RoutinesComponent
  ]
})
export class RoutinesModule { }

In addition to the MdToolbarModule (representing the header), MdListModule, and MdButtonModule, there is a re-usable icon module for the button's icon, MdIconModule. Beyond these imports, most of the Angular Material code is in the RoutinesComponent HTML template in routines.component.html file:

<md-toolbar color="primary">
  <span>Routines</span>
</md-toolbar>

<md-nav-list>
  <md-list-item *ngFor="let routine of viewModel.routines; index as i; trackBy: trackRoutine">
    <a>
      <div md-line>
        <span class="mat-title">{{ routine.name }}</span>
      </div>
      <div md-line>
        <span class="mat-subheading-2">{{ routine.lastCompletedDate }}</span>
      </div>
    </a>
  </md-list-item>
</md-nav-list>

<a md-fab class="add-routine-button"><md-icon>add</md-icon></a>

The header is the MdToolbar using the md-toolbar selector. The template uses the color attribute to apply the theme's primary color to the background. For more information on this component, check out the toolbar documentation.

The routine list is made up of the MdList and MdListItem components. A simple ngFor creates each list item. Within each list item, Angular Material's typography CSS classes mat-title and mat-subheading-2 apply the theme's font sizes. Read more about the MdList and MdListItem in the corresponding documentation.

Finally, the button in this case is an MdAnchor component with the md-fab attribute. There are several styles of buttons with their own selectors. To apply the icon, there is an MdIcon component. The MdIcon component's image is configured in the element's content, in this case add. These icons correspond to the list of Material Icons referenced from Google's CDN in the index.html file.

The button is the only component in this screen that has custom CSS to position it fixed in the bottom right of the screen. The CSS for this is in the routines.component.css file.

Read more about the button components here and the icon component here.

One final note is to include the RoutinesModule in the AppModule and the AppComponent template. To see how this fits together, look at the first-angular-material-screen branch in the GitHub repository.

End of the First Round

This being my first time using Angular Material, I found some snags. Hopefully this post helps you avoid the same. Overall, I like my experience with the library so far and I look forward to implementing more advanced scenarios as this application progresses. Have you given Angular Material a try? What do you think? Leave your thoughts in the comments.

The NgModule ‘forRoot()’ Convention

The NgModule forRoot Convention Hero Image

Derived from photo by Boris Tassev / flickr.com, CC BY-SA

The NgModule forRoot() convention is a curious one. The naming explains how to use it but not what it is or more importantly why it's necessary to import an NgModule in this way.

This post peeks beneath the API to help you understand this design and get the most out of the convention.

The NgModule forRoot() Convention

There comes a point when developing in Angular when an NgModule requires a call to its forRoot() method when importing. The most notable example of this is with the RouterModule. When registering this module at the root of an Angular application, importing the RouterModule looks like this:

import { RouterModule, Routes } from '@angular/router';

const routes: Routes = [
    { path: '',   redirectTo: '/index', pathMatch: 'full' }
];

@NgModule({
  imports: [
    RouterModule.forRoot(routes)
  ],
  ...
})
export class AppModule { }

This convention is also used in ngx-bootstrap and was previously used in Angular Material. The convention implies that a given module must be registered with the root NgModule of an application while invoking the forRoot() method. What is so special about this method that it needs to be called at the root of an application as opposed to any other NgModule?

For starters, what does the forRoot() convention return? Generally, the return type for this method is an object conforming to the ModuleWithProviders interface. This interface is an accepted NgModule import and has two properties:

interface ModuleWithProviders { 
  ngModule: Type<any>
  providers: Provider[]
}

Put simply, the forRoot() method returns an NgModule and its provider dependencies. What does this have to do with the root NgModule? Maybe nothing. In fact, while this convention implies that it must be imported at the root of the application, in many cases you can import it in a non-root NgModule and it will work – GASP!

Putting that aside for a moment, this is how the ModalModule in ngx-bootstrap uses the forRoot() convention:

import { NgModule, ModuleWithProviders } from '@angular/core';

import { ModalBackdropComponent } from './modal-backdrop.component';
import { ModalDirective } from './modal.component';
import { PositioningService } from '../positioning';
import { ComponentLoaderFactory } from '../component-loader';

@NgModule({
  declarations: [ModalBackdropComponent, ModalDirective],
  exports: [ModalBackdropComponent, ModalDirective],
  entryComponents: [ModalBackdropComponent]
})
export class ModalModule {
  public static forRoot(): ModuleWithProviders {
    return {ngModule: ModalModule, providers: [ComponentLoaderFactory, PositioningService]};
  }
}

Notice how the ModalModule does not declare any providers in the @NgModule decorator but does so in the static forRoot() method.

Why is the Root NgModule Important?

Even though importing the additional providers of the forRoot() method theoretically works in child NgModules, registering it at the root of the application helps in a number of ways.

First, consider how Providers are injected differently than components and directives. Typically, when decorating a class with @Injectable and registering as a provider in an NgModule, this class is created once and that one instance is shared amongst the entire application. When Angular bootstraps the root NgModule, all available imports in all NgModules are registered at that time and made available to the whole application – they are global. This is why providers registered in a child NgModule are available throughout the whole application.

Components and directives on the other hand are instantiated multiple times, once per instance in the markup. In addition, these items are scoped to the NgModule in which they are imported to prevent naming conflicts where two components might have the same selector for example. Because of this difference in dependency injection (DI) behavior, the need to differentiate an NgModule containing components and directives from a ModuleWithProviders containing components, directives, and providers is helpful which is where the forRoot() method makes that distinction.

Dependency injection, however, doesn't always work this simply. There are times when all the application's NgModules are not available during the bootstrap process. Lazy-loading is such an example. When lazy-loading an NgModule during routing, the providers registered in the lazy-loaded NgModule and its children aren't available during the bootstrap process and Angular is unable to register them at that time. Therefore, they are added as providers only when the route is loaded and furthermore they are scoped to be injected starting at the lazily-loaded NgModule and its children. If there are multiple lazy-loaded NgModules attempting to register the same providers, each of these nodes of the NgModule tree end up with different instances. By importing the providers at the root, it helps ensure that all lazy-loaded NgModules get the same instance of the provider and is why forRoot() is named as such.

Be sure to read more about the nuances of Angular's dependency injection in the documentation.

When to Use the forRoot() Convention

As a consumer, use it when a library dependency requires it. Import the module at the root of the application and register with the forRoot() method to import the providers globally. In other NgModules, use the appropriate non-root form of the import when necessary to import the components and directives.

In the case of both Angular Routing and ngx-bootstrap, this convention helps share providers amongst multiple instances of directives and components to achieve a global concern for the application. For example, ngx-bootstrap uses this convention for the modal dialog component. While there may be many modal instances defined in the markup of an application, the modal takes over the entire UI hence all instances of the dialog should understand how they affect this global concern.

In the case of routing, there is only one window.location for the application so even though there may be child routes and various instances of router-outlet components, they all need that one global dependency of the window's location so that they can work together.

In both cases, the consumer of the modal dialog or router does not need to know how these items communicate and manage shared concerns. In this way, the forRoot() method abstracts away the necessary provider registration.

In short, consider using the convention when you have multiple custom components or directives that all take a dependency on a global UI concern and need to work together to manage this global state. To be clear, the forRoot() convention is a form of coupling which should only be used after careful design consideration.

It's best to avoid using this convention with third-party libraries. For instance, don't try to bubble up all the NgModule dependencies of the application using this convention. The providers returned by the forRoot() method should be internal dependencies that work exclusively with the other components included in the ModuleWithProviders. Third-party dependencies should be treated like npm peerDependencies and imported and registered in the root NgModule directly so that other components can more easily share the same dependencies and help ensure all consumers reference the same package version when npm versioning resolves.

Takeaways

In summary, the forRoot() convention represents a way to import an NgModule along with its providers using the ModuleWithProviders interface.

When a feature NgModule exports components and directives that require sharing the same custom provider instances, consider registering these providers in the root NgModule with a forRoot() method. This can help ensure that all child NgModules have access to the same provider instances without requiring the consumer to handle the provider registration explicitly.

For more information on implementing the forRoot() convention, there are some additional implementation ideas found in the Angular documentation.

Please leave a comment below sharing your experiences using the forRoot() convention.

When to Use ES2015 Modules Instead of Angular Dependency Injection (and When Not to)

When to Use ES2015 Modules Instead of Angular Dependency Injection Title Image

Derived from photo by fdecomite / flickr.com, CC-BY

This post discusses some of the nuances between ECMAScript 2015 module dependencies and Angular's dependency injection system. These are design considerations and while you may decide not to use the approaches discussed, the goal is to provide you with a context in which to make more thoughtful decisions about how to use dependencies throughout your Angular application.

A Tale of Two Dependency Systems

When building an Angular application, there are two main dependency systems at play. The first is EcmaScript 2015 (ES2015) modules. This is the system used when creating an import statement or exporting an object. This system uses string identifiers to obtain dependencies via URL or npm package name.

import { platformBrowser } from '@angular/core';

The other dependency system is Angular's dependency injection (DI) system which is built on top of the ES2015 module system. Unlike the static nature of ES2015 dependencies, these dependencies are configurable when the application bootstraps. They are typically defined as a provider in an NgModule.

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule
  ],
  providers: [
    ShoppingCartService
  ],
  bootstrap: [ 
    AppComponent 
  ]
})
export class AppModule {

}

While not entirely a one-to-one relationship, consider the idea that ES2015 module dependencies are hard dependencies meaning they cannot easily change upon running the application. On the other hand, Angular provides a more loosely-coupled dependency structure that is more easily configured at runtime.

Note: There are in fact more than two dependency mechanisms if you consider npm module resolution where version numbers can influence the resolution of ES2015 modules or a bundler like webpack which can conditionally swap ES2015 dependencies based on configuration. This would be tooling-based configuration whereas this post focuses on code-based configuration.

Angular Dependency Injection (DI)

At a high level, Angular reads the NgModule metadata during the bootstrap process and creates an injector capable of providing these dependencies through the class constructor of components and services throughout the application. Please read more about Angular's dependency injection in the documentation to understand the full details. This post refers to Angular dependency injection at a high level.

To look at an example, consider an e-commerce application with a digital shopping cart. Imagine a mini-view of the cart while the user is browsing product pages and a separate, detailed view when it's time to check out. The logic to calculate the quantity of items in the cart is the same for each view. Therefore, both pieces of the UI may depend on a ShoppingCartService to calculate the total quantity of items in the cart. This service is provided to the components through dependency injection.

shopping-cart.service.ts

import { Injectable } from '@angular/core';
import { CartDetail } from './shopping-cart.model';

@Injectable()
export class ShoppingCartService {
  getTotalQuantity(cartDetails: CartDetail[]): number {
    if (!cartDetails) {
      return null;
    }

    const total = cartDetails.map(carDetail => carDetail.quantity)
      .reduce((previous, current) => previous + current, 0);

    return total;
  }
}

shopping-cart.component.ts

import { Component } from '@angular/core';
import { CartDetail } from './shopping-cart.model';
import { ShoppingCartService } from './shopping-cart.service';

@Component({
  selector: 'app-shopping-cart',
  templateUrl: './shopping-cart.component.html'
})
export class ShoppingCartComponent {

  shoppingCart: CartDetail[] = [
    { productName: 'Hammer', quantity: 1 },
    { productName: 'Purple Shorts', quantity: 26 },
    { productName: 'Web Shooter', quantity: 2 }
  ];

  totalCartQuantity = this.shoppingCartService.getTotalQuantity(this.shoppingCart);

  constructor(private shoppingCartService: ShoppingCartService) { }
}

Consider the number of steps involved in registering and using this dependency in a component through Angular's DI:

  1. Create the ShoppingCartService class
  2. Import and decorate the dependency with the @Injectible() decorator
  3. Import the dependency into the appropriate NgModule
  4. Register the service as a provider
  5. Import the dependency in the component's ES2015 module
  6. Add it as an argument in the component's constructor
  7. Use the dependency

That's quite a bit of code to implement the default DI recommendation. This isn't a criticism, just an observation. Because this implementation uses the ES2015 module standard and includes the added TypeScript benefits, these steps are reasonable in pursuit of loosely-coupled dependencies.

Note: The Angular CLI can help you avoid having to write some of this code by hand.

While Angular's dependency injection implementation comes with many benefits, it's worth considering an alternative.

Using ECMAScript 2015 (ES2015) Module Dependencies

Instead of creating a class and registering it with an NgModule, consider writing a more generic piece of code that can map and calculate a total from an array of objects. Create a function called mapArraySum in its own ES2015 module. Look at the steps for using this function in a component:

  1. Create the mapArraySum function
  2. Import the dependency in the component's ES2015 module
  3. Use the dependency

mapArraySum.ts

export function mapArraySum<T>(array: T[], mappingFunc: (item: T) => number): number {
  if (!array) {
    return null;
  }

  const total = array.map(mappingFunc)
    .reduce((previous, current) => previous + current, 0);

  return total;
}

shopping-cart.component.ts

import { Component } from '@angular/core';
import { CartDetail } from './shopping-cart.model';
import { mapArraySum } from './mapArraySum';

@Component({
  selector: 'app-shopping-cart',
  templateUrl: './shopping-cart.component.html'
})
export class ShoppingCartComponent {

  shoppingCart: CartDetail[] = [
    { productName: 'Hammer', quantity: 1 },
    { productName: 'Purple Shorts', quantity: 26 },
    { productName: 'Web Shooter', quantity: 2 }
  ];

  totalCartQuantity = mapArraySum(this.shoppingCart, item => item.quantity);
}

Wow, this is a much simpler approach. Furthermore, the dependency is completely decoupled from anything Angular-specific like @Injectable. Which implementation should you use? There are several considerations that should factor into this decision.

When to Consider Using ECMAScript 2015 Modules

The reason the ShoppingCartService is probably not a good candidate for straight ES2015 module dependency management is twofold. First, the UI of a shopping cart is generic enough where you may want to re-use the shopping cart UI components in multiple applications. These applications may employ different shopping cart logic. Second, the checkout process is prone to business changes and totaling a cart may change very much depending on promotions, shipping rates, or other business rules. You may find that calculation can no longer be done on the client and must involve a call to the server.

Potentially, it makes sense to employ both implementations where the ShoppingCartService exists as a loosely-coupled dependency yet its implementation leverages the generic mapArraySum function.

shopping-cart.service.ts

import { Injectable } from '@angular/core';
import { CartDetail } from './shopping-cart.model';
import { mapArraySum } from './mapArraySum';

@Injectable()
export class ShoppingCartService {
  getTotalQuantity(cartDetails: CartDetail[]): number {
    const total = mapArraySum(this.shoppingCart, item => item.quantity);
    return total;
  }
}

Before detailing what criteria indicates a potential candidate for ES2015 dependency management, remember that when in doubt the safer choice is to use Angular's dependency injection. In the end, it provides much greater flexibility if you ever need it.

These criteria potentially identify code suitable to use ES2015 dependency management:

  1. Pure functions with stateless logic (e.g. calculations, validations, mapping data structures)
  2. No external dependencies (e.g. HTTP requests)
  3. Generic functionality (e.g. language-level functionality, contract-based data mapping)
  4. Throw-away code for prototyping purposes

Given these criteria, and other than rough prototypes, what type of code specifically does this cover? This type of code can go by many names, but falls into the category of utility functions. This is code that helps you do things that you repeatedly do in any JavaScript application. It's the code you get sick of writing over and over. A simple example is a function that checks if an object is null or undefined.

export function isNullOrUndefined(value: any) {
    const isNullOrUndefined = value === null || value === undefined;
    return isNullOrUndefined;
}

This is a one-liner, but consider building on it:

export function isAnyNullOrUndefined(values: any[]) {
    // this would be more lines without support for .some()    
    const isAnyNullOrUndefined = values.some(value => isNullOrUndefined(value));
    return isAnyNullOrUndefined;
}

// another common example
export function isEmptyString(value: string) {
    const isEmptyString = !isNullOrUndefined(value) && value.length === 0;
    return isEmptyString;
}

What about a function that adds all the numbers in an array and rounds to a specific number of decimal places? This logic is prone to errors or inconsistencies in implementation. Do you round before or after you add? How do you guard against precision errors? These are all things a developer shouldn't have to keep revisiting while building an application. How do you perform a deep clone or a deep compare of two objects? These are all examples of common logic that developers use again and again.

Could you still put this logic in an Angular service and inject it into a component? Absolutely. The point is that you have another option that might prove more simple, elegant, and/or re-usable. Ultimately, choose what's best for your team and your project.

Use ES2015 Modules to Abstract Hard Dependencies

You may have been thinking during the last section, "Aren't there utility libraries to use for these types of utility functions?". There are and developers use them every day. The question is, should your application take a hard dependency on these libraries?

Based off the information thus far, this functionality can probably safely exist as an ES2015-based dependency. However, you can partially ease the coupling by abstracting the specific framework or library reference to within a library under your control. In this way, if you decide for examples that lodash's deep compare function is no longer required because of an emerging ECMAScript standard, you can easily replace it in one place without affecting the rest of the application.

Consider an isEqual module:

isEqual.ts

import * as _ from 'lodash.isequal';

export function isEqual(target: any, comparison: any) {
    const isEqual = _.isEqual(target, comparison);
    return isEqual;
}

app.component.ts

import { isEqual } from './app-utility/isEqual.ts';

@Component({
    selector: 'my-app',
    template: './app.component.html'
})
export class AppComponent {
    foo: any;

    onSomeEvent(newFoo: any) {
        if (isEqual(this.foo, newFoo)) {
            return;            
        }

        console.log('foo is new');
        this.foo = newFoo;
    }
}

So instead of repeatedly coupling your components to third party libraries, depend on your own utility library where you can replace or remove third-party utility libraries as necessary.

Conclusion

Again, this post is intended to help you think more deeply about the dependencies in your Angular application. There are many different approaches to handling these designs. What approaches do you use to manage dependencies in your applications?

Build 2017 Videos for Angular Developers

Build 2017 Videos for Angular Developers

After watching approximately six hours of keynote videos from Microsoft's Build Conference 2017, it's clear that no single company produces more software for developers than Microsoft. There are tools for virtually every platform. There are SDKs for Windows, Xbox, and Office. You can even use Visual Studio to develop an extension for Visual Studio.

With so many developer products, how do you determine which is relevant to you? That question is the inspiration for this post.

The following is a categorized list of videos from Build 2017 that are most relevant to an Angular web developer. The usual suspects are here including TypeScript, Visual Studio 2017, Visual Studio Code, Progressive Web Apps (PWA), ASP.NET, and Node.js. Going further are newer technologies such as Azure Serverless products, brand-new Cosmos DB, Mixed Reality, Artificial Intelligence offerings, and the debut of Fluent Design.

For the full list of all 450 videos, visit the event page on Channel9. Also if you find a favorite video absent from the list, please leave a comment.

Angular and Front-End

Tooling

Visual Studio Code

Visual Studio 2017

More Tooling

ASP.NET

Node.js

Azure – Serverless

Keynotes

Mixed Reality

Azure – Cosmos DB

Microsoft Graph

Fluent Design

Artificial Intelligence (AI)

Bot Framework

Cognitive Services

More AI

ASP.NET Core JavaScript Services with Webpack HMR

ASP.NET Core JavaScript Services with Webpack HMR Hero

Derived from photo by Alasdair / flickr.com, CC BY

This tutorial covers two aspects of the ASP.NET Core JavaScript Services library. First, MapSpaFallbackRoute, is a utility to help redirect client-side routes to the browser. Second is a utility for integrating webpack dev middleware as ASP.NET Core middleware. This provides webpack's hot module replacement (HMR) functionality integrated with the ASP.NET Core request pipeline. When this is enabled, the code in the browser automatically replaces the updated ES2015 module without a full application refresh. This is an incredibly productive development setup.

The tutorial uses Visual Studio Code as the editor. If you would like to go directly to the completed example, the code is on GitHub.

Initial Setup

  1. Follow the steps in this post to create an 'empty' Angular, ASP.NET Core project using the Angular and dotnet command line interfaces (CLI). The rest of the tutorial builds off this base project.
  2. Next, add the code for multiple components to the project's src/app directory. Upon completion, the files should be in this structure with the following contents:

    ├── src
    |   ├── app
    |   |   └── app.component.ts
    |   |   ├── app.module.ts
    |   |   ├── base.component.html
    |   |   ├── main.component.ts
    |   |   └── other.component.ts
    

    app.component.ts

    import { Component } from '@angular/core';
    
    // exported to use with webpack HMR
    export const rootElementTagName = 'app-root';
    
    @Component({
    selector: rootElementTagName,
    template: `
      <nav>
        <a routerLink="/main">main</a> |
        <a routerLink="/other">other</a>
      </nav>
      <router-outlet></router-outlet>
    `
    })
    export class AppComponent { }
    

    base.component.html

    <h1>
    {{title}}
    </h1>
    

    main.component.ts

    import { Component } from '@angular/core';
    
    @Component({
    selector: 'app-main',
    templateUrl: './base.component.html'
    })
    export class MainComponent {
    title = 'this is the main component';
    }
    

    other.component.ts

    import { Component } from '@angular/core';
    
    @Component({
    selector: 'app-other',
    templateUrl: './base.component.html'
    })
    export class OtherComponent {
    title = 'this is the other component';
    }
    
  3. Finally, configure components in the NgModule app.module.ts.

    app.module.ts

    import { BrowserModule } from '@angular/platform-browser';
    import { NgModule } from '@angular/core';
    import { FormsModule } from '@angular/forms';
    import { HttpModule } from '@angular/http';
    import { RouterModule, Routes } from '@angular/router';
    
    import { AppComponent, rootElementTagName } from './app.component';
    import { MainComponent } from './main.component';
    import { OtherComponent } from './other.component';
    
    const routes: Routes = [
    { path: '', redirectTo: '/main', pathMatch: 'full' },
    { path: 'main',  component: MainComponent },
    { path: 'other', component: OtherComponent },
    ];
    
    @NgModule({
    declarations: [
      AppComponent,
      MainComponent,
      OtherComponent
    ],
    imports: [
      BrowserModule,
      FormsModule,
      HttpModule,
      RouterModule.forRoot(routes)
    ],
    providers: [],
    bootstrap: [AppComponent]
    })
    export class AppModule { }
    
    export { rootElementTagName } from './app.component';
    

    Now run the application in Visual Studio Code using F5. The client-side routes are configured and you can switch between the main and other views.

Configure Server-Side Routing with MapSpaFallbackRoute

The ASP.NET Core JavaScript Services library includes a utility to configure single page application (SPA) routing. This enables the server to handle any requests intended for the Angular router by returning the Angular application to the browser. Once the browser loads the Angular application, Angular takes over the URL routing. For more information on how Angular and ASP.NET Core routes integrate, see this post.

These steps configure server-side routing.

  1. Within the project directory, use the command dotnet add package Microsoft.AspNetCore.SpaServices to add the ASP.NET Core JavaScript Services library to the project. Restore the new package when Visual Studio Code displays the prompt or by using the command dotnet restore.

  2. In the Startup.cs file add the ASP.NET MVC service as well as the MapSpaFallbackRoute:

    Startup.cs

    using Microsoft.AspNetCore.Builder;
    using Microsoft.AspNetCore.Hosting;
    using Microsoft.AspNetCore.SpaServices.Webpack;
    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)
      {
        services.AddMvc();
      }
    
      // 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();     
    
        app.UseMvc(routes => {
          routes.MapSpaFallbackRoute(
            name: "spa-fallback",
            defaults: new { controller = "Home", action = "Index" });
        });
      }
    }
    }
    

    Note: Ensure that the namespace my-directory is replaced with the namespace of your application.

  3. The MapSpaFallbackRoute points to a HomeController. Create a Controllers directory and add this controller as a pass-through to simply return the Angular application for routes that should be addressed on the client.

    HomeController.cs

    using Microsoft.AspNetCore.Mvc;
    
    public class HomeController : ControllerBase
    {
      public IActionResult Index()
      {
          var fileName = "index.html";
          var contentType = "text/html";
    
          return File(fileName, contentType);
      }
    }
    

Run the application with F5. Go directly to the URL http://localhost:5000/other in the browser's address bar (not by linking from within the application) and see that the Angular application successfully loads the route.

Configure Hot Module Replacement

One of the more compelling features of webpack is its ability to apply code updates in the browser while the application is running so you can quickly see the resulting changes. This is webpack's Hot Module Replacement (HMR) feature and the JavaScript Services library includes the ability to integrate with this functionality using ASP.NET Core middleware.

  1. Type ng eject in the terminal to have Angular CLI switch the configuration to a webpack-based configuration. At this point any configuration changes that you want to make to the build must be done through the webpack configuration. The Angular CLI configuration no longer applies.

  2. Run npm install aspnet-webpack webpack-hot-middleware -D to download required Node.js packages. Also, run npm install to ensure all the new webpack package dependencies are downloaded.

  3. Configure the webpack middleware in the Startup.cs file. Add the webpack dev middleware configuration within the env.IsDevelopment() conditaion. By default, Visual Studio Code configures ASP.NET Core to run in Development mode so this condition evaluates to true.

    Startup.cs

    // ...
    public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
    {
    // ...
    if (env.IsDevelopment())
    {
      app.UseDeveloperExceptionPage();
      app.UseWebpackDevMiddleware(new WebpackDevMiddlewareOptions {
        HotModuleReplacement = true
      });
    }
    
    app.UseDefaultFiles();
    
    // ...
    }
    
  4. When running the project with webpack HMR, you do not need to build with webpack as part of the .csproj build. Update this configuration to avoid the extra processing at compile time. By default, the build runs with a 'Debug' configuration which will not trigger the webpack build.

    my-directory.csproj

    <Target Name="AngularBuild" AfterTargets="Build">
    <Exec Condition="'$(Configuration)' == 'Release'" Command="npm run build" />
    </Target>
    

    Note: Attentive readers may notice that the tutorial uses ASP.NET Core configuration for enabling the webpack dev middleware and alternatively a build configuration constant for enabling the non-HMR build. This tutorial focuses on improving the developer experience with webpack HMR. There are many options for synchronizing these configurations to be explored in future posts.

  5. Set "publicPath" to "/" in the webpack.config.js file under the "output" section.

    "output": {
    "path": path.join(process.cwd(), "wwwroot"),
    "filename": "[name].bundle.js",
    "publicPath": "/",
    "chunkFilename": "[id].chunk.js"
    },
    
  6. Finally, you must handle the HMR changes in the browser to refresh the Angular application with updated modules. Add a TypeScript file to the src directory named handleHotModule.ts and use it to handle the HMR changes during the Angular bootstrap process in main.ts.

    handleHotModule.ts

    import { Type, PlatformRef } from '@angular/core';
    
    interface HotModule extends NodeModule {
    hot?: HotModuleHandler;
    }
    
    interface HotModuleHandler {
    accept: () => void;
    dispose: (callback: () => void) => void;
    }
    
    export function handleHotModule(
    bootstrapModule: HotModule,
    rootElemTagName: string,
    platform: PlatformRef,
    bootFunction: (isModuleHot: boolean) => void
    )  : void {
    const isModuleHot = !!bootstrapModule.hot; 
    
    if (isModuleHot) {
      bootstrapModule.hot.accept();
      bootstrapModule.hot.dispose(() => {
        const oldRootElem = document.querySelector(rootElemTagName);
        const newRootElem = document.createElement(rootElemTagName);
        oldRootElem.parentNode.insertBefore(newRootElem, oldRootElem);
        platform.destroy();
      });
    }
    
    if (document.readyState === 'complete') {
      bootFunction(isModuleHot);
    } else {
      document.addEventListener('DOMContentLoaded', () => {
        bootFunction(isModuleHot);
      });
    }
    }
    

    main.ts

    import { enableProdMode } from '@angular/core';
    import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
    
    import { AppModule, rootElementTagName } from './app/app.module';
    import { environment } from './environments/environment';
    import { handleHotModule } from './handleHotModule';
    
    const platform = platformBrowserDynamic();
    
    handleHotModule(module, rootElementTagName, platform, isModuleHot => {
    if (environment.production && !isModuleHot) {
      enableProdMode();
    }
    
    platform.bootstrapModule(AppModule);
    });
    

    Now run the application. After the application loads, make a change to one of the components and save the file. Upon saving, you see the update appear in the browser almost immediately after you press save in the editor.

Your New Developer Experience

Welcome to your new developer experience with ASP.NET Core and Angular powered by webpack and ASP.NET Core JavaScript Services. The ASP.NET Core JavaScript Services library has so much more including server-side pre-rendering with Angular Universal and lower level utilities to interface with Node.js code.

Take some time to look at the respository on GitHub. There are additional details included there for your reference.

Give this setup a try and drop a comment about your experience. What other build configurations and developer features do you want to see featured on the blog?

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.

Angular Build with Webpack from Scratch

Angular Build with Webpack Hero

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

This tutorial walks through creating a webpack-based build for the Angular Tour of Heroes sample application. By the end, you will have a basic build and development server with automatic refresh and source maps.

The tutorial is divided into two parts. In this first part, you setup the project and create the Angular-specific build. In part two, you process the other files of the application and setup the development server. Read part two here.

More importantly, you will understand what each piece of the webpack configuration does. Why is it important? How does it relate to other pieces of the build? This understanding is critical when you inevitably want to modify or add new webpack functionality.

Migrate Tour of Heroes

First, create a new directory for your project. Before copying any of the Tour of Heroes code into your repository, create a folder named src in the root of your project. This folder will house the source code related to the browser-based Angular web application. In contrast, the project's root directory is the place for configuration files for the build and overall project. These specific directory names and locations aren't required but consider partitioning your project's source code according to the application's concerns, e.g. client versus configuration.

Tip: Clone the Angular Tour of Heroes sample application locally to copy files easily.

Create the src directory within your project and copy the entire app directory, index.html, and styles.css files from Angular Tour of Heroes into the newly created src directory. Then, copy the tsconfig.json and package.json files into the project's root directory.

Your application structure should look like this:

├── src
|   ├── app
|   |   └── (...)
|   ├── index.html
|   └── styles.css
├── package.json
└── tsconfig.json

Now, make a couple modifications to the copied content. First, delete the src\app\main-aot.ts file. Ahead-of-Time (AOT) compilation is outside the scope of this tutorial and keeping this file causes a TypeScript compilation error.

Next, modify the packages in the package.json file. Since the entire build system is changing, replace the package list under devDependencies with this list:

"devDependencies": {
    "@types/node": "^6.0.45",
    "angular2-template-loader": "^0.6.0",
    "awesome-typescript-loader": "^3.0.3",
    "css-loader": "^0.26.1",
    "extract-text-webpack-plugin": "^2.0.0-beta.5",
    "html-loader": "^0.4.3",
    "html-webpack-plugin": "^2.16.1",
    "raw-loader": "^0.5.1",
    "rimraf": "^2.5.2",
    "style-loader": "^0.13.1",
    "typescript": "~2.0.10",
    "webpack": "2.2.0",
    "webpack-dev-server": "2.2.0-rc.0",
    "webpack-replace": "^1.0.0"
}

For now, don't worry about these packages as they are noted throughout the relevant sections of the tutorial.

Now that the list of packages is updated, run npm install from the terminal to install the required packages.

Finally, replace the existing npm scripts with a single script to invoke the webpack build:

"scripts": {    
    "build": "rimraf dist && webpack"
}

Note: If you attempt to run this script from a tasks.json file in Visual Studio Code, webpack might throw an error. To work around, run the npm script from the terminal instead.

This script calls the npm package utility rimraf to clear the contents of the dist directory and then calls webpack. The dist directory will be where webpack saves the build output.

Going forward, use the build script by invoking the command npm run build in the terminal to create the build. As you iterate your build configuration, use this command to verify the output.

TypeScript Build

In most cases, an Angular application consists of TypeScript files using ES2015 modules with HTML templates and styles defined either in line with components or in external files. The webpack build needs to understand how to process this structure and bundle it into a format the browser understands.

The Angular framework requires a couple polyfill scripts from core-js and zone.js. In Tour of Heroes, these scripts are included in the index.html file prior to loading the application. Being that the bundling process uses ES2015 import statements to create the bundle, move these script references to a new file called index.ts. Later, when processing the index.html file, the build will reference this new script. Place the index.ts file in the src directory with the rest of the Angular application and add this code:

// Polyfills
import 'core-js/es6';
import 'core-js/es7/reflect';
import 'zone.js/dist/zone';

// App
import './app/main.ts';

In addition to the polyfills, this file also imports the main.ts script to bootstrap the Angular application.

Now, it's time to create the webpack configuration. By default, webpack looks for a file at the root of the working directory named webpack.config.js. Go ahead and create this file and add the following contents:

const path = require('path');

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

module.exports = {
    entry: source,
    output: {
        filename: 'index.js',
        path: destination
    },
    resolve: {
        extensions: ['.ts', '.js']
    },
    module: {
        rules: [
            {
                test: /\.ts$/,
                loaders: [
                    'awesome-typescript-loader'
                ]
            }
        ]
    }
};

At a high level, this file contains three variables: path, source, and destination. The path variable represents an imported npm module to assist in concatenating the required file and directory paths represented by source and destination.

The configuration also defines an object literal as the module's exports. The object literal is the webpack configuration and has the following properties: entry, output, resolve, and module. The entry property points to the file where webpack begins to construct the dependency tree, in this case, the index.ts file you created earlier. The output property defines the output file's name and path, in this case, dist\index.js.

The resolve property assists loading the TypeScript files. Because the import statements don't typically use a file extension, webpack uses the extensions defined in resolve.extensions to reconcile the imports. The compilation of the TypeScript files is ultimately handled by the awesome-typescript-loader. This loader is included in the project as an npm package defined in the package.json file.

The module.rules property is an array of objects that represent which tools load which files. In webpack, there is a concept of a loader. Loaders are an extension point which customize how code is processed by the webpack build. The defined loaders array is used only when an import matches the regular expression supplied in the test property.

The awesome-typescript-loader compiles TypeScript into JavaScript. The loader is used only for files that match the regular expression /\.ts$/ as defined in the test property.

With this configuration defined, test your build by typing npm run build in the terminal. Look at the newly created dist folder to see the result – one single JavaScript file index.js. Examine the contents and you will start to get a sense of how webpack packages the application dependencies.

Angular Component HTML and CSS

Compiling TypeScript to JavaScript is the first step in an Angular build. Now, you must include the contents of any external templates and styles. Components define these resources in the @Component decorator:

@Component({
    templateUrl: 'my-template.html',
    styleUrls: [ 'my-styles.css' ]
})
export class MyComponent { }

The angular2-template-loader is listed under defDependencies in the package.json file. It extends webpack to load these external files and place the contents in line with the component. The resulting code looks something like this:

@Component({
    template: '<h1>Hello World!</h1>',
    styles: ['h1 { color: blue }']
})
export class MyComponent { }

The angular2-template-loader requires that webpack understands how to load HTML and CSS files. Therefore, add another rule to the rules list to test for .css and .html files and use the raw-loader package. The raw-loader is a simple loader that emits the contents of a file as a string.

This is how the new rules configuration looks with these changes:

rules: [
    {
        test: /\.ts$/,
        loaders: [
            'awesome-typescript-loader',
            'angular2-template-loader'
        ]
    }, 
    {
        test: /\.(css|html)$/, 
        loader: 'raw-loader'
    },
]

Run the build again to test that the new configuration works. To verify that the HTML templates are included, perform a search in the emitted index.js file for a piece of code from one of the external templates such as {{hero.name}}.

The moduleId Problem

The Tour of Heroes application is designed to use SystemJS. Instead of combining the ES2015 modules into one bundle on the server, SystemJS dynamically loads all of the modules it needs when the application runs in the browser.

To assist the dynamic loading process, developers oftentimes configure the moduleId property like this:

@Component({
    moduleId: module.id,
    templateUrl: 'my-template.html',
    styleUrls: [ 'my-styles.css' ]
})
export class MyComponent { }

The value passed by module.id is populated at runtime by SystemJS. Since SystemJS is not there anymore to populate the property, it is not defined and Angular doesn't like this. It throws the error 'moduleId should be a string in "AppComponent".

One way to get around this is to manually change all of the files by removing the moduleId definitions. After all, who needs all of this boilerplate code anyway? However, you may not always have access to this code. You may be using code authored by a third party which hasn't in lined the templates as part of a build. They have moduleId defined so that consumers can use SystemJS if they choose.

For this tutorial, instead of manually removing the moduleId settings, webpack will do it for you. Use the webpack-replace loader (another npm package) to search for the moduleId settings and remove them.

The webpack rules configuration now looks like this:

rules: [
    {
        test: /\.ts$/,
        loaders: [
            'awesome-typescript-loader',
            'angular2-template-loader'
        ]
    }, 
    {
        test: /\.ts$/,
        loader: 'webpack-replace',
        query: {
            search: 'moduleId: module.id,',
            replace: ''
        }
    },
    {
        test: /\.(css|html)$/, 
        loader: 'raw-loader'
    }
]

Run your build again using npm run build and search in the output for moduleId: module – the search should return no results.

Finally, the webpack.config.js file looks like this:

const path = require('path');

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

module.exports = {
    entry: source,
    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|html)$/, 
                loader: 'raw-loader'
            }
        ]
    }
};

The working directory structure now looks like this:

├── dist
├── node_modules
├── src
|   ├── app
|   |   └── (...)
|   ├── index.html
|   ├── index.ts
|   └── styles.css
├── package.json
├── tsconfig.json
└── webpack.config.js

That's All for Now

This tutorial has taken you through the basics of building the Angular portion of the application. However, there are more pieces of the application that require processing. In part two, you handle the index.html and CSS portions of the application. In addition, you learn how to configure the webpack development server to run the application. Continue with part two here.

Angular and the Server

Angular and the Server Header

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

Coming from an ASP.NET background, developers are familiar with the File, New Project… experience. This is the process to setup a new project in Visual Studio – typically from a template to help you get started effectively.

These starter projects include everything in the box. They have UI rendering, authentication, sample data APIs, and more. And while they are great for getting started, they may not be the best design for the application you are building.

When it comes to building an Angular application, how should it integrate with the server-side application if at all? What design options are available to you and how do you know which is the right one? This is an often-overlooked topic in Angular-focused material. One the other end, server-side articles tend to encourage using server-side features without considering that you are using a very powerful front-end framework already.

This article details several application responsibilities and groups them in terms of their integration with the host application. For the purposes of this article, the host application is the server-side application that sits beneath your Angular application and serves it to the browser/client. (Spoiler alert, there might be NO application sitting beneath your Angular application.) The Angular application consists of the TypeScript and/or JavaScript you write against the Angular framework.

Level 0 – No Server Application

This level includes features that are configurable to have no server-side dependency. This means that when the application runs, Angular handles these features without requiring the server to process any of the logic. This configuration may be desirable especially when static file hosting is the preferred deployment option.

Even though there is no run-time server dependency, you can still leverage the power of a build to optimize your application. These are some of the features you can configure with no server-side backend.

Routing

The Angular router is optionally configured to use the hash URL style. In this configuration, each route is represented in the URL following the hash symbol, like example.com/#/about or example.com/#/products/1. The main benefit is that when someone enters this URL into the browser, they are going to the same endpoint each time, example.com. When the application loads, the router kicks in and navigates to the correct view.

Internationalization (i18n)

Angular includes utilities to internationalize applications. There are several phases to this process which you can read about in the documentation. At the end, you are left with multiple versions of your application that are deployed to their own directory as static files. At this point, there are several options for routing the user to the correct version of the application. The choice can be left to the user in the UI or the application can read the language settings in the browser to automatically route to the correct locale.

View Rendering

One of the first highlights of the Angular framework was its rendering. Being able to bind a JavaScript object to a template and see the data and events wire themselves up was almost magic. Angular accomplishes this in the browser and while server frameworks have their own rendering engines, you can generally rely on client-side rendering for your views when using Angular.

Level 1 – Host Application Integration

This level consists of functionality where the host application and the Angular application work together to produce the runtime functionality. While the server may not know intimately about the Angular application, both the server and client must be configured in a complimentary manner and make assumptions about the other's behavior.

Routing

As opposed to the hash style URL discussed earlier, the more common HTML 5 pushState style URL depends on server support. These URLs lack the hash symbol, for example example.com/about or example.com/products/1. The issue here is that when someone navigates to your application the first time using one of these URLs, the Angular application doesn't have a chance to handle the route. Instead, the server receives the request first and then returns the Angular application to the client to then complete the routing process. You can see an example with ASP.NET Core here.

Authentication

Sometimes, there are reasons to lock down your Angular application to only authorized users. While the Angular application could handle this based off of Web API authentication, you also have the option to authorize with the host application. For instance, the first time someone navigates to your web application, the server can return the 'not authorized' messaging without returning any of the Angular code to the browser.

View Rendering

As mentioned previously, Angular's rendering is more than capable of generating your application's UI. Generally, this rendering occurs in the browser. However, applications optimized for SEO and time-to-load performance, benefit from server-side rendering.

This is where Angular Universal fits in. Instead of using the server framework's view engine, Angular Universal renders Angular templates server-side. The browser receives the HTML and CSS to display the page immediately without client-side rendering. Subsequent views are loaded via AJAX and rendered on the client to reduce subsequent payload sizes. The framework supports both Node.js and ASP.NET Core backends.

Using Angular Universal does increase complexity and imposes certain restrictions in how you write your Angular application. Be sure that if you need this extra performance boost that you understand the trade-offs. Read more about it on GitHub.

Internationalization

Internationalization is an area where the host application may not have a direct role but may need to assist integrated routing, authentication, and/or rendering to provide the desired experience.

Logging

Logging also doesn't necessarily represent a tight integration between the host application and the Angular application. However, consider that as you increase the server's role with routing, authentication, and/or rendering, you should consider logging any errors resulting from this increased responsibility.

Level 2 – Web APIs

These dependencies are server-side dependencies. However, they do not have to live within the same web application hosting the Angular application. By keeping these dependencies in their own code base, there are many advantages. Builds and deployments are performed only for the applications that have changed. The APIs are built with whichever technology the team and/or company decide so long as it works over a common protocol – typically HTTPS. If you are familiar with the concepts of Web APIs and/or Microservices, this group encompasses those concepts.

Web APIs

Some examples of web API responsibilities that an application requires include data access, logging, and usage analytics. Interestingly, none of these concerns require any knowledge of the Angular application. In fact, they could serve many different front-end applications.

Authentication

Authentication at this level is typically handled directly between the Angular application client and the Web API server. There are many ways to do this but today's solutions generally use some form of JSON web tokens to maintain the user's identity.

Internationalization

At this level, any assets requiring i18n would be handled by the web API using server-side techniques. Again, this has no coupling to the Angular application beyond the API contract.

A Case for Bundling

Sometimes, applications are small and the API that they access is small. In this case, you may decide to keep this functionality together in the same application. They share the same build and the same deployment. Sometimes the effort to split these functions isn't greater than the payoff.

In this case, consider your development and design carefully as to avoid creating unnecessary coupling between the concerns. The web API may one day 'grow up' to need its own project so develop accordingly.

Final Thoughts

Every application is different. Hopefully by reading about how application concerns create dependencies between the server and the Angular application, you can apply these considerations to your own architecture.

One thing you might have noticed, there's no discussion of development tools that integrate with the server. This is a topic for a future post so stay tuned.

Ultimately, there is no one way to design an application. However, by knowing your options, you're better equipped to make lasting design decisions for your project.

What do you think? Is there functionality you prefer to run on the server? The client? Please share in the comments.