Created
November 30, 2016 14:01
-
-
Save aegyed91/4d5f75faac28101081721f6ce36d876b to your computer and use it in GitHub Desktop.
address google api places typeahead angular 2 formgroup integrated hax thing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<div class="form-group" [ngClass]="{'has-danger': feedback && !state.valid && !state.control.pristine }"> | |
<label *ngIf="label" class="form-control-label">{{label}}</label> | |
<ng-content></ng-content> | |
<div *ngIf="feedback && !state.valid && !state.pristine"> | |
<div *ngIf="state.hasError('required')" class="form-control-feedback">Required field.</div> | |
<div *ngIf="state.hasError('minlength')" class="form-control-feedback">Has to be at least {{state.getError('minlength').requiredLength}} characters long.</div> | |
<div *ngIf="state.hasError('maxlength')" class="form-control-feedback">Has to be less than {{state.getError('maxlength').requiredLength}} characters long.</div> | |
<div *ngIf="state.hasError('incorrectFileExtension')" class="form-control-feedback">File extension has to be <code>{{state.getError('incorrectFileExtension').requiredFileExtension}}</code>.</div> | |
<div *ngIf="state.hasError('incorrectFileSize')" class="form-control-feedback">File size has to be between <code>{{state.getError('incorrectFileSize').requiredMinFileSize / 1024}} - {{state.getError('incorrectFileSize').requiredMaxFileSize / 1024}} kilobyte</code>.</div> | |
<div *ngIf="state.hasError('invalidEmail')" class="form-control-feedback">Invalid email address.</div> | |
<div *ngIf="state.hasError('invalidTime')" class="form-control-feedback">Time has to be between <code>{{(state.getError('invalidTime').reqMinHour) | amDateFormat: state.getError('invalidTime').is24Hrs ? 'HH:mm' : 'hh:mmA'}}</code> - <code>{{(state.getError('invalidTime').reqMaxHour) | amDateFormat: state.getError('invalidTime').is24Hrs ? 'HH:mm' : 'hh:mmA'}}</code>.</div> | |
<!-- NON GENERAL ERR MESSAGES --> | |
<div *ngIf="state.control.hasError('uniqueProjectName')" class="form-control-feedback">Project name is not unique.</div> | |
</div> | |
<div *ngIf="debug"> | |
<pre><code>{{state.control.errors | json}}</code></pre> | |
<pre><code>{{state.control.value | json}}</code></pre> | |
</div> | |
</div> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { Component, ContentChild, Input } from '@angular/core'; | |
import { FormControlName } from '@angular/forms'; | |
import { IS_DEV } from '../../constants'; | |
@Component({ | |
selector: 'form-group-c', | |
templateUrl: './form-group.c.html', | |
styles: [` | |
:host { display: block; } | |
`] | |
}) | |
export class FormGroupC { | |
@Input() label: string; | |
@Input() feedback = true; | |
@Input() debug = false && IS_DEV; | |
@ContentChild(FormControlName) state; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<form [formGroup]="form" novalidate (submit)="onSubmit($event, form.value)"> | |
<div formGroupName="available" class="row"> | |
<div class="col-sm-6"> | |
<form-group-c label="Available from *"> | |
<ngb-timepicker formControlName="from"></ngb-timepicker> | |
</form-group-c> | |
</div> | |
<div class="col-sm-6"> | |
<form-group-c label="Available until *"> | |
<ngb-timepicker formControlName="until"></ngb-timepicker> | |
</form-group-c> | |
</div> | |
</div> | |
<form-group-c label="Minimum time between two meeting"> | |
<ngb-timepicker [meridian]="false" formControlName="timeBw2Meetin"></ngb-timepicker> | |
</form-group-c> | |
<form-group-c label="Preferred meeting places"> | |
<input #typeahead type="text" class="form-control" [ngbTypeahead]="search" | |
[resultFormatter]="formatter" [inputFormatter]="formatter" (selectItem)="selectPlace($event, typeahead)"/> | |
<span *ngIf="searching">searching...</span> | |
<div class="form-control-feedback" *ngIf="searchFailed">Sorry, suggestions could not be loaded.</div> | |
<div *ngIf="selectedPlaces.length" class="multiselect-container"> | |
<span *ngFor="let place of selectedPlaces" class="multiselect-item btn btn-secondary btn-sm"> | |
<a class="close" (click)="remove(place)">×</a> | |
<span>{{place}}</span> | |
</span> | |
</div> | |
<select multiple formControlName="prefPlaces" class="form-control hidden-xs-up"> | |
<option *ngFor="let place of selectedPlaces" [value]="place" selected>{{place}}</option> | |
</select> | |
</form-group-c> | |
<form-group-c label="Send notification X hours/minutes before meeting"> | |
<ngb-timepicker [meridian]="false" formControlName="notifyB4"></ngb-timepicker> | |
</form-group-c> | |
<form-group-c label="Maximum length of a meeting"> | |
<ngb-timepicker [meridian]="false" formControlName="maxLen"></ngb-timepicker> | |
</form-group-c> | |
<form-group-c label="How many meetings per day"> | |
<input formControlName="numPerDay" class="form-control" type="number"> | |
</form-group-c> | |
<button-c btnCls="btn-primary btn-lg btn-block" [isLoding]="submitPending" [disabled]="submitPending || !form.valid"> | |
Submit | |
</button-c> | |
<alert-list-c [alerts]="formAlerts"> | |
<hr> | |
</alert-list-c> | |
</form> | |
<hr> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { Component, OnInit } from '@angular/core'; | |
import { FormGroup, FormBuilder, Validators, FormArray } from '@angular/forms'; | |
import { IAlert, IPreferencesData, ITimePicker } from '../../../../shared/interfaces'; | |
import { ResMsgHandlerS } from '../../../../shared/services/res-msg-handler.s'; | |
import { UserSt } from '../../user.st'; | |
import { UserS } from '../../user.s'; | |
import { NgbTimepickerConfig, NgbTypeaheadSelectItemEvent } from '@ng-bootstrap/ng-bootstrap'; | |
import { GoogPlacesS } from '../../../../shared/services/goog-places.s'; | |
import { required } from '../../../../shared/validators/index'; | |
import { Observable } from 'rxjs'; | |
// import { timepicker } from '../../../../shared/validators/index'; | |
@Component({ | |
selector: 'preferences-c', | |
templateUrl: './preferences.c.html', | |
styleUrls: ['./preferences.c.css'], | |
providers: [NgbTimepickerConfig] | |
}) | |
export class PreferencesC implements OnInit { | |
form: FormGroup; | |
submitPending = false; | |
formAlerts: IAlert[] = []; | |
searching = false; | |
searchFailed = false; | |
selectedPlaces: string[] = []; | |
selectPlace(evt: NgbTypeaheadSelectItemEvent, self) { | |
evt.preventDefault(); | |
self.value = ''; | |
this.selectedPlaces.push(evt.item.formatted_address); | |
this.notifyPrefPlacesControl(); | |
} | |
remove(item: string) { | |
this.selectedPlaces.splice(this.selectedPlaces.indexOf(item), 1); | |
this.notifyPrefPlacesControl(); | |
} | |
notifyPrefPlacesControl() { | |
this.form.get('prefPlaces').setValue(this.selectedPlaces); | |
this.form.get('prefPlaces').markAsDirty(); | |
this.form.get('prefPlaces').markAsTouched(); | |
} | |
constructor(private formBuilder: FormBuilder, private timePickerConf: NgbTimepickerConfig, private mh: ResMsgHandlerS, | |
public userSt: UserSt, public userS: UserS, private googPlacesS: GoogPlacesS) { | |
Object.assign(this.timePickerConf, { | |
meridian: true, | |
seconds: false, | |
minuteStep: 15 | |
}); | |
this.selectedPlaces.push(...this.userSt.get('meetinPref.prefPlaces')); | |
} | |
ngOnInit() { | |
this.form = this.formBuilder.group({ | |
available: this.formBuilder.group({ | |
from: [this.dateToTimePickerObj(this.userSt.get('meetinPref.available.from')), [Validators.required/*timepicker(10, 18, this.is24Hours)*/]], | |
until: [this.dateToTimePickerObj(this.userSt.get('meetinPref.available.until')), [Validators.required]] | |
}), | |
timeBw2Meetin: [this.dateToTimePickerObj(this.userSt.get('meetinPref.timeBw2Meetin')), [Validators.required]], | |
prefPlaces: [this.selectedPlaces, [required()]], | |
notifyB4: [this.dateToTimePickerObj(this.userSt.get('meetinPref.notifyB4')), [Validators.required]], | |
maxLen: [this.dateToTimePickerObj(this.userSt.get('meetinPref.maxLen')), [Validators.required]], | |
numPerDay: [this.userSt.get('meetinPref.numPerDay'), [Validators.required]] | |
}); | |
} | |
search = (text$: Observable<string>) => { | |
return text$ | |
.debounceTime(300) | |
.distinctUntilChanged() | |
.do(() => this.searching = true) | |
.switchMap(query => | |
Observable.fromPromise(this.googPlacesS.search(query)) | |
.do(() => this.searchFailed = false) | |
.catch(() => { | |
this.searchFailed = true; | |
return Observable.of([]); | |
})) | |
.do(() => this.searching = false); | |
}; | |
formatter(x) { | |
return x.formatted_address; | |
}; | |
onSubmit(evt, data: IPreferencesData) { | |
evt.preventDefault(); | |
this.submitPending = true; | |
const normData = Object.assign(data, { | |
available: { | |
from: this.TimePickerObjToDate(data.available.from), | |
until: this.TimePickerObjToDate(data.available.until) | |
}, | |
timeBw2Meetin: this.TimePickerObjToDate(data.timeBw2Meetin), | |
notifyB4: this.TimePickerObjToDate(data.notifyB4), | |
maxLen: this.TimePickerObjToDate(data.maxLen) | |
}); | |
this.userS.setMeetingPref(normData).then(res => { | |
this.userSt.assign(res.user); | |
this.submitPending = false; | |
this.mh.showInToastr(res.message); | |
}).catch(err => { | |
this.mh.showInToastr(err, 1); | |
this.submitPending = false; | |
}); | |
} | |
TimePickerObjToDate(tData: ITimePicker) { | |
const d = new Date(); | |
d.setMilliseconds(0); | |
d.setSeconds(0); | |
d.setMinutes(tData.minute); | |
d.setHours(tData.hour); | |
return d; | |
} | |
dateToTimePickerObj(timestamp) { | |
const d = new Date(timestamp); | |
return { | |
hour: d.getHours(), | |
minute: d.getMinutes() | |
}; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { ValidatorFn, AbstractControl } from '@angular/forms'; | |
import { isNull, isEmpty } from 'lodash'; | |
export function requiredValidator(): ValidatorFn { | |
return (control: AbstractControl): {[key: string]: any} => { | |
if (isNull(control.value) || isEmpty(control.value)) { | |
return {required: true}; | |
} | |
return null; | |
}; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment