mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-23 01:25:38 -05:00
Add default model view input types and validation (#1397)
This commit is contained in:
@@ -24,7 +24,8 @@ export class ItemDescriptor<T> {
|
||||
|
||||
export abstract class ComponentBase extends Disposable implements IComponent, OnDestroy, OnInit {
|
||||
protected properties: { [key: string]: any; } = {};
|
||||
protected _valid: boolean = true;
|
||||
private _valid: boolean = true;
|
||||
protected _validations: (() => boolean | Thenable<boolean>)[] = [];
|
||||
private _eventQueue: IComponentEventArgs[] = [];
|
||||
constructor(
|
||||
protected _changeRef: ChangeDetectorRef) {
|
||||
@@ -44,6 +45,7 @@ export abstract class ComponentBase extends Disposable implements IComponent, On
|
||||
protected baseInit(): void {
|
||||
if (this.modelStore) {
|
||||
this.modelStore.registerComponent(this);
|
||||
this._validations.push(() => this.modelStore.validate(this));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,6 +69,7 @@ export abstract class ComponentBase extends Disposable implements IComponent, On
|
||||
}
|
||||
this.properties = properties;
|
||||
this.layout();
|
||||
this.validate();
|
||||
}
|
||||
|
||||
protected getProperties<TPropertyBag>(): TPropertyBag {
|
||||
@@ -84,6 +87,7 @@ export abstract class ComponentBase extends Disposable implements IComponent, On
|
||||
eventType: ComponentEventType.PropertiesChanged,
|
||||
args: this.getProperties()
|
||||
});
|
||||
this.validate();
|
||||
}
|
||||
|
||||
public get enabled(): boolean {
|
||||
@@ -96,16 +100,6 @@ export abstract class ComponentBase extends Disposable implements IComponent, On
|
||||
return this._valid;
|
||||
}
|
||||
|
||||
public setValid(valid: boolean): void {
|
||||
if (this._valid !== valid) {
|
||||
this._valid = valid;
|
||||
this.fireEvent({
|
||||
eventType: ComponentEventType.validityChanged,
|
||||
args: valid
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public registerEventHandler(handler: (event: IComponentEventArgs) => void): IDisposable {
|
||||
if (this._eventQueue) {
|
||||
while (this._eventQueue.length > 0) {
|
||||
@@ -123,6 +117,21 @@ export abstract class ComponentBase extends Disposable implements IComponent, On
|
||||
this._eventQueue.push(event);
|
||||
}
|
||||
}
|
||||
|
||||
public validate(): Thenable<boolean> {
|
||||
let validations = this._validations.map(validation => Promise.resolve(validation()));
|
||||
return Promise.all(validations).then(values => {
|
||||
let isValid = values.every(value => value === true);
|
||||
if (this._valid !== isValid) {
|
||||
this._valid = isValid;
|
||||
this.fireEvent({
|
||||
eventType: ComponentEventType.validityChanged,
|
||||
args: this._valid
|
||||
});
|
||||
}
|
||||
return isValid;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export abstract class ContainerBase<T> extends ComponentBase {
|
||||
@@ -133,11 +142,17 @@ export abstract class ContainerBase<T> extends ComponentBase {
|
||||
) {
|
||||
super(_changeRef);
|
||||
this.items = [];
|
||||
this._validations.push(() => this.items.every(item => this.modelStore.getComponent(item.descriptor.id).valid));
|
||||
}
|
||||
|
||||
/// IComponent container-related implementation
|
||||
public addToContainer(componentDescriptor: IComponentDescriptor, config: any): void {
|
||||
this.items.push(new ItemDescriptor(componentDescriptor, config));
|
||||
this.modelStore.eventuallyRunOnComponent(componentDescriptor.id, component => component.registerEventHandler(event => {
|
||||
if (event.eventType === ComponentEventType.validityChanged) {
|
||||
this.validate();
|
||||
}
|
||||
}));
|
||||
this._changeRef.detectChanges();
|
||||
}
|
||||
|
||||
|
||||
@@ -52,15 +52,17 @@ export default class InputBoxComponent extends ComponentBase implements ICompone
|
||||
return undefined;
|
||||
} else {
|
||||
return {
|
||||
content: nls.localize('invalidValueError', 'Invalid value'),
|
||||
content: this._input.inputElement.validationMessage || nls.localize('invalidValueError', 'Invalid value'),
|
||||
type: MessageType.ERROR
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
useDefaultValidation: true
|
||||
};
|
||||
|
||||
this._input = new InputBox(this._inputContainer.nativeElement, this._commonService.contextViewService, inputOptions);
|
||||
this._validations.push(() => !this._input.inputElement.validationMessage);
|
||||
|
||||
this._register(this._input);
|
||||
this._register(attachInputBoxStyler(this._input, this._commonService.themeService));
|
||||
@@ -74,6 +76,13 @@ export default class InputBoxComponent extends ComponentBase implements ICompone
|
||||
}
|
||||
}
|
||||
|
||||
public validate(): Thenable<boolean> {
|
||||
return super.validate().then(valid => {
|
||||
this._input.validate();
|
||||
return valid;
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.baseDestroy();
|
||||
}
|
||||
@@ -91,6 +100,10 @@ export default class InputBoxComponent extends ComponentBase implements ICompone
|
||||
|
||||
public setProperties(properties: { [key: string]: any; }): void {
|
||||
super.setProperties(properties);
|
||||
this._input.inputElement.type = this.inputType;
|
||||
if (this.inputType === 'number') {
|
||||
this._input.inputElement.step = 'any';
|
||||
}
|
||||
this._input.value = this.value;
|
||||
this._input.setAriaLabel(this.ariaLabel);
|
||||
this._input.setPlaceHolder(this.placeHolder);
|
||||
@@ -98,11 +111,8 @@ export default class InputBoxComponent extends ComponentBase implements ICompone
|
||||
if (this.width) {
|
||||
this._input.width = this.width;
|
||||
}
|
||||
}
|
||||
|
||||
public setValid(valid: boolean): void {
|
||||
super.setValid(valid);
|
||||
this._input.validate();
|
||||
this._input.inputElement.required = this.required;
|
||||
this.validate();
|
||||
}
|
||||
|
||||
// CSS-bound properties
|
||||
@@ -146,4 +156,20 @@ export default class InputBoxComponent extends ComponentBase implements ICompone
|
||||
public set width(newValue: number) {
|
||||
this.setPropertyFromUI<sqlops.InputBoxProperties, number>((props, value) => props.width = value, newValue);
|
||||
}
|
||||
|
||||
public get inputType(): string {
|
||||
return this.getPropertyOrDefault<sqlops.InputBoxProperties, string>((props) => props.inputType, 'text');
|
||||
}
|
||||
|
||||
public set inputType(newValue: string) {
|
||||
this.setPropertyFromUI<sqlops.InputBoxProperties, string>((props, value) => props.inputType = value, newValue);
|
||||
}
|
||||
|
||||
public get required(): boolean {
|
||||
return this.getPropertyOrDefault<sqlops.InputBoxProperties, boolean>((props) => props.required, false);
|
||||
}
|
||||
|
||||
public set required(newValue: boolean) {
|
||||
this.setPropertyFromUI<sqlops.InputBoxProperties, boolean>((props, value) => props.required = value, newValue);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ export interface IComponent {
|
||||
setLayout?: (layout: any) => void;
|
||||
setProperties?: (properties: { [key: string]: any; }) => void;
|
||||
readonly valid?: boolean;
|
||||
setValid(valid: boolean): void;
|
||||
validate(): Thenable<boolean>;
|
||||
}
|
||||
|
||||
export const COMPONENT_CONFIG = new InjectionToken<IComponentConfig>('component_config');
|
||||
@@ -88,4 +88,12 @@ export interface IModelStore {
|
||||
* @memberof IModelStore
|
||||
*/
|
||||
eventuallyRunOnComponent<T>(componentId: string, action: (component: IComponent) => T): Promise<T>;
|
||||
/**
|
||||
* Register a callback that will validate components when given a component ID
|
||||
*/
|
||||
registerValidationCallback(callback: (componentId: string) => Thenable<boolean>): void;
|
||||
/**
|
||||
* Run all validations for the given component and return the new validation value
|
||||
*/
|
||||
validate(component: IComponent): Thenable<boolean>;
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ export class ModelStore implements IModelStore {
|
||||
private _descriptorMappings: { [x: string]: IComponentDescriptor } = {};
|
||||
private _componentMappings: { [x: string]: IComponent } = {};
|
||||
private _componentActions: { [x: string]: Deferred<IComponent> } = {};
|
||||
private _validationCallbacks: ((componentId: string) => Thenable<boolean>)[] = [];
|
||||
constructor() {
|
||||
}
|
||||
|
||||
@@ -66,6 +67,15 @@ export class ModelStore implements IModelStore {
|
||||
}
|
||||
}
|
||||
|
||||
registerValidationCallback(callback: (componentId: string) => Thenable<boolean>): void {
|
||||
this._validationCallbacks.push(callback);
|
||||
}
|
||||
|
||||
validate(component: IComponent): Thenable<boolean> {
|
||||
let componentId = Object.entries(this._componentMappings).find(([id, mappedComponent]) => component === mappedComponent)[0];
|
||||
return Promise.all(this._validationCallbacks.map(callback => callback(componentId))).then(validations => validations.every(validation => validation === true));
|
||||
}
|
||||
|
||||
private addPendingAction<T>(componentId: string, action: (component: IComponent) => T): Promise<T> {
|
||||
// We create a promise and chain it onto a tracking promise whose resolve method
|
||||
// will only be called once the component is created
|
||||
|
||||
@@ -39,9 +39,10 @@ export abstract class ViewBase extends AngularDisposable implements IModelView {
|
||||
private _onEventEmitter = new Emitter<any>();
|
||||
|
||||
|
||||
initializeModel(rootComponent: IComponentShape): void {
|
||||
initializeModel(rootComponent: IComponentShape, validationCallback: (componentId: string) => Thenable<boolean>): void {
|
||||
let descriptor = this.defineComponent(rootComponent);
|
||||
this.rootDescriptor = descriptor;
|
||||
this.modelStore.registerValidationCallback(validationCallback);
|
||||
// Kick off the build by detecting changes to the model
|
||||
this.changeRef.detectChanges();
|
||||
}
|
||||
@@ -91,10 +92,6 @@ export abstract class ViewBase extends AngularDisposable implements IModelView {
|
||||
this.queueAction(componentId, (component) => component.setProperties(properties));
|
||||
}
|
||||
|
||||
setValid(componentId: string, valid: boolean): void {
|
||||
this.queueAction(componentId, (component) => component.setValid(valid));
|
||||
}
|
||||
|
||||
private queueAction<T>(componentId: string, action: (component: IComponent) => T): void {
|
||||
this.modelStore.eventuallyRunOnComponent(componentId, action).catch(err => {
|
||||
// TODO add error handling
|
||||
@@ -113,4 +110,8 @@ export abstract class ViewBase extends AngularDisposable implements IModelView {
|
||||
public get onEvent(): Event<IComponentEventArgs> {
|
||||
return this._onEventEmitter.event;
|
||||
}
|
||||
|
||||
public validate(componentId: string): Thenable<boolean> {
|
||||
return new Promise(resolve => this.modelStore.eventuallyRunOnComponent(componentId, component => resolve(component.validate())));
|
||||
}
|
||||
}
|
||||
@@ -15,12 +15,12 @@ export interface IView {
|
||||
}
|
||||
|
||||
export interface IModelView extends IView {
|
||||
initializeModel(rootComponent: IComponentShape): void;
|
||||
initializeModel(rootComponent: IComponentShape, validationCallback?: (componentId: string) => Thenable<boolean>): void;
|
||||
clearContainer(componentId: string): void;
|
||||
addToContainer(containerId: string, item: IItemConfig): void;
|
||||
setLayout(componentId: string, layout: any): void;
|
||||
setProperties(componentId: string, properties: { [key: string]: any }): void;
|
||||
setValid(componentId: string, valid: boolean): void;
|
||||
registerEvent(componentId: string);
|
||||
onEvent: Event<any>;
|
||||
validate(componentId: string): Thenable<boolean>;
|
||||
}
|
||||
|
||||
8
src/sql/sqlops.proposed.d.ts
vendored
8
src/sql/sqlops.proposed.d.ts
vendored
@@ -72,7 +72,7 @@ declare module 'sqlops' {
|
||||
/**
|
||||
* Run the component's validations
|
||||
*/
|
||||
validate(): void;
|
||||
validate(): Thenable<boolean>;
|
||||
}
|
||||
|
||||
export interface FormComponent {
|
||||
@@ -219,12 +219,16 @@ declare module 'sqlops' {
|
||||
status?: StatusIndicator;
|
||||
}
|
||||
|
||||
export type InputBoxInputType = 'color' | 'date' | 'datetime-local' | 'email' | 'month' | 'number' | 'password' | 'range' | 'search' | 'text' | 'time' | 'url' | 'week';
|
||||
|
||||
export interface InputBoxProperties {
|
||||
value?: string;
|
||||
ariaLabel?: string;
|
||||
placeHolder?: string;
|
||||
height: number;
|
||||
width: number;
|
||||
inputType?: InputBoxInputType;
|
||||
required?: boolean;
|
||||
}
|
||||
|
||||
export interface CheckBoxProperties {
|
||||
@@ -315,7 +319,7 @@ declare module 'sqlops' {
|
||||
/**
|
||||
* Run the model view root component's validations
|
||||
*/
|
||||
validate(): void;
|
||||
validate(): Thenable<boolean>;
|
||||
|
||||
/**
|
||||
* Initializes the model with a root component definition.
|
||||
|
||||
@@ -92,7 +92,8 @@ export interface IItemConfig {
|
||||
export enum ComponentEventType {
|
||||
PropertiesChanged,
|
||||
onDidChange,
|
||||
onDidClick
|
||||
onDidClick,
|
||||
validityChanged
|
||||
}
|
||||
|
||||
export interface IComponentEventArgs {
|
||||
|
||||
@@ -18,7 +18,7 @@ import { IActionDescriptor } from 'vs/editor/standalone/browser/standaloneCodeEd
|
||||
|
||||
class ModelBuilderImpl implements sqlops.ModelBuilder {
|
||||
private nextComponentId: number;
|
||||
private readonly _eventHandlers = new Map<string, IWithEventHandler>();
|
||||
private readonly _componentBuilders = new Map<string, ComponentBuilderImpl<any>>();
|
||||
|
||||
constructor(private readonly _proxy: MainThreadModelViewShape, private readonly _handle: number) {
|
||||
this.nextComponentId = 0;
|
||||
@@ -27,72 +27,91 @@ class ModelBuilderImpl implements sqlops.ModelBuilder {
|
||||
navContainer(): sqlops.ContainerBuilder<sqlops.NavContainer, any, any> {
|
||||
let id = this.getNextComponentId();
|
||||
let container: ContainerBuilderImpl<sqlops.NavContainer, any, any> = new ContainerBuilderImpl(this._proxy, this._handle, ModelComponentTypes.NavContainer, id);
|
||||
this._eventHandlers.set(id, container);
|
||||
this._componentBuilders.set(id, container);
|
||||
return container;
|
||||
}
|
||||
|
||||
flexContainer(): sqlops.FlexBuilder {
|
||||
let id = this.getNextComponentId();
|
||||
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);
|
||||
this._componentBuilders.set(id, container);
|
||||
return container;
|
||||
}
|
||||
|
||||
formContainer(): sqlops.FormBuilder {
|
||||
let id = this.getNextComponentId();
|
||||
let container = new FormContainerBuilder(this._proxy, this._handle, ModelComponentTypes.Form, id);
|
||||
this._eventHandlers.set(id, container);
|
||||
this._componentBuilders.set(id, container);
|
||||
return container;
|
||||
}
|
||||
|
||||
card(): sqlops.ComponentBuilder<sqlops.CardComponent> {
|
||||
let id = this.getNextComponentId();
|
||||
return this.withEventHandler(new CardWrapper(this._proxy, this._handle, id), id);
|
||||
let builder: ComponentBuilderImpl<sqlops.CardComponent> = this.getComponentBuilder(new CardWrapper(this._proxy, this._handle, id), id);
|
||||
this._componentBuilders.set(id, builder);
|
||||
return builder;
|
||||
}
|
||||
|
||||
inputBox(): sqlops.ComponentBuilder<sqlops.InputBoxComponent> {
|
||||
let id = this.getNextComponentId();
|
||||
return this.withEventHandler(new InputBoxWrapper(this._proxy, this._handle, id), id);
|
||||
let builder: ComponentBuilderImpl<sqlops.InputBoxComponent> = this.getComponentBuilder(new InputBoxWrapper(this._proxy, this._handle, id), id);
|
||||
this._componentBuilders.set(id, builder);
|
||||
return builder;
|
||||
}
|
||||
|
||||
checkBox(): sqlops.ComponentBuilder<sqlops.CheckBoxComponent> {
|
||||
let id = this.getNextComponentId();
|
||||
return this.withEventHandler(new CheckBoxWrapper(this._proxy, this._handle, id), id);
|
||||
let builder: ComponentBuilderImpl<sqlops.CheckBoxComponent> = this.getComponentBuilder(new CheckBoxWrapper(this._proxy, this._handle, id), id);
|
||||
this._componentBuilders.set(id, builder);
|
||||
return builder;
|
||||
}
|
||||
|
||||
button(): sqlops.ComponentBuilder<sqlops.ButtonComponent> {
|
||||
let id = this.getNextComponentId();
|
||||
return this.withEventHandler(new ButtonWrapper(this._proxy, this._handle, id), id);
|
||||
let builder: ComponentBuilderImpl<sqlops.ButtonComponent> = this.getComponentBuilder(new ButtonWrapper(this._proxy, this._handle, id), id);
|
||||
this._componentBuilders.set(id, builder);
|
||||
return builder;
|
||||
}
|
||||
|
||||
dropDown(): sqlops.ComponentBuilder<sqlops.DropDownComponent> {
|
||||
let id = this.getNextComponentId();
|
||||
return this.withEventHandler(new DropDownWrapper(this._proxy, this._handle, id), id);
|
||||
let builder: ComponentBuilderImpl<sqlops.DropDownComponent> = this.getComponentBuilder(new DropDownWrapper(this._proxy, this._handle, id), id);
|
||||
this._componentBuilders.set(id, builder);
|
||||
return builder;
|
||||
}
|
||||
|
||||
dashboardWidget(widgetId: string): sqlops.ComponentBuilder<sqlops.WidgetComponent> {
|
||||
let id = this.getNextComponentId();
|
||||
return this.withEventHandler<sqlops.WidgetComponent>(new ComponentWrapper(this._proxy, this._handle, ModelComponentTypes.DashboardWidget, id), id);
|
||||
let builder = this.getComponentBuilder<sqlops.WidgetComponent>(new ComponentWrapper(this._proxy, this._handle, ModelComponentTypes.DashboardWidget, id), id);
|
||||
this._componentBuilders.set(id, builder);
|
||||
return builder;
|
||||
}
|
||||
|
||||
dashboardWebview(webviewId: string): sqlops.ComponentBuilder<sqlops.WebviewComponent> {
|
||||
let id = this.getNextComponentId();
|
||||
return this.withEventHandler(new ComponentWrapper(this._proxy, this._handle, ModelComponentTypes.DashboardWebview, id), id);
|
||||
let builder: ComponentBuilderImpl<sqlops.WebviewComponent> = this.getComponentBuilder(new ComponentWrapper(this._proxy, this._handle, ModelComponentTypes.DashboardWebview, id), id);
|
||||
this._componentBuilders.set(id, builder);
|
||||
return builder;
|
||||
}
|
||||
|
||||
withEventHandler<T extends sqlops.Component>(component: ComponentWrapper, id: string): sqlops.ComponentBuilder<T> {
|
||||
getComponentBuilder<T extends sqlops.Component>(component: ComponentWrapper, id: string): ComponentBuilderImpl<T> {
|
||||
let componentBuilder: ComponentBuilderImpl<T> = new ComponentBuilderImpl<T>(component);
|
||||
this._eventHandlers.set(id, componentBuilder);
|
||||
this._componentBuilders.set(id, componentBuilder);
|
||||
return componentBuilder;
|
||||
}
|
||||
|
||||
handleEvent(componentId: string, eventArgs: IComponentEventArgs): void {
|
||||
let eventHandler = this._eventHandlers.get(componentId);
|
||||
let eventHandler = this._componentBuilders.get(componentId);
|
||||
if (eventHandler) {
|
||||
eventHandler.handleEvent(eventArgs);
|
||||
}
|
||||
}
|
||||
|
||||
public runCustomValidations(componentId: string): boolean {
|
||||
let component = this._componentBuilders.get(componentId).componentWrapper();
|
||||
return component.runCustomValidations();
|
||||
}
|
||||
|
||||
private getNextComponentId(): string {
|
||||
return `component${this._handle}_${this.nextComponentId++}`;
|
||||
}
|
||||
@@ -112,13 +131,17 @@ class ComponentBuilderImpl<T extends sqlops.Component> implements sqlops.Compone
|
||||
return <T><any>this._component;
|
||||
}
|
||||
|
||||
componentWrapper(): ComponentWrapper {
|
||||
return this._component;
|
||||
}
|
||||
|
||||
withProperties<U>(properties: U): sqlops.ComponentBuilder<T> {
|
||||
this._component.properties = properties;
|
||||
return this;
|
||||
}
|
||||
|
||||
withValidation(validation: (component: T) => boolean): sqlops.ComponentBuilder<T> {
|
||||
this._component.validations.push(validation);
|
||||
this._component.customValidations.push(validation);
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -150,7 +173,6 @@ 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;
|
||||
}
|
||||
}
|
||||
@@ -182,7 +204,6 @@ class FormContainerBuilder extends ContainerBuilderImpl<sqlops.FormContainer, sq
|
||||
this._component.itemConfigs.push(new InternalItemConfig(componentWrapper, itemLayout));
|
||||
});
|
||||
}
|
||||
formItem.component.onValidityChanged(() => this._component.validate());
|
||||
});
|
||||
return this;
|
||||
}
|
||||
@@ -208,7 +229,7 @@ class ComponentWrapper implements sqlops.Component {
|
||||
public properties: { [key: string]: any } = {};
|
||||
public layout: any;
|
||||
public itemConfigs: InternalItemConfig[];
|
||||
public validations: ((component: ThisType<ComponentWrapper>) => boolean)[] = [];
|
||||
public customValidations: ((component: ThisType<ComponentWrapper>) => boolean)[] = [];
|
||||
private _valid: boolean = true;
|
||||
private _onValidityChangedEmitter = new Emitter<boolean>();
|
||||
public readonly onValidityChanged = this._onValidityChangedEmitter.event;
|
||||
@@ -224,12 +245,6 @@ 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 {
|
||||
@@ -280,7 +295,6 @@ 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);
|
||||
}
|
||||
|
||||
@@ -303,7 +317,9 @@ class ComponentWrapper implements sqlops.Component {
|
||||
public onEvent(eventArgs: IComponentEventArgs) {
|
||||
if (eventArgs && eventArgs.eventType === ComponentEventType.PropertiesChanged) {
|
||||
this.properties = eventArgs.args;
|
||||
this.validate();
|
||||
}
|
||||
else if (eventArgs && eventArgs.eventType === ComponentEventType.validityChanged) {
|
||||
this._valid = eventArgs.args;
|
||||
} else if (eventArgs) {
|
||||
let emitter = this._emitterMap.get(eventArgs.eventType);
|
||||
if (emitter) {
|
||||
@@ -312,11 +328,10 @@ class ComponentWrapper implements sqlops.Component {
|
||||
}
|
||||
}
|
||||
|
||||
protected setProperty(key: string, value: any): Thenable<boolean> {
|
||||
protected async setProperty(key: string, value: any): Promise<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);
|
||||
@@ -326,10 +341,10 @@ class ComponentWrapper implements sqlops.Component {
|
||||
this._onErrorEmitter.fire(err);
|
||||
}
|
||||
|
||||
public validate(): void {
|
||||
public runCustomValidations(): boolean {
|
||||
let isValid = true;
|
||||
try {
|
||||
this.validations.forEach(validation => {
|
||||
this.customValidations.forEach(validation => {
|
||||
if (!validation(this)) {
|
||||
isValid = false;
|
||||
}
|
||||
@@ -337,12 +352,11 @@ class ComponentWrapper implements sqlops.Component {
|
||||
} 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);
|
||||
}
|
||||
return isValid;
|
||||
}
|
||||
|
||||
public validate() {
|
||||
return this._proxy.$validate(this._handle, this._id);
|
||||
}
|
||||
|
||||
public get valid(): boolean {
|
||||
@@ -434,6 +448,13 @@ class InputBoxWrapper extends ComponentWrapper implements sqlops.InputBoxCompone
|
||||
this.setProperty('width', v);
|
||||
}
|
||||
|
||||
public get inputType(): sqlops.InputBoxInputType {
|
||||
return this.properties['inputType'];
|
||||
}
|
||||
public set inputType(v: sqlops.InputBoxInputType) {
|
||||
this.setProperty('inputType', v);
|
||||
}
|
||||
|
||||
public get onTextChanged(): vscode.Event<any> {
|
||||
let emitter = this._emitterMap.get(ComponentEventType.onDidChange);
|
||||
return emitter && emitter.event;
|
||||
@@ -566,12 +587,15 @@ class ModelViewImpl implements sqlops.ModelView {
|
||||
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();
|
||||
public validate(): Thenable<boolean> {
|
||||
return this._proxy.$validate(this._handle, this._component.id);
|
||||
}
|
||||
|
||||
public runCustomValidations(componentId: string): boolean {
|
||||
return this._modelBuilder.runCustomValidations(componentId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -610,4 +634,9 @@ export class ExtHostModelView implements ExtHostModelViewShape {
|
||||
view.handleEvent(componentId, eventArgs);
|
||||
}
|
||||
}
|
||||
|
||||
$runCustomValidations(handle: number, componentId: string): Thenable<boolean> {
|
||||
const view = this._modelViews.get(handle);
|
||||
return Promise.resolve(view.runCustomValidations(componentId));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ export class MainThreadModelView extends Disposable implements MainThreadModelVi
|
||||
|
||||
$initializeModel(handle: number, rootComponent: IComponentShape): Thenable<void> {
|
||||
return this.execModelViewAction(handle, (modelView) => {
|
||||
modelView.initializeModel(rootComponent);
|
||||
modelView.initializeModel(rootComponent, (componentId) => this.runCustomValidations(handle, componentId));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -82,8 +82,12 @@ 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));
|
||||
$validate(handle: number, componentId: string): Thenable<boolean> {
|
||||
return new Promise(resolve => this.execModelViewAction(handle, (modelView) => resolve(modelView.validate(componentId))));
|
||||
}
|
||||
|
||||
private runCustomValidations(handle: number, componentId: string): Thenable<boolean> {
|
||||
return this._proxy.$runCustomValidations(handle, componentId);
|
||||
}
|
||||
|
||||
private execModelViewAction<T>(handle: number, action: (m: IModelView) => T): Thenable<T> {
|
||||
|
||||
@@ -520,6 +520,7 @@ export interface ExtHostModelViewShape {
|
||||
$onClosed(handle: number): void;
|
||||
$registerWidget(handle: number, id: string, connection: sqlops.connection.Connection, serverInfo: sqlops.ServerInfo): void;
|
||||
$handleEvent(handle: number, id: string, eventArgs: any);
|
||||
$runCustomValidations(handle: number, id: string): Thenable<boolean>;
|
||||
}
|
||||
|
||||
export interface MainThreadModelViewShape extends IDisposable {
|
||||
@@ -530,7 +531,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>;
|
||||
$validate(handle: number, componentId: string): Thenable<boolean>;
|
||||
}
|
||||
|
||||
export interface ExtHostObjectExplorerShape {
|
||||
|
||||
Reference in New Issue
Block a user