From d8c96e29fdbc2b317e20750670e566b6c5bb6979 Mon Sep 17 00:00:00 2001 From: Jane Sandberg Date: Tue, 25 Jun 2019 11:17:07 -0700 Subject: [PATCH] LP1831390: combobox and date-select implement ControlValueAccessor This makes both components compatible with [(ngModel)] and reactive forms. Also adds sandbox examples. Signed-off-by: Jane Sandberg --- .../src/app/share/combobox/combobox.component.html | 2 +- .../src/app/share/combobox/combobox.component.ts | 35 ++++++++++++++++-- .../app/share/date-select/date-select.component.ts | 27 ++++++++++++-- .../src/app/staff/sandbox/sandbox.component.html | 43 ++++++++++++++++++++++ .../eg2/src/app/staff/sandbox/sandbox.component.ts | 19 ++++++++++ .../eg2/src/app/staff/sandbox/sandbox.module.ts | 3 ++ 6 files changed, 122 insertions(+), 7 deletions(-) diff --git a/Open-ILS/src/eg2/src/app/share/combobox/combobox.component.html b/Open-ILS/src/eg2/src/app/share/combobox/combobox.component.html index 0a5deeeb8c..b6099fcae9 100644 --- a/Open-ILS/src/eg2/src/app/share/combobox/combobox.component.html +++ b/Open-ILS/src/eg2/src/app/share/combobox/combobox.component.html @@ -16,7 +16,7 @@ [ngbTypeahead]="filter" [resultTemplate]="displayTemplate" [inputFormatter]="formatDisplayString" - (click)="click$.next($event.target.value)" + (click)="onClick($event)" (blur)="onBlur()" (selectItem)="selectorChanged($event)" #instance="ngbTypeahead"/> diff --git a/Open-ILS/src/eg2/src/app/share/combobox/combobox.component.ts b/Open-ILS/src/eg2/src/app/share/combobox/combobox.component.ts index 63f964ef81..82041be444 100644 --- a/Open-ILS/src/eg2/src/app/share/combobox/combobox.component.ts +++ b/Open-ILS/src/eg2/src/app/share/combobox/combobox.component.ts @@ -3,7 +3,8 @@ * * */ -import {Component, OnInit, Input, Output, ViewChild, EventEmitter, ElementRef} from '@angular/core'; +import {Component, OnInit, Input, Output, ViewChild, EventEmitter, ElementRef, forwardRef} from '@angular/core'; +import {ControlValueAccessor, NG_VALUE_ACCESSOR} from '@angular/forms'; import {Observable, of, Subject} from 'rxjs'; import {map, tap, reduce, mergeMap, mapTo, debounceTime, distinctUntilChanged, merge, filter} from 'rxjs/operators'; import {NgbTypeahead, NgbTypeaheadSelectItemEvent} from '@ng-bootstrap/ng-bootstrap'; @@ -22,9 +23,14 @@ export interface ComboboxEntry { styles: [` .icons {margin-left:-18px} .material-icons {font-size: 16px;font-weight:bold} - `] + `], + providers: [{ + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => ComboboxComponent), + multi: true + }] }) -export class ComboboxComponent implements OnInit { +export class ComboboxComponent implements ControlValueAccessor, OnInit { selected: ComboboxEntry; click$: Subject; @@ -110,9 +116,15 @@ export class ComboboxComponent implements OnInit { ngOnInit() { } + onClick($event) { + this.registerOnTouched(); + this.click$.next($event.target.value) + } + openMe($event) { // Give the input a chance to focus then fire the click // handler to force open the typeahead + this.registerOnTouched(); this.elm.nativeElement.getElementsByTagName('input')[0].focus(); setTimeout(() => this.click$.next('')); } @@ -187,6 +199,7 @@ export class ComboboxComponent implements OnInit { // Fired by the typeahead to inform us of a change. selectorChanged(selEvent: NgbTypeaheadSelectItemEvent) { this.onChange.emit(selEvent.item); + this.propagateChange(selEvent.item); } // Adds matching async entries to the entry list @@ -250,6 +263,22 @@ export class ComboboxComponent implements OnInit { }) ); } + + writeValue(value: any) { + if (value !== undefined) { + this.startId = value; + this.startIdFiresOnChange = true; + } + } + + propagateChange = (_: any) => {}; + + registerOnChange(fn) { + this.propagateChange = fn; + } + + registerOnTouched() { } + } diff --git a/Open-ILS/src/eg2/src/app/share/date-select/date-select.component.ts b/Open-ILS/src/eg2/src/app/share/date-select/date-select.component.ts index 625629026f..b78cebeeb8 100644 --- a/Open-ILS/src/eg2/src/app/share/date-select/date-select.component.ts +++ b/Open-ILS/src/eg2/src/app/share/date-select/date-select.component.ts @@ -1,5 +1,6 @@ -import {Component, OnInit, Input, Output, ViewChild, EventEmitter} from '@angular/core'; +import {Component, OnInit, Input, Output, ViewChild, EventEmitter, forwardRef} from '@angular/core'; import {NgbDateStruct} from '@ng-bootstrap/ng-bootstrap'; +import {ControlValueAccessor, NG_VALUE_ACCESSOR} from '@angular/forms'; /** * RE: displaying locale dates in the input field: @@ -9,9 +10,14 @@ import {NgbDateStruct} from '@ng-bootstrap/ng-bootstrap'; @Component({ selector: 'eg-date-select', - templateUrl: './date-select.component.html' + templateUrl: './date-select.component.html', + providers: [ { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => DateSelectComponent), + multi: true + } ] }) -export class DateSelectComponent implements OnInit { +export class DateSelectComponent implements OnInit, ControlValueAccessor { @Input() initialIso: string; // ISO string @Input() initialYmd: string; // YYYY-MM-DD (uses local time zone) @@ -61,6 +67,7 @@ export class DateSelectComponent implements OnInit { const iso = date.toISOString(); this.onChangeAsDate.emit(date); this.onChangeAsYmd.emit(ymd); + this.propagateChange(ymd); this.onChangeAsIso.emit(iso); } @@ -71,6 +78,20 @@ export class DateSelectComponent implements OnInit { return new Date( Number(parts[0]), Number(parts[1]) - 1, Number(parts[2])); } + + writeValue(value: string) { + if (value !== undefined) { + this.initialYmd = value; + } + } + + propagateChange = (_: any) => {}; + + registerOnChange(fn) { + this.propagateChange = fn; + } + + registerOnTouched() { } } diff --git a/Open-ILS/src/eg2/src/app/staff/sandbox/sandbox.component.html b/Open-ILS/src/eg2/src/app/staff/sandbox/sandbox.component.html index aa2164432c..cc802d1a2b 100644 --- a/Open-ILS/src/eg2/src/app/staff/sandbox/sandbox.component.html +++ b/Open-ILS/src/eg2/src/app/staff/sandbox/sandbox.component.html @@ -173,3 +173,46 @@

PCRUD auto flesh and FormatService detection

Fingerprint: {{aMetarecord}}
+
+
+
+

Do you like template-driven forms?

+
+ + + + + + + + + + Result: {{templateEntry.value | json}} +
+ + + ngModel: {{dateString}} +
+
+
+
+
+

Or perhaps reactive forms interest you?

+
+ Choose your favorite law of library science: + + + + + + + + +
+ error + That isn't a real law of library science! +
+
+
+
+
diff --git a/Open-ILS/src/eg2/src/app/staff/sandbox/sandbox.component.ts b/Open-ILS/src/eg2/src/app/staff/sandbox/sandbox.component.ts index de94b5ef74..51501bfc54 100644 --- a/Open-ILS/src/eg2/src/app/staff/sandbox/sandbox.component.ts +++ b/Open-ILS/src/eg2/src/app/staff/sandbox/sandbox.component.ts @@ -15,6 +15,7 @@ import {PrintService} from '@eg/share/print/print.service'; import {ComboboxEntry} from '@eg/share/combobox/combobox.component'; import {FormatService} from '@eg/core/format.service'; import {FmRecordEditorComponent} from '@eg/share/fm-editor/fm-editor.component'; +import {FormGroup, FormControl} from '@angular/forms'; @Component({ templateUrl: 'sandbox.component.html' @@ -60,6 +61,10 @@ export class SandboxComponent implements OnInit { dynamicTitleText: string; + ranganathan: FormGroup; + + dateString = '2019-09-09'; + complimentEvergreen: (rows: IdlObject[]) => void; notOneSelectedRow: (rows: IdlObject[]) => boolean; @@ -86,6 +91,20 @@ export class SandboxComponent implements OnInit { } ngOnInit() { + this.ranganathan = new FormGroup({ + 'law': new FormControl('second', (c: FormControl) => { + // An Angular custom validator + if ("wrong" === c.value.id) { + return { notALaw: 'That\'s not a real law of library science!' }; + } else { + return null; + } + } ) + }); + + this.ranganathan.get('law').valueChanges.subscribe(l => { + this.toast.success('You chose: ' + l.label); + }); this.gridDataSource.data = [ {name: 'Jane', state: 'AZ'}, diff --git a/Open-ILS/src/eg2/src/app/staff/sandbox/sandbox.module.ts b/Open-ILS/src/eg2/src/app/staff/sandbox/sandbox.module.ts index 58910dddbb..ebb886a67f 100644 --- a/Open-ILS/src/eg2/src/app/staff/sandbox/sandbox.module.ts +++ b/Open-ILS/src/eg2/src/app/staff/sandbox/sandbox.module.ts @@ -2,6 +2,7 @@ import {NgModule} from '@angular/core'; import {StaffCommonModule} from '@eg/staff/common.module'; import {SandboxRoutingModule} from './routing.module'; import {SandboxComponent} from './sandbox.component'; +import {FormsModule, ReactiveFormsModule} from '@angular/forms'; @NgModule({ declarations: [ @@ -10,6 +11,8 @@ import {SandboxComponent} from './sandbox.component'; imports: [ StaffCommonModule, SandboxRoutingModule, + FormsModule, + ReactiveFormsModule, ], providers: [ ] -- 2.11.0