Click here to Skip to main content
15,884,298 members
Articles / Web Development / HTML5

Angular2 in ASP.NET MVC & Web API - Part 2

Rate me:
Please Sign up or sign in to vote.
4.94/5 (28 votes)
12 Jun 2017CPOL17 min read 60K   2.4K   36   14
In this part, we will enhance our user management application to have search/filter, global error handling and debugging functionalities.

Articles in the Series

Introduction

In ASP.NET MVC & Web API - Part 1, we learned basic Angular2 setup in ASP.NET MVC. In this part, we will learn:

  • How we can implement search/filter functionality in UserComponent to search user by FirstName, LastName or Gender using Angular2 pipe?
  • How to implement global error handling by extending ErrorHandler class?
  • How to debug the client code using Firefox debugger?

Let's Start

  1. First, go through the ASP.NET MVC & Web API - Part 1 and download the attached Angular2MVC_Finish.zip file. Extract it to your desired location in your computer and double click on Angular2MVC.sln to open the solution in Visual Studio (2015 with Update 3 or 2017).

    Image 1

  2. Since Angular2MVC solution doesn’t have the required NuGet and node_modules packages, go to Build menu and select Rebuild Solution. Visual Studio will download all the .NET packages listed in packages.config and client packages mentioned in package.json.

    Image 2

  3. You would find packages folder containing all required .NET packages and node_modules folder containing all client packages:

    Image 3

    Image 4

  4. Compile and run the application, you shouldn’t receive any error.
  5. Since all project dependiences are downloaded now, let's start implementing the Search/ filter the data functionality according to the user input, the final output page after filter implementation would be as follows:

    Image 5

    Image 6

  6. From screenshots, you might have gotten an idea how this search/filter functionality would work, as soon as user would start entering the text in Search textbox, the data would start being filtered on runtime in list after matching the user entered text with First Name, last Name and Gender fields. This is very handy and fast due to the client side filtering. I like this feature because it avoids ugly textbox with search button next to it and server side complex filtering query.
  7. So, in the next steps, we will learn how to achieve this functionality. We will user Angular2 pipe to filter the data but before jumping into the code, let’s learn what is the pipe and how to use it.
  8. Though Angular2 docs have very easy and comprehensive explanation about pipes here, for my kind of lazy people, let me summarize it for you. pipe transforms the data into the meaningful representation, for example, you are getting the date 12/03/2016 from database and want to transform it to Dec 03, 2016, You could do it through pipe. Other built-in Pipes are Uppercase, lowercase, json, etc. that are self-explanatory by their names. Why it is called pipe, because I think we use | sign to apply them to the variable or value. E.g. {{Value | uppercase}}. You can apply as many pipes as you want to the certain value delimiting by | sign, e.g., {{ birthdate | date | uppercase }}. You can also specify the parameter to the pipe by : (colon) e.g. the date filter can take format parameter, {{birthdate | date : ‘MM/dd/yyyy’}}.
  9. Now that we got the basic idea of pipe, let’s implement the user search functionality through pipe. Just like built in pipes available in Angular2, we can also implement our own custom pipes, all we need is to implement the PipeTransform interface and develop custom logic in transform method that takes two parameters, data (to be filtered from) and optional arguments, e.g., user input string to be searched in the data. To read more about custom pipes, click here.
  10. Let’s create the user filter pipe, right click on the app folder and select Add -> New Folder, name the folder as filter (or pipe, whatever you prefer):

    Image 7

  11. Right click on newly created filter folder and select Add -> TypeScript File:

    Image 8

  12. Enter the name user.pipe.ts in Item name textbox and click on OK button:

    Image 9

  13. Paste the following code in newly added user.pipe.ts file:
    JavaScript
    import { PipeTransform, Pipe } from '@angular/core';
    import { IUser } from '../Model/user';
    
    @Pipe({
    name: 'userFilter'
    })
    
    export class UserFilterPipe implements PipeTransform {
    
    transform(value: IUser[], filter: string): IUser[] {
    filter = filter ? filter.toLocaleLowerCase() : null;
    return filter ? value.filter((app: IUser) =>
    app.FirstName != null && app.FirstName.toLocaleLowerCase().indexOf(filter) != -1
    || app.LastName != null && app.LastName.toLocaleLowerCase().indexOf(filter) != -1
    || app.Gender != null && app.Gender.toLocaleLowerCase().indexOf(filter) != -1
    
       ) : value;
    
      }
    }
  14. Let’s understand what we just added in user.pipe.ts:
    1. In the first line, we are importing the PipeTransform and Pipe interfaces that we are implementing to achieve filtering functionality.
    2. In the second line, we are importing the IUser interface that we created in the first part to hold the list of users. Over here, we are also using it to hold the list of users that is the source data for filtering.
    3. In the next line, we are specifying the pipe selector/name userFilter through which we will use the pipe (you will find in future steps, how).
    4. Next, we are creating the UserFilterPipe class that is implementing the PipeTransform interface (implementing interface means providing the body to all methods mentioned in the interface).
    5. Right click on PipeTransform and select the option Go To Definition:

      Image 10

    6. You will be landed to the pipe_transform_d.ts file where you will find the nice brief description how to use the pipe with an example and transform method that we must need to implement:

      Image 11

    7. So let’s go back to user.pipe.ts where can see we have transform method with first argument as IUser array and second is named as filter that is the input string to be searched in the IUser array.
    8. In transform method, the first line is only to check if the filter is not null.
    9. The next statement is the actual implementation of search, if you are a C# developer, you can compare it to the LINQ to Object. We are calling Array’s filter method, checking through conditional operator that if any of IUser member (FirstName, LastName or Gender) is matching with user input search string and if YES, returning the filtered result. toLocaleLowerCase method is converting string to lower case, to read more about it, click here. If there is no matching record in User list, we are returning all rows.
  15. Now that we have our filter ready, let’s add it to the AppModule to use it in our application, double click on app.module.ts file in app folder to edit it:

    Image 12

  16. Update the AppModule according to the following screenshot:

    Image 13

  17. We added the UserFilterPipe reference by import statement and in the declaration section. Just for revision, components in declaration sections know each other, that means, we can use UserFilterPipe in UserComponent (or in any other component) without adding the reference in UserComponent itself. We can declare components, pipes, etc. in declaration section.
  18. So, our user filter/search functionality is ready, the next step is to use it in UserComponent but instead of directly using it in UserComponent, let’s create the shared SearchComponent that all components can share, this will help us to understand the:
    1. Interaction between parent (UserComponent) and child (SearchComponent) components.
    2. How to send the input parameters through @Input and get the value back through @Output aliases.
  19. Right click on Shared folder in main app folder and select Add -> TypeScript File:

    Image 14

  20. Enter the Item name as search.component.ts and click on OK button:

    Image 15

  21. Copy the following code in search.component.ts file and let’s understand it step by step:
    JavaScript
    import { Component, Input, Output, EventEmitter } from '@angular/core';
    
     @Component({
     selector: 'search-list',
      template: `<div class="form-inline">
                    <div class="form-group">
                      <label><h3>{{title}}</h3></label>
                    </div>
              <div class="form-group">
                <div class="col-lg-12">
                  <input class="input-lg" placeholder="Enter any text to filter" 
                         (paste)="getPasteData($event)" 
                         (keyup)="getEachChar($event.target.value)" 
                         type="text" [(ngModel)]="listFilter" />
                         <img src="../../images/cross.png" class="cross-btn" 
                         (click)="clearFilter()" *ngIf="listFilter"/>
               </div>
             </div>
             <div class="form-group">
                 <div *ngIf='listFilter'>
               <div class="h3 text-muted">Filter by: {{listFilter}}</div>
            </div>
          </div>
         </div> `
    })
    
     export class SearchComponent {
    
       listFilter: string;
       @Input() title: string;
       @Output() change: EventEmitter<string> = new EventEmitter<string>();
    
       getEachChar(value: any) {
                  this.change.emit(value);
            }
    
        clearFilter() {
         this.listFilter = null;
         this.change.emit(null);
        }
    
        getPasteData(value: any) {
          let pastedVal = value.clipboardData.getData('text/plain');
          this.change.emit(pastedVal);
           value.preventDefault();
          }
    }
    1. In the first line, we are importing Input, Output interfaces and EventEmitter class. Input and Output interfaces are self-explanatory, to take the input parameter from UserComponent (in our case, the search string from user), Output is to send the value back from SearchComponent but it is little interesting, the output is sent back through event using EventEmitter class. This will get more clear in the further steps.
    2. In the next line, we are providing the Component metadata, i.e., selector (tag name through which we will use SearchComponent in UserComponent, e.g., <search-list></search-list>). template is the HTML part of component. You can also put it in a separate HTML file and specify the templateUrl property instead but since this is quite slim, I would prefer to have it in the same file.
    3. In SearchComponent class, we are declaring one local variable listFilter that is search string we will use to display here <div class="h3 text-muted">Filter by: {{listFilter}}</div>. That is only for cosmetic purposes to show what we are searching.
    4. The second variable title is with @Input decorator, we will send search textbox title from UserComponent. The third variable change is with @Output decorator and of EventEmitter type. This is how we send data back to parent component. change EventEmitter<string> means change is an event that parent component needs to subscribe and will get string argument type. We will explicitly call emit function (i.e., change.emit(“test”)) to send the value back to the parent component.
    5. getEachChar(value: any): This function will be called for every character user will enter in search textbox. We are only calling this.change.emit(value); that is sending that character to parent component where it is being sent to the UserFilterPipe pipe to be filtered from User list. Just for revision, in UserPipeFilter, we are comparing that character with FirstName, LastName and Gender and returning only those records where this character(s) exist. So as long the user would be entering characters in Search textbox, data would be filtering on runtime.
    6. clearFilter(): Will clear the filter to reset the User list to default without any filtering.
    7. getPasteData(value: any): This is a slightly interesting function that will take care if user would copy search string from somewhere and paste it in search textbox to filter the Users list. Through value.clipboardData.getData('text/plain'), we are getting the pasted data and sending it through change.emit(value) function to parent component.
    8. Now, we have some idea about these functions, if you jump back to SearchComponent template (HTML). We are calling getEachChar on keyup event that will trigger every time user would type in Search textbox, getPasteData is being called on paste event that will occur when user would paste value in Search textbox, clearFilter function would be called on clicking the cross image that would only be visible if search textbox would have at least one character.
  22. So we are done creating the SearchComponent and hopefully you got an idea how it would work, let’s add it in AppModule so that we can use it, double click on app -> app.module.ts to edit it:

    Image 16

  23. Add the following import statement:
    JavaScript
    import { SearchComponent } from './Shared/search.component';
  24. Add SearchComponent in declarations section to use it in any component:
    JavaScript
    declarations: [AppComponent, UserComponent, 
                   HomeComponent, UserFilterPipe, SearchComponent],

    Image 17

  25. So now our SearchComponent is ready to be used, let’s use it in our UserComponent. Double click on app -> Components -> user.component.html to edit it:

    Image 18

  26. We will add SearchComponent on top of the User list, so append the following div on top of Add button:
    HTML
    <div>
       <search-list [title]='searchTitle' (change)="criteriaChange($event)"></search-list>
    </div>
  27. Let’s understand it, it looks like normal HTML but with search-list tags. If you remember, this is the selector property for SearchComponent that we defined in search.component.ts file. If you remember in Part 1, we learned about Property Binding [ ], that is used to send data from parent component to child component. We are assinging value to child's component title variable through searchTitle variable that we defined in UserComponent. Second is event binding ( ), we created change event in SearchComponent and we are providing the function criteriaChange in UserComponent that will execute every time when change event will occur. $event will hold any value that change event will send, in our case, we are sending each character user will enter in search text box (refer to getEachChar function in SearchComponent). This is how we get value back from child component.
  28. Since we specified criteriaChange function in event binding of search-list, let’s add it in our UserComponent. Double click on app -> Components -> user.component.ts to edit it:

    Image 19

  29. Add the following function in user.component.ts:
    JavaScript
    criteriaChange(value: string): void {
        if (value != '[object Event]') 
        this.listFilter = value;
    }
  30. You can see that we are getting the input parameter value (user entered text in search textbox) from change event and assigning it to listFilter variable that we will use for our pipe filter. Let’s go ahead and declare listFilter variable. Add the following line with other variable declaration statements:
    JavaScript
    listFilter: string;
  31. So far, we have created the SearchComponent that has one textbox with cross image button next to it to clear the search along read-only display of user search text. In parent UserComponent, we subscribed the change event and getting each character of user input in search textbox and assigning it to listFilter variable where it is getting cumulative (e.g. user enters character 'a', it would be sent to filter where all records containing 'a' would be filtered, after 'a' if user would any other character like 'f', then both 'a' and 'f' would be sent as "af" to filter and all records with both "af" combination would be filtered and so on). You will get it once you would start using it or you can debug it that I am explaining in upcoming steps). So, the final step is how to filter the user list according to search text entered in search textbox? So, refresh your pipe knowledge from previous steps and update <tr *ngFor="let user of users"> in app->Components -> user.component.html to <tr *ngFor="let user of users | userFilter:listFilter">. Where userFilter is the filter we created in earlier steps and listFilter is the input parameter to filter.
  32. Since we used [(ngModel)] for listFilter variable for two-way data binding that is defined in FormsModule, let’s add it in AppModule, update the:
    JavaScript
    import { ReactiveFormsModule } from '@angular/forms';

    to:

    JavaScript
    import { FormsModule, ReactiveFormsModule } from '@angular/forms'; in AppModule.
  33. Add the formsModule in imports section.

    Image 20

  34. Compile and run the project in any browser (Firefox or Chrome is recommended). Go to UserManagement Page, right now there may be few records, you can go ahead and add 15, 20 more. Start typing the First Name, Last Name or Gender in Search textbox and you would see records getting filtered in runtime:

    Image 21

  35. That’s all with our filtering.
  36. Next, we will learn about Error Handling in Angular2, I will keep it simple and wouldn’t go in every error type but would let you know how you can have custom error for each error type. For quick reference about Angular2 ErrorHandler class, click here.
  37. For custom error handler class, we can extend ErrorHandler class that has constructor and handleError method with error parameter. error parameter has complete error information, e.g., status, error code, error text, etc. depends on error type (HTTP, Application, etc.). that really helps to customize the error message. ErrorHandler handles any kind of error, e.g., undeclared variable/function, any data exception or HTTP error. Besides ErrorHandler, I will also explain how you can debug the code in Firefox browser (You can also debug it in Chrome or Internet Explorer).
  38. We will comment out error handling code from UserService and UserComponent so that we can capture all errors in ErrorHandler class, then we examine the error using Firefox debugger. So, let’s start.
  39. First of all, let’s create custom error handler class. Right click on app -> Shared folder and select Add -> TypeScript File:

    Image 22

  40. Enter the name errorhandler.ts and click on OK button:

    Image 23

  41. Paste the following code in the newly created file:
    JavaScript
    import { ErrorHandler } from '@angular/core';
    
     export default class AppErrorHandler extends ErrorHandler {
    
        constructor() {
           // We rethrow exceptions, so operations like 'bootstrap' will result in an error
           // when an error happens. If we do not rethrow, bootstrap will always succeed.
           super(true);
       }
    
    handleError(error: any) {
         debugger;
         alert(error);
         super.handleError(error);
       }
    }
  42. The code is quite self-explanatory with comments, i.e., why we are calling super(true) in constructor. AppErrorHandler is our custom class that is extending Angular2 ErrorHandler class and implementing the handleError function. In handleError, I put debugger to show you what error will come and how you can customize it. We are showing the error message by simple JavaScript alert function.
  43. First, let’s see the HTTP error. Assume we have authentication logic before loading all users from database and request is somehow not authenticated, we will send not authorized (401) error to Angular2 from ASP.NET Web API. Let’s get this error in AppErrorHandler and examine it.
  44. Next, add the AppErrorHandler in AppModule to capture the all error. Add the following import statement:
    JavaScript
    import AppErrorHandler from './Shared/errorhandler';
  45. Update the provider section to have ErrorHandler:
    JavaScript
    providers: [{ provide: ErrorHandler, useClass: AppErrorHandler },
                { provide: APP_BASE_HREF, useValue: '/' }, UserService]
  46. We are telling our module to use our custom error handler for any kind of error. Don’t forget to add the ErrorHandler class reference in @angular2/core import statement:

    Image 24

  47. Let’s comment the Error handling in UserComponent.ts file in app -> Components folder. Double click to edit it. Go to LoadUsers function and update it as follows:
    JavaScript
    LoadUsers(): void {
         this.indLoading = true;
            this._userService.get(Global.BASE_USER_ENDPOINT)
                  .subscribe(users => { this.users = users; this.indLoading = false; }
                         //,error => this.msg = <any>error
                            );
                      }
  48. You can see that I commented out the error statement that was saving in msg variable to show at the bottom of screen.
  49. Next, let’s comment the error handling in user.service.ts file, find it in app -> Service folder and double click on it to edit. Update the get method as follows, I commented the catch statement:
    JavaScript
    get(url: string): Observable<any> {
    
         return this._http.get(url)
                      .map((response: Response) => <any>response.json());
                    // .do(data => console.log("All: " + JSON.stringify(data)))
                    // .catch(this.handleError);
    }
  50. Now our client code is ready to capture HTTP exception, let’s add the unauthorized exception code in UserAPIController (Basically, we will add it in BaseAPIController and call it in UserAPIController).
  51. Go to Controllers folder and double click on BaseAPIController.cs to edit it:

    Image 25

  52. Add the following ErrorJson function that is actually the copy of ToJson method but just with Unauthorized status code (I just created it for sample, you should create more professional error handling code for HTTP calls):
    C#
    protected HttpResponseMessage ErrorJson(dynamic obj)
    {
       var response = Request.CreateResponse(HttpStatusCode.Unauthorized);
       response.Content = new StringContent
         (JsonConvert.SerializeObject(obj), Encoding.UTF8, "application/json");
         return response;
    }
  53. Since I do not have any authentication logic so far, so in UserAPIController, I am only updating the Get() method as follows, just replaced the ToJson function to ErrorJson, now API will always throw Unauthorized exception when we will try to load the users:
    C#
    public HttpResponseMessage Get()
       {
          return ErrorJson(UserDB.TblUsers.AsEnumerable());
      }
  54. Compile and run the project, go to User Management page. After few moments, you would see an ugly alert message like the following:

    Image 26

  55. Great, so our test environment is successfully created. We sent this error from UserAPI Get() method to client where it is being captured in our custom AppErrorHandler.
  56. Let’s debug the error, in errorhandler.ts file, click on gray bar next to debugger to setup the break point:

    Image 27

  57. Run the application in Firefox, press the Ctrl+Shift+S or click on open menu button => Developer => Debugger:

    Image 28

  58. You should end up with the following screen:

    Image 29

  59. Go to User Management page, after few moments, you would see the execution gets stopped at debugger:

    Image 30

  60. Mouse hover on error and you would see all the parameters in error:

    Image 31

  61. Since this is an HTTP error, you can see the HTTPStatusCode that is 401 (Unauthorized request), the body section still has data that definitely you are never going to send back, instead you can send user friendly error message here.
  62. By considering these error parameters, we can extend our error handling by checking the status code. Let’s do it.
  63. Update the handleError as following in errorhandler.ts file:
    JavaScript
    handleError(error: any) {
        debugger;
        if (error.status == '401')
              alert("You are not logged in, please log in and come back!")
          else
            alert(error);
    
        super.handleError(error);
    }
  64. Compile and run the application again, go to User Management page again. You would see the following user friendly error message now:

    Image 32

  65. The Firefox debugger is an awesome tool for debugging client code, spend some time exploring more useful features. You can step to next line, into the function or step out by highlighted buttons:

    Image 33

  66. Next, let’s mess up with our application and check what would be in error variable through Firefox debug. Double click on app => Components => home.component.ts to edit it.
  67. Enter the following HTML in template section:
    HTML
    <button class="btn btn-primary" (click)="IdontExist()">ErrorButton</button>
  68. Final template should be as follows:

    Image 34

  69. I added one button with click event that is calling IdontExist() function that doesn’t exist in HomeComponent.
  70. Let’s run the application and then run the debugger, you would see a stupid ErrorButton in the middle of the screen:

    Image 35

  71. Click on ErrorButton, again you would see the execution stopped at debugger (breakpoint), mouse hover on error, browse the parameter in pop up or click on watch link at bottom put the error variable on right side Variables section:

    Image 36

  72. You can see this whole bunch of new information, expand the originalError section and you would see the actual error:

    Image 37

  73. You can see very detailed information to dig down the complex error.
  74. Press the Resume button on the left side to continue the execution:

    Image 38

  75. You would see the brief error message:

    Image 39

  76. Debugging is a great tool to get complete information on the client side.

History

  • 5/13/2017: Created

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Architect
United States United States
A Solutions Architect with more than fourteen years of experience in application development. I mostly work in .NET, Angular, MEAN stack technologies and love to share what I do and learn during my day to day job. Please check my tutorials and blog:
https://fullstackhub.io
https://fullstackhubblog.com

Comments and Discussions

 
QuestionIs part 3 coming Pin
vijaypsi14-Mar-19 3:16
vijaypsi14-Mar-19 3:16 
AnswerRe: Is part 3 coming Pin
Yaseer Mumtaz24-Apr-19 8:29
professionalYaseer Mumtaz24-Apr-19 8:29 
GeneralMy vote of 5 Pin
Member 1359144829-Apr-18 8:00
Member 1359144829-Apr-18 8:00 
QuestionProblem with generated WebAPI Controller using Entity framework Pin
ogz25-Jan-18 11:36
ogz25-Jan-18 11:36 
GeneralMy vote of 4 Pin
Member 366988926-Dec-17 18:42
Member 366988926-Dec-17 18:42 
GeneralMy vote of 3 Pin
cdavide75@gmail.com14-Dec-17 23:43
cdavide75@gmail.com14-Dec-17 23:43 
QuestionHow to publish the npm packages Pin
Daniel Raj3-Dec-17 7:00
Daniel Raj3-Dec-17 7:00 
Suggestionlazy load suggest Pin
spritekuang19-Oct-17 1:10
spritekuang19-Oct-17 1:10 
Questionwhy not use asp.net core 2.0 Pin
ovisariesdk8-Oct-17 23:43
ovisariesdk8-Oct-17 23:43 
AnswerRe: why not use asp.net core 2.0 Pin
Yaseer Mumtaz9-Oct-17 3:36
professionalYaseer Mumtaz9-Oct-17 3:36 
QuestionQuery In Filter Pin
Munjal Pandya23-Sep-17 4:49
Munjal Pandya23-Sep-17 4:49 
AnswerRe: Query In Filter Pin
Marcelo Mohr Maciel26-Oct-17 20:38
Marcelo Mohr Maciel26-Oct-17 20:38 
QuestionBraces for Imports Pin
frazGJF22-Aug-17 16:17
frazGJF22-Aug-17 16:17 
AnswerRe: Braces for Imports Pin
Yaseer Mumtaz25-Aug-17 4:53
professionalYaseer Mumtaz25-Aug-17 4:53 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.