More Components and Custom Validators Check code at: https://github.com/wghglory/angular2-fundamental 
We can vote for sessions whatever user likes.
Voter function 1) events/event-detail/session-list.component.html add this:
1 2 3 4 5 <div  class ="col-md-1" >     <div  *ngIf ="auth.isAuthenticated()" >          <upvote  (vote )="toggleVote(session)"  [count ]="session.voters.length"  [voted ]="userHasVoted(session)" > </upvote >      </div >  </div > 
 
2) events/event-detail/session-list.component.ts
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 import  { Component, Input, OnChanges } from  '@angular/core' import  { ISession } from  '../shared/index' + import  { AuthService } from  '../../user/auth.service'  + import  { VoterService } from  './voter.service'  @Component({     selector: 'session-list' ,     templateUrl: 'app/events/event-detail/session-list.component.html'  }) export  class  SessionListComponent  implements  OnChanges   {    @Input() sessions: ISession[]     @Input() filterBy: string     visibleSessions: ISession[] = [];     @Input() sortBy: string +    constructor (private auth: AuthService, private voterService: VoterService) { }          ngOnChanges() {         if  (this .sessions) {             this .filterSessions(this .filterBy);             this .sortBy === 'name'  ? this .visibleSessions.sort(sortByNameAsc) : this .visibleSessions.sort(sortByVotesDesc)         }     }     filterSessions(filter) {         if  (filter === 'all' ) {                          this .visibleSessions = this .sessions.slice(0 );           } else  {             this .visibleSessions = this .sessions.filter(s  =>  {                 return  s.level.toLocaleLowerCase() == filter;             })         }     } +    toggleVote(session: ISession) { +        if  (this .userHasVoted(session)) { +            this .voterService.deleteVoter(session, this .auth.currentUser.userName); +        } else  { +            this .voterService.addVoter(session, this .auth.currentUser.userName); +        } +        if  (this .sortBy === 'votes' ) { +            this .visibleSessions.sort(sortByVotesDesc); +        } +    } +    userHasVoted(session: ISession) { +        return  this .voterService.userHasVoted(session, this .auth.currentUser.userName); +    } } function  sortByNameAsc (s1: ISession, s2: ISession )  {    if  (s1.name > s2.name) return  1 ;     else  if  (s1.name === s2.name) return  0 ;     else  return  -1 ; } function  sortByVotesDesc (s1: ISession, s2: ISession )  {    return  (s2.voters.length - s1.voters.length); } 
 
3) add events/event-detail/upvote.component.ts, register in barrel and app.module.ts
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 import  { Component, EventEmitter, Input, Output } from  '@angular/core' @Component({     selector: 'upvote' ,     template: `      <div class="votingWidgetContainer pointable" (click)="onClick()">         <div class="well votingWidget">             <div class="votingButton">                 <i *ngIf="voted" class="glyphicon glyphicon-heart"></i>                 <i *ngIf="!voted" class="glyphicon glyphicon-heart-empty"></i>             </div>             <div class="badge badge-inverse votingCount">                 <div>{{count}}</div>             </div>         </div>     </div>` ,    styleUrls: ['/app/events/event-detail/upvote.component.css' ] }) export  class  UpvoteComponent   {    @Input() count: number;     @Input() voted: boolean;     @Output() vote = new  EventEmitter();     onClick() {         this .vote.emit({});     } } 
 
4) voter.service.ts
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 import  { Injectable } from  '@angular/core' import  { ISession } from  '../shared/event.model' @Injectable() export  class  VoterService   {    deleteVoter(session: ISession, voterName : string) {         session.voters = session.voters.filter(v  =>  v !== voterName);     }     addVoter(session: ISession, voterName : string) {         session.voters.push(voterName);     }     userHasVoted(session: ISession, voterName : string) {         return  session.voters.some(v  =>  v === voterName);     } } 
 
Update feature: voted red color, unvoted white color Using @Input Setters 
upvote.component.ts:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 import  { Component, EventEmitter, Input, Output } from  '@angular/core' @Component({     selector: 'upvote' ,     template: `      <div class="votingWidgetContainer pointable" (click)="onClick()">         <div class="well votingWidget">             <div class="votingButton"> +                <i [style.color]="iconColor" class="glyphicon glyphicon-heart"></i> -                <!-- -                    <i *ngIf="voted" class="glyphicon glyphicon-heart"></i> -                    <i *ngIf="!voted" class="glyphicon glyphicon-heart-empty"></i> -                -->             </div>             <div class="badge badge-inverse votingCount">                 <div>{{count}}</div>             </div>         </div>     </div>` ,    styleUrls: ['/app/events/event-detail/upvote.component.css' ] }) export  class  UpvoteComponent   {    @Input() count: number; +    iconColor: string; +    @Input() set voted(val) { +        this .iconColor = val ? 'red'  : 'white' ; +    }     @Output() vote = new  EventEmitter();     onClick() {         this .vote.emit({});     } } 
 
Creating a Complex Custom Validator when user creates a new event, we want to make sure all 3 location fields or onlineUrl are typed.
location-validator.directive.ts, register it in barrel and app.module.ts
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 import  { Directive } from  '@angular/core' import  { Validator, FormGroup, NG_VALIDATORS } from  '@angular/forms' @Directive({     selector: '[validateLocation]' ,     providers: [{ provide : NG_VALIDATORS, useExisting : LocationValidator, multi : true  }]  }) export  class  LocationValidator  implements  Validator   {    validate(formGroup: FormGroup): { [key: string]: any } {         let  addressControl = formGroup.controls['address' ];         let  cityControl = formGroup.controls['city' ];         let  countryControl = formGroup.controls['country' ];         let  onlineUrlControl = (<FormGroup > formGroup.root).controls['onlineUrl'];         console.log(addressControl && addressControl.value)         if ((addressControl && addressControl.value             && cityControl && cityControl.value             && countryControl && countryControl.value)             || (onlineUrlControl && onlineUrlControl.value)) {             return null;         } else {             return { validateLocation: false }         }     } } 
 
create-event.component.ts
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 <form  #newEventForm ="ngForm"  (ngSubmit )="saveEvent(newEventForm.value)"  autocomplete ="off"  novalidate > +    <div  ngModelGroup ="location"  validateLocation  #locationGroup ="ngModelGroup" >          <div  class ="form-group" >              <label  for ="address" > Event Location:</label >  +            <em  *ngIf ="locationGroup?.invalid && locationGroup?.touched" > You must enter either flll location or online url</em >              <input  [(ngModel )]="address"  name ="address"  id ="address"  type ="text"  class ="form-control"  placeholder ="Address of event..."  />          </div >          <div  class ="row" >              <div  class ="col-md-6" >                  <input  [(ngModel )]="city"  name ="city"  id ="city"  type ="text"  class ="form-control"  placeholder ="City..."  />              </div >              <div  class ="col-md-6" >                  <input  [(ngModel )]="country"  name ="country"  id ="country"  type ="text"  class ="form-control"  placeholder ="Country..."  />              </div >          </div >      </div >      <div  class ="form-group" >          <label  for ="onlineUrl" > Online Url:</label >  +        <input  (change )="locationGroup.control.controls.address.updateValueAndValidity()"  [(ngModel )]="onlineUrl"  name ="onlineUrl"  id ="onlineUrl"  type ="text"  class ="form-control"  placeholder ="Online Url..."  />      </div >  </form >