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

0
110








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 Visual Studio (2015 with Update 3 or 2017).

  1. Since Angular2MVC solution doesn’t have 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.

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

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

  1. 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 be starting 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 on next to it and server side complex filtering query.
  2. 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 coding, let’s learn what is the pipe and how to use it.
  3. Though Angular2 docs has very easy and comprehensive explanation about pipes here but 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’}}.
  4. 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.
  5. 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):

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

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

  1. Paste the following code in newly added user.pipe.ts file:
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;

 }
}
  1. 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 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 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:

    1. 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:

    1. 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.
    2. In transform method, the first line is only to check if the filter is not null.
    3. The next statement is the actual implementation of search, if you are 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 the all rows.
  1. 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:

  1. Update the AppModule according to the following screenshot:

  1. 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.
  2. So, our user filter/search functionality is ready, 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.
  3. Right click on Shared folder in main app folder and select Add -> TypeScript File:

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

  1. Copy the following code in search.component.ts file and let’s understand it step by step:
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 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 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 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 purpose to show what we are searching.
    4. Second variable title is with @Input decorator, we will send search textbox title from UserComponent. 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 little 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 got some idea about these function, 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.
  1. 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:

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

  1. 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:

  1. We will add SearchComponent on the top of the User list, so append following div on the top of Add button:
<div>
 <search-list [title]='searchTitle' (change)="criteriaChange($event)"></search-list>
</div>
  1. 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.
  2. 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:

  1. Add following function in user.component.ts:
criteriaChange(value: string): void {
 if (value != '[object Event]') 
 this.listFilter = value;
}
  1. You can see 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 following line with other variable declaration statements:
listFilter: string;
  1. 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.
  2. 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
import { ReactiveFormsModule } from '@angular/forms';

to

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

  1. 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:

  1. That’s all with our filtering.
  2. 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.
  3. 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 help 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 IE).
  4. 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.
  5. First of all, let’s create custom error handler class. Right click on app -> Shared folder and select Add -> TypeScript File:

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

  1. Paste the following code in newly created file:
import { ErrorHandler } from '@angular/core';

 export default class AppErrorHandler extends ErrorHandler {

 constructor() {
 
 
 super(true);
 }

handleError(error: any) {
 debugger;
 alert(error);
 super.handleError(error);
 }
}
  1. 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.
  2. 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.
  3. Next, add the AppErrorHandler in AppModule to capture the all error. Add following import statement:

import AppErrorHandler from './Shared/errorhandler';

  1. Update the provider section to have ErrorHandler:
providers: [{ provide: ErrorHandler, useClass: AppErrorHandler },{ provide: APP_BASE_HREF, useValue: '/' }, UserService]
  1. 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:

  1. 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 following:
LoadUsers(): void {
 this.indLoading = true;
 this._userService.get(Global.BASE_USER_ENDPOINT)
 .subscribe(users => { this.users = users; this.indLoading = false; }
 
 );
 }
  1. You can see I commented out the error statement that was saving in msg variable to show at the bottom of screen.
  2. 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 following, I commented the catch statement:
get(url: string): Observable<any> {

 return this._http.get(url)
 .map((response: Response) => <any>response.json());
 
 
}
  1. 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).
  2. Go to Controllers folder and double click on BaseAPIController.cs to edit it:

  1. Add 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):
protected HttpResponseMessage ErrorJson(dynamic obj)
{
 var response = Request.CreateResponse(HttpStatusCode.Unauthorized);
 response.Content = new StringContent(JsonConvert.SerializeObject(obj), Encoding.UTF8, "application/json");
 return response;
}
  1. Since I do not have any authentication logic so far, so in UserAPIController I am only updating the Get() method as following, just replaced the ToJson function to ErrorJson, now API will always throws Unauthorized excpetion when we will try to load the users:
public HttpResponseMessage Get()
 {
 return ErrorJson(UserDB.TblUsers.AsEnumerable());
 }
  1. Compile and run the project, go to User Management page. After few moment you would see ugly alert message like following:

  1. 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.
  2. Let’s debug the error, in errorhandler.ts file, click on gray bar next to debugger to setup the break point:

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

  1. You should end up with following screen:

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

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

  1. Since this is HTTP error, you can see the HTTPStatusCode that is 401 (Unauthorized request), the body section still has data that definitely you never going to send back, instead you can send user friendly error message here.
  2. By considering these error parameter, we can extend our error handling by checking the status code. Let’s do it.
  3. Update the handleError as following in errorhandler.ts file:
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);
}
  1. Compile and run the application again, go to User Management page again. You would see following user friendly error message now:

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

  1. 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.
  2. Enter the following html in template section:
<button class="btn btn-primary" (click)="IdontExist()">ErrorButton</button>
  1. Final template should be as following:

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

  1. 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:

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

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

  1. You would see the brief error message:

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

History

Created: 5/13/2017


License


Share



About the Author



I am senior .Net developer and tech lead in one of leading financial company in Virginia. My area of experience is web development using ASP.NET MVC, C#, JQuery, SQL Server and other sever side, client side and databases technologies.

Please follow my blog for more useful posts: http://www.yasserblog.com
WyzAnt: https://www.wyzant.com/Tutors/yaseer




Comments and Discussions






— There are no messages in this forum —

LEAVE A REPLY