mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-25 17:23:10 -05:00
Add validation to model view components (#1356)
This commit is contained in:
@@ -4,7 +4,8 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import 'vs/css!./flexContainer';
|
||||
|
||||
import { Component, Input, Inject, ChangeDetectorRef, forwardRef, ComponentFactoryResolver,
|
||||
import {
|
||||
Component, Input, Inject, ChangeDetectorRef, forwardRef, ComponentFactoryResolver,
|
||||
ViewChild, ElementRef, Injector, OnDestroy, OnInit
|
||||
} from '@angular/core';
|
||||
|
||||
@@ -18,14 +19,16 @@ import Event, { Emitter } from 'vs/base/common/event';
|
||||
import { IDisposable, Disposable } from 'vs/base/common/lifecycle';
|
||||
|
||||
export class ItemDescriptor<T> {
|
||||
constructor(public descriptor: IComponentDescriptor, public config: T) {}
|
||||
constructor(public descriptor: IComponentDescriptor, public config: T) { }
|
||||
}
|
||||
|
||||
export abstract class ComponentBase extends Disposable implements IComponent, OnDestroy, OnInit {
|
||||
protected properties: { [key: string]: any; } = {};
|
||||
constructor (
|
||||
protected _valid: boolean = true;
|
||||
private _eventQueue: IComponentEventArgs[] = [];
|
||||
constructor(
|
||||
protected _changeRef: ChangeDetectorRef) {
|
||||
super();
|
||||
super();
|
||||
}
|
||||
|
||||
/// IComponent implementation
|
||||
@@ -56,7 +59,7 @@ export abstract class ComponentBase extends Disposable implements IComponent, On
|
||||
this.dispose();
|
||||
}
|
||||
|
||||
abstract setLayout (layout: any): void;
|
||||
abstract setLayout(layout: any): void;
|
||||
|
||||
public setProperties(properties: { [key: string]: any; }): void {
|
||||
if (!properties) {
|
||||
@@ -77,21 +80,49 @@ export abstract class ComponentBase extends Disposable implements IComponent, On
|
||||
|
||||
protected setPropertyFromUI<TPropertyBag, TValue>(propertySetter: (TPropertyBag, TValue) => void, value: TValue) {
|
||||
propertySetter(this.getProperties<TPropertyBag>(), value);
|
||||
this._onEventEmitter.fire({
|
||||
this.fireEvent({
|
||||
eventType: ComponentEventType.PropertiesChanged,
|
||||
args: this.getProperties()
|
||||
});
|
||||
}
|
||||
|
||||
public get onEvent(): Event<IComponentEventArgs> {
|
||||
return this._onEventEmitter.event;
|
||||
}
|
||||
|
||||
public get title(): string {
|
||||
let properties = this.getProperties();
|
||||
let title = properties['title'];
|
||||
return title ? <string>title : '';
|
||||
}
|
||||
|
||||
public get valid(): boolean {
|
||||
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) {
|
||||
let event = this._eventQueue.pop();
|
||||
handler(event);
|
||||
}
|
||||
this._eventQueue = undefined;
|
||||
}
|
||||
return this._onEventEmitter.event(handler);
|
||||
}
|
||||
|
||||
private fireEvent(event: IComponentEventArgs) {
|
||||
this._onEventEmitter.fire(event);
|
||||
if (this._eventQueue) {
|
||||
this._eventQueue.push(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export abstract class ContainerBase<T> extends ComponentBase {
|
||||
@@ -115,5 +146,5 @@ export abstract class ContainerBase<T> extends ComponentBase {
|
||||
this._changeRef.detectChanges();
|
||||
}
|
||||
|
||||
abstract setLayout (layout: any): void;
|
||||
abstract setLayout(layout: any): void;
|
||||
}
|
||||
|
||||
@@ -10,10 +10,11 @@ import {
|
||||
|
||||
import * as sqlops from 'sqlops';
|
||||
import Event, { Emitter } from 'vs/base/common/event';
|
||||
import * as nls from 'vs/nls';
|
||||
|
||||
import { ComponentBase } from 'sql/parts/modelComponents/componentBase';
|
||||
import { IComponent, IComponentDescriptor, IModelStore, ComponentEventType } from 'sql/parts/modelComponents/interfaces';
|
||||
import { InputBox, IInputOptions } from 'vs/base/browser/ui/inputbox/inputBox';
|
||||
import { InputBox, IInputOptions, MessageType } from 'vs/base/browser/ui/inputbox/inputBox';
|
||||
import { CommonServiceInterface } from 'sql/services/common/commonServiceInterface.service';
|
||||
import { attachInputBoxStyler, attachListStyler } from 'vs/platform/theme/common/styler';
|
||||
|
||||
@@ -44,7 +45,19 @@ export default class InputBoxComponent extends ComponentBase implements ICompone
|
||||
if (this._inputContainer) {
|
||||
let inputOptions: IInputOptions = {
|
||||
placeholder: '',
|
||||
ariaLabel: ''
|
||||
ariaLabel: '',
|
||||
validationOptions: {
|
||||
validation: () => {
|
||||
if (this.valid) {
|
||||
return undefined;
|
||||
} else {
|
||||
return {
|
||||
content: nls.localize('invalidValueError', 'Invalid value'),
|
||||
type: MessageType.ERROR
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this._input = new InputBox(this._inputContainer.nativeElement, this._commonService.contextViewService, inputOptions);
|
||||
@@ -81,6 +94,11 @@ export default class InputBoxComponent extends ComponentBase implements ICompone
|
||||
this._input.value = this.value;
|
||||
}
|
||||
|
||||
public setValid(valid: boolean): void {
|
||||
super.setValid(valid);
|
||||
this._input.validate();
|
||||
}
|
||||
|
||||
// CSS-bound properties
|
||||
|
||||
public get value(): string {
|
||||
|
||||
@@ -6,6 +6,7 @@ import { InjectionToken } from '@angular/core';
|
||||
|
||||
import * as sqlops from 'sqlops';
|
||||
import Event, { Emitter } from 'vs/base/common/event';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
|
||||
/**
|
||||
* An instance of a model-backed component. This will be a UI element
|
||||
@@ -17,12 +18,14 @@ export interface IComponent {
|
||||
descriptor: IComponentDescriptor;
|
||||
modelStore: IModelStore;
|
||||
layout();
|
||||
registerEventHandler(handler: (event: IComponentEventArgs) => void): IDisposable;
|
||||
clearContainer?: () => void;
|
||||
addToContainer?: (componentDescriptor: IComponentDescriptor, config: any) => void;
|
||||
setLayout?: (layout: any) => void;
|
||||
setProperties?: (properties: { [key: string]: any; }) => void;
|
||||
readonly valid?: boolean;
|
||||
setValid(valid: boolean): void;
|
||||
title?: string;
|
||||
onEvent?: Event<IComponentEventArgs>;
|
||||
}
|
||||
|
||||
export const COMPONENT_CONFIG = new InjectionToken<IComponentConfig>('component_config');
|
||||
@@ -60,7 +63,8 @@ export interface IComponentEventArgs {
|
||||
export enum ComponentEventType {
|
||||
PropertiesChanged,
|
||||
onDidChange,
|
||||
onDidClick
|
||||
onDidClick,
|
||||
validityChanged
|
||||
}
|
||||
|
||||
export interface IModelStore {
|
||||
|
||||
@@ -12,7 +12,7 @@ import { IModelStore, IComponentDescriptor, IComponent } from './interfaces';
|
||||
import { Extensions, IComponentRegistry } from 'sql/platform/dashboard/common/modelComponentRegistry';
|
||||
import { Deferred } from 'sql/base/common/promise';
|
||||
|
||||
const componentRegistry = <IComponentRegistry> Registry.as(Extensions.ComponentContribution);
|
||||
const componentRegistry = <IComponentRegistry>Registry.as(Extensions.ComponentContribution);
|
||||
|
||||
|
||||
class ComponentDescriptor implements IComponentDescriptor {
|
||||
|
||||
@@ -91,6 +91,10 @@ 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
|
||||
@@ -99,12 +103,10 @@ export abstract class ViewBase extends AngularDisposable implements IModelView {
|
||||
|
||||
registerEvent(componentId: string) {
|
||||
this.queueAction(componentId, (component) => {
|
||||
if (component.onEvent) {
|
||||
this._register(component.onEvent(e => {
|
||||
e.componentId = componentId;
|
||||
this._onEventEmitter.fire(e);
|
||||
}));
|
||||
}
|
||||
this._register(component.registerEventHandler(e => {
|
||||
e.componentId = componentId;
|
||||
this._onEventEmitter.fire(e);
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user