├── .gitignore ├── .npmignore ├── .travis.yml ├── LICENSE.md ├── README.md ├── angular2-recaptcha.ts ├── index.ts ├── lib ├── captcha.component.ts └── captcha.service.ts ├── package-lock.json ├── package.json └── tsconfig.json /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | typings 3 | factories 4 | 5 | 6 | # misc 7 | *.log 8 | .idea 9 | *.d.ts 10 | *.js 11 | *.metadata.json 12 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | README.md 2 | tsconfig.json 3 | .npmignore 4 | .idea 5 | *.ts 6 | !*.d.ts 7 | factories 8 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - 6 4 | 5 | os: 6 | - linux 7 | 8 | script: npm run prepare 9 | 10 | cache: 11 | directories: node_modules 12 | 13 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017 xmaestro 2 | 3 | Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. 4 | 5 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Travis](https://travis-ci.org/xmaestro/angular2-recaptcha.svg?branch=master) 2 | 3 | # Angular 2 : TypeScript component for Google reCaptcha 2 4 | 5 | This is just very simple Angular 2 component that implements Google [reCaptcha 2](https://www.google.com/recaptcha/intro/index.html). 6 | 7 | Installation 8 | -------------------------------------- 9 | 10 | Install it from npm: 11 | 12 | ```bash 13 | npm install angular2-recaptcha 14 | ``` 15 | 16 | Usage 17 | -------------------------------------- 18 | 19 | ### SystemJS config 20 | 21 | ```js 22 | System.config({ 23 | map: { 24 | 'angular2-recaptcha': 'node_modules/angular2-recaptcha' 25 | }, 26 | packages: { 27 | app: { 28 | format: 'register', 29 | defaultExtension: 'js' 30 | }, 31 | 'angular2-recaptcha': {defaultExtension: 'js', main:'index'} 32 | } 33 | }); 34 | ``` 35 | 36 | ### Module 37 | 38 | ```typescript 39 | ... 40 | import { ReCaptchaModule } from 'angular2-recaptcha'; 41 | ... 42 | ``` 43 | 44 | ```typescript 45 | ... 46 | @NgModule({ 47 | imports: [...,ReCaptchaModule] 48 | }) 49 | ... 50 | ``` 51 | 52 | ### View 53 | 54 | Use in template like below 55 | 56 | ```html 57 | 58 | ``` 59 | 60 | Where **site_key** is the Google reCaptcha public key. Optional parameters as follows: 61 | * **language** One of the ISO language values supported by Google: https://developers.google.com/recaptcha/docs/language Note that due to the design of the reCaptcha API, only the first component on a page can change the language from default English. 62 | * **theme** Either `light` (default) or `dark`. 63 | * **type** Either `image` (default) or `audio`. 64 | * **size** Either `normal` (default), `compact` or `invisible`. 65 | * **tabindex** Tabindex for navigation, default 0. 66 | * **global** If true, the reCaptcha script will be loaded from www.recaptcha.net instead of www.google.com 67 | 68 | 69 | ## Callback 70 | 71 | To catch the success callback, you will need to subscribe to the `captchaResponse` event. The response token will be passed in the `$event` parameter. 72 | To wait for component to be loaded subscribe to `loaded` event. 73 | 74 | ```html 75 | 76 | ``` 77 | 78 | The event `captchaExpired` is triggered when the displayed image has expired. It does not have any event parameters. 79 | 80 | ## Methods 81 | 82 | To access the methods, use [@ViewChild](https://angular.io/docs/ts/latest/api/core/index/ViewChild-decorator.html). 83 | 84 | ### Import 85 | ```typescript 86 | import { ViewChild } from '@angular/core'; 87 | import { ReCaptchaComponent } from 'angular2-recaptcha'; 88 | 89 | export class RegisterComponent { 90 | @ViewChild(ReCaptchaComponent) captcha: ReCaptchaComponent; 91 | } 92 | ``` 93 | 94 | ### Usage 95 | You can request a new captcha to be displayed: 96 | ```typescript 97 | this.captcha.reset(); 98 | ``` 99 | 100 | The previous response can be retrieved: 101 | ```typescript 102 | let token = this.captcha.getResponse(); 103 | ``` 104 | -------------------------------------------------------------------------------- /angular2-recaptcha.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { ReCaptchaComponent } from "./lib/captcha.component"; 3 | import { RECAPTCHA_SERVICE_PROVIDER } from "./lib/captcha.service"; 4 | 5 | @NgModule({ 6 | declarations: [ReCaptchaComponent], 7 | exports: [ReCaptchaComponent], 8 | providers: [RECAPTCHA_SERVICE_PROVIDER] 9 | }) 10 | export class ReCaptchaModule {} 11 | 12 | export * from './lib/captcha.component'; 13 | -------------------------------------------------------------------------------- /index.ts: -------------------------------------------------------------------------------- 1 | export * from './angular2-recaptcha'; 2 | -------------------------------------------------------------------------------- /lib/captcha.component.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Component, 3 | OnInit, 4 | Input, 5 | Output, 6 | EventEmitter, 7 | NgZone, 8 | ViewChild, ElementRef, forwardRef 9 | } from '@angular/core'; 10 | import { NG_VALUE_ACCESSOR, ControlValueAccessor } from '@angular/forms'; 11 | import { ReCaptchaService } from './captcha.service'; 12 | 13 | @Component({ 14 | selector: 're-captcha', 15 | template: '
', 16 | providers: [ 17 | { 18 | provide: NG_VALUE_ACCESSOR, 19 | useExisting: forwardRef(() => ReCaptchaComponent), 20 | multi: true 21 | } 22 | ] 23 | }) 24 | export class ReCaptchaComponent implements OnInit, ControlValueAccessor { 25 | 26 | @Input() site_key: string = null; 27 | @Input() theme = 'light'; 28 | @Input() type = 'image'; 29 | @Input() size = 'normal'; 30 | @Input() tabindex = 0; 31 | @Input() badge = 'bottomright'; 32 | /* Available languages: https://developers.google.com/recaptcha/docs/language */ 33 | @Input() language: string = null; 34 | @Input() global: boolean = false; 35 | 36 | @Output() captchaResponse = new EventEmitter(); 37 | @Output() captchaExpired = new EventEmitter(); 38 | @Output() loaded = new EventEmitter(); 39 | 40 | @ViewChild('target') targetRef: ElementRef; 41 | widgetId: any = null; 42 | 43 | onChange: Function = () => {}; 44 | onTouched: Function = () => {}; 45 | 46 | constructor( 47 | private _zone: NgZone, 48 | private _captchaService: ReCaptchaService 49 | ) { 50 | } 51 | 52 | ngOnInit() { 53 | this._captchaService.getReady(this.language, this.global) 54 | .subscribe((ready) => { 55 | if (!ready) 56 | return; 57 | // noinspection TypeScriptUnresolvedVariable,TypeScriptUnresolvedFunction 58 | this.widgetId = (window).grecaptcha.render(this.targetRef.nativeElement, { 59 | 'sitekey': this.site_key, 60 | 'badge': this.badge, 61 | 'theme': this.theme, 62 | 'type': this.type, 63 | 'size': this.size, 64 | 'tabindex': this.tabindex, 65 | 'callback': ((response: any) => this._zone.run(this.recaptchaCallback.bind(this, response))), 66 | 'expired-callback': (() => this._zone.run(this.recaptchaExpiredCallback.bind(this))) 67 | }); 68 | setTimeout(() => { 69 | this.loaded.emit(true); 70 | }, 0); 71 | }); 72 | } 73 | 74 | // noinspection JSUnusedGlobalSymbols 75 | public reset() { 76 | if (this.widgetId === null) 77 | return; 78 | // noinspection TypeScriptUnresolvedVariable 79 | this._zone.runOutsideAngular((window).grecaptcha.reset.bind(this.widgetId)); 80 | this.onChange(null); 81 | } 82 | 83 | // noinspection JSUnusedGlobalSymbols 84 | public execute() { 85 | if (this.widgetId === null) 86 | return; 87 | // noinspection TypeScriptUnresolvedVariable 88 | (window).grecaptcha.execute(this.widgetId); 89 | } 90 | 91 | public getResponse(): string { 92 | if (this.widgetId === null) 93 | return null; 94 | // noinspection TypeScriptUnresolvedVariable 95 | return (window).grecaptcha.getResponse(this.widgetId); 96 | } 97 | 98 | writeValue(newValue: any): void { 99 | /* ignore it */ 100 | } 101 | 102 | registerOnChange(fn: any): void { 103 | this.onChange = fn; 104 | } 105 | 106 | registerOnTouched(fn: any): void { 107 | this.onTouched = fn; 108 | } 109 | 110 | private recaptchaCallback(response: string) { 111 | this.onChange(response); 112 | this.onTouched(); 113 | this.captchaResponse.emit(response); 114 | } 115 | 116 | private recaptchaExpiredCallback() { 117 | this.onChange(null); 118 | this.onTouched(); 119 | this.captchaExpired.emit(); 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /lib/captcha.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable, NgZone, Optional, SkipSelf } from '@angular/core'; 2 | import { BehaviorSubject, Observable } from 'rxjs'; 3 | 4 | /* 5 | * Common service shared by all reCaptcha component instances 6 | * through dependency injection. 7 | * This service has the task of loading the reCaptcha API once for all. 8 | * Only the first instance of the component creates the service, subsequent 9 | * components will use the existing instance. 10 | * 11 | * As the language is passed to the