Wednesday, May 29, 2019

Thank you Blogger, hello Medium

Hey guys,
I've been writing in Blogger for almost 10 years this is a time to move on.
I'm happy to announce my new blog at Medium:
Ohad.

Saturday, May 11, 2019

Refactor to a better code

On today’s show: how to massively improve a piece of code.


Old method


Take a glimpse on this given function (you don't have to read all 😄):

public  string BuildFilterQuery(string Xmlquery)
        {
            var query = string.Empty;
            var doc = new XmlDocument();
            var values = string.Empty;
            doc.LoadXml(Xmlquery);
            
                try
                {
                    string fieldID;
                    string conditionOperator;
                    string conditionValues = string.Empty;
                    XmlNodeList filters = doc.SelectNodes("//Root//Filter");
                    for (int i = 0; (i
                                     <= (filters.Count - 1)); i++)
                    {
                        XmlNodeList root = filters[i].SelectNodes(".//FilterItem");
                        query = (query + Convert.ToString("("));
                        foreach (XmlNode item in root)
                        {
                            fieldID = item.SelectSingleNode("FieldID").InnerText;
                            conditionOperator = item.SelectSingleNode("Operator").InnerText.ToLower();
       conditionValue = item.SelectSingleNode("Operator").InnerText;
                            
                                switch (conditionOperator)
                                {
                                    case "contains":
                                        query = string.Format("{0} [{1}] {2} \'%{3}%\' AND", query, DataManipulate.ConvertFromSafe(fieldID), dOperators[conditionOperator], conditionValue);
                                        break;
                                    case "after":
                                        query = string.Format("{0} Convert([{1}], \'System.DateTime\') {2} \"#{3}#\" AND", query, DataManipulate.ConvertFromSafe(fieldID), dOperators[conditionOperator], conditionValue);
                                        break;
                                    case "greater than":
                                        query = string.Format("{0} Convert([{1}], \'System.Int64\') {2} {3} AND", query, DataManipulate.ConvertFromSafe(fieldID), dOperators[conditionOperator], conditionValue);
                                        break;
                                    default:
                                        query = string.Format("{0} [{1}] {2} \'{3}\' AND", query, DataManipulate.ConvertFromSafe(fieldID), dOperators[conditionOperator], conditionValue);
                                        break;
                                }                            

                        }
      
                        if (query.EndsWith(" AND"))
                        {
                           query = (query.Substring(0, query.LastIndexOf(" AND")) + ")");
                        } 

                        query = (query + " OR ");
                    }
                }
                catch (Exception ex)
                {
                    throw ex;
                }            

            return query;
        }


Basically - this function iterates an xml which contains query details and returns one single query string.


What’s wrong with that function?

      1. Code handles xml object instead desterilize to a convenient and more readable object.

      2. It’s long and not fun to read by other developers.

      3. Code contains many different logics (responsibilities) which can probably be reused across the
      application. In addition, adding too much logic makes it less suitable for unit-testing.
To test this method – one should create many different xml strings and execute it.
But what if there is an issue with iterating the xml? All tests would fail – and we wouldn’t know immediately why. Much logic causes failure reason range to be wide.

      4. Code is hard for extension. Adding another “case” to “switch” means modifying a working method.

      5. Because method is long – it’s hard to maintain by a development team. Chances of code conflicts are bigger.

      6. On a performance matter - string type is immutable and query gets changed again and again (better use a StringBuilder object).


     Code surgery!

     
Deserialize to custom C# object

       1st paragraph is the easiest step. A proper C# class generated from xml structure. There are various   ways of doing that. Personally, I like to use an online tool like http://xmltocsharp.azurewebsites.net/ .
       Here is part of the new class:


       [XmlRoot(ElementName = "Filter")]
    public class Filter
    {
        [XmlElement(ElementName = "FilterName")]
        public string FilterName { get; set; }
        [XmlElement(ElementName = "FilterItem")]
        public List<FilterItem> FilterItem { get; set; }
    }

      
     Method breakdown

     Switch-case breakdown

     Easy to identify that there is kind of a switch-case on operator. The purpose of each 
     if-statement is to create a query string in a certain format.
     IQueryStringType consists one method which gets relevant data and returns a query string.
   
public interface IQueryStringType {
string ConcatQueryString(QueryStringTypeModel queryStringTypeModel); }

QueryStringTypeModel consists relevant information to create a query string.


public class QueryStringTypeModel
{
  public string FilterName { get; set; }
  public string Operator { get; set; }
  public List<string> ValueList { get; set; }
  public Dictionary<string, string> TranslateOperators { get; set; }
  public QueryStringTypeSettingsModel QueryStringTypeSettingsModel { get; set; }
}

Here is an example of a query string type implementation:


public class ContainsQueryStringType : IQueryStringType
{   
  public string ConcatQueryString(QueryStringTypeModel queryStringTypeModel)
  {
    var convertedFromSafe = $"[{DataManipulate.ConvertFromSafe(queryStringTypeModel.FilterName)}]";
    var concatOperator = $"{queryStringTypeModel.TranslateOperators[queryStringTypeModel.Operator]}";
    var value = $"'%{queryStringTypeModel.ValueList[0]}%'";
    var response = $"{convertedFromSafe} {concatOperator} {value}";
    return response;
  }        
}

QueryStringTypeModel object is generated by QueryStringTypeModelGenerator.
This object also contains QueryStringTypeSettingsModel property which holds the required QueryStringType for current calculation.

QueryStringTypeSettingsModelRepository class holds all types of QueryString classes. GetModel(string @operator) method returns the appropriate instance of QueryStringTypeSettingsModel according to a given operator.
Shortend version of this repository class (only with "Contains"):


public class QueryStringTypeSettingsModelRepository
{
  private readonly Dictionary<string, QueryStringTypeSettingsModel> _queryStringTypeSettingsModelDic = new Dictionary<string, QueryStringTypeSettingsModel>();

  public QueryStringTypeSettingsModelRepository()
  {
    var containsSettingsModel = new QueryStringTypeSettingsModel()
    {
       QueryStringType = new ContainsQueryStringType(),
    };
    
    this._queryStringTypeSettingsModelDic.Add("Contains", containsSettingsModel); 
    this._queryStringTypeSettingsModelDic.Add("Not contains", containsSettingsModel);
  }

  public QueryStringTypeSettingsModel GetModel(string @operator)
  {
     if (!this._queryStringTypeSettingsModelDic.ContainsKey(@operator))
       return null;

     var queryStringTypeSettingsModel = this._queryStringTypeSettingsModelDic[@operator];
     return queryStringTypeSettingsModel;
  }
}
    

      If-statements gets activated only when condition is fulfilled

QueryStringCalculator checks for these conditions and activates ConcatQueryString of a the requested IQueryStringType sent.


public class QueryStringCalculator : IQueryStringCalculator
{
   public string GetQueryString(QueryStringTypeModel queryStringTypeModel)
   {
     var response = string.Empty;
     if (queryStringTypeModel.QueryStringTypeSettingsModel == null 
         || string.IsNullOrEmpty(queryStringTypeModel.FilterName)
         || string.IsNullOrEmpty(queryStringTypeModel.Operator))
                return response;

     var queryStringType = queryStringTypeModel.QueryStringTypeSettingsModel.QueryStringType;
     response = queryStringType.ConcatQueryString(queryStringTypeModel);
     return response;
   }
}


      Merge FilterItems into a single query string (with “AND” between each couple)

Each of FilterItems is a filter statement and all of them should be united to one single query string with an “AND” operator between them. This logic is encapsulated into a new class.


public class FilterItemToQueryStringConvertor : IFilterItemToQueryStringConvertor
{
  private readonly IQueryStringCalculator _queryStringCalculator;
  private readonly IQueryStringTypeModelGenerator _queryStringTypeModelGenerator;

  public FilterItemToQueryStringConvertor(IQueryStringCalculator queryStringCalculator,
            IQueryStringTypeModelGenerator queryStringTypeModelGenerator)
  {
    _queryStringCalculator = queryStringCalculator;
    _queryStringTypeModelGenerator = queryStringTypeModelGenerator;
  }

  public string GetQueryString(List<FilterItem> filterItems, 
            Dictionary<string,string> dOperators)
  {
    var response = string.Empty;
    if (filterItems.Count == 0)
      return response;

    var queryStringCollection = new List<string>();

     foreach (var filterItem in filterItems)
     {
       var queryStringTypeGeneratorModel = new QueryStringTypeGeneratorModel
       {
          FilterItem = filterItem,
          TranslateOperators = dOperators
       };

       var queryStringTypeModel = _queryStringTypeModelGenerator.GetQueryStringTypeModel(queryStringTypeGeneratorModel);
       var queryString = _queryStringCalculator.GetQueryString(queryStringTypeModel);
       queryStringCollection.Add(queryString);
     }

    response = queryStringCollection.Aggregate((current, next) => current + " AND " + next);
    return response;
   }
}

Merge each all query strings into one (with “OR” between each couple)

All query strings that were generated using FilterItemToQueryStringConvertor should be united into a single query string.


public class FilterCollectionToQueryStringConvertor : IFilterCollectionToQueryStringConvertor
{
        private readonly IFilterItemToQueryStringConvertor _filterItemToQueryStringConvertor;

        public FilterCollectionToQueryStringConvertor(IFilterItemToQueryStringConvertor filterItemToQueryStringConvertor)
        {
            this._filterItemToQueryStringConvertor = filterItemToQueryStringConvertor;
        }

        public string GetQueryString(List<Filter> filters, 
            Dictionary<string, string> dOperators)
        {
            var response = string.Empty;
            if (filters.Count == 0)
                return response;
            
            var queryStringCollection = new List<string>();
            foreach (var filter in filters)
            {
                var queryString = _filterItemToQueryStringConvertor.GetQueryString(filter.FilterItem, dOperators);
                queryStringCollection.Add(queryString);
            }

            response = queryStringCollection.Aggregate((current, next) => current + " OR " + next);
            return response;
        }
}

    Summary + Tips

   
If you wish to enchance your coding level:
      
      a. Before writing any code - think about your code infrastracture.
Even during\after code writing - try to figure out if it's possible to encapsulate it and make it more generic.
      

      
      
      c. Write your code that it would be able to use dependency injection (For instance: don't use concrete types\model parameters in constructors, create an interface for each class..).
Dependency injection is a way to create dependencies outside of the class using it. It allows developers to create decoupled & testable applications.
      SimpleInjector is a great DI library backed up with very informative documentation even about developing concepts:

Monday, July 30, 2018

RxJs observable tip - DON'T subscribe inside subscribe

Hi,

I've been working with RxJs observable for a while and I have a little tip for rxjs starters: 
Don't subscribe inside subscribe!

For some of you it may seem obvious but let me explain.



The Don't

this.observableGeneratorService.getBooleanObservable().subscribe((shouldPresentNumbers) => {
      if(shouldPresentNumbers) {
        this.observableGeneratorService.getNumberObservable().subscribe((val) => {
          this.uglyNumbers.push(val);
        })
      }
    });

In this case:
1. getBooleanObservable gets executed
2. A response received into shouldPresentNumbers
3. If shouldPresentNumbers is true => getNumberObservable is executed
4. A response received into val

We want to keep this executive order but not using this anti-pattern. What if we had more service calls? Then we had to deal with "Callback" hell. It's really unnecessary when rxjs library has so many methods that help to manipulate data (take a look at https://www.learnrxjs.io/ ).

Right way


this.goodObservable$ = this.observableGeneratorService.getBooleanObservable()
    .concatMap((shouldPresentNumbers) => {
      let numbersObservable = shouldPresentNumbers ? this.observableGeneratorService.getNumberObservable() : Observable.empty();
      return numbersObservable;
    }) // concatMap would map "true" to each emitted value (1,2,3,4,5) and creates an observable.
    // Finally it returns: Observable of 1,2,3,4,5
    // this could also be done by using ".map(...).combineAll()"
    .toArray(); // turn into an array of observable

concatMap allows returning an observable from inner method. In contrast to map which wraps inner method with an observable (It's possible to use map and combineAll right after).
That's it. Now it's possible to use subscribe once and finish this thing off.
Now we can even get a  nice performance boost that we've couldn't get earlier.
An html element can subscribe to the observable result by using async pipe!
toArray is used in order to turn Observable.of(1,2,3,4,5) into [Observable.of(1,2,3,4,5)]. 
With this data type - it's possible to use it in a template view like:

 <li *ngFor="let good of goodObservable$ | async">
     {{good}}
    </li>

Saturday, June 16, 2018

Custom *ngIf directive

Hi,

Case scenario: Parts of your web-site should consider user permission.


Goal: Logged-in users can see parts that logged-out users cannot and vice-versa.


If you already wrote a basic angular application - then you are probably familier with *ngIf stractural directive which conditionally includes a template based on the value of an expression (Official doc: https://angular.io/api/common/NgIf).

In order to achieve our goal we can simply use this kind of *ngIf:


<div *ngIf="authService.isLoggedIn">
      <h2>Logged In</h2>
</div>
<div *ngIf="!authService.isLoggedIn">
      <h2>Not Logged</h2>
</div>

What bothers here ?
This *ngIf would be used across many components and AuthService would have to be injected to each component.


Solution: Custom *ngIf directive


Searching through the net i've encountered this question:
https://stackoverflow.com/questions/43517660/directive-that-works-as-ng-if-angular-2

The accepted answer may lack some functionality because it's not using angular ngIf.

Here is a directive which uses ngIf :




app.ts


<div *allowed="true">
      <h2>Logged In</h2>
    </div>
    <div *allowed="false">
      <h2>Not Logged</h2>
    </div>

 Usage of allowed directive is pretty simple.

allowed.directive.ts


private _allowed: boolean;
  private ngIfDirective: NgIf;
  private isLoggedInSubs: Subscription;
  
  @Input()
    public set allowed(val: boolean) {
        this._allowed = val;
        this.setNgIf();
    };
  
  constructor(private templateRef: TemplateRef<any>, private viewContainer: ViewContainerRef, 
  private authService: AuthService) {
    if(!this.ngIfDirective) {
          this.ngIfDirective = new NgIf(this.viewContainer,this.templateRef);
    }
        
    this.isLoggedInSubs = this.authService.isLoggedInChanged.subscribe((value) => { 
      this.setNgIf();
    });
  }
  
  setNgIf() {
    this.ngIfDirective.ngIf = this.authService.authModel.isLoggedIn == this._allowed;
  }

constructor needs templateRef and viewContainer references from angular injector in order to create ngIf directive instance. In addition, authService is injected in order to notify the directive about changes of isLoggedIn property.

Each time isLoggedIn property changes - ngIf property of ngIfDirective needs to be re-set.

auth.service.ts


public isLoggedInChanged: Subject<string> = new Subject<string>();
  
  constructor() {
    this.authModel = new AuthModel();
  }
  
  set isLoggedIn(val: boolean) {
    this.authModel.isLoggedIn = val;
    this.isLoggedInChanged.next(val);
  }
  
  get isLoggedIn(): boolean {
    return this.authModel.isLoggedIn;
  }
  
  public switchLoggedIn() {
    this.isLoggedIn = !this.isLoggedIn;
  }

In authService, after isLoggedIn is set - isLoggedInChanged subject emits the new value using next function.

Sunday, January 21, 2018

Angular - Communication between components (Global notifications service)

Hi,

This post would focus on this case scenario:
An angular app has components which doesn't have a strong connection but still needs to communicate.
For instance, look at this app architecture:



















How the heck does component E can shout something to B and F or even make them trigger some actions?

1. Global Notifications Service



This solution is based on rxjs Subject.

Relevant notes about subjects:
1. Subject class implements both Observer and Observable interfaces which means senders & receivers shares the same subject instance. Subject is kind of a channel.

2. Unlike an Observable which can have only one observer (subscriber) - Subject has state and actually keeps a list of observers (Read more at: https://medium.com/@benlesh/on-the-subject-of-subjects-in-rxjs-2b08b7198b93). 

3. next function - Sends message to the channel.
subscribe function - Registers a handler function which executes after a message received.
unsubscribe function - Unregister current subscriber.

4. In this sample a regular Subject is used which means it's very important to subscribe before next is executed (Read more at: https://stackoverflow.com/questions/36814995/rxjs-multiple-subscriptions-to-observable).

5. Always remember to unsubscribe & do not subscribe (accidentally) more than once.

Plunker explanation

Here is a brief on what's going on between these app components:

  • Second & Third component subscribed to First component messages (subscription is taken place in the constructor).
  • Third component subscribed to Fourth component messages.
  • Third component has an option to unsubscribe from First component messages and an option to subscribe again.


@Injectable()
export class GlobalNotifications {
  private nameToSubject: Map<string, Subject> = new Map<string, Subject>();

This solution is based on Map type (key-value store). nameToValue key represents the notification name (= channel name) and value is the subject created for it.

getSubject(name: any) : Subject {
    let subjectObj : Subject = this.nameToSubject.get(name);
    if(!subjectObj) {
      subjectObj = new Subject();
      this.nameToSubject.set(name,subjectObj);
    }
    
    return subjectObj;
  }

getSubject simply returns a subject by name from this store. If a subject doesn't exist - this method would also create one.

export class SecondComponent implements OnDestroy {
    private dataReceived : Array<string> = [];
    private subscriberFirst : Subscriber;
    
  constructor(private _globalNot:GlobalNotifications) {
    this.subscriberFirst = this._globalNot.getSubject('firstToSubs').subscribe((val) => this.dataReceived.push(val));
  }

Second component gets GlobalNotifications service instance from angular injector.
It executes getSubject with a relevant channel name ('firstToSubs'), gets the desired Subject instance and subscribes to its messages with a method.
subscribe method returns Subscriber instance which is important to save as a class member as it would be used to unsubscribe.


ngOnDestroy() {
    this.subscriberFirst.unsubscribe();
  }

In most cases - when a component destroyed it's implies that unsubscribe is required. You DON'T want to a subscriptions hell.


export class FirstComponent {
  constructor(private _globalNot:GlobalNotifications) {
  }
  
  onClick() {
  let subjectObj : Subject = this._globalNot.getSubject('firstToSubs');
   subjectObj.next("Sent from First Component");
  }
}

First component sends messages over 'firstToSubs' subject by getting its instance (getSubject) and using next method on it.

If you would click the Second component Unsubscribe from first button, First component messages would arrive only to the Fourth component.

Notes:

1. This post isn't just about sharing data (or just sending messages). It's possible to use it in order to trigger different methods on different components when something occurs.

2. This solution offers an async one-way invoke with no response. In order to response back to caller - another subject has to be defined.

3. If an app uses ngrx\store library - it's possible to use store as a similar solution (https://auth0.com/blog/managing-state-in-angular-with-ngrx-store/).
Plunker: https://embed.plnkr.co/cr4rCJ0hRVMwuLzKe4mg/

2. Bubbling events and using ngOnChanges



This solution uses an event that bubbles from a child to parent. Parent notifies children by changing an @Input property.

Plunker explanation

  • First component sends message to Second component

@Component({
  selector: 'first-component',
  template: '<h1>First Component (Sender)</h1><button (click)="onClick()">Send to components</button>'
})
export class FirstComponent {
@Output('messageSent') messageSent = new EventEmitter<string>();
  
  onClick() {
    this.messageSent.emit("Sent from First Component");
  }
}

First component is extremely simple. onClick simply emits a message (component output).

Note: it's impossible to use @Output or EventEmitter inside a typescript class or service.
In this case, this Observer & Observable pattern can be used: https://embed.plnkr.co/eDDlFsMfWYcP24RFOKtA/


@Component({
  selector: 'my-app',
  template: `
    <first-component (messageSent)="onMessageSentFromFirst($event)"></first-component>
    <br/>
    <second-component [dataReceived]="messages" [isChanged]="isChanged"></second-component>
    <br/>
  `,
})
export class App {
  public messages:any = [];
  private isChanged:boolean;
  
  public onMessageSentFromFirst(event : any) {
    this.messages.push(event);
    this.isChanged = !this.isChanged;
  }
}

App component is First and Second components parent and here is how data flows:
1. (messageSent) is the event fired from first-component - onMessageSentFromFirst is triggered.
2. Inside onMessageSentFromFirst the message (event) is pushed into messages array.
3. messages array is assigned to [dataReceived] (an input of second-component).
4. isChanged is used to notify second-component a change has been made to messages array.

@Component({
  selector: 'second-component',
  template: '<h1>Second Component (Receiver)</h1><div *ngFor="let data of dataReceived">{{data}}</div>'
})
export class SecondComponent {
  @Input()
    private dataReceived : Array<string>;
    private dataReceivedLength = 0;
    @Input()
    public isChanged:boolean;

  
  ngOnChanges(data:any) {
    if(data.isChanged) {
      this.onMessageReceived(this.dataReceived[this.dataReceived.length-1]);
    }  
  }
  
  ngDoCheck() {
    if(this.dataReceived.length != this.dataReceivedLength) {
      this.dataReceivedLength = this.dataReceived.length;
      this.onMessageReceived(this.dataReceived[this.dataReceived.length-1]);
    }
  }
  
  public onMessageReceived(message : string) {
   // Perform actions
  }
}

Second component always displays dataReceived array content.

There are two ways to trigger a change in dataReceived array:
1. ngOnChanges triggers only when an input reference is changed. In this case, dataReceived array reference stays exactly as before (just with one more item added to the array). By adding another boolean @Input (isChangedApp component notify a change has been made and ngOnChanges gets triggered.

2. By using ngDoCheck it's possible to check for change without the need of outer intervention. Simply by compare previous and current dataReceived length. On performance matters, ngDoCheck gets called in a very high-frequency so it should be efficient.

Notes:

1. dataReceived reference equals to the messages reference sent from app component - that means any change in it would affect both arrays.

2. Parent can also notify child by grabbing it's instance (using @ViewChild) and invoke one of his public methods.
For more: https://angular-2-training-book.rangle.io/handout/advanced-components/access_child_components.html

3. emit default is synchronous invocation.

3. Connect components via service for a regular request-response (synchronous) invocation



This solution uses a service to expose one component to another.

Plunker explanation


interface IReceiver {
  receiveMessage: (message:string) => string
};

To avoid tight-coupling - service would a reference to IReceiver (a reference to a component which would receive a message).


@Injectable()
export class Communicator {
  public receiver : IReceiver;
  
}

Service just holds a reference.
This could be a general service that holds several references by using a map structure (similar to the service presented in the first solution presented in this post).


export class SecondComponent implements IReceiver {
  private message: string;
  
  constructor(private _communicator:Communicator) {
    _communicator.receiver = this;
  }

  public receiveMessage(message:string) : string {
    this.message = message;
    return message + " Received";
  }
}

Second component implements IReceiver and registers as a receiver in the constructor.


export class FirstComponent implements OnInit {
  message: string;
  status: string;
  
  constructor(private _communicator:Communicator) {
  }
  
  onClick() {
    this.status = this._communicator.receiver.receiveMessage(this.message);
  }
}

First component easily invokes receiveMessage method synchronically and receives an immediate response.

Summary


This post presents various ways in order to communicate between siblings components.
Choosing which one to use depends on case scenario.
For instance: If two siblings are hierarchically far from each other - using the second method can be pretty annoying (you would have to bubble up the event again and again and then sink it many levels down).

For more helpful information visit Angular official site:

Thursday, August 10, 2017

Angular - Numbers only input (or Regex input)

Hi,
In this post we'll learn how to enforce some kind of behavior on input by using Angular core.

Our goal: 
Create an input which can get only numbers (or empty value). 
If any character other than a number is typed - input should not be updated!

Here is how angular performs when using an input:



1 + 2 - Anonymous function inside registerOnChange (1) sent to fn which is set to onChange (2).

3 + 4 - input event handler is defined to handleInput which just executes onChange.


Directive:




It's possible to solve the issue by writing a behavior directive.
This directive gets angular defaultValueAccessor from injector and override onChange so it won't trigger fn immediately - only after it validates input value.

Step-by-step explanation of app.ts :


@Directive({ selector: '[ngModel][numbersOnlyInput]'})

In order to use this directive - an html DOM element should have ngModel and numbersOnlyInput attributes.


constructor(private valueAccessor: DefaultValueAccessor, private model: NgModel

Constructor gets 2 references from Angular's injector: 
DefaultValueAccessor - which is actually Angular default wrapper that write and listen to changes of some form elements (https://angular.io/api/forms/DefaultValueAccessor).
NgModel - will be used in to extract and override the value in case it isn't valid.


valueAccessor.registerOnChange = (fn: (val: any) => void) => {  
valueAccessor.onChange = (val) => { } 

Overriding default onChange functionality.
Please note that actual change implementation should be written inside onChange method. Inside registerOnChange one should define the onChange method.


let regexp = new RegExp('\\d+(?:[\.]\\d{0,})?$'); 
let isNumber : boolean = regexp.test(val);
let isEmpty : boolean = val == '';

This is onChange implementation. val is the value that was now written to the input.
val is tested against a regex that enforces only numbers and isNumber gets the boolean result.
Input can also be empty - so val is also tested against an empty value and isEmpty gets the boolean result.


if(!isNumber && !isEmpty) {
   model.control.setValue(model.value);
   return;
}

return fn(val);

First, remember that onChange signature is (_: any) => {} which means that it's a function that gets a value of any type and returns a function.
- If val is not number and is not empty - model.value has the input value before something changed because input model hasn't updated yet.
model.control.setValue(model.value); sets previous input text into the input element. 
After setting the old value - change cycle is stopped so no function is returned to onChange.
- If val is a number or empty - change cycle continues regularly by return default fn with val.

Note: If this part isn't that clear - take a look again at the flow diagram above.


<input type="text" [(ngModel)]="val" numbersOnlyInput>

As mentioned, by writing ngModel and numbersOnlyInput - NumbersOnlyDirective is activated on input. 

Note: If you're using directives in your app - it's recommended to define each one on a separate file.

Custom Input Component:



Suppose you don't need that kind of functionality across all application and you need to write a custom input which behaves differently than Angular's input.

Step-by-step explanation of custom-input.ts:

export const CUSTOM_INPUT_CONTROL_VALUE_ACCESSOR: any = {
    provide: NG_VALUE_ACCESSOR,
    useExisting: forwardRef(() => CustomInputComponent),
    multi: true
};

NG_VALUE_ACCESSOR is an angular token which tells angular dependency injection (DI) core that this class implements ControlValueAccessor. Now Angular DI system can grab an instance of it. Using this token allows to use this input in a dynamic form as angular would recognize it as a form control (In this awesome custom form component blogpost refer to the paragraph that starts with "The beauty of this is that we never have to tell NgForm what our inputs are").


@Component({
    selector: 'custom-input',
    template: `<div class="form-group">
                    <label><ng-content></ng-content>
                        <input  (input)="handleInput($event.target)" 
                                (blur)="onBlur()" >
                    </label>
                </div>`,
    providers: [CUSTOM_INPUT_CONTROL_VALUE_ACCESSOR]
})

In contrast to the directive solution - now all events must be handled by custom-input implementation.


//From ControlValueAccessor interface
registerOnChange(fn: any) {
    this.onChangeCallback = fn;
}

onChange gets fn as is. fn is the same anonymous function from the diagram above. Overriding that function won't help like it did before. This is a new input (with a new ControlValueAccessor) which doesn't trigger onChange. The component's developer should state when or where to execute it.


handleInput(target: any): void {
    const val = target.value;
    let regexp = new RegExp('\\d+(?:[\.]\\d{0,})?$');
    let isNumber = regexp.test(val);
    let isEmpty = val == '';
    if(!isNumber && !isEmpty) {
       let model = this._inj.get(NgControl);  // get NgModel
       model.control.setValue(model.value);
       target.value = model.value;
       return;
        }
     
    this.onChangeCallback(val);
}

In this case, handleInput which has "numbers-only" input functionality decides if to trigger onChange
or not (continue change cycle or stop).

Thank you Blogger, hello Medium

Hey guys, I've been writing in Blogger for almost 10 years this is a time to move on. I'm happy to announce my new blog at Med...