Skip to content

Commit

Permalink
add: manage template of instances of same app
Browse files Browse the repository at this point in the history
  • Loading branch information
enlob committed Mar 30, 2024
1 parent c29ed51 commit 327039a
Show file tree
Hide file tree
Showing 4 changed files with 15 additions and 13 deletions.
5 changes: 4 additions & 1 deletion libs/single-spa-angular/internals/src/dom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,11 @@ export function getContainerElementAndSetTemplate<T extends BaseSingleSpaAngular
);
}

// If the template is a function, we call it with the props to get the string template.
const template = typeof options.template === 'function' ? options.template(props) : options.template;

const containerElement = getContainerElement(domElementGetter, props);
containerElement.innerHTML = options.template;
containerElement.innerHTML = template;
return containerElement;
}

Expand Down
2 changes: 1 addition & 1 deletion libs/single-spa-angular/internals/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { ApplicationRef, NgModuleRef } from '@angular/core';
export type DomElementGetter = (props: any) => HTMLElement;

export interface BaseSingleSpaAngularOptions {
template: string;
template: string | ((props: AppProps) => string);
domElementGetter?: DomElementGetter;
bootstrapFunction(props: AppProps): Promise<NgModuleRef<any> | ApplicationRef>;
}
19 changes: 10 additions & 9 deletions libs/single-spa-angular/src/single-spa-angular.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ const defaultOptions = {
Router: undefined,
domElementGetter: undefined, // only optional if you provide a domElementGetter as a custom prop
updateFunction: () => Promise.resolve(),
bootstrappedNgModuleRefOrAppRef: null,
instances: {},
};

// This will be provided through Terser global definitions by Angular CLI. This will
Expand All @@ -38,8 +38,8 @@ export function singleSpaAngular<T>(userOptions: SingleSpaAngularOptions<T>): Li
throw Error('single-spa-angular must be passed an options.bootstrapFunction');
}

if (NG_DEV_MODE && typeof options.template !== 'string') {
throw Error('single-spa-angular must be passed options.template string');
if (NG_DEV_MODE && typeof options.template !== 'string' && typeof options.template !== 'function') {
throw Error('single-spa-angular must be passed an options.template string or function');
}

if (NG_DEV_MODE && !options.NgZone) {
Expand All @@ -62,7 +62,7 @@ export function singleSpaAngular<T>(userOptions: SingleSpaAngularOptions<T>): Li

async function bootstrap(options: BootstrappedSingleSpaAngularOptions, props: any): Promise<void> {
const instance: Instance = {
bootstrappedNgModuleRefOrAppRef: null
bootstrappedNgModuleRefOrAppRef: null,
};
options.instances[props.name || props.appName] = instance;

Expand All @@ -73,9 +73,6 @@ async function bootstrap(options: BootstrappedSingleSpaAngularOptions, props: an
return;
}

// Set NgZone on instance.
instance.NgZone = options.NgZone;

// In order for multiple Angular apps to work concurrently on a page, they each need a unique identifier.
instance.zoneIdentifier = `single-spa-angular:${props.name || props.appName}`;

Expand All @@ -84,9 +81,10 @@ async function bootstrap(options: BootstrappedSingleSpaAngularOptions, props: an
// https://github.com/single-spa/single-spa-angular/issues/47,
// https://github.com/angular/angular/blob/a14dc2d7a4821a19f20a9547053a5734798f541e/packages/core/src/zone/ng_zone.ts#L144,
// and https://github.com/angular/angular/blob/a14dc2d7a4821a19f20a9547053a5734798f541e/packages/core/src/zone/ng_zone.ts#L257
instance.NgZone.isInAngularZone = () => {
options.NgZone.isInAngularZone = () => {
// @ts-ignore
return window.Zone.current._properties[options.zoneIdentifier] === true;
// Check zone for any instance.
return Object.values(options.instances).some(instance => window.Zone.current._properties[instance.zoneIdentifier] === true);
};

instance.routingEventListener = () => {
Expand Down Expand Up @@ -170,6 +168,9 @@ function unmount(options: BootstrappedSingleSpaAngularOptions, props: any): Prom

instance.bootstrappedNgModuleRefOrAppRef!.destroy();
instance.bootstrappedNgModuleRefOrAppRef = null;

// Delete instance from array of instances.
delete options.instances[props.name || props.appName];
});
}

Expand Down
2 changes: 0 additions & 2 deletions libs/single-spa-angular/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,6 @@ export interface BootstrappedSingleSpaAngularOptions extends SingleSpaAngularOpt

export interface Instance {
bootstrappedNgModuleRefOrAppRef: NgModuleRef<any> | ApplicationRef | null;
// This is a reference to the `NgZone` class that was used to bootstrap the application.
NgZone?: typeof NgZone | 'noop';
// All below properties can be optional in case of
// `SingleSpaAngularOpts.NgZone` is a `noop` string and not an `NgZone` class.
bootstrappedNgZone?: NgZone;
Expand Down

0 comments on commit 327039a

Please sign in to comment.