SKY UX Add-in Client Library
The SKY UX Add-in Client Library wraps the lower-level SKY Add-in Client Library and provides an Angular service that can be more easily consumed by SKY UX SPAs. It also provides an optional plugin that can be used to inject the Environment ID context into your SKY UX SPA automatically, removing the need to track and manage the Environment ID in each component.
Installation
Install the library as a dependency of your project using npm. The SKY UX Add-in Client Library has a direct dependency on the SKY Add-in Client Library, so it doesn't need to be installed separately:
npm install --save @blackbaud/skyux-lib-addin-client
Use
To use the service, import it into your SPA's app-extras.module.ts file and add it to the providers array:
import {
NgModule
} from '@angular/core';
import {
AddinClientService
} from '@blackbaud/skyux-lib-addin-client';
@NgModule({
providers: [AddinClientService]
})
export class AppExtrasModule { }
Then, inject it into your component as part of the constructor:
import {
Component,
OnInit
} from '@angular/core';
import {
AddinClientService
} from '@blackbaud/skyux-lib-addin-client';
import {
AddinClientInitArgs
} from '@blackbaud/sky-addin-client';
@Component({
selector: 'my-tile',
templateUrl: './my-tile.component.html',
styleUrls: ['./my-tile.component.scss']
})
export class MyTileComponent implements OnInit {
public context: any;
constructor(private addinClientService: AddinClientService) {}
public ngOnInit() {
this.addinClientService.args.subscribe((args: AddinClientInitArgs) => {
this.context = args.context;
args.ready({
showUI: true,
title: 'My tile',
tileConfig: {
summaryStyle: AddinTileSummaryStyle.Text,
summaryText: 'Summary text',
showInlineHelp: true,
showSettings: true
}
});
});
// Handle tile inline help icon click
this.addinClientService.inlineHelpClick.subscribe(() => {
this.showHelp();
});
// Handle tile settings icon click
this.addinClientService.settingsClick.subscribe(() => {
this.showSettings();
});
}
private showHelp() {
// Define what happens when the inline help icon is clicked
}
private showSettings() {
// Define what happens when the settings icon is clicked
}
}
The AddinClientService provides wrapper methods over the lower-level implementation. So you can obtain a user identity token using the getUserIdentityToken() function (previously named getAuthToken()):
this.addinClientService.getUserIdentityToken().subscribe((token: string) => {
this.userIdentityToken = token;
});
You can navigate the host page using the navigate method:
this.addinClientService.navigate({
url: someUrl
});
The help window can be popped using the openHelp method:
this.addinClientService.openHelp({
helpKey: someHelpKey
});
You can open help for SKY UX components that directly support a helpKey by providing the AddinClientHelpService:
import {
AddinClientService,
provideAddinClientHelp,
} from '@blackbaud/skyux-lib-addin-client'
private openModal() {
const options: SkyModalConfigurationInterface = {
providers: [
{
...
}
provideAddinClientHelp(),
],
};
const modalInstance = this.modalService.open(
MyModalComponent,
options,
);
}
You can show a modal using the showModal method:
this.addinClientService.showModal({
url: someUrl,
context: someContextObject
}).subscribe((modalResponse: any) => {
// Define response from closing a modal
});
You can show a toast using the showToast method:
this.addinClientService.showToast({
message: someMessage,
style: AddinToastStyle.Info
});
You can show a flyout using the showFlyout method:
this.addinClientService.showFlyout({
url: someUrl,
context: someContextObject
}).subscribe(() => {
// Define what happens when a flyout has closed
});
You can show a confirm dialog using the showConfirm method:
this.addinClientService.showConfirm({
message: 'confirm title',
body: 'confirm message body',
buttons: [
{
action: 'ok',
text: 'OK',
autofocus: true,
style: AddinConfirmButtonStyle.Primary
},
{
action: 'cancel',
text: 'Cancel',
style: AddinConfirmButtonStyle.Link
}
]
}).subscribe((action) => {
// Handle the action returned when the dialog closes
});
You can show an error dialog using the showError method:
this.addinClientService.showError({
closeText: 'OK',
description: 'An unexpected error occurred',
title: 'Error'
});
You can handle entry form and data add-in events using the addEventHandler method:
this.addinClientService.addEventHandler({
eventType: 'form-data-update'
}).addinEvent.subscribe((addinEvent: AddinEvent) => {
// get updated data from addinEvent.context object
});
this.addinClientService.addEventHandler({
eventType: 'form-save'
}).addinEvent.subscribe((addinEvent: AddinEvent) => {
// handle the save event, asynchronously
addinEvent.done(); // When done, call the done function (tells the entry form it's OK to close)
});
this.addinClientService.addEventHandler({
eventType: 'form-cancel'
}).addinEvent.subscribe((addinEvent: AddinEvent) => {
// handle the cancel event, asynchronously
addinEvent.done(); // When done, call the done function (tells the entry form it's OK to close)
});
this.addinClientService.addEventHandler({
eventType: 'load-data'
}).addinEvent.subscribe((addinEvent: AddinEvent) => {
// retrieve data based on the addinEvent.context object
const data = [
{ id: 1, name: 'Item 1' },
{ id: 2, name: 'Item 2' }
];
addinEvent.done(data);
});
You can send a custom event to the host page, as long as the host supports the event type.
Before sending an event to the host page, you must subscribe to the AddinClientService's init args
event and check the supportedEventTypes property to determine what event types the host page
will handle.
this.addinClientService.args.subscribe((args: AddinClientInitArgs) => {
this.supportedEventTypes = args.supportedEventTypes;
args.ready({
showUI: true
});
});
// Before sending event, check to make sure the event is supported by the host page
if (this.supportedEventTypes.includes('my-event-type')) {
// To send add-in events to the host, call the sendEvent method
this.addinClientService.sendEvent({
type: 'my-event-type',
context: { /* context object to pass to host page */ }
}).subscribe(() => {
// host page received the event
}, (err) => {
// an error occurred while attempting to send the event
});
}
To determine the add-in extension points that support custom events, please see https://developer.blackbaud.com/skyapi/docs/addins/concepts/extension-points.
Finally, you can show/hide a page-blocking wait using the showWait and hideWait methods:
this.addinClientService.showWait();
// Execute code
this.addinClientService.hideWait();
Additional configuration
The SKY UX Add-in Client allows for additional configuration to be specified by extending AddinClientConfigService.
You can allow additional origins where your add-in client may run within a Blackbaud host page. Additional origins are supplied as regular expression patterns. The AddinClientService already allows several Blackbaud host origins by default. If you find a need to extend the defaults, you may do so by overriding the AddinClientConfigService's getAddinClientConfig() method.
class AddinConfigService extends AddinClientConfigService {
public override getAddinClientConfig(): AddinClientConfig {
return {
allowedOrigins: [
/^https\:\/\/[\w\-\.]+\.additionalblackbauddomain\.com$/
]
};
}
}
@NgModule({
// specify providers
providers: [
AddinClientService,
{
provide: AddinClientConfigService,
useClass: AddinConfigService
}
]
})
export class MyModule { }
For more information on creating SKY Add-ins, view the documentation on the SKY Developer Portal
Hosted add-in backgrounds
Starting in version 15.0.0, the add-in client gives the add-in document the SKY UX background that matches where the add-in is hosted, in the current theme and mode:
- Modal add-ins get a transparent background, so the SKY UX modal backdrop shows behind the modal.
- Box, Tile, Flyout, and Vertical Tab Form add-ins use the SKY UX container background, as SKY UX boxes, tiles, and flyouts do for their own content.
- Page, Tab, Generic, full-page modal, Button, Action Button, and Dataset add-ins keep their
own background. SKY UX tabs don't give their content a background, so a Tab add-in
matches whatever is behind its tabset, usually the page. A Generic add-in is embedded with
sky-addinand no more specific type, typically as part of a page. - When the client can't determine where the add-in is hosted, it uses the SKY UX container background.
The client adds one <style> element for these backgrounds. If the add-in's Content
Security Policy restricts style-src, provide the nonce to Angular through CSP_NONCE or
the ngCspNonce attribute, as Angular requires for component styles. The client applies
the same nonce.
Remove historical style overrides
Modal add-ins previously needed global style overrides to make their document transparent and hide the SKY UX modal backdrop. Remove them:
::ng-deep body { background: transparent; }
::ng-deep .sky-modal-host-backdrop { display: none; }
The client now makes the document transparent itself and keeps the SKY UX modal backdrop visible. Until you remove these rules, the client overrides the rule that hides the backdrop, so existing add-ins keep working.
Modal add-ins that don't open a SKY UX modal
A modal add-in that renders its content directly, instead of opening a SKY UX modal,
should keep its own background. Send an empty style in ready():
args.ready({
showUI: true,
modalConfig: {
style: {}
}
});
Any modalConfig.style, including an empty object, turns off the automatic background for
that ready() call. See Modal style.
Choose the add-in's background
To choose the background instead of the automatic one, extend AddinClientConfigService
(see Additional configuration) and override
getHostedSurfaceMode(). It can return:
| Mode | Behavior |
|---|---|
'automatic' (default) | Uses the background for where the add-in is hosted, as described above. |
'preserve' | Keeps the add-in's own background. The client doesn't change the add-in document. |
'container' | Uses the SKY UX container background, wherever the add-in is hosted. |
The client asks on every ready() call and passes the add-in type the host reported, if
any, and the arguments the add-in passed to ready(). That lets an add-in that's hosted in
several places choose for each one, for example by route:
class AddinConfigService extends AddinClientConfigService {
readonly #router = inject(Router);
public override getHostedSurfaceMode(
context: AddinClientHostedSurfaceContext
): AddinClientHostedSurfaceMode {
// The list page renders as part of the host's page, so it keeps the page background.
return this.#router.url.startsWith('/single-list') ? 'preserve' : 'automatic';
}
}
A modal add-in can still set modalConfig.style explicitly, whatever the mode.
Modal style
A modal add-in can set its document background and the host's overlay explicitly through
modalConfig.style. The two options are independent:
| Option | Behavior |
|---|---|
transparentBackground: true | Makes the add-in document transparent with inline !important background-color: transparent and background-image: none declarations on <html> and <body>. |
transparentBackground: false or omitted | Keeps the add-in's own background. |
hostOverlay: false | Asks the host to make its modal overlay transparent. |
hostOverlay: true or omitted | The host keeps its visible modal overlay. |
args.ready({
showUI: true,
modalConfig: {
style: {
transparentBackground: true,
hostOverlay: false
}
}
});
With both options set, the add-in document and the host's overlay are transparent, so the
SKY UX modal backdrop is the only visible scrim. Defining modalConfig.style, including an
empty object, turns off the automatic background for that ready() call.