diff --git a/ui/src/app/metadata/metadata.component.ts b/ui/src/app/metadata/metadata.component.ts index 52ca814e2..62170d286 100644 --- a/ui/src/app/metadata/metadata.component.ts +++ b/ui/src/app/metadata/metadata.component.ts @@ -5,6 +5,7 @@ import { LoadResolverRequest } from './resolver/action/collection.action'; import { LoadFilterRequest } from './filter/action/collection.action'; import { LoadDraftRequest } from './resolver/action/draft.action'; import * as fromRoot from '../app.reducer'; +import { LoadProviderRequest } from './provider/action/collection.action'; @Component({ selector: 'metadata-page', @@ -20,5 +21,6 @@ export class MetadataPageComponent { this.store.dispatch(new LoadResolverRequest()); this.store.dispatch(new LoadFilterRequest()); this.store.dispatch(new LoadDraftRequest()); + this.store.dispatch(new LoadProviderRequest()); } } diff --git a/ui/src/app/metadata/provider/container/provider-wizard-step.component.html b/ui/src/app/metadata/provider/container/provider-wizard-step.component.html index b04035bf7..831e71fbb 100644 --- a/ui/src/app/metadata/provider/container/provider-wizard-step.component.html +++ b/ui/src/app/metadata/provider/container/provider-wizard-step.component.html @@ -1,5 +1,6 @@ \ No newline at end of file + [validators]="validators" + (onChange)="valueChangeSubject.next($event)" + (onErrorChange)="statusChangeSubject.next($event)"> \ No newline at end of file diff --git a/ui/src/app/metadata/provider/container/provider-wizard-step.component.ts b/ui/src/app/metadata/provider/container/provider-wizard-step.component.ts index 962445d30..ed583d251 100644 --- a/ui/src/app/metadata/provider/container/provider-wizard-step.component.ts +++ b/ui/src/app/metadata/provider/container/provider-wizard-step.component.ts @@ -1,6 +1,6 @@ import { Component, OnDestroy } from '@angular/core'; import { Observable, Subject } from 'rxjs'; -import { withLatestFrom, map } from 'rxjs/operators'; +import { withLatestFrom, map, distinctUntilChanged } from 'rxjs/operators'; import { Store } from '@ngrx/store'; import * as fromProvider from '../reducer'; @@ -21,8 +21,11 @@ import { pick } from '../../../shared/util'; }) export class ProviderWizardStepComponent implements OnDestroy { - changeSubject = new Subject>(); - private changeEmitted$ = this.changeSubject.asObservable(); + valueChangeSubject = new Subject>(); + private valueChangeEmitted$ = this.valueChangeSubject.asObservable(); + + statusChangeSubject = new Subject>(); + private statusChangeEmitted$ = this.statusChangeSubject.asObservable(); schema$: Observable; schema: any; @@ -32,6 +35,35 @@ export class ProviderWizardStepComponent implements OnDestroy { valid$: Observable; model$: Observable; + namesList: string[] = []; + + validators = { + '/': (value, property, form_current) => { + let errors; + // iterate all customer + Object.keys(value).forEach((key) => { + const item = value[key]; + const validatorKey = `/${key}`; + const validator = this.validators.hasOwnProperty(validatorKey) ? this.validators[validatorKey] : null; + const error = validator ? validator(item, { path: `/${key}` }, form_current) : null; + if (error) { + errors = errors || []; + errors.push(error); + } + }); + return errors; + }, + '/name': (value, property, form) => { + const err = this.namesList.indexOf(value) > -1 ? { + code: 'INVALID_NAME', + path: `#${property.path}`, + message: 'Name must be unique.', + params: [value] + } : null; + return err; + } + }; + constructor( private store: Store ) { @@ -39,6 +71,8 @@ export class ProviderWizardStepComponent implements OnDestroy { this.definition$ = this.store.select(fromWizard.getWizardDefinition); this.changes$ = this.store.select(fromProvider.getEntityChanges); + this.store.select(fromProvider.getProviderNames).subscribe(list => this.namesList = list); + this.model$ = this.schema$.pipe( withLatestFrom( this.store.select(fromWizard.getModel), @@ -55,7 +89,7 @@ export class ProviderWizardStepComponent implements OnDestroy { map(({ model, definition }) => definition.translate ? definition.translate.formatter(model) : model) ); - this.changeEmitted$.pipe( + this.valueChangeEmitted$.pipe( withLatestFrom(this.schema$, this.definition$), map(([changes, schema, definition]) => { const type = changes.value['@type']; @@ -78,15 +112,20 @@ export class ProviderWizardStepComponent implements OnDestroy { map(({ changes, definition }) => definition.translate ? definition.translate.parser(changes) : changes) ) .subscribe(changes => this.store.dispatch(new UpdateProvider(changes))); - } - ngOnDestroy() { - this.changeSubject.complete(); + this.statusChangeEmitted$.pipe( + distinctUntilChanged() + ).subscribe(errors => { + console.log(!(errors.value)); + const status = { [this.currentPage]: !(errors.value) ? 'VALID' : 'INVALID' }; + this.store.dispatch(new UpdateStatus(status)); + }); + + this.store.select(fromWizard.getWizardIndex).subscribe(i => this.currentPage = i); } - onStatusChange(value): void { - const status = { [this.currentPage]: value ? 'VALID' : 'INVALID' }; - this.store.dispatch(new UpdateStatus(status)); + ngOnDestroy() { + this.valueChangeSubject.complete(); } } diff --git a/ui/src/app/metadata/provider/container/provider-wizard.component.ts b/ui/src/app/metadata/provider/container/provider-wizard.component.ts index ad042590b..793f491da 100644 --- a/ui/src/app/metadata/provider/container/provider-wizard.component.ts +++ b/ui/src/app/metadata/provider/container/provider-wizard.component.ts @@ -35,14 +35,6 @@ export class ProviderWizardComponent implements OnDestroy { provider: MetadataProvider; - namesList: string[] = []; - - validators = { - '/name': (value, property, form) => { - return this.namesList.indexOf(value) > -1 ? { 'name': { 'expectedValue': 'unique' } } : null; - } - }; - constructor( private store: Store, private router: Router, @@ -75,8 +67,6 @@ export class ProviderWizardComponent implements OnDestroy { ); this.changes$.subscribe(c => this.provider = c); - - this.store.select(fromProvider.getProviderNames).subscribe(list => this.namesList = list); } ngOnDestroy() { diff --git a/ui/src/app/metadata/provider/effect/collection.effect.ts b/ui/src/app/metadata/provider/effect/collection.effect.ts index 19e521f3c..5a471dfdd 100644 --- a/ui/src/app/metadata/provider/effect/collection.effect.ts +++ b/ui/src/app/metadata/provider/effect/collection.effect.ts @@ -9,7 +9,10 @@ import { ProviderCollectionActionTypes, AddProviderRequest, AddProviderSuccess, - AddProviderFail + AddProviderFail, + LoadProviderRequest, + LoadProviderSuccess, + LoadProviderError } from '../action/collection.action'; import { MetadataProviderService } from '../../domain/service/provider.service'; @@ -17,6 +20,19 @@ import { MetadataProviderService } from '../../domain/service/provider.service'; @Injectable() export class CollectionEffects { + @Effect() + loadFilters$ = this.actions$.pipe( + ofType(ProviderCollectionActionTypes.LOAD_PROVIDER_REQUEST), + switchMap(() => + this.providerService + .query() + .pipe( + map(providers => new LoadProviderSuccess(providers)), + catchError(error => of(new LoadProviderError(error))) + ) + ) + ); + @Effect() createProvider$ = this.actions$.pipe( ofType(ProviderCollectionActionTypes.ADD_PROVIDER_REQUEST), @@ -38,14 +54,12 @@ export class CollectionEffects { tap(provider => this.router.navigate(['metadata'])) ); - /* @Effect() addResolverSuccessReload$ = this.actions$.pipe( - ofType(EntityActionTypes.CREATE_PROVIDER_SUCCESS), + ofType(ProviderCollectionActionTypes.ADD_PROVIDER_SUCCESS), map(action => action.payload), map(provider => new LoadProviderRequest()) ); - */ constructor( private actions$: Actions, diff --git a/ui/src/app/metadata/provider/provider.module.ts b/ui/src/app/metadata/provider/provider.module.ts index 7d60e47bb..104bae13d 100644 --- a/ui/src/app/metadata/provider/provider.module.ts +++ b/ui/src/app/metadata/provider/provider.module.ts @@ -18,6 +18,7 @@ import { FormModule } from '../../schema-form/schema-form.module'; import { CustomWidgetRegistry } from '../../schema-form/registry'; import { SummaryPropertyComponent } from './component/summary-property.component'; import { CollectionEffects } from './effect/collection.effect'; +import { SharedModule } from '../../shared/shared.module'; @NgModule({ @@ -34,6 +35,7 @@ import { CollectionEffects } from './effect/collection.effect'; CommonModule, WizardModule, RouterModule, + SharedModule, FormModule ], exports: [] diff --git a/ui/src/app/schema-form/widget/text/string.component.html b/ui/src/app/schema-form/widget/text/string.component.html index b581f242e..435c2079a 100644 --- a/ui/src/app/schema-form/widget/text/string.component.html +++ b/ui/src/app/schema-form/widget/text/string.component.html @@ -11,6 +11,7 @@ {{ schema.description }} + + {{ errorMessages }} + + + {{ schema.widget.help }} + \ No newline at end of file diff --git a/ui/src/app/schema-form/widget/text/string.component.ts b/ui/src/app/schema-form/widget/text/string.component.ts index 26c1f5c44..cf46aa6e2 100644 --- a/ui/src/app/schema-form/widget/text/string.component.ts +++ b/ui/src/app/schema-form/widget/text/string.component.ts @@ -1,18 +1,9 @@ import { Component } from '@angular/core'; -import { ControlWidget } from 'ngx-schema-form'; +import { StringWidget } from 'ngx-schema-form'; @Component({ selector: 'custom-string', templateUrl: `./string.component.html`, styleUrls: ['../widget.component.scss'] }) -export class CustomStringComponent extends ControlWidget { - - getInputType() { - if (!this.schema.widget.id || this.schema.widget.id === 'string') { - return 'text'; - } else { - return this.schema.widget.id; - } - } -} +export class CustomStringComponent extends StringWidget {} diff --git a/ui/src/app/shared/shared.module.ts b/ui/src/app/shared/shared.module.ts index 4af6af8ce..f166ad254 100644 --- a/ui/src/app/shared/shared.module.ts +++ b/ui/src/app/shared/shared.module.ts @@ -35,6 +35,7 @@ import { PrettyXml } from './pipe/pretty-xml.pipe'; InputDefaultsDirective, I18nTextComponent, ValidFormIconComponent, + ValidationClassDirective, InfoLabelDirective ] }) diff --git a/ui/src/assets/schema/provider/metadata-provider.schema.json b/ui/src/assets/schema/provider/metadata-provider.schema.json index af33d3e45..f5a840f35 100644 --- a/ui/src/assets/schema/provider/metadata-provider.schema.json +++ b/ui/src/assets/schema/provider/metadata-provider.schema.json @@ -1,5 +1,5 @@ { - "title": "@MetadataResolver", + "title": "MetadataResolver", "type": "object", "widget": { "id": "fieldset" @@ -8,7 +8,11 @@ "name": { "title": "Metadata Provider Name (Dashboard Display Only)", "description": "Metadata Provider Name (Dashboard Display Only)", - "type": "string" + "type": "string", + "widget": { + "id": "string", + "help": "Must be unique." + } }, "@type": { "title": "Metadata Provider Type",