-
Notifications
You must be signed in to change notification settings - Fork 911
Angular UI Guidelines
Note that this is not a guide on Angular; this is a guide on the specifics of using Angular inside Azure Data Studio.
Azure Data Studio uses Angular2 to display some UI components. Azure Data Studio uses a slightly different Angular design philosophy than most Angular applications due to the fact that Azure Data Studio:
- is a fork of an existing non-Angular app.
- reuses Angular Components from vscode-mssql.
- uses a custom module loader.
The UI for standard Angular applications is handled entirely with Angular. In such applications, a single root Angular Module is bootstrapped, launching with an entry point into a bootstrap
(root) component. The single root Angular Module persists for the entire duration of the UI's existence.
Azure Data Studio's UI is handled differently. Azure Data Studio has a mixture of both raw JavaScript/HTML and Angular. Azure Data Studio has various Angular Modules sprinkled throughout its UI, each of which can be constructed or deconstructed at any time.
Azure Data Studio's unique UI results in the need to have patterns that differ from what the official documentation suggests. Specifically, Azure Data Studio differs in the ways described below.
Bootstrapping is the process of instantiating an instance of an Angular Module. Bootstrapping an Angular Module in Azure Data Studio requires the following steps.
- Create a root Angular Component. This is what describes the initial UI of the Module. Pass
this._el.nativeElement.tagName
toBootstrapService.getBootstrapParams
to receive any parameters you pass when callingbootstrap
.
import { IBootstrapService, BOOTSTRAP_SERVICE_ID } from 'sql/parts/bootstrap/bootstrapService';
declare let AngularCore;
export const MY_SELECTOR: string = 'my-component';
@AngularCore.Component({
selector: MY_SELECTOR,
template: '<span>Some HTML</span>'
})
export class MyComponent {
private myParams: any;
constructor(@AngularCore.Inject(BOOTSTRAP_SERVICE_ID) bootstrapService: IBootstrapService) {
this.myParams = bootstrapService.getBootstrapParams(this._el.nativeElement.tagName);
}
}
- Create a Module using the Component you created an
entryComponent
. OverridengDoBootstrap
as follows with the selector and type of your root component.
import { IBootstrapService, BOOTSTRAP_SERVICE_ID } from 'sql/parts/bootstrap/bootstrapService';
import { ApplicationRef, ComponentFactoryResolver } from '@angular/core';
import { MyComponent, MY_SELECTOR } from 'sql/example/myComponent';
declare let AngularCore;
@AngularCore.NgModule({
imports: [ AngularCommon.CommonModule, AngularPlatformBrowser.BrowserModule ],
declarations: [ MyComponent ],
entryComponents: [ MyComponent ]
})
export class MyModule {
constructor(
@AngularCore.Inject(AngularCore.forwardRef(() => AngularCore.ComponentFactoryResolver)) private _resolver: ComponentFactoryResolver,
@AngularCore.Inject(BOOTSTRAP_SERVICE_ID) private _bootstrapService: IBootstrapService
) {
}
ngDoBootstrap(appRef: ApplicationRef) {
const factory = this._resolver.resolveComponentFactory(MyComponent);
const uniqueSelector: string = this._bootstrapService.getUniqueSelector(MY_SELECTOR);
factory.selector = uniqueSelector;
appRef.bootstrap(factory);
}
}
- Call
BootstrapService.bootstrap
and pass in any parameters you want your root Component to receive. For examplemyParams
in the Component example above will receive{ myNumber: 1234, myBool: true }
. The module will be bootstrapped and appended to the HTML element you pass.
import { IBootstrapService } from 'sql/parts/bootstrap/bootstrapService';
import { MyModule, MY_SELECTOR } from 'sql/example/myModule';
...
let params: any = { myNumber: 1234, myBool: true };
let htmlElement: HTMLElement = this.getContainer().getHTMLElement();
bootstrapService.bootstrap(
MyModule,
htmlElement,
MY_SELECTOR,
params);
Programmatic changes to the UI can go undetected by Angular. For example, if I dynamically change a class variable that is bound to the Angular UI, Angular may not notice. In this case you have to manually tell Angular to detect changes.
Let's say you have a function makeUiChanges()
that dynamically makes changes to the UI. To get Angular to notice these changes, you can either:
- Use
ChangeDetectorRef
to manually check for changes:
import { ChangeDetectorRef } from '@angular/core';
declare let AngularCore;
...
constructor(@AngularCore.Inject(AngularCore.forwardRef(() => AngularCore.ChangeDetectorRef)) cd: ChangeDetectorRef) {}
...
makeUiChanges();
this.cd.detectChanges();
- Run all UI-changing code inside of
NgZone.run
to automatically check for changes:
import { NgZone } from '@angular/core';
declare let AngularCore;
...
constructor(@AngularCore.Inject(AngularCore.forwardRef(() => AngularCore.NgZone )) zone: NgZone ) {}
...
this.zone.run(() => {
makeUiChanges();
});
Documentation
- Get Started
- Install Azure Data Studio
- Telemetry
- Microsoft Ready 2019 Lab
- MS Docs Overview
- Debug Trace Logging
- Troubleshoot Azure Authentication Issues
- FAQ
Contributing
- How to Contribute
- Developer Getting Started
- Submitting Bugs and Suggestions
- Localization
- Troubleshooting Build Issues
- Engineering FAQ
- How to update typings files
- Importing and using modules
- UI Guidelines
- Angular UI Guidelines
- Contributor License Agreement
- Azure Data Studio Tests
- Why is the Azure Data Studio license different than the repository license?
Tool Services
Extensibility Reference
- Getting Started
- Extensibility API
- Contribution Points
- Context Variables
- Servers and Data Explorer Tree Views
- Debugging with VS Code
- Extension Authoring
- Building multiple insight widgets
- Microsoft Ignite lab
- List of Extensions
- Replace sqlops namespace
Project Management