mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-15 01:25:36 -05:00
Add validation to model view components (#1356)
This commit is contained in:
@@ -25,17 +25,23 @@ class ModelBuilderImpl implements sqlops.ModelBuilder {
|
||||
|
||||
navContainer(): sqlops.ContainerBuilder<sqlops.NavContainer, any, any> {
|
||||
let id = this.getNextComponentId();
|
||||
return new ContainerBuilderImpl(this._proxy, this._handle, ModelComponentTypes.NavContainer, id);
|
||||
let container: ContainerBuilderImpl<sqlops.NavContainer, any, any> = new ContainerBuilderImpl(this._proxy, this._handle, ModelComponentTypes.NavContainer, id);
|
||||
this._eventHandlers.set(id, container);
|
||||
return container;
|
||||
}
|
||||
|
||||
flexContainer(): sqlops.FlexBuilder {
|
||||
let id = this.getNextComponentId();
|
||||
return new ContainerBuilderImpl<sqlops.FlexContainer, sqlops.FlexLayout, sqlops.FlexItemLayout>(this._proxy, this._handle, ModelComponentTypes.FlexContainer, id);
|
||||
let container: ContainerBuilderImpl<sqlops.FlexContainer, any, any> = new ContainerBuilderImpl<sqlops.FlexContainer, sqlops.FlexLayout, sqlops.FlexItemLayout>(this._proxy, this._handle, ModelComponentTypes.FlexContainer, id);
|
||||
this._eventHandlers.set(id, container);
|
||||
return container;
|
||||
}
|
||||
|
||||
formContainer(): sqlops.FormBuilder {
|
||||
let id = this.getNextComponentId();
|
||||
return new FormContainerBuilder(this._proxy, this._handle, ModelComponentTypes.Form, id);
|
||||
let container = new FormContainerBuilder(this._proxy, this._handle, ModelComponentTypes.Form, id);
|
||||
this._eventHandlers.set(id, container);
|
||||
return container;
|
||||
}
|
||||
|
||||
card(): sqlops.ComponentBuilder<sqlops.CardComponent> {
|
||||
@@ -110,6 +116,11 @@ class ComponentBuilderImpl<T extends sqlops.Component> implements sqlops.Compone
|
||||
return this;
|
||||
}
|
||||
|
||||
withValidation(validation: (component: T) => boolean): sqlops.ComponentBuilder<T> {
|
||||
this._component.validations.push(validation);
|
||||
return this;
|
||||
}
|
||||
|
||||
handleEvent(eventArgs: IComponentEventArgs) {
|
||||
this._component.onEvent(eventArgs);
|
||||
}
|
||||
@@ -138,6 +149,7 @@ class ContainerBuilderImpl<T extends sqlops.Component, TLayout, TItemLayout> ext
|
||||
let componentWrapper = item as ComponentWrapper;
|
||||
return new InternalItemConfig(componentWrapper, itemLayout);
|
||||
});
|
||||
components.forEach(component => component.onValidityChanged(() => this._component.validate()));
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -169,6 +181,7 @@ class FormContainerBuilder extends ContainerBuilderImpl<sqlops.FormContainer, sq
|
||||
this._component.itemConfigs.push(new InternalItemConfig(componentWrapper, itemLayout));
|
||||
});
|
||||
}
|
||||
formItem.component.onValidityChanged(() => this._component.validate());
|
||||
});
|
||||
return this;
|
||||
}
|
||||
@@ -194,6 +207,10 @@ class ComponentWrapper implements sqlops.Component {
|
||||
public properties: { [key: string]: any } = {};
|
||||
public layout: any;
|
||||
public itemConfigs: InternalItemConfig[];
|
||||
public validations: ((component: ThisType<ComponentWrapper>) => boolean)[] = [];
|
||||
private _valid: boolean = true;
|
||||
private _onValidityChangedEmitter = new Emitter<boolean>();
|
||||
public readonly onValidityChanged = this._onValidityChangedEmitter.event;
|
||||
|
||||
private _onErrorEmitter = new Emitter<Error>();
|
||||
public readonly onError: vscode.Event<Error> = this._onErrorEmitter.event;
|
||||
@@ -206,6 +223,12 @@ class ComponentWrapper implements sqlops.Component {
|
||||
) {
|
||||
this.properties = {};
|
||||
this.itemConfigs = [];
|
||||
this.validations.push((component: this) => {
|
||||
return component.items.every(item => {
|
||||
item.validate();
|
||||
return item.valid;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public get id(): string {
|
||||
@@ -248,6 +271,7 @@ class ComponentWrapper implements sqlops.Component {
|
||||
}
|
||||
let config = new InternalItemConfig(itemImpl, itemLayout);
|
||||
this.itemConfigs.push(config);
|
||||
itemImpl.onValidityChanged(() => this.validate());
|
||||
this._proxy.$addToContainer(this._handle, this.id, config.toIItemConfig()).then(undefined, this.handleError);
|
||||
}
|
||||
|
||||
@@ -270,18 +294,20 @@ class ComponentWrapper implements sqlops.Component {
|
||||
public onEvent(eventArgs: IComponentEventArgs) {
|
||||
if (eventArgs && eventArgs.eventType === ComponentEventType.PropertiesChanged) {
|
||||
this.properties = eventArgs.args;
|
||||
this.validate();
|
||||
} else if (eventArgs) {
|
||||
let emitter = this._emitterMap.get(eventArgs.eventType);
|
||||
if (emitter) {
|
||||
emitter.fire();
|
||||
}
|
||||
let emitter = this._emitterMap.get(eventArgs.eventType);
|
||||
if (emitter) {
|
||||
emitter.fire();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected setProperty(key: string, value: any): Thenable<boolean> {
|
||||
if (!this.properties[key] || this.properties[key] !== value) {
|
||||
// Only notify the front end if a value has been updated
|
||||
this.properties[key] = value;
|
||||
this.validate();
|
||||
return this.notifyPropertyChanged();
|
||||
}
|
||||
return Promise.resolve(true);
|
||||
@@ -290,6 +316,29 @@ class ComponentWrapper implements sqlops.Component {
|
||||
private handleError(err: Error): void {
|
||||
this._onErrorEmitter.fire(err);
|
||||
}
|
||||
|
||||
public validate(): void {
|
||||
let isValid = true;
|
||||
try {
|
||||
this.validations.forEach(validation => {
|
||||
if (!validation(this)) {
|
||||
isValid = false;
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
isValid = false;
|
||||
}
|
||||
let oldValid = this._valid;
|
||||
if (this._valid !== isValid) {
|
||||
this._valid = isValid;
|
||||
this._proxy.$notifyValidation(this._handle, this._id, isValid);
|
||||
this._onValidityChangedEmitter.fire(this._valid);
|
||||
}
|
||||
}
|
||||
|
||||
public get valid(): boolean {
|
||||
return this._valid;
|
||||
}
|
||||
}
|
||||
|
||||
class ContainerWrapper<T, U> extends ComponentWrapper implements sqlops.Container<T, U> {
|
||||
@@ -428,8 +477,11 @@ class ButtonWrapper extends ComponentWrapper implements sqlops.ButtonComponent {
|
||||
class ModelViewImpl implements sqlops.ModelView {
|
||||
|
||||
public onClosedEmitter = new Emitter<any>();
|
||||
private _onValidityChangedEmitter = new Emitter<boolean>();
|
||||
public readonly onValidityChanged = this._onValidityChangedEmitter.event;
|
||||
|
||||
private _modelBuilder: ModelBuilderImpl;
|
||||
private _component: sqlops.Component;
|
||||
|
||||
constructor(
|
||||
private readonly _proxy: MainThreadModelViewShape,
|
||||
@@ -456,17 +508,28 @@ class ModelViewImpl implements sqlops.ModelView {
|
||||
return this._modelBuilder;
|
||||
}
|
||||
|
||||
public get valid(): boolean {
|
||||
return this._component.valid;
|
||||
}
|
||||
|
||||
public handleEvent(componentId: string, eventArgs: IComponentEventArgs): void {
|
||||
this._modelBuilder.handleEvent(componentId, eventArgs);
|
||||
}
|
||||
|
||||
public initializeModel<T extends sqlops.Component>(component: T): Thenable<void> {
|
||||
component.onValidityChanged(valid => this._onValidityChangedEmitter.fire(valid));
|
||||
this._component = component;
|
||||
let componentImpl = <any>component as ComponentWrapper;
|
||||
if (!componentImpl) {
|
||||
return Promise.reject(nls.localize('unknownConfig', 'Unkown component configuration, must use ModelBuilder to create a configuration object'));
|
||||
}
|
||||
componentImpl.validate();
|
||||
return this._proxy.$initializeModel(this._handle, componentImpl.toComponentShape());
|
||||
}
|
||||
|
||||
public validate(): void {
|
||||
this._component.validate();
|
||||
}
|
||||
}
|
||||
|
||||
export class ExtHostModelView implements ExtHostModelViewShape {
|
||||
|
||||
@@ -21,10 +21,18 @@ class DialogImpl implements sqlops.window.modelviewdialog.Dialog {
|
||||
public okButton: sqlops.window.modelviewdialog.Button;
|
||||
public cancelButton: sqlops.window.modelviewdialog.Button;
|
||||
public customButtons: sqlops.window.modelviewdialog.Button[];
|
||||
public readonly onValidityChanged: vscode.Event<boolean>;
|
||||
private _valid: boolean = true;
|
||||
|
||||
constructor(private _extHostModelViewDialog: ExtHostModelViewDialog) {
|
||||
this.okButton = this._extHostModelViewDialog.createButton(nls.localize('dialogOkLabel', 'Done'));
|
||||
this.cancelButton = this._extHostModelViewDialog.createButton(nls.localize('dialogCancelLabel', 'Cancel'));
|
||||
this.onValidityChanged = this._extHostModelViewDialog.getValidityChangedEvent(this);
|
||||
this.onValidityChanged(valid => this._valid = valid);
|
||||
}
|
||||
|
||||
public get valid(): boolean {
|
||||
return this._valid;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,6 +97,7 @@ export class ExtHostModelViewDialog implements ExtHostModelViewDialogShape {
|
||||
private readonly _tabHandles = new Map<sqlops.window.modelviewdialog.DialogTab, number>();
|
||||
private readonly _buttonHandles = new Map<sqlops.window.modelviewdialog.Button, number>();
|
||||
|
||||
private readonly _validityEmitters = new Map<number, Emitter<boolean>>();
|
||||
private readonly _onClickCallbacks = new Map<number, () => void>();
|
||||
|
||||
constructor(
|
||||
@@ -134,6 +143,13 @@ export class ExtHostModelViewDialog implements ExtHostModelViewDialogShape {
|
||||
this._onClickCallbacks.get(handle)();
|
||||
}
|
||||
|
||||
public $onDialogValidityChanged(handle: number, valid: boolean): void {
|
||||
let emitter = this._validityEmitters.get(handle);
|
||||
if (emitter) {
|
||||
emitter.fire(valid);
|
||||
}
|
||||
}
|
||||
|
||||
public open(dialog: sqlops.window.modelviewdialog.Dialog): void {
|
||||
let handle = this.getDialogHandle(dialog);
|
||||
this.updateDialogContent(dialog);
|
||||
@@ -208,4 +224,14 @@ export class ExtHostModelViewDialog implements ExtHostModelViewDialogShape {
|
||||
button.label = label;
|
||||
return button;
|
||||
}
|
||||
|
||||
public getValidityChangedEvent(dialog: sqlops.window.modelviewdialog.Dialog) {
|
||||
let handle = this.getDialogHandle(dialog);
|
||||
let emitter = this._validityEmitters.get(handle);
|
||||
if (!emitter) {
|
||||
emitter = new Emitter<boolean>();
|
||||
this._validityEmitters.set(handle, emitter);
|
||||
}
|
||||
return emitter.event;
|
||||
}
|
||||
}
|
||||
@@ -82,6 +82,10 @@ export class MainThreadModelView extends Disposable implements MainThreadModelVi
|
||||
return this.execModelViewAction(handle, (modelView) => modelView.setProperties(componentId, properties));
|
||||
}
|
||||
|
||||
$notifyValidation(handle: number, componentId: string, valid: boolean): Thenable<void> {
|
||||
return this.execModelViewAction(handle, (modelView) => modelView.setValid(componentId, valid));
|
||||
}
|
||||
|
||||
private execModelViewAction<T>(handle: number, action: (m: IModelView) => T): Thenable<T> {
|
||||
let modelView: IModelView = this._dialogs.get(handle);
|
||||
let result = action(modelView);
|
||||
|
||||
@@ -52,6 +52,7 @@ export class MainThreadModelViewDialog implements MainThreadModelViewDialogShape
|
||||
let cancelButton = this.getButton(details.cancelButton);
|
||||
dialog.okButton = okButton;
|
||||
dialog.cancelButton = cancelButton;
|
||||
dialog.onValidityChanged(valid => this._proxy.$onDialogValidityChanged(handle, valid));
|
||||
this._dialogs.set(handle, dialog);
|
||||
}
|
||||
|
||||
|
||||
@@ -530,6 +530,7 @@ export interface MainThreadModelViewShape extends IDisposable {
|
||||
$setLayout(handle: number, componentId: string, layout: any): Thenable<void>;
|
||||
$setProperties(handle: number, componentId: string, properties: { [key: string]: any }): Thenable<void>;
|
||||
$registerEvent(handle: number, componentId: string): Thenable<void>;
|
||||
$notifyValidation(handle: number, componentId: string, valid: boolean): Thenable<void>;
|
||||
}
|
||||
|
||||
export interface ExtHostObjectExplorerShape {
|
||||
@@ -547,6 +548,7 @@ export interface MainThreadObjectExplorerShape extends IDisposable {
|
||||
|
||||
export interface ExtHostModelViewDialogShape {
|
||||
$onButtonClick(handle: number): void;
|
||||
$onDialogValidityChanged(handle: number, valid: boolean): void;
|
||||
}
|
||||
|
||||
export interface MainThreadModelViewDialogShape extends IDisposable {
|
||||
|
||||
Reference in New Issue
Block a user