Tuesday, 24 April, 2018 UTC


Summary

Displaying feedback to your users is one of those common, cross-cutting (boring!) requirements that should be baked in to your application’s framework. That’s just as true with old-school ASP.NET MVC as it is with ASP.NET Core and Angular. In this post, I’ll show you how you can bake this capability in to a modern Angular app with ASP.NET Core on the backend!
What We’ll Build
We’ll take the same approach we did in our Bootstrap-based notifications. We’ll decorate our ASP.NET Core action results using an extension method:
[HttpGet]
public IActionResult Success()
{
    return Ok(new {id = 1, name = "success"}).WithSuccess("It worked!", "All is well...");
}
Client-side, we’ll automatically grab the status and display it using angular2-notifications:
Jaspero’s angular2-notifications: A light and easy to use notifications library for Angular 2.
We’ll only write that code once, so client-side, our normal API calls will be free of any boring, repetitive logic, leaving us with simple, clean code:
async create(form: Widget) {
    const result = await.httpClient.post('/some/api', form).toPromise();
    
    // No need to manually show feedback!
}
Let’s get stated!
Project Setup
I’m going to creat a new ASP.NET Core 2.1 (preview) project with Angular. I’ve already fixed the bugs in the template and updated it to provide a better dev-time experience, like I described, so check that post if you want more information.
We’ll also need Jaspero’s angular2-notifications package, which we can install with npm:
npm i --save angular2-notifications
And we’ll need to import the module in our app.module.ts, along with the BrowserAnimationModule:
//...snip
import { SimpleNotificationsModule } from 'angular2-notifications';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
//...snip

@NgModule({
  declarations: [
    AppComponent,
    NavMenuComponent,
    HomeComponent,
    CounterComponent,
    FetchDataComponent
  ],
  imports: [
    BrowserModule.withServerTransition({ appId: 'ng-cli-universal' }),
    HttpClientModule,
    BrowserAnimationsModule,
    FormsModule,
    RouterModule.forRoot([
      { path: '', component: HomeComponent, pathMatch: 'full' },
      { path: 'counter', component: CounterComponent },
      { path: 'fetch-data', component: FetchDataComponent },
    ]),
    SimpleNotificationsModule.forRoot()
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }
The final step is to add the simple-notifications component somewhere that is available globally throughout our app. The simplest thing is to just add it to app.component.html, like so:
<div class='container-fluid'>
  <div class='row'>
    <div class='col-sm-3'>
      <app-nav-menu></app-nav-menu>
    </div>
    <div class='col-sm-9 body-content'>
      <router-outlet></router-outlet>
    </div>
  </div>
</div>
<simple-notifications></simple-notifications>
Now we can inject and use NotificationService to display the various types of notifications that are supported:
import { Component, OnInit } from '@angular/core';
import { NotificationsService } from 'angular2-notifications';

@Component({
  selector: 'app-home',
  templateUrl: './home.component.html',
})
export class HomeComponent implements OnInit {

  constructor(private notifications: NotificationsService) {
  }

  ngOnInit() {
    this.notifications.success('Hi!', 'Everything is initialized!');
  }
}

We’ll come back and use simple-notifications in a bit. But for now, let’s see what we need to do server-side!
Implementing Notification Extension Methods
Now we need a way to attach a notification to a response before it goes back down to our Angular app. We’re going to follow the same approach as we took for our Bootstrap-based approach, but we’ll simplify it a bit since we aren’t going to worry about ViewResults.
Here’s what our extension methods look like:
    public static class NotificationExtensions
    {
        public static IActionResult WithSuccess(this IActionResult result, string title, string body)
        {
            return Notification(result, "success", title, body);
        }

        //snip: repeat for the other notification types!
    }
And our simplified decorator:
    public class NotificationDecoratorResult : IActionResult
    {
        public IActionResult Result { get; }
        public string Type { get; }
        public string Title { get; }
        public string Body { get; }

        public NotificationDecoratorResult(IActionResult result, string type, string title, string body)
        {
            Result = result;
            Type = type;
            Title = title;
            Body = body;
        }

        public async Task ExecuteResultAsync(ActionContext context)
        {
            context.HttpContext.Response.Headers.Add("x-notification-type", Type);
            context.HttpContext.Response.Headers.Add("x-notification-title", Title);
            context.HttpContext.Response.Headers.Add("x-notification-body", Body);

            await Result.ExecuteResultAsync(context);
        }
    }
Now we can add a notification to any IHttpActionResult that we return:
return Ok(new {id = 1, name = "success"})
         .WithSuccess("It worked!", "All is well...");
Easy enough!
When a decorated action is executed, it will add three headers to the response: x-notification-type, x-notification-title, and x-notification-body.
We just need to process those headers client-side.
Displaying Notifications Automatically
So our API responses are going to have some new headers that tell us about the notification to show. We could handle those headers manually, writing code like this each and every time we invoke our API:
    const response = await this.httpClient.get<HttpResponse<string>>(`/api/some/action`, { observe: 'response' }).toPromise();

    const type = response.headers.get('x-notification-type');
    const title = response.headers.get('x-notification-title');
    const body = response.headers.get('x-notification-body');

    if (type && title && body) {
      this.notifications.create(title, body, type);
    }
But that’s terrible. We don’t want to waste time repeating that pattern. And we don’t have to!
Instead, we can leverage implement an HttpInterceptor.
This simple interceptor will do the trick:
@Injectable()
export class NotificationInterceptorService implements HttpInterceptor {

  constructor(
    //The angular2-notification service
    private notifications: NotificationsService,
    ) { }

  intercept(request: HttpRequest<any>, next: HttpHandler) {

    return next.handle(request)
      .do((ev: HttpEvent<any>) => {
        if (ev instanceof HttpResponse) {
          const headers = ev.headers;

          const type = headers.get('x-notification-type');
          const title = headers.get('x-notification-title');
          const body = headers.get('x-notification-body');

          if (type && title && body) {
            this.notifications.create(title, body, type);
          }
        }
      });
  }
}
And that’s it!
Our components can remain blissfully ignorant of these status messages:
    const result = await this.httpClient.post(`/api/widget/activate`, {id: 2}).toPromise();
And our interceptor will take care of displaying them:
Final Thoughts
So there you have it: the same pattern I’ve shown before, but this time in Angular.
If you want to check out the code, the repository is on Github.
So what do you think? Useful? Not useful? Let me know in the comments!
I’ve got more Angular and ASP.NET Core content in the works, but if there are specific topics you’d like to see, please drop me a note, and I’ll do my best to accommodate!