Add validation to model view components (#1356)

This commit is contained in:
Matt Irvine
2018-05-08 14:15:26 -07:00
committed by GitHub
parent c2b32fd64a
commit f10e281ffc
18 changed files with 459 additions and 35 deletions

View File

@@ -11,9 +11,11 @@ import { ModelViewContent } from 'sql/parts/modelComponents/modelViewContent.com
import { BootstrapParams } from 'sql/services/bootstrap/bootstrapParams';
import { BOOTSTRAP_SERVICE_ID, IBootstrapService } from 'sql/services/bootstrap/bootstrapService';
import Event, { Emitter } from 'vs/base/common/event';
import { ComponentEventType } from '../../parts/modelComponents/interfaces';
export interface DialogComponentParams extends BootstrapParams {
modelViewId: string;
validityChangedCallback: (valid: boolean) => void;
}
@Component({
@@ -27,16 +29,23 @@ export interface DialogComponentParams extends BootstrapParams {
export class DialogContainer implements AfterContentInit {
private _onResize = new Emitter<void>();
public readonly onResize: Event<void> = this._onResize.event;
private _params: DialogComponentParams;
public modelViewId: string;
@ViewChild(ModelViewContent) private _modelViewContent: ModelViewContent;
constructor(
@Inject(forwardRef(() => ElementRef)) el: ElementRef,
@Inject(BOOTSTRAP_SERVICE_ID) bootstrapService: IBootstrapService) {
this.modelViewId = (bootstrapService.getBootstrapParams(el.nativeElement.tagName) as DialogComponentParams).modelViewId;
this._params = bootstrapService.getBootstrapParams(el.nativeElement.tagName) as DialogComponentParams;
this.modelViewId = this._params.modelViewId;
}
ngAfterContentInit(): void {
this._modelViewContent.onEvent(event => {
if (event.eventType === ComponentEventType.validityChanged) {
this._params.validityChangedCallback(event.args);
}
});
}
public layout(): void {

View File

@@ -21,6 +21,8 @@ import { attachButtonStyler } from 'vs/platform/theme/common/styler';
import { Button } from 'vs/base/browser/ui/button/button';
import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme';
import { localize } from 'vs/nls';
import Event, { Emitter } from 'vs/base/common/event';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
export class DialogModal extends Modal {
private _dialogPane: DialogPane;
@@ -102,8 +104,10 @@ export class DialogModal extends Modal {
}
public done(): void {
this.dispose();
this.hide();
if (this._dialog.okButton.enabled) {
this.dispose();
this.hide();
}
}
public cancel(): void {
@@ -119,6 +123,20 @@ export class DialogModal extends Modal {
super.show();
}
/**
* Overridable to change behavior of escape key
*/
protected onClose(e: StandardKeyboardEvent) {
this.cancel();
}
/**
* Overridable to change behavior of enter key
*/
protected onAccept(e: StandardKeyboardEvent) {
this.done();
}
public dispose(): void {
super.dispose();
this._dialogPane.dispose();

View File

@@ -16,12 +16,16 @@ import { DialogComponentParams } from 'sql/platform/dialog/dialogContainer.compo
import { Builder } from 'vs/base/browser/builder';
import { IThemable } from 'vs/platform/theme/common/styler';
import { Disposable } from 'vs/base/common/lifecycle';
import Event, { Emitter } from 'vs/base/common/event';
export class DialogPane extends Disposable implements IThemable {
private _activeTabIndex: number;
private _tabbedPanel: TabbedPanel;
private _moduleRef: NgModuleRef<{}>;
// Validation
private _modelViewValidityMap = new Map<string, boolean>();
// HTML Elements
private _body: HTMLElement;
private _tabBar: HTMLElement;
@@ -46,12 +50,20 @@ export class DialogPane extends Disposable implements IThemable {
} else {
this._tabbedPanel = new TabbedPanel(this._body);
this._dialog.content.forEach((tab, tabIndex) => {
let tabContainer = document.createElement('div');
tabContainer.style.display = 'none';
this._body.appendChild(tabContainer);
this.initializeModelViewContainer(tabContainer, tab.content);
this._tabbedPanel.pushTab({
title: tab.title,
identifier: 'dialogPane.' + this._dialog.title + '.' + tabIndex,
view: {
render: (container) => {
this.initializeModelViewContainer(container, tab.content);
if (tabContainer.parentElement === this._body) {
this._body.removeChild(tabContainer);
}
container.appendChild(tabContainer);
tabContainer.style.display = 'block';
},
layout: (dimension) => { }
} as IPanelView
@@ -72,7 +84,10 @@ export class DialogPane extends Disposable implements IThemable {
DialogModule,
bodyContainer,
'dialog-modelview-container',
{ modelViewId: modelViewId } as DialogComponentParams,
{
modelViewId: modelViewId,
validityChangedCallback: (valid: boolean) => this._setValidity(modelViewId, valid)
} as DialogComponentParams,
undefined,
(moduleRef) => this._moduleRef = moduleRef);
}
@@ -93,6 +108,21 @@ export class DialogPane extends Disposable implements IThemable {
this._body.style.color = styles.dialogForeground ? styles.dialogForeground.toString() : undefined;
}
private _setValidity(modelViewId: string, valid: boolean) {
let oldValidity = this.isValid();
this._modelViewValidityMap.set(modelViewId, valid);
let newValidity = this.isValid();
if (newValidity !== oldValidity) {
this._dialog.notifyValidityChanged(newValidity);
}
}
private isValid(): boolean {
let valid = true;
this._modelViewValidityMap.forEach(value => valid = valid && value);
return valid;
}
public dispose() {
super.dispose();
this._moduleRef.destroy();

View File

@@ -28,11 +28,24 @@ export class Dialog implements sqlops.window.modelviewdialog.Dialog {
public cancelButton: DialogButton = new DialogButton(Dialog.CANCEL_BUTTON_LABEL, true);
public customButtons: DialogButton[];
private _valid: boolean = true;
private _validityChangedEmitter = new Emitter<boolean>();
public readonly onValidityChanged = this._validityChangedEmitter.event;
constructor(public title: string, content?: string | DialogTab[]) {
if (content) {
this.content = content;
}
}
public get valid(): boolean {
return this._valid;
}
public notifyValidityChanged(valid: boolean) {
this._valid = valid;
this._validityChangedEmitter.fire(valid);
}
}
export class DialogButton implements sqlops.window.modelviewdialog.Button {