Tuesday, August 28, 2018

Angular7 Creating First Application

Below you will find how to configure the solution architecture for Angular 7 using Visual Studio Code as editor.

Below are the prerequisites to create Angular 7 Application:

Use below instructions/steps to start creating your first Angular 7 application:

  • Create new folder in your drive where you want to keep the code base
  • Launch Visual Studio Code editor and select the created folder using File -> Open -> Select Folder
  • Create new file and name it as package.json under the project folder and paste below code. This file contains all the dependencies of the project.
  • {
      "name": "angular7-demo",
      "version": "0.0.0",
      "description": "QuickStart package.json from the documentation, supplemented with testing support",
      "scripts": {
        "ng": "ng" ",
        "start": "ng serve" ",
        "build": "ng build",
        "test": "ng test",
        "lint": "ng lint",
        "e2e": "ng e2e"
      },
      "private": true,
      "dependencies": {
        "@angular/animations": "~7.0.0",
        "@angular/common": "~7.0.0",
        "@angular/compiler": "~7.0.0",
        "@angular/core": "~7.0.0",
        "@angular/forms": "~7.0.0",
        "@angular/http": "~7.0.0",
        "@angular/platform-browser": "~7.0.0",
        "@angular/platform-browser-dynamic": "~7.0.0",
        "@angular/router": "~7.0.0",
        "angular-font-awesome": "^3.1.2",
        "core-js": "^2.5.4",
        "font-awesome": "^4.7.0",
        "ngx-pagination": "^3.2.1",
        "rxjs": "~6.3.3",
        "rxjs-compat": "^6.3.3",
        "underscore": "^1.9.1",
        "zone.js": "~0.8.26"
      },
      "devDependencies": {
        "@angular-devkit/build-angular": "~0.10.0",
        "@angular/cli": "~7.0.2",
        "@angular/compiler-cli": "~7.0.0",
        "@angular/language-service": "~7.0.0",
        "@types/jasmine": "~2.8.8",
        "@types/jasminewd2": "~2.0.3",
        "@types/node": "~8.9.4",
        "bootstrap": "^3.3.7",
        "codelyzer": "~4.5.0",
        "jasmine-core": "~2.99.1",
        "jasmine-spec-reporter": "~4.2.1",
        "karma": "~3.0.0",
        "karma-chrome-launcher": "~2.2.0",
        "karma-coverage-istanbul-reporter": "~2.0.1",
        "karma-jasmine": "~1.1.2",
        "karma-jasmine-html-reporter": "^0.2.2",
        "protractor": "~5.4.0",
        "ts-node": "~7.0.0",
        "tslint": "~5.11.0",
        "typescript": "~3.1.1"
      }
    }
  • Create another file and name it as tsconfig.json and paste below code. This file helps guide compiler as it generates JavaScript files.
  • {
      "compileOnSave": false,
      "compilerOptions": {
        "baseUrl": "./",
        "outDir": "./dist/out-tsc",
        "sourceMap": true,
        "declaration": false,
        "module": "es2015",
        "moduleResolution": "node",
        "emitDecoratorMetadata": true,
        "experimentalDecorators": true,
        "target": "es5",
        "typeRoots": [
              "node_modules/@types"
        ],
        "typeRoots": [
              "es2018",
              "dom"
        ]
      }
    }
  • Create another file and name it as tslint.json. This is a static analysis tool which will help to check code readability, maintainability, and functionality errors.
  • {
      "rulesDirectory": [
        "arrow-return-shorthand": true
      ]
    }
  • Launch Integrated Command Terminal using View tab to install dependencies. Use npm install command to start installing the dependencies.
  • After above step, you should see node_modules and package-lock.json files created in your solution. Which is a good sign, if not, check back
  • Create two more configuration files to handle version controlling and editor configurations namely .gitignore and .editorconfig
  • Create new folder and name it as src which contains all application related folders and files in it.

You are good to run your first Angular 7 application. Using Integrated Command Terminal run ng serve --open command to launch and run the above created application.

Happy Programming !!!

Angular7 Environment Features and Components

Coming Soon ...

Differences between Angular2 and Angular7

Coming Soon ...

Differences between AngularJS and Angular2

Here are some of the differences between AngularJS and Angular 2

Feature AngularJS Angular 2
Introduction

AngularJS is a JavaScript framework which is used to build web applications. It was released by Google and is developed using TypeScript and JavaScript and uses HTML. And also uses Controllers and Scope objects.

This is not an upgraded version of AngularJS but completely rewritten with lot of improvements and released in 2016 with the goal of building complicated applications in a feasible way. Angular2 follows component-based UI and no more controllers and scope in objects in this. This is currently build uing TypeScript but also compatible with ES5 & ES5 JavaScript standards.

Prerequisites

JavaScript and HTML alone

Basic JavaScript, CSS, HTML OOP concepts and any programming language knowledge like C++, C# or Java

File Type

Uses JavaScript

Uses TypeScript

Binding

Supports both one way (<div>ng-bind="message"</div>) and two way binding (Ex: {{"message"}})

Supports both one way and two way binding but

  • One way binding supports
    Interpolation (<div>message </div>) and
    Property binding (<img [src]="imagePath" />)
  • In Two way binding you need to use [(message)] parenthesis inside brackets

Bootstrapping

Using ng-app and code are the two ways to Bootstrap AngularJS

The change is, we connect angular components to view and not modules

Routing

Performance

Watchers are killers in AngularJS, performance will hit as the number of watchers are more here

As Angular2 uses DI hierarchical system, this is much more faster than AngularJS

Mobile Support

Does not have mobile support. Using 3rd party frameworks one can achieve the functionality

Is mobile oriented as it is designed from ground up with mobile support

Services

Some of various ways to create services are like Service, Factory, Provider etc..

There is only one way t use services

Filters

Are used to filter result sets

These are renamed as Pipes and no change in functionality or purpose of the same

Angular 4 over Angular 2

Smaller & Faster Apps Comparing with Angular 2, these applications are smaller & faster
Reduced View Engine Size Changes under hood to what AOT generated code compilation that means in Angular 4, improved compilation time. These changes reduce around 60% size in most cases
Animation Package Animations now have their own package which ic @angular/platform-browser/animations
Improvement Improved *ngIf and *ngFor
Template The template is now ng-template. You should use the "ng-template" tag instead of template.
NgIf with Else Angular 4 uses else syntax:
<div *ngIf="user.length > 0; else empty"> <h2> Users </h2> </div>
As Keyword A new addition to the template syntax is the "as" keyword is use to simplify to the "let" syntax :
<div *ngFor="let user of users | slice:0:2 as total; index as = i >
       {{i+1}}/{{total.length}}; {{user.name}}
</div>

To subscribe only once to a pipe "|" with "async" and if a user is an observable, you can now use to write :
<div *ngIf="users | async as usersModel;>
       <h2> {{UserModel.name}} </h2>
</div>
Pipes Angular 4 introduced "tittlecase" pipe "|" and use to changes the first letter of each word into the uppercase
<h2> {{ 'balaji t' | titlecase }} </h2>
Http Adding search parameters to an "HTTP request" has been simplified as:
http.get('${baseUrl}/api/users', {params: {sort: 'ascending'}});
Test Overriding a template in a test has also been simplified, as :
TestBed.overrideTemplate(UsersComponent, '<h2> {{Users.name}} </h2>');
Service A new service has been introduced to easily get or update "Meta Tags" as:
@Component({
selector: 'users-app',
template: '<h1> Users </h1>'
})
export class Users/AppComponent {
    Constructor(meta: Meta) {
        meta.addTag({name: 'Blogger', content: 'Balaji T'});
   }
}
Forms Validators One new validator joins the existing "required", "minLength", "maxLength" and "Pattern". An email helps you validate that the input is a valid email.
Compare Select Options A new "compareWith" directive has been added and it used to help you compare options from a select, as:
<select> [compareWith]="byUId" [(ngModel)]="selectedUsers">
    <option *ngFor="let user of users" [ngValue]="user:UId" >{{user.name}} </option>
</select>
Router A new interface "paramMap" and "queryParamMap" has been added and it introduced to represent the parameters of a URL
const uid = this.route.snapshot.paramMap.get('UId');
this.userService.get(uid).subscribe(user => this.name = name);
CanDeativate This "CanDeactivate" interface now has an extra parameter and it is containing the next state
118n The internationalization is tiny improvement, as :
<div [ngPlural]="value">
    <ng-template ngPluralCase="0">there is nothing</ng-template>
    <ng-template ngPluralCase="1">there is one</ng-template>
</div>

Angular 5 over Angular 4

Performance Improvements
  • Use of addEventListener for the faster rendering and it is the core functionality
  • Update to new version of build-optimizer
  • Added some improvements on the abstract class methods and interfaces
  • Remove decorator DSL which depends on Reflect for Improve the Performance of Apps and this is core functionality
  • Added an option to remove blank text nodes from compiled templates
  • Switch Angular to se Static-Injector instead of Reflective-Injector
  • Improve the applications testing
  • Improve the performance of hybrid applications
  • Improvement of lazy loading
HttpClient Improvements
  • Improvement on Type-checking the response
  • Improvement on Reading the full response
  • Improvement on Error handling and fetching error details
  • Improvement on intercepting all requests or responses
  • Improvement on Logging
  • Improvement on Caching
  • Improvement on XSRF Protection
Added Features
  • Added Representation of Placeholders to xliff and xmb in the compiler
  • Added an Options Arg to Abstract controls in the forms controls
  • Added add default updateOn values for groups and arrays to form controls
  • Added updateOn blur option to form controls
  • dded updateOn submit option to form controls
  • Added an Events Tracking Activation of Individual Routes
  • Added NgTemplateOutlet API as stable in the common controls
  • Create StaticInjector which does not depend on Reflect polyfill
  • Added [@.disabled] attribute to disable animation children in the animations
Router Life Cycle Events
  • GuardsCheckStart
  • GuardsCheckEnd
  • ResolveStart and
  • ResolveEnd

Creating First Application using AngularJS

Here you will learn the important basics of an AngularJS application along with step by step of creating your first application.

ng-app - used to define and link an AngularJS application with HTML

ng-model - is a directive which helps to bind the vlues of AngularJS application data to HTML controls

ng-bind - this is the actual directive which binds the AngularJS application data to HTML tags


How AngularJS Integrates with HTML
  • ng-app directive starts the AngularJS application
  • ng-model then creates the required variables which can be used within HTML
  • ng-bind uses the created model to be displayed as part of HTML page

Use below instructions/steps to start creating your first AngularJS application
  • As AngularJS is based on pure JavaScript framework, this JavaScript library can be referenced/added as below either by using <script> tag
    <script src = "https://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js"></script>
    OR including required library files using NuGet packages
    • Create an empty project using Visual Studio
    • Using Manage Nuget option refer AngularJS to the project
    • Refer or include the required JavaScript files using <script> tag as part of Index.html
  • One of the example AngularJS application structure can look like below
    • index.html - contains all the referenced/included files with ng-app definition
    • app.js - contains the ng-module definition along with application level constant variable and required module includes
    • appScripts - this folder contains all the customized folders with .js and .html file definitions with actual code
    • Scripts - folder contains the extracted Nuget package library files required to include to work with AngularJS
  • Run your AngularJS application by launching it using index.html page as startup page


With this I am concluding the illustration. Feel free to share your feedback.

Happy Programming !!!

AngularJS Keywords and Helpful Definitions

In this section, I would like to highlight some of the key features and how to work with them in AngularJS.

Directives In AngularJS world these are helpful to extend HTML which are special attributes starting with "ng-" prefix. Below are some of the important Directives one should know as part of AngularJS.

  • ng-app this directive help start AngularJS application. And also, it defines the root element, automatically initializes or bootstraps and to load different modules of the application
    <div ng-app="myApp">
    <!-- you code goes here related to app -->
    </div>
  • ng-Init this directive help initialize application data by setting up the values to the variables which are going to be used as part of application
  • <div ng-app="myApp"> ng-init="countries = [{'United States}]"
    <!-- you code goes here related to app -->
    </div>
  • ng-model this directive binds the values to HTML controls
    <div ng-app="myApp">
    Enter Name: <input type="text" ng-model="name" >
    </div>
  • ng-Repeat this directive help repeating HTML elements of collections
    <p> List of Countries with locale </p>
    <ol>
    <li ng-repeat="country in countries" >
    {{ 'country: ' + country.name }}
    </li>
    </ol>

Expressions These are used to bind data to HTML and are written using double braces like {{ }}. Expressions behaves the same way as ng-bind directives. Some examples are as below:

Integers example:
Total Cost: $ {{ quantity * price }}
string example:
Hi {{ employee.LastName + " " + employee.FirstName }}
object example:
Hi {{ employee.Designation }}
array example:
Hi {{ employee[1].FirstName }}

Controllers These will help to control the flow of AngularJS application. A Controller is defined using ng-controller directive, and contains attributes/properties and functions. $scope refers to the application or module that controller is to control. Here is an example of Controller in an AngularJS application:

In HTML, this is how a controller is referenced:
<div ng-controller="StudentController">
... Your code goes here ...
<div>

The Controller will e defined as below:
<script>
function studentController($scope) {
$scope.student = {
firstNme: "Balaji",
lastNme: "Telugunti",
};
}
</scope>

Filters These are helpful to modify the data using a pipe (|) character and can be combined in an expression or directive. Below are the commonly used filters with examples:

Name Description Example
uppercase Converts data to UPPERCASE Last Name: {{ student.lastName | uppercase }}
lowercase Converts data to LOWERCASE Last Name: {{ student.lastName | lowercase }}
currency Converts data to a CURRENCY format Fees: {{ student.Fees | currency}}
filter FILTERS a collection to a subset based on criteria ng-repeat="subject in subjects | filter.subjectName
orderby ORDERS a collection based on criteria ng-repeat="subject in subjects | orderBy.subjectName

Modules: These are used to separate logic's like services, controllers, application etc. and keep the code clean. Best practice is to define modules in separate js files and name them as per the functionality of the module.

Ajax: $https: control works as a service to read and get data from server in AngularJS. Below is an example of how to use this:

function studentController($scope, $https:) {
var url = "data.txt"

$https:.get(url).success(function(response) {
$scope.students = response;
});
}

Views: AngularJS supports Single Page Application via multiple views on a single page. To support this, Angular has ng-view, ng-template directives as well as $routeProvider services. Here is how we can work with these:

  • ng-view This creates a place holder where a corresponding view or template can be placed by the configuration. This can be achieved as below:
    <div ng-app="mainApp">
    ...
    <div ng-view> </div>
    </div>
  • ng-template This directive is used to create a html view using script tag which contains Id attribute to refer and used by $routeProvider to map the view with a controller as below:
    <div ng-app="mainApp">
    ...
    <script type="text/ng-template" id="addStudent.htm" >
    <h2> Add Student </h2>
    {{message}}
    </script>
    </div>
  • $routeProvider This is the key service which set the configuration of urls, map them with the corresponding html or ng-templateby attaching it to controller as below:
    var mainApp = angular.module("mainApp", ['ngRoute']);

    mainApp.config(['$routeProvider', function($routeProvider) {
    $routeProvider.

    when('/addStudent', {
    templateUrl: 'addStudent.htm', controller: 'AddStudentController'
    }).

    when('/viewStudents', {
    templateUrl: 'viewStudents.htm', controller: 'ViewStudentsController'
    }).

    otherwise({
    redirectTo: '/addStudent'
    });
    }]);

Scope: Scope's responsibility is to join the controller with view. This contains the model data as well as met data which can be accessed via $scope object. Scope is controller specific and if we have nested controllers then the child scope will inherit the scope of its parent.

<scope>
var mainApp = angular.module("mainApp", []);

mainApp.controller("shapeController", function($scope) {
$scope.message = "In shape controller";
$scope.type = "Shape";
});
</scope>

Services: Separation of Concerns is implements in AngularJS using services architecture. These are meant and responsible for doing a specific task only. These can be used on requirement basis and uses Dependency Injection mechanism to be used as part of controllers and filters. Services can be created wither using a factory or service

  • factory example:
    var mainApp = angular.module("mainApp", []);
    mainApp.factory('MathService', function() {
    var factory = {};

    factory.multiply = function(a, b) {
    return a * b
    }

    return factory;
    });
  • Service example:
    mainApp.service('CalcService', function(MathService){
    this.square = function(a) {
    return MathService.multiply(a,a);
    }
    });

Keywords and Helpful Definitions

In this section, I would like to highlight some of the key features and how to work with them in Angular 2.

Modules These are helpful to create boundaries in an application, meaning instead of coding everything in one module, these modules will help developing functionality separately. app.module.ts in app folder is the root module class available in an project solution.

A module is made up of following parts:
  • Bootstrap array: This is used to tell Angular which components needed to be loaded. Once the component is included in this array, it needs to be declared so that they can be used across other components within the application.
  • Export array: This helps to define export components, directives and pipes which can be used in other modules of the application
  • Import array: Helpful to import the functionality from other modules of the application
Example of app.module.ts file in an typical Angular application is as below:
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';

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

Components are the logical piece of code in an Angular application, which consists of the following. app.components.ts class in app folder contains all the definitions of components for the application.

  • Template: This contains the HTML that needs to be rendered as a view of the application, which also includes binding and directives in it.
  • Class: This is like any class defined in an object oriented world which contains properties and methods to support the view and defined in TypeScript.
  • Meta Data: This has the extra data defined for the Angular class. It is defined with a decorator.
Example of app.components.ts class is as follows:
import { Component, Input, OnInit } from '@angular/core';
import { Product } from '../../interfaces/products/product';
import { NgForm } from '@angular/forms';

// template assignment by defining the component
@Component ({
selector: 'ProductDetails',
templateUrl:'app/views/products/productDetailsView.html'
})

export class ProductDetailsComponent implements OnInit {
@Input() product: Product;
@Input() isReadOnly: boolean;
constructor() {}
ngOnInit() { }
CloseProductDetailsView() {
this.product = null
}
}

Directives A directive is a custom HTML element that is used to extend the power of HTML. As part BrowserModule Angular provides ngIf and ngFor as directives. Under app.module.ts file you can see the definition of importing BrowserModule as import { BrowserModule } from '@angular/platform-browser';

  • ngIf element is used to add elements to HTML if it evaluates to true else it will not add it to the HTML code
  • ngFor element is used to elements based on the condition of the For loop

Metadata is helpful and used to configure the expected behavior of a class. One can work with Metadata either at Class level or at Constructor level as below

  • Annotations These are at class level decorators. @Component and @Routes are examples of this type
    @Component ({
    selector: 'my-app',
    templateUrl: 'app/app.component.html'
    })
  • Parameters These set by the decoeators at the constructor level
    export class AppComponent {
    @Environment(‘test’)
    appTitle: string = 'Welcome';
    }

Error Handling By using ReactJS catch library and then using catch function, we can incorporate error handling in Angular. Below code can be added on top of CRUB operations using http

import { Injectable } from '@angular/core';
import { Http , Response } from '@angular/http';
import { Observable } from 'rxjs/Observable';

import 'rxjs/add/operator/map';
import 'rxjs/add/operator/do';
import 'rxjs/add/operator/catch';
import { IProduct } from './product';

@Injectable()
export class ProductService {
private _producturl = 'app/products.json';
constructor(private _http: Http){}
getproducts(): Observable {
return this._http.get(this._producturl)
.map((response: Response) => response.json())
.do(data => console.log(JSON.stringify(data)))
.catch(this.handleError);
}

private handleError(error: Response) {
console.error(error);
return Observable.throw(error.json().error());
}
}
  • The catch function creates link to the Error Handler function
  • In error handler function, the error will be sent to console and also we can throw the error back to the main proram.

Dependency Injection Dependency Injection is the ability to add the functionality of components at runtime. @Injectable keyword allows class functionality to be injected and used by any modules. Using Provider inside the module the injectable service can be used.

Definiiton and Usage of Injectble class :
Definition
@Injectable()
export class classname {}

Usage
@Component ({
providers: [classname]
}]

Configuration Files Below are the helpful configuration files to set the expectations as part of Angular application

tsconfig.json: This configuration file allows to configure TypeScript used for Angular application
{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"moduleResolution": "node",
"sourceMap": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"lib": [ "es2015", "dom" ],
"noImplicitAny": true,
"suppressImplicitAnyIndexErrors": true
}
}
package.json: This configuration file contains information about Angular 2 project
{
"name": "angular-quickstart",
"version": "1.0.0",
"description": "QuickStart package.json from the documentation,
supplemented with testing support",

"scripts": {
"build": "tsc -p src/",
"build:watch": "tsc -p src/ -w",
"build:e2e": "tsc -p e2e/",
"serve": "lite-server -c=bs-config.json",
"serve:e2e": "lite-server -c=bs-config.e2e.json",
"prestart": "npm run build",
"start": "concurrently \"npm run build:watch\" \"npm run serve\"",
"pree2e": "npm run build:e2e",
"e2e": "concurrently \"npm run serve:e2e\" \"npm run protractor\"
--killothers --success first",
"preprotractor": "webdriver-manager update",
"protractor": "protractor protractor.config.js",
"pretest": "npm run build",
"test": "concurrently \"npm run build:watch\" \"karma start karma.conf.js\"",
"pretest:once": "npm run build",
"test:once": "karma start karma.conf.js --single-run",
"lint": "tslint ./src/**/*.ts -t verbose"
},

"keywords": [],
"author": "",
"license": "MIT",
"dependencies": {
"@angular/common": "~2.4.0",
"@angular/compiler": "~2.4.0",
"@angular/core": "~2.4.0",
"@angular/forms": "~2.4.0",
"@angular/http": "~2.4.0",
"@angular/platform-browser": "~2.4.0",
"@angular/platform-browser-dynamic": "~2.4.0",
"@angular/router": "~3.4.0",
"angular-in-memory-web-api": "~0.2.4",
"systemjs": "0.19.40",
"core-js": "^2.4.1",
"rxjs": "5.0.1",
"zone.js": "^0.7.4"
},

"devDependencies": {
"concurrently": "^3.2.0",
"lite-server": "^2.2.2",
"typescript": "~2.0.10",
"canonical-path": "0.0.2",
"tslint": "^3.15.1",
"lodash": "^4.16.4",
"jasmine-core": "~2.4.1",
"karma": "^1.3.0",
"karma-chrome-launcher": "^2.0.0",
"karma-cli": "^1.0.1",
"karma-jasmine": "^1.0.2",
"karma-jasmine-html-reporter": "^0.2.2",
"protractor": "~4.0.14",
"rimraf": "^2.5.4",
"@types/node": "^6.0.46",
"@types/jasmine": "2.5.36"
},
"repository": {}
}
systemjs.config.json: This file contain the required system files for an Angular application which loads all the necessary script files without the need to add a script tag to the HTML pages
/**
* System configuration for Angular samples
* Adjust as necessary for your application needs.
*/
(function (global) {
System.config ({
paths: {
// paths serve as alias
'npm:': 'node_modules/'
},

// map tells the System loader where to look for things
map: {
// our app is within the app folder
app: 'app',

// angular bundles
'@angular/core': 'npm:@angular/core/bundles/core.umd.js',
'@angular/common': 'npm:@angular/common/bundles/common.umd.js',
'@angular/compiler': 'npm:@angular/compiler/bundles/compiler.umd.js',
'@angular/platform-browser': 'npm:@angular/platformbrowser/bundles/platform-browser.umd.js',
'@angular/platform-browser-dynamic':
'npm:@angular/platform-browserdynamic/bundles/platform-browser-dynamic.umd.js',
'@angular/http': 'npm:@angular/http/bundles/http.umd.js',
'@angular/router': 'npm:@angular/router/bundles/router.umd.js',
'@angular/forms': 'npm:@angular/forms/bundles/forms.umd.js',

// other libraries
'rxjs': 'npm:rxjs',
'angular-in-memory-web-api':
'npm:angular-in-memory-web-api/bundles/inmemory-web-api.umd.js'
},

// packages tells the System loader how to load when no filename and/or no extension
packages: {
app: {
defaultExtension: 'js'
},
rxjs: {
defaultExtension: 'js'
}
} }); })(this);

Monday, August 27, 2018

RequiredEither - Conditional Validation Attribute using MVC / Web API

This topic illustrates how to extend ValidationAttribute to enforce customized validation of checking if either of the field's value is provided or not.

Scenario: Contact.Phone or Contact.Email value should be required.

We can achieve this by following below steps, and the illustration is developed using MVC 5, Web API 2, EF 6 and Moq

As a first step, Create a new class with name "RequiredEitherValidator" (preferably in a common location of the solution ie. either in Model or a Common project, as applied) and copy below code:
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
public class RequriedEitherValidator: ValidationAttribute
{
private string[] PropertyList { get; set; }

public RequriedEitherValidator(params string[] propertyList)
{
this.PropertyList = propertyList;
}

public override object TypeId
{
get { return this; }
}

public override bool IsValid(object value)
{
PropertyInfo propertyInfo;
foreach (string propertyName in PropertyList)
{
propertyInfo = value.GetType().GetProperty(propertyName);
if (propertyInfo != null && propertyInfo.GetValue(value, null) != null)
{
return true;
}
}
return false;
}
}
We need to create or make changes to Model(s) and Controller(s) as below to implement the created custom validation.

POCO / Model: Create or Modify Contact class to apply the custom validation on Phone and Email properties as below to define either of the field value is required.
[RequriedEitherValidator("Email", "Phone", ErrorMessage = "Either Email or Phone Data is Required")]
public class Contact
{
[Display(Name="First Name")]
[StringLength(50, ErrorMessage = "First Name cannot Exceed 50 Character length")]
[Required(ErrorMessage = "First Name is Required")]
public string FirstName {get; set; }

[Display(Name="Last Name")]
[StringLength(50, ErrorMessage = "Last Name cannot Exceed 50 Character length")]
[Required(ErrorMessage = "Last Name is Required")]
public string LastName { get; set; }

[StringLength(150)]
[RegularExpression("^[a-zA-Z0-9_\\.-]+@([a-zA-Z0-9-]+\\.)+[a-zA-Z]{2,6}$", ErrorMessage = "Invalid Email address")]
public string Email { get; set; }

[RegularExpression(@"^([0-9]{10})$", ErrorMessage = "Invalid Phone Number")]
public string Phone { get; set; }
}
Controller: The controller for Contact entity with a POST method will look like below.
public class ContactController : ApiController
{
[AcceptVerbs("GET", "POST")]
[HttpPost]
[ActionName("CreateContact")]
public bool CreateContact([FromBody]Contact contact)
{
try
{
var isAdded = _contactRepository.CreateContact(contact);
return isAdded;
}
catch (Exception e)
{
throw e;
}
}
}
Testing: Below TestMethods will help testing the post method to check for the expected validation error:
[TestMethod]
public void FailedPhoneOrEmailDetailsTest()
{
Contact contact = new Contact { FirstName = "TestFirstName", LastName = "TestLastName", Phone = null, Email = null };
var validationResult = Validator.TryValidateObject(_Contacts, _ContactsContext, _validationResults, validateAllProperties: true);

Assert.IsFalse(validationResult);
Assert.AreEqual(1, _validationResults.Count);
Assert.AreEqual("Either Email or Phone Data is Required", _validationResults[0].ErrorMessage);
}
}
With this I am concluding the illustration. Feel free to share your feedback.

Happy Programming !!!