<!DOCTYPE html>
<html>

  <head>
    <title>angular2 playground</title>
    <link data-require="bootstrap-css@3.3.6" data-semver="3.3.6" rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.css" />
    <link rel="stylesheet" href="style.css" />
    <script src="https://code.angularjs.org/2.0.0-beta.17/angular2-polyfills.js"></script>
    <script src="https://code.angularjs.org/tools/system.js"></script>
    <script src="https://code.angularjs.org/tools/typescript.js"></script>
    <script src="config.js"></script>
    <script>
    System.import('app')
      .catch(console.error.bind(console));
  </script>
  </head>

  <body>
    <my-app>
    loading...
  </my-app>
  </body>

</html>
/* Styles go here */

### Angular2 Click Outside Directive (using DOM event bubbling)
This is a basic starter project contains the Click-Outside directive.
This directive is for detecting clicks outside of a DOM element where the directive is placed on.
Unlike many other click-outside detecting techniques (usually some kind of DOM traversing),
this directive will detect the outSide click using DOM event bubbling.

##Explenation
If an click event is triggered within our component it will bubble up all the way up the DOM, passing our Host element, till it reaches the Document root level.
As seen below, we are binding to the Element:click, and to the Document:click.
Element click:
If we click from inside the Element, the 1st method to be called is the trackEvent(event)- where we save a reference to the event.
Document click:
Every click will eventually (unless stopped - highly not recommended https://css-tricks.com/dangers-stopping-event-propagation/ ) will reach the document level.
At that point we compare to see if the event passed the Element Host component.

```[html]
@Directive({
    selector: '[clickOutside]',
    host: {
        // save a reference to the event click
        "(click)": "trackEvent( $event )",
        // compare the event that bubbled up 
        // to the one we save in the trackEvent method
        "(document: click)": "compareEvent( $event )" 
    }
})
```

System.config({
  //use typescript for compilation
  transpiler: 'typescript',
  //typescript compiler options
  typescriptOptions: {
    emitDecoratorMetadata: true
  },
  //map tells the System loader where to look for things
  map: {
    app: "./src",
    '@angular': 'https://npmcdn.com/@angular',
    'rxjs': 'https://npmcdn.com/rxjs@5.0.0-beta.6'
  },
  //packages defines our app package
  packages: {
    app: {
      main: './main.ts',
      defaultExtension: 'ts'
    },
    '@angular/core': {
      main: 'core.umd.js',
      defaultExtension: 'js'
    },
    '@angular/compiler': {
      main: 'compiler.umd.js',
      defaultExtension: 'js'
    },
    '@angular/common': {
      main: 'common.umd.js',
      defaultExtension: 'js'
    },
    '@angular/platform-browser-dynamic': {
      main: 'platform-browser-dynamic.umd.js',
      defaultExtension: 'js'
    },
    '@angular/platform-browser': {
      main: 'platform-browser.umd.js',
      defaultExtension: 'js'
    },
    rxjs: {
      defaultExtension: 'js'
    }
  }
});
//main entry point
import {bootstrap} from '@angular/platform-browser-dynamic';
import {App} from './app';

bootstrap(App, [])
  .catch(err => console.error(err));
//our root app component
import {Component} from '@angular/core';
import {ClickOutsideDirective} from './click-outside.directive';

@Component({
  selector: 'my-app',
  providers: [],
  template: `
    <div class="jumbotron">
    <div class="container">
        <h1>{{title}}</h1>
        <p class="btn btn-lg  btn-default" (click)="handleClick( $event )"
           (clickOutside)="handleClickOutside( $event )">
            <span > Click  to find where you clicked</span>
        </p>
        <p *ngIf="clickResult" class="alert-{{isInside?'info':'success'}} alert">{{clickResult}}</p>
   </div>
</div>
  `,
  directives: [ClickOutsideDirective]
})
export class App {
  constructor() {
    this.title = 'Angular 2 - Click-Outside directive example'
  }
   handleClick(event){
        this.isInside = true; 
        this.clickResult = `Inside Button, on element: ${event.target.tagName}`;
    }

    handleClickOutside(event){
        this.isInside = false;
        this.clickResult = `Outside Button, on element: ${event.target.tagName}`;
    }
}
import { Directive, Output, EventEmitter } from '@angular/core';

@Directive({
    selector: '[clickOutside]',
    host: {
        "(click)": "trackEvent( $event )",            // we can use instead =>  @HostListener('click', ['$event'])
        "(document: click)": "compareEvent( $event )" // we can use instead =>  @HostListener('document:click', ['$event'])
    }
})
export class ClickOutsideDirective {
    @Output('clickOutside') clickOutside:EventEmitter<any> = new EventEmitter();

    private localEvent = null;
    constructor() {}

    /** Compare event at the Document level to a reference of the Element:click
     * This method triggers when we are on Document level  - Document was clicked or event bubbling
     * If the Document click DON'T MATCH the Event:click reference  => than the click is from outside of the Element
     * @param event
     */
    compareEvent(event){
        if ( event !== this.localEvent ) {
            this.clickOutside.emit( event );
        }
        this.localEvent = null;
    }


    /** Track user click from inside the bound target
     *  We use this to track the click Event when it bubbles up the DOM tree
     * @param event
     */
    trackEvent(event){
        this.localEvent = event;
    }
}