mirror of https://github.com/abpframework/abp.git
133 changed files with 2551 additions and 1144 deletions
@ -0,0 +1,101 @@ |
|||
# ContainerStrategy |
|||
|
|||
`ContainerStrategy` is an abstract class exposed by @abp/ng.core package. There are two container strategies extending it: `ClearContainerStrategy` and `InsertIntoContainerStrategy`. Implementing the same methods and properties, both of these strategies help you define how your containers will be prepared and where your content will be projected. |
|||
|
|||
|
|||
|
|||
## API |
|||
|
|||
`ClearContainerStrategy` is a class that extends `ContainerStrategy`. It lets you **clear a container before projecting content in it**. |
|||
|
|||
|
|||
### constructor |
|||
|
|||
```js |
|||
constructor( |
|||
public containerRef: ViewContainerRef, |
|||
private index?: number, // works only in InsertIntoContainerStrategy |
|||
) |
|||
``` |
|||
|
|||
- `containerRef` is the `ViewContainerRef` that will be used when projecting the content. |
|||
|
|||
|
|||
### getIndex |
|||
|
|||
```js |
|||
getIndex(): number |
|||
``` |
|||
|
|||
This method return the given index clamped by `0` and `length` of the `containerRef`. For strategies without an index, it returns `0`. |
|||
|
|||
|
|||
### prepare |
|||
|
|||
```js |
|||
prepare(): void |
|||
``` |
|||
|
|||
This method is called before content projection. Based on used container strategy, it either clears the container or does nothing (noop). |
|||
|
|||
|
|||
|
|||
## ClearContainerStrategy |
|||
|
|||
`ClearContainerStrategy` is a class that extends `ContainerStrategy`. It lets you **clear a container before projecting content in it**. |
|||
|
|||
|
|||
|
|||
## InsertIntoContainerStrategy |
|||
|
|||
`InsertIntoContainerStrategy` is a class that extends `ContainerStrategy`. It lets you **project your content at a specific node index in the container**. |
|||
|
|||
|
|||
|
|||
## Predefined Container Strategies |
|||
|
|||
Predefined container strategies are accessible via `CONTAINER_STRATEGY` constant. |
|||
|
|||
|
|||
### Clear |
|||
|
|||
```js |
|||
CONTAINER_STRATEGY.Clear(containerRef: ViewContainerRef) |
|||
``` |
|||
|
|||
Clears given container before content projection. |
|||
|
|||
|
|||
### Append |
|||
|
|||
```js |
|||
CONTAINER_STRATEGY.Append(containerRef: ViewContainerRef) |
|||
``` |
|||
|
|||
Projected content will be appended to the container. |
|||
|
|||
|
|||
### Prepend |
|||
|
|||
```js |
|||
CONTAINER_STRATEGY.Prepend(containerRef: ViewContainerRef) |
|||
``` |
|||
|
|||
Projected content will be prepended to the container. |
|||
|
|||
|
|||
### Insert |
|||
|
|||
```js |
|||
CONTAINER_STRATEGY.Insert( |
|||
containerRef: ViewContainerRef, |
|||
index: number, |
|||
) |
|||
``` |
|||
|
|||
Projected content will be inserted into to the container at given index (clamped by `0` and `length` of the `containerRef`). |
|||
|
|||
|
|||
## See Also |
|||
|
|||
- [ProjectionStrategy](./Projection-Strategy.md) |
|||
@ -0,0 +1,78 @@ |
|||
# Content Projection |
|||
|
|||
You can use the `ContentProjectionService` in @abp/ng.core package in order to project content in an easy and explicit way. |
|||
|
|||
## Getting Started |
|||
|
|||
You do not have to provide the `ContentProjectionService` at module or component level, because it is already **provided in root**. You can inject and start using it immediately in your components, directives, or services. |
|||
|
|||
```js |
|||
import { ContentProjectionService } from '@abp/ng.core'; |
|||
|
|||
@Component({ |
|||
/* class metadata here */ |
|||
}) |
|||
class DemoComponent { |
|||
constructor(private contentProjectionService: ContentProjectionService) {} |
|||
} |
|||
``` |
|||
|
|||
## Usage |
|||
|
|||
You can use the `projectContent` method of `ContentProjectionService` to render components and templates dynamically in your project. |
|||
|
|||
### How to Project Components to Root Level |
|||
|
|||
If you pass a `RootComponentProjectionStrategy` as the first parameter of `projectContent` method, the `ContentProjectionService` will resolve the projected component and place it at the root level. If provided, it will also pass the component a context. |
|||
|
|||
```js |
|||
const strategy = PROJECTION_STRATEGY.AppendComponentToBody( |
|||
SomeOverlayComponent, |
|||
{ someOverlayProp: "SOME_VALUE" } |
|||
); |
|||
|
|||
const componentRef = this.contentProjectionService.projectContent(strategy); |
|||
``` |
|||
|
|||
In the example above, `SomeOverlayComponent` component will placed at the **end** of `<body>` and a `ComponentRef` will be returned. Additionally, the given context will be applied, so `someOverlayProp` of the component will be set to `SOME_VALUE`. |
|||
|
|||
> You should keep the returned `ComponentRef` instance, as it is a reference to the projected component and you will need that reference to destroy the projected view and the component instance. |
|||
|
|||
### How to Project Components and Templates into a Container |
|||
|
|||
If you pass a `ComponentProjectionStrategy` or `TemplateProjectionStrategy` as the first parameter of `projectContent` method, and a `ViewContainerRef` as the second parameter of that strategy, the `ContentProjectionService` will project the component or template to the given container. If provided, it will also pass the component or the template a context. |
|||
|
|||
```js |
|||
const strategy = PROJECTION_STRATEGY.ProjectComponentToContainer( |
|||
SomeComponent, |
|||
viewContainerRefOfTarget, |
|||
{ someProp: "SOME_VALUE" } |
|||
); |
|||
|
|||
const componentRef = this.contentProjectionService.projectContent(strategy); |
|||
``` |
|||
|
|||
In this example, the `viewContainerRefOfTarget`, which is a `ViewContainerRef` instance, will be cleared and `SomeComponent` component will be placed inside it. In addition, the given context will be applied and `someProp` of the component will be set to `SOME_VALUE`. |
|||
|
|||
> You should keep the returned `ComponentRef` or `EmbeddedViewRef`, as they are a reference to the projected content and you will need them to destroy it when necessary. |
|||
|
|||
Please refer to [ProjectionStrategy](./Projection-Strategy.md) to see all available projection strategies and how you can build your own projection strategy. |
|||
|
|||
## API |
|||
|
|||
### projectContent |
|||
|
|||
```js |
|||
projectContent<T extends Type<any> | TemplateRef<any>>( |
|||
projectionStrategy: ProjectionStrategy<T>, |
|||
injector = this.injector, |
|||
): ComponentRef<C> | EmbeddedViewRef<C> |
|||
``` |
|||
|
|||
- `projectionStrategy` parameter is the primary focus here and is explained above. |
|||
- `injector` parameter is the `Injector` instance you can pass to the projected content. It is not used in `TemplateProjectionStrategy`. |
|||
|
|||
|
|||
## What's Next? |
|||
|
|||
- [TrackByService](./Track-By-Service.md) |
|||
@ -0,0 +1,117 @@ |
|||
# ContextStrategy |
|||
|
|||
`ContextStrategy` is an abstract class exposed by @abp/ng.core package. There are three context strategies extending it: `ComponentContextStrategy`, `TemplateContextStrategy`, and `NoContextStrategy`. Implementing the same methods and properties, all of these strategies help you define how projected content will get their context. |
|||
|
|||
|
|||
|
|||
## ComponentContextStrategy |
|||
|
|||
`ComponentContextStrategy` is a class that extends `ContextStrategy`. It lets you **pass context to a projected component**. |
|||
|
|||
|
|||
### constructor |
|||
|
|||
```js |
|||
constructor(public context: Partial<InferredInstanceOf<T>>) {} |
|||
``` |
|||
|
|||
- `T` refers to component type here, i.e. `Type<C>`. |
|||
- `InferredInstanceOf` is a utility type exposed by @abp/ng.core package. It infers component shape. |
|||
- `context` will be mapped to properties of the projected component. |
|||
|
|||
|
|||
### setContext |
|||
|
|||
```js |
|||
setContext(componentRef: ComponentRef<InferredInstanceOf<T>>): Partial<InferredInstanceOf<T>> |
|||
``` |
|||
|
|||
This method maps each prop of the context to the component property with the same name and calls change detection. It returns the context after mapping. |
|||
|
|||
|
|||
|
|||
## TemplateContextStrategy |
|||
|
|||
`TemplateContextStrategy` is a class that extends `ContextStrategy`. It lets you **pass context to a projected template**. |
|||
|
|||
|
|||
### constructor |
|||
|
|||
```js |
|||
constructor(public context: Partial<InferredContextOf<T>>) {} |
|||
``` |
|||
|
|||
- `T` refers to template context type here, i.e. `TemplateRef<C>`. |
|||
- `InferredContextOf` is a utility type exposed by @abp/ng.core package. It infers context shape. |
|||
- `context` will be mapped to properties of the projected template. |
|||
|
|||
|
|||
### setContext |
|||
|
|||
```js |
|||
setContext(): Partial<InferredContextOf<T>> |
|||
``` |
|||
|
|||
This method does nothing and only returns the context, because template context is not mapped but passed in as parameter to `createEmbeddedView` method. |
|||
|
|||
|
|||
|
|||
## NoContextStrategy |
|||
|
|||
`NoContextStrategy` is a class that extends `ContextStrategy`. It lets you **skip passing any context to projected content**. |
|||
|
|||
|
|||
### constructor |
|||
|
|||
```js |
|||
constructor() |
|||
``` |
|||
|
|||
Unlike other context strategies, `NoContextStrategy` contructor takes no parameters. |
|||
|
|||
|
|||
### setContext |
|||
|
|||
```js |
|||
setContext(): undefined |
|||
``` |
|||
|
|||
Since there is no context, this method gets no parameters and will return `undefined`. |
|||
|
|||
|
|||
|
|||
## Predefined Context Strategies |
|||
|
|||
Predefined context strategies are accessible via `CONTEXT_STRATEGY` constant. |
|||
|
|||
|
|||
### None |
|||
|
|||
```js |
|||
CONTEXT_STRATEGY.None() |
|||
``` |
|||
|
|||
This strategy will not pass any context to the projected content. |
|||
|
|||
|
|||
### Component |
|||
|
|||
```js |
|||
CONTEXT_STRATEGY.Component(context: Partial<InferredContextOf<T>>) |
|||
``` |
|||
|
|||
This strategy will help you pass the given context to the projected component. |
|||
|
|||
|
|||
### Template |
|||
|
|||
```js |
|||
CONTEXT_STRATEGY.Template(context: Partial<InferredContextOf<T>>) |
|||
``` |
|||
|
|||
This strategy will help you pass the given context to the projected template. |
|||
|
|||
|
|||
## See Also |
|||
|
|||
- [ProjectionStrategy](./Projection-Strategy.md) |
|||
@ -0,0 +1,200 @@ |
|||
# ProjectionStrategy |
|||
|
|||
`ProjectionStrategy` is an abstract class exposed by @abp/ng.core package. There are three projection strategies extending it: `ComponentProjectionStrategy`, `RootComponentProjectionStrategy`, and `TemplateProjectionStrategy`. Implementing the same methods and properties, all of these strategies help you define how your content projection will work. |
|||
|
|||
|
|||
|
|||
## ComponentProjectionStrategy |
|||
|
|||
`ComponentProjectionStrategy` is a class that extends `ProjectionStrategy`. It lets you **project a component into a container**. |
|||
|
|||
|
|||
### constructor |
|||
|
|||
```js |
|||
constructor( |
|||
component: T, |
|||
private containerStrategy: ContainerStrategy, |
|||
private contextStrategy?: ContextStrategy, |
|||
) |
|||
``` |
|||
|
|||
- `component` is class of the component you would like to project. |
|||
- `containerStrategy` is the `ContainerStrategy` that will be used when projecting the component. |
|||
- `contextStrategy` is the `ContextStrategy` that will be used on the projected component. (_default: None_) |
|||
|
|||
Please refer to [ContainerStrategy](./Container-Strategy.md) and [ContextStrategy](./Context-Strategy.md) documentation for their usage. |
|||
|
|||
|
|||
### injectContent |
|||
|
|||
```js |
|||
injectContent(injector: Injector): ComponentRef<T> |
|||
``` |
|||
|
|||
This method prepares the container, resolves the component, sets its context, and projects it to the container. It returns a `ComponentRef` instance, which you should keep in order to clear projected components later on. |
|||
|
|||
|
|||
|
|||
## RootComponentProjectionStrategy |
|||
|
|||
`RootComponentProjectionStrategy` is a class that extends `ProjectionStrategy`. It lets you **project a component into the document**, such as appending it to `<body>`. |
|||
|
|||
|
|||
### constructor |
|||
|
|||
```js |
|||
constructor( |
|||
component: T, |
|||
private contextStrategy?: ContextStrategy, |
|||
private domStrategy?: DomStrategy, |
|||
) |
|||
``` |
|||
|
|||
- `component` is class of the component you would like to project. |
|||
- `contextStrategy` is the `ContextStrategy` that will be used on the projected component. (_default: None_) |
|||
- `domStrategy` is the `DomStrategy` that will be used when inserting component. (_default: AppendToBody_) |
|||
|
|||
Please refer to [ContextStrategy](./Context-Strategy.md) and [DomStrategy](./Dom-Strategy.md) documentation for their usage. |
|||
|
|||
|
|||
### injectContent |
|||
|
|||
```js |
|||
injectContent(injector: Injector): ComponentRef<T> |
|||
``` |
|||
|
|||
This method resolves the component, sets its context, and projects it to the document. It returns a `ComponentRef` instance, which you should keep in order to clear projected components later on. |
|||
|
|||
|
|||
|
|||
## TemplateProjectionStrategy |
|||
|
|||
`TemplateProjectionStrategy` is a class that extends `ProjectionStrategy`. It lets you **project a template into a container**. |
|||
|
|||
|
|||
### constructor |
|||
|
|||
```js |
|||
constructor( |
|||
template: T, |
|||
private containerStrategy: ContainerStrategy, |
|||
private contextStrategy?: ContextStrategy, |
|||
) |
|||
``` |
|||
|
|||
- `template` is `TemplateRef` you would like to project. |
|||
- `containerStrategy` is the `ContainerStrategy` that will be used when projecting the component. |
|||
- `contextStrategy` is the `ContextStrategy` that will be used on the projected component. (_default: None_) |
|||
|
|||
Please refer to [ContainerStrategy](./Container-Strategy.md) and [ContextStrategy](./Context-Strategy.md) documentation for their usage. |
|||
|
|||
|
|||
### injectContent |
|||
|
|||
```js |
|||
injectContent(): EmbeddedViewRef<T> |
|||
``` |
|||
|
|||
This method prepares the container, and projects the template together with the defined context to it. It returns an `EmbeddedViewRef`, which you should keep in order to clear projected templates later on. |
|||
|
|||
|
|||
|
|||
## Predefined Projection Strategies |
|||
|
|||
Predefined projection strategies are accessible via `PROJECTION_STRATEGY` constant. |
|||
|
|||
|
|||
### AppendComponentToBody |
|||
|
|||
```js |
|||
PROJECTION_STRATEGY.AppendComponentToBody( |
|||
component: T, |
|||
contextStrategy?: ComponentContextStrategy<T>, |
|||
) |
|||
``` |
|||
|
|||
Sets given context to the component and places it at the **end** of `<body>` tag in the document. |
|||
|
|||
|
|||
### AppendComponentToContainer |
|||
|
|||
```js |
|||
PROJECTION_STRATEGY.AppendComponentToContainer( |
|||
component: T, |
|||
containerRef: ViewContainerRef, |
|||
contextStrategy?: ComponentContextStrategy<T>, |
|||
) |
|||
``` |
|||
|
|||
Sets given context to the component and places it at the **end** of the container. |
|||
|
|||
|
|||
### AppendTemplateToContainer |
|||
|
|||
```js |
|||
PROJECTION_STRATEGY.AppendTemplateToContainer( |
|||
templateRef: T, |
|||
containerRef: ViewContainerRef, |
|||
contextStrategy?: ComponentContextStrategy<T>, |
|||
) |
|||
``` |
|||
|
|||
Sets given context to the template and places it at the **end** of the container. |
|||
|
|||
|
|||
### PrependComponentToContainer |
|||
|
|||
```js |
|||
PROJECTION_STRATEGY.PrependComponentToContainer( |
|||
component: T, |
|||
containerRef: ViewContainerRef, |
|||
contextStrategy?: ComponentContextStrategy<T>, |
|||
) |
|||
``` |
|||
|
|||
Sets given context to the component and places it at the **beginning** of the container. |
|||
|
|||
|
|||
### PrependTemplateToContainer |
|||
|
|||
```js |
|||
PROJECTION_STRATEGY.PrependTemplateToContainer( |
|||
templateRef: T, |
|||
containerRef: ViewContainerRef, |
|||
contextStrategy?: ComponentContextStrategy<T>, |
|||
) |
|||
``` |
|||
|
|||
Sets given context to the template and places it at the **beginning** of the container. |
|||
|
|||
|
|||
### ProjectComponentToContainer |
|||
|
|||
```js |
|||
PROJECTION_STRATEGY.ProjectComponentToContainer( |
|||
component: T, |
|||
containerRef: ViewContainerRef, |
|||
contextStrategy?: ComponentContextStrategy<T>, |
|||
) |
|||
``` |
|||
|
|||
Clears the container, sets given context to the component, and places it **in the cleared** the container. |
|||
|
|||
|
|||
### ProjectTemplateToContainer |
|||
|
|||
```js |
|||
PROJECTION_STRATEGY.ProjectTemplateToContainer( |
|||
templateRef: T, |
|||
containerRef: ViewContainerRef, |
|||
contextStrategy?: ComponentContextStrategy<T>, |
|||
) |
|||
``` |
|||
|
|||
Clears the container, sets given context to the template, and places it **in the cleared** the container. |
|||
|
|||
|
|||
## See Also |
|||
|
|||
- [DomInsertionService](./Dom-Insertion-Service.md) |
|||
@ -0,0 +1,9 @@ |
|||
export const enum eAccountComponents { |
|||
Login = 'Account.LoginComponent', |
|||
Register = 'Account.RegisterComponent', |
|||
ManageProfile = 'Account.ManageProfileComponent', |
|||
TenantBox = 'Account.TenantBoxComponent', |
|||
AuthWrapper = 'Account.AuthWrapperComponent', |
|||
ChangePassword = 'Account.ChangePasswordComponent', |
|||
PersonalSettings = 'Account.PersonalSettingsComponent', |
|||
} |
|||
@ -1,5 +1,6 @@ |
|||
export * from './lib/account.module'; |
|||
export * from './lib/components'; |
|||
export * from './lib/enums/components'; |
|||
export * from './lib/tokens'; |
|||
export * from './lib/models'; |
|||
export * from './lib/services'; |
|||
|
|||
@ -0,0 +1,4 @@ |
|||
import { TemplateRef, Type } from '@angular/core'; |
|||
|
|||
export type InferredInstanceOf<T> = T extends Type<infer U> ? U : never; |
|||
export type InferredContextOf<T> = T extends TemplateRef<infer U> ? U : never; |
|||
@ -0,0 +1,14 @@ |
|||
import { Injectable, Injector, TemplateRef, Type } from '@angular/core'; |
|||
import { ProjectionStrategy } from '../strategies/projection.strategy'; |
|||
|
|||
@Injectable({ providedIn: 'root' }) |
|||
export class ContentProjectionService { |
|||
constructor(private injector: Injector) {} |
|||
|
|||
projectContent<T extends Type<any> | TemplateRef<any>>( |
|||
projectionStrategy: ProjectionStrategy<T>, |
|||
injector = this.injector, |
|||
) { |
|||
return projectionStrategy.injectContent(injector); |
|||
} |
|||
} |
|||
@ -0,0 +1,44 @@ |
|||
import { ViewContainerRef } from '@angular/core'; |
|||
|
|||
export abstract class ContainerStrategy { |
|||
constructor(public containerRef: ViewContainerRef) {} |
|||
|
|||
abstract getIndex(): number; |
|||
|
|||
prepare(): void {} |
|||
} |
|||
|
|||
export class ClearContainerStrategy extends ContainerStrategy { |
|||
getIndex(): number { |
|||
return 0; |
|||
} |
|||
|
|||
prepare() { |
|||
this.containerRef.clear(); |
|||
} |
|||
} |
|||
|
|||
export class InsertIntoContainerStrategy extends ContainerStrategy { |
|||
constructor(containerRef: ViewContainerRef, private index: number) { |
|||
super(containerRef); |
|||
} |
|||
|
|||
getIndex() { |
|||
return Math.min(Math.max(0, this.index), this.containerRef.length); |
|||
} |
|||
} |
|||
|
|||
export const CONTAINER_STRATEGY = { |
|||
Clear(containerRef: ViewContainerRef) { |
|||
return new ClearContainerStrategy(containerRef); |
|||
}, |
|||
Append(containerRef: ViewContainerRef) { |
|||
return new InsertIntoContainerStrategy(containerRef, containerRef.length); |
|||
}, |
|||
Prepend(containerRef: ViewContainerRef) { |
|||
return new InsertIntoContainerStrategy(containerRef, 0); |
|||
}, |
|||
Insert(containerRef: ViewContainerRef, index: number) { |
|||
return new InsertIntoContainerStrategy(containerRef, index); |
|||
}, |
|||
}; |
|||
@ -0,0 +1,47 @@ |
|||
import { ComponentRef, TemplateRef, Type } from '@angular/core'; |
|||
import { InferredContextOf, InferredInstanceOf } from '../models'; |
|||
|
|||
export abstract class ContextStrategy<T = any> { |
|||
constructor(public context: Partial<ContextType<T>>) {} |
|||
|
|||
/* tslint:disable-next-line:no-unused-variable */ |
|||
setContext(componentRef?: ComponentRef<InferredInstanceOf<T>>): Partial<ContextType<T>> { |
|||
return this.context; |
|||
} |
|||
} |
|||
|
|||
export class NoContextStrategy< |
|||
T extends Type<any> | TemplateRef<any> = any |
|||
> extends ContextStrategy<T> { |
|||
constructor() { |
|||
super(undefined); |
|||
} |
|||
} |
|||
|
|||
export class ComponentContextStrategy<T extends Type<any> = any> extends ContextStrategy<T> { |
|||
setContext(componentRef: ComponentRef<InferredInstanceOf<T>>): Partial<InferredInstanceOf<T>> { |
|||
Object.keys(this.context).forEach(key => (componentRef.instance[key] = this.context[key])); |
|||
componentRef.changeDetectorRef.detectChanges(); |
|||
return this.context; |
|||
} |
|||
} |
|||
|
|||
export class TemplateContextStrategy<T extends TemplateRef<any> = any> extends ContextStrategy<T> { |
|||
setContext(): Partial<InferredContextOf<T>> { |
|||
return this.context; |
|||
} |
|||
} |
|||
|
|||
export const CONTEXT_STRATEGY = { |
|||
None<T extends Type<any> | TemplateRef<any> = any>() { |
|||
return new NoContextStrategy<T>(); |
|||
}, |
|||
Component<T extends Type<any> = any>(context: Partial<InferredInstanceOf<T>>) { |
|||
return new ComponentContextStrategy<T>(context); |
|||
}, |
|||
Template<T extends TemplateRef<any> = any>(context: Partial<InferredContextOf<T>>) { |
|||
return new TemplateContextStrategy<T>(context); |
|||
}, |
|||
}; |
|||
|
|||
type ContextType<T> = T extends Type<infer U> | TemplateRef<infer U> ? U : never; |
|||
@ -1,5 +1,8 @@ |
|||
export * from './container.strategy'; |
|||
export * from './content-security.strategy'; |
|||
export * from './content.strategy'; |
|||
export * from './context.strategy'; |
|||
export * from './cross-origin.strategy'; |
|||
export * from './dom.strategy'; |
|||
export * from './loading.strategy'; |
|||
export * from './projection.strategy'; |
|||
|
|||
@ -0,0 +1,176 @@ |
|||
import { |
|||
ApplicationRef, |
|||
ComponentFactoryResolver, |
|||
ComponentRef, |
|||
EmbeddedViewRef, |
|||
Injector, |
|||
TemplateRef, |
|||
Type, |
|||
ViewContainerRef, |
|||
} from '@angular/core'; |
|||
import { InferredContextOf, InferredInstanceOf } from '../models/utility'; |
|||
import { ContainerStrategy, CONTAINER_STRATEGY } from './container.strategy'; |
|||
import { ContextStrategy, CONTEXT_STRATEGY } from './context.strategy'; |
|||
import { DomStrategy, DOM_STRATEGY } from './dom.strategy'; |
|||
|
|||
export abstract class ProjectionStrategy<T = any> { |
|||
constructor(public content: T) {} |
|||
|
|||
abstract injectContent(injector: Injector): ComponentRefOrEmbeddedViewRef<T>; |
|||
} |
|||
|
|||
export class ComponentProjectionStrategy<T extends Type<any>> extends ProjectionStrategy<T> { |
|||
constructor( |
|||
component: T, |
|||
private containerStrategy: ContainerStrategy, |
|||
private contextStrategy: ContextStrategy = CONTEXT_STRATEGY.None(), |
|||
) { |
|||
super(component); |
|||
} |
|||
|
|||
injectContent(injector: Injector) { |
|||
this.containerStrategy.prepare(); |
|||
|
|||
const resolver = injector.get(ComponentFactoryResolver) as ComponentFactoryResolver; |
|||
const factory = resolver.resolveComponentFactory<InferredInstanceOf<T>>(this.content); |
|||
|
|||
const componentRef = this.containerStrategy.containerRef.createComponent( |
|||
factory, |
|||
this.containerStrategy.getIndex(), |
|||
injector, |
|||
); |
|||
this.contextStrategy.setContext(componentRef); |
|||
|
|||
return componentRef as ComponentRefOrEmbeddedViewRef<T>; |
|||
} |
|||
} |
|||
|
|||
export class RootComponentProjectionStrategy<T extends Type<any>> extends ProjectionStrategy<T> { |
|||
constructor( |
|||
component: T, |
|||
private contextStrategy: ContextStrategy = CONTEXT_STRATEGY.None(), |
|||
private domStrategy: DomStrategy = DOM_STRATEGY.AppendToBody(), |
|||
) { |
|||
super(component); |
|||
} |
|||
|
|||
injectContent(injector: Injector) { |
|||
const appRef = injector.get(ApplicationRef); |
|||
const resolver = injector.get(ComponentFactoryResolver) as ComponentFactoryResolver; |
|||
const componentRef = resolver |
|||
.resolveComponentFactory<InferredInstanceOf<T>>(this.content) |
|||
.create(injector); |
|||
|
|||
this.contextStrategy.setContext(componentRef); |
|||
|
|||
appRef.attachView(componentRef.hostView); |
|||
const element: HTMLElement = (componentRef.hostView as EmbeddedViewRef<any>).rootNodes[0]; |
|||
this.domStrategy.insertElement(element); |
|||
|
|||
return componentRef as ComponentRefOrEmbeddedViewRef<T>; |
|||
} |
|||
} |
|||
|
|||
export class TemplateProjectionStrategy<T extends TemplateRef<any>> extends ProjectionStrategy<T> { |
|||
constructor( |
|||
templateRef: T, |
|||
private containerStrategy: ContainerStrategy, |
|||
private contextStrategy = CONTEXT_STRATEGY.None(), |
|||
) { |
|||
super(templateRef); |
|||
} |
|||
|
|||
injectContent() { |
|||
this.containerStrategy.prepare(); |
|||
|
|||
const embeddedViewRef = this.containerStrategy.containerRef.createEmbeddedView( |
|||
this.content, |
|||
this.contextStrategy.context, |
|||
this.containerStrategy.getIndex(), |
|||
); |
|||
embeddedViewRef.detectChanges(); |
|||
|
|||
return embeddedViewRef as ComponentRefOrEmbeddedViewRef<T>; |
|||
} |
|||
} |
|||
|
|||
export const PROJECTION_STRATEGY = { |
|||
AppendComponentToBody<T extends Type<unknown>>(component: T, context?: InferredInstanceOf<T>) { |
|||
return new RootComponentProjectionStrategy<T>( |
|||
component, |
|||
context && CONTEXT_STRATEGY.Component(context), |
|||
); |
|||
}, |
|||
AppendComponentToContainer<T extends Type<unknown>>( |
|||
component: T, |
|||
containerRef: ViewContainerRef, |
|||
context?: InferredInstanceOf<T>, |
|||
) { |
|||
return new ComponentProjectionStrategy<T>( |
|||
component, |
|||
CONTAINER_STRATEGY.Append(containerRef), |
|||
context && CONTEXT_STRATEGY.Component(context), |
|||
); |
|||
}, |
|||
AppendTemplateToContainer<T extends TemplateRef<unknown>>( |
|||
templateRef: T, |
|||
containerRef: ViewContainerRef, |
|||
context?: InferredContextOf<T>, |
|||
) { |
|||
return new TemplateProjectionStrategy<T>( |
|||
templateRef, |
|||
CONTAINER_STRATEGY.Append(containerRef), |
|||
context && CONTEXT_STRATEGY.Template(context), |
|||
); |
|||
}, |
|||
PrependComponentToContainer<T extends Type<unknown>>( |
|||
component: T, |
|||
containerRef: ViewContainerRef, |
|||
context?: InferredInstanceOf<T>, |
|||
) { |
|||
return new ComponentProjectionStrategy<T>( |
|||
component, |
|||
CONTAINER_STRATEGY.Prepend(containerRef), |
|||
context && CONTEXT_STRATEGY.Component(context), |
|||
); |
|||
}, |
|||
PrependTemplateToContainer<T extends TemplateRef<unknown>>( |
|||
templateRef: T, |
|||
containerRef: ViewContainerRef, |
|||
context?: InferredContextOf<T>, |
|||
) { |
|||
return new TemplateProjectionStrategy<T>( |
|||
templateRef, |
|||
CONTAINER_STRATEGY.Prepend(containerRef), |
|||
context && CONTEXT_STRATEGY.Template(context), |
|||
); |
|||
}, |
|||
ProjectComponentToContainer<T extends Type<unknown>>( |
|||
component: T, |
|||
containerRef: ViewContainerRef, |
|||
context?: InferredInstanceOf<T>, |
|||
) { |
|||
return new ComponentProjectionStrategy<T>( |
|||
component, |
|||
CONTAINER_STRATEGY.Clear(containerRef), |
|||
context && CONTEXT_STRATEGY.Component(context), |
|||
); |
|||
}, |
|||
ProjectTemplateToContainer<T extends TemplateRef<unknown>>( |
|||
templateRef: T, |
|||
containerRef: ViewContainerRef, |
|||
context?: InferredContextOf<T>, |
|||
) { |
|||
return new TemplateProjectionStrategy<T>( |
|||
templateRef, |
|||
CONTAINER_STRATEGY.Clear(containerRef), |
|||
context && CONTEXT_STRATEGY.Template(context), |
|||
); |
|||
}, |
|||
}; |
|||
|
|||
type ComponentRefOrEmbeddedViewRef<T> = T extends Type<infer U> |
|||
? ComponentRef<U> |
|||
: T extends TemplateRef<infer C> |
|||
? EmbeddedViewRef<C> |
|||
: never; |
|||
@ -0,0 +1,80 @@ |
|||
import { ViewContainerRef } from '@angular/core'; |
|||
import { |
|||
ClearContainerStrategy, |
|||
CONTAINER_STRATEGY, |
|||
InsertIntoContainerStrategy, |
|||
} from '../strategies'; |
|||
|
|||
describe('ClearContainerStrategy', () => { |
|||
const containerRef = ({ |
|||
clear: jest.fn(), |
|||
length: 7, |
|||
} as any) as ViewContainerRef; |
|||
|
|||
describe('#getIndex', () => { |
|||
it('should return 0', () => { |
|||
const strategy = new ClearContainerStrategy(containerRef); |
|||
expect(strategy.getIndex()).toBe(0); |
|||
}); |
|||
}); |
|||
|
|||
describe('#prepare', () => { |
|||
it('should call clear method of containerRef once', () => { |
|||
const strategy = new ClearContainerStrategy(containerRef); |
|||
strategy.prepare(); |
|||
expect(strategy.getIndex()).toBe(0); |
|||
expect(containerRef.clear).toHaveBeenCalledTimes(1); |
|||
}); |
|||
}); |
|||
}); |
|||
|
|||
describe('InsertIntoContainerStrategy', () => { |
|||
const containerRef = ({ |
|||
clear: jest.fn(), |
|||
length: 7, |
|||
} as any) as ViewContainerRef; |
|||
|
|||
describe('#getIndex', () => { |
|||
test.each` |
|||
index | expected |
|||
${0} | ${0} |
|||
${4} | ${4} |
|||
${9} | ${7} |
|||
${-1} | ${0} |
|||
${Infinity} | ${7} |
|||
`(
|
|||
'should return $expected when index is given $index', |
|||
({ index, expected }: { index: number; expected: number }) => { |
|||
const strategy = new InsertIntoContainerStrategy(containerRef, index); |
|||
expect(strategy.getIndex()).toBe(expected); |
|||
}, |
|||
); |
|||
}); |
|||
|
|||
describe('#prepare', () => { |
|||
it('should not call clear method of containerRef', () => { |
|||
const strategy = new InsertIntoContainerStrategy(containerRef, 0); |
|||
strategy.prepare(); |
|||
expect(containerRef.clear).not.toHaveBeenCalled(); |
|||
}); |
|||
}); |
|||
}); |
|||
|
|||
describe('CONTAINER_STRATEGY', () => { |
|||
const containerRef = ({ |
|||
clear: jest.fn(), |
|||
length: 7, |
|||
} as any) as ViewContainerRef; |
|||
|
|||
test.each` |
|||
name | Strategy | index |
|||
${'Clear'} | ${ClearContainerStrategy} | ${undefined} |
|||
${'Append'} | ${InsertIntoContainerStrategy} | ${containerRef.length} |
|||
${'Prepend'} | ${InsertIntoContainerStrategy} | ${0} |
|||
${'Insert'} | ${InsertIntoContainerStrategy} | ${4} |
|||
`('should successfully map $name to $Strategy.name', ({ name, Strategy, index }) => {
|
|||
expect(CONTAINER_STRATEGY[name](containerRef, index)).toEqual( |
|||
new Strategy(containerRef, index), |
|||
); |
|||
}); |
|||
}); |
|||
@ -0,0 +1,38 @@ |
|||
import { Component, ComponentRef, NgModule } from '@angular/core'; |
|||
import { createServiceFactory, SpectatorService } from '@ngneat/spectator'; |
|||
import { ContentProjectionService } from '../services'; |
|||
import { PROJECTION_STRATEGY } from '../strategies'; |
|||
|
|||
describe('ContentProjectionService', () => { |
|||
@Component({ template: '<div class="foo">bar</div>' }) |
|||
class TestComponent {} |
|||
|
|||
// createServiceFactory does not accept entryComponents directly
|
|||
@NgModule({ |
|||
declarations: [TestComponent], |
|||
entryComponents: [TestComponent], |
|||
}) |
|||
class TestModule {} |
|||
|
|||
let componentRef: ComponentRef<TestComponent>; |
|||
let spectator: SpectatorService<ContentProjectionService>; |
|||
const createService = createServiceFactory({ |
|||
service: ContentProjectionService, |
|||
imports: [TestModule], |
|||
}); |
|||
|
|||
beforeEach(() => (spectator = createService())); |
|||
|
|||
afterEach(() => componentRef.destroy()); |
|||
|
|||
describe('#projectContent', () => { |
|||
it('should call injectContent of given projectionStrategy and return what it returns', () => { |
|||
const strategy = PROJECTION_STRATEGY.AppendComponentToBody(TestComponent); |
|||
componentRef = spectator.service.projectContent(strategy); |
|||
const foo = document.querySelector('body > ng-component > div.foo'); |
|||
|
|||
expect(componentRef).toBeInstanceOf(ComponentRef); |
|||
expect(foo.textContent).toBe('bar'); |
|||
}); |
|||
}); |
|||
}); |
|||
@ -0,0 +1,79 @@ |
|||
import { ComponentRef } from '@angular/core'; |
|||
import { |
|||
ComponentContextStrategy, |
|||
CONTEXT_STRATEGY, |
|||
NoContextStrategy, |
|||
TemplateContextStrategy, |
|||
} from '../strategies'; |
|||
import { uuid } from '../utils'; |
|||
|
|||
describe('ComponentContextStrategy', () => { |
|||
describe('#setContext', () => { |
|||
let componentRef: ComponentRef<any>; |
|||
|
|||
beforeEach( |
|||
() => |
|||
(componentRef = { |
|||
instance: { |
|||
x: '', |
|||
y: '', |
|||
z: '', |
|||
}, |
|||
changeDetectorRef: { |
|||
detectChanges: jest.fn(), |
|||
}, |
|||
} as any), |
|||
); |
|||
|
|||
test.each` |
|||
props | values |
|||
${['x']} | ${[uuid()]} |
|||
${['x', 'y']} | ${[uuid(), uuid()]} |
|||
${['x', 'y', 'z']} | ${[uuid(), uuid(), uuid()]} |
|||
`(
|
|||
'should set $props as $values and call detectChanges once', |
|||
({ props, values }: { props: string[]; values: string[] }) => { |
|||
const context = {}; |
|||
props.forEach((prop, i) => { |
|||
context[prop] = values[i]; |
|||
}); |
|||
|
|||
const strategy = new ComponentContextStrategy(context); |
|||
strategy.setContext(componentRef); |
|||
|
|||
expect(props.every(prop => componentRef.instance[prop] === context[prop])).toBe(true); |
|||
expect(componentRef.changeDetectorRef.detectChanges).toHaveBeenCalledTimes(1); |
|||
}, |
|||
); |
|||
}); |
|||
}); |
|||
|
|||
describe('NoContextStrategy', () => { |
|||
describe('#setContext', () => { |
|||
it('should return undefined', () => { |
|||
const strategy = new NoContextStrategy(); |
|||
expect(strategy.setContext(null)).toBeUndefined(); |
|||
}); |
|||
}); |
|||
}); |
|||
|
|||
describe('TemplateContextStrategy', () => { |
|||
describe('#setContext', () => { |
|||
it('should return context', () => { |
|||
const context = { x: uuid() }; |
|||
const strategy = new TemplateContextStrategy(context); |
|||
expect(strategy.setContext()).toEqual(context); |
|||
}); |
|||
}); |
|||
}); |
|||
|
|||
describe('CONTEXT_STRATEGY', () => { |
|||
test.each` |
|||
name | Strategy |
|||
${'Component'} | ${ComponentContextStrategy} |
|||
${'None'} | ${NoContextStrategy} |
|||
${'Template'} | ${TemplateContextStrategy} |
|||
`('should successfully map $name to $Strategy.name', ({ name, Strategy }) => {
|
|||
expect(CONTEXT_STRATEGY[name](undefined)).toEqual(new Strategy(undefined)); |
|||
}); |
|||
}); |
|||
@ -0,0 +1,276 @@ |
|||
import { |
|||
Component, |
|||
ComponentRef, |
|||
EmbeddedViewRef, |
|||
TemplateRef, |
|||
ViewChild, |
|||
ViewContainerRef, |
|||
} from '@angular/core'; |
|||
import { createComponentFactory, Spectator } from '@ngneat/spectator/jest'; |
|||
import { |
|||
ComponentProjectionStrategy, |
|||
ContainerStrategy, |
|||
CONTAINER_STRATEGY, |
|||
CONTEXT_STRATEGY, |
|||
DOM_STRATEGY, |
|||
PROJECTION_STRATEGY, |
|||
RootComponentProjectionStrategy, |
|||
TemplateProjectionStrategy, |
|||
} from '../strategies'; |
|||
|
|||
describe('ComponentProjectionStrategy', () => { |
|||
@Component({ |
|||
template: '<div class="foo">{{ bar || baz }}</div>', |
|||
}) |
|||
class TestComponent { |
|||
bar: string; |
|||
baz = 'baz'; |
|||
} |
|||
|
|||
@Component({ |
|||
template: '<ng-container #container></ng-container>', |
|||
}) |
|||
class HostComponent { |
|||
@ViewChild('container', { static: true, read: ViewContainerRef }) |
|||
containerRef: ViewContainerRef; |
|||
} |
|||
|
|||
let containerStrategy: ContainerStrategy; |
|||
let spectator: Spectator<HostComponent>; |
|||
let componentRef: ComponentRef<TestComponent>; |
|||
|
|||
const createComponent = createComponentFactory({ |
|||
component: HostComponent, |
|||
entryComponents: [TestComponent], |
|||
}); |
|||
|
|||
beforeEach(() => { |
|||
spectator = createComponent({}); |
|||
containerStrategy = CONTAINER_STRATEGY.Clear(spectator.component.containerRef); |
|||
}); |
|||
|
|||
afterEach(() => { |
|||
componentRef.destroy(); |
|||
spectator.detectChanges(); |
|||
}); |
|||
|
|||
describe('#injectContent', () => { |
|||
it('should should insert content into container and return a ComponentRef', () => { |
|||
const strategy = new ComponentProjectionStrategy(TestComponent, containerStrategy); |
|||
componentRef = strategy.injectContent(spectator); |
|||
spectator.detectChanges(); |
|||
|
|||
const div = spectator.query('div.foo'); |
|||
expect(div.textContent).toBe('baz'); |
|||
expect(componentRef).toBeInstanceOf(ComponentRef); |
|||
}); |
|||
|
|||
it('should be able to map context to projected component', () => { |
|||
const contextStrategy = CONTEXT_STRATEGY.Component({ bar: 'bar' }); |
|||
const strategy = new ComponentProjectionStrategy( |
|||
TestComponent, |
|||
containerStrategy, |
|||
contextStrategy, |
|||
); |
|||
componentRef = strategy.injectContent(spectator); |
|||
spectator.detectChanges(); |
|||
|
|||
const div = spectator.query('div.foo'); |
|||
expect(div.textContent).toBe('bar'); |
|||
expect(componentRef.instance.bar).toBe('bar'); |
|||
}); |
|||
}); |
|||
}); |
|||
|
|||
describe('RootComponentProjectionStrategy', () => { |
|||
@Component({ |
|||
template: '<div class="foo">{{ bar || baz }}</div>', |
|||
}) |
|||
class TestComponent { |
|||
bar: string; |
|||
baz = 'baz'; |
|||
} |
|||
|
|||
@Component({ template: '' }) |
|||
class HostComponent {} |
|||
|
|||
let spectator: Spectator<HostComponent>; |
|||
let componentRef: ComponentRef<TestComponent>; |
|||
|
|||
const createComponent = createComponentFactory({ |
|||
component: HostComponent, |
|||
entryComponents: [TestComponent], |
|||
}); |
|||
|
|||
beforeEach(() => { |
|||
spectator = createComponent({}); |
|||
}); |
|||
|
|||
afterEach(() => { |
|||
componentRef.destroy(); |
|||
spectator.detectChanges(); |
|||
}); |
|||
|
|||
describe('#injectContent', () => { |
|||
it('should should insert content into body and return a ComponentRef', () => { |
|||
const strategy = new RootComponentProjectionStrategy(TestComponent); |
|||
componentRef = strategy.injectContent(spectator); |
|||
spectator.detectChanges(); |
|||
|
|||
const div = document.querySelector('body > ng-component > div.foo'); |
|||
expect(div.textContent).toBe('baz'); |
|||
expect(componentRef).toBeInstanceOf(ComponentRef); |
|||
componentRef.destroy(); |
|||
spectator.detectChanges(); |
|||
}); |
|||
|
|||
it('should be able to map context to projected component', () => { |
|||
const contextStrategy = CONTEXT_STRATEGY.Component({ bar: 'bar' }); |
|||
const strategy = new RootComponentProjectionStrategy(TestComponent, contextStrategy); |
|||
componentRef = strategy.injectContent(spectator); |
|||
spectator.detectChanges(); |
|||
|
|||
const div = document.querySelector('body > ng-component > div.foo'); |
|||
expect(div.textContent).toBe('bar'); |
|||
expect(componentRef.instance.bar).toBe('bar'); |
|||
}); |
|||
}); |
|||
}); |
|||
|
|||
describe('TemplateProjectionStrategy', () => { |
|||
@Component({ |
|||
template: ` |
|||
<ng-template #template let-bar> |
|||
<div class="foo">{{ bar || baz }}</div> |
|||
</ng-template> |
|||
<ng-container #container></ng-container> |
|||
`,
|
|||
}) |
|||
class HostComponent { |
|||
@ViewChild('container', { static: true, read: ViewContainerRef }) |
|||
containerRef: ViewContainerRef; |
|||
|
|||
@ViewChild('template', { static: true }) |
|||
templateRef: TemplateRef<{ $implicit?: string }>; |
|||
|
|||
baz = 'baz'; |
|||
} |
|||
|
|||
let containerStrategy: ContainerStrategy; |
|||
let spectator: Spectator<HostComponent>; |
|||
let embeddedViewRef: EmbeddedViewRef<{ $implicit?: string }>; |
|||
|
|||
const createComponent = createComponentFactory({ |
|||
component: HostComponent, |
|||
}); |
|||
|
|||
beforeEach(() => { |
|||
spectator = createComponent({}); |
|||
containerStrategy = CONTAINER_STRATEGY.Clear(spectator.component.containerRef); |
|||
}); |
|||
|
|||
afterEach(() => { |
|||
embeddedViewRef.destroy(); |
|||
spectator.detectChanges(); |
|||
}); |
|||
|
|||
describe('#injectContent', () => { |
|||
it('should should insert content into container and return an EmbeddedViewRef', () => { |
|||
const templateRef = spectator.component.templateRef; |
|||
const strategy = new TemplateProjectionStrategy(templateRef, containerStrategy); |
|||
embeddedViewRef = strategy.injectContent(); |
|||
spectator.detectChanges(); |
|||
|
|||
const div = spectator.query('div.foo'); |
|||
expect(div.textContent).toBe('baz'); |
|||
expect(embeddedViewRef).toHaveProperty('detectChanges'); |
|||
expect(embeddedViewRef).toHaveProperty('markForCheck'); |
|||
expect(embeddedViewRef).toHaveProperty('detach'); |
|||
expect(embeddedViewRef).toHaveProperty('reattach'); |
|||
expect(embeddedViewRef).toHaveProperty('destroy'); |
|||
expect(embeddedViewRef).toHaveProperty('rootNodes'); |
|||
expect(embeddedViewRef).toHaveProperty('context'); |
|||
}); |
|||
|
|||
it('should be able to map context to projected template', () => { |
|||
const templateRef = spectator.component.templateRef; |
|||
const contextStrategy = CONTEXT_STRATEGY.Template<typeof templateRef>({ $implicit: 'bar' }); |
|||
const strategy = new TemplateProjectionStrategy( |
|||
templateRef, |
|||
containerStrategy, |
|||
contextStrategy, |
|||
); |
|||
embeddedViewRef = strategy.injectContent(); |
|||
spectator.detectChanges(); |
|||
|
|||
const div = spectator.query('div.foo'); |
|||
expect(div.textContent).toBe('bar'); |
|||
expect(embeddedViewRef.context).toEqual(contextStrategy.context); |
|||
}); |
|||
}); |
|||
}); |
|||
|
|||
describe('PROJECTION_STRATEGY', () => { |
|||
const content = undefined; |
|||
const containerRef = ({ length: 0 } as any) as ViewContainerRef; |
|||
let context: any; |
|||
|
|||
test.each` |
|||
name | Strategy | containerStrategy |
|||
${'AppendComponentToContainer'} | ${ComponentProjectionStrategy} | ${CONTAINER_STRATEGY.Append} |
|||
${'AppendTemplateToContainer'} | ${TemplateProjectionStrategy} | ${CONTAINER_STRATEGY.Append} |
|||
${'PrependComponentToContainer'} | ${ComponentProjectionStrategy} | ${CONTAINER_STRATEGY.Prepend} |
|||
${'PrependTemplateToContainer'} | ${TemplateProjectionStrategy} | ${CONTAINER_STRATEGY.Prepend} |
|||
${'ProjectComponentToContainer'} | ${ComponentProjectionStrategy} | ${CONTAINER_STRATEGY.Clear} |
|||
${'ProjectTemplateToContainer'} | ${TemplateProjectionStrategy} | ${CONTAINER_STRATEGY.Clear} |
|||
`(
|
|||
'should successfully map $name to $Strategy.name with $containerStrategy.name container strategy and $contextStrategy.name context strategy', |
|||
({ name, Strategy, containerStrategy }) => { |
|||
expect(PROJECTION_STRATEGY[name](content, containerRef, context)).toEqual( |
|||
new Strategy(content, containerStrategy(containerRef), CONTEXT_STRATEGY.None()), |
|||
); |
|||
}, |
|||
); |
|||
test.each` |
|||
name | Strategy | domStrategy |
|||
${'AppendComponentToBody'} | ${RootComponentProjectionStrategy} | ${DOM_STRATEGY.AppendToBody} |
|||
`(
|
|||
'should successfully map $name to $Strategy.name with $domStrategy.name dom strategy', |
|||
({ name, Strategy, domStrategy }) => { |
|||
expect(PROJECTION_STRATEGY[name](content, context)).toEqual( |
|||
new Strategy(content, CONTEXT_STRATEGY.None(), domStrategy()), |
|||
); |
|||
}, |
|||
); |
|||
|
|||
test.each` |
|||
name | Strategy | containerStrategy | contextStrategy |
|||
${'AppendComponentToContainer'} | ${ComponentProjectionStrategy} | ${CONTAINER_STRATEGY.Append} | ${CONTEXT_STRATEGY.Component} |
|||
${'AppendTemplateToContainer'} | ${TemplateProjectionStrategy} | ${CONTAINER_STRATEGY.Append} | ${CONTEXT_STRATEGY.Template} |
|||
${'PrependComponentToContainer'} | ${ComponentProjectionStrategy} | ${CONTAINER_STRATEGY.Prepend} | ${CONTEXT_STRATEGY.Component} |
|||
${'PrependTemplateToContainer'} | ${TemplateProjectionStrategy} | ${CONTAINER_STRATEGY.Prepend} | ${CONTEXT_STRATEGY.Template} |
|||
${'ProjectComponentToContainer'} | ${ComponentProjectionStrategy} | ${CONTAINER_STRATEGY.Clear} | ${CONTEXT_STRATEGY.Component} |
|||
${'ProjectTemplateToContainer'} | ${TemplateProjectionStrategy} | ${CONTAINER_STRATEGY.Clear} | ${CONTEXT_STRATEGY.Template} |
|||
`(
|
|||
'should successfully map $name to $Strategy.name with $containerStrategy.name container strategy and $contextStrategy.name context strategy', |
|||
({ name, Strategy, containerStrategy, contextStrategy }) => { |
|||
context = { x: true }; |
|||
expect(PROJECTION_STRATEGY[name](content, containerRef, context)).toEqual( |
|||
new Strategy(content, containerStrategy(containerRef), contextStrategy(context)), |
|||
); |
|||
}, |
|||
); |
|||
|
|||
test.each` |
|||
name | Strategy | contextStrategy | domStrategy |
|||
${'AppendComponentToBody'} | ${RootComponentProjectionStrategy} | ${CONTEXT_STRATEGY.Component} | ${DOM_STRATEGY.AppendToBody} |
|||
`(
|
|||
'should successfully map $name to $Strategy.name with $contextStrategy.name context strategy and $domStrategy.name dom strategy', |
|||
({ name, Strategy, domStrategy, contextStrategy }) => { |
|||
context = { x: true }; |
|||
expect(PROJECTION_STRATEGY[name](content, context)).toEqual( |
|||
new Strategy(content, contextStrategy(context), domStrategy()), |
|||
); |
|||
}, |
|||
); |
|||
}); |
|||
@ -0,0 +1,3 @@ |
|||
export const enum eFeatureManagementComponents { |
|||
FeatureManagement = 'FeatureManagement.FeatureManagementComponent', |
|||
} |
|||
@ -1,2 +1,3 @@ |
|||
export * from './lib/feature-management.module'; |
|||
export * from './lib/components'; |
|||
export * from './lib/enums/components'; |
|||
|
|||
@ -0,0 +1,4 @@ |
|||
export const enum eIdentityComponents { |
|||
Roles = 'Identity.RolesComponent', |
|||
Users = 'Identity.UsersComponent', |
|||
} |
|||
@ -0,0 +1,3 @@ |
|||
export const enum ePermissionManagementComponents { |
|||
PermissionManagement = 'PermissionManagement.PermissionManagementComponent', |
|||
} |
|||
@ -0,0 +1,3 @@ |
|||
export const enum eSettingManagementComponents { |
|||
SettingManagement = 'SettingManagement.SettingManagementComponent', |
|||
} |
|||
@ -1,2 +1,3 @@ |
|||
export * from './lib/setting-management.module'; |
|||
export * from './lib/components/setting-management.component'; |
|||
export * from './lib/enums/components'; |
|||
|
|||
@ -0,0 +1,3 @@ |
|||
export const enum eTenantManagementComponents { |
|||
Tenants = 'TenantManagement.TenantsComponent', |
|||
} |
|||
@ -1,6 +1,7 @@ |
|||
export * from './lib/tenant-management.module'; |
|||
export * from './lib/actions'; |
|||
export * from './lib/components'; |
|||
export * from './lib/enums/components'; |
|||
export * from './lib/models'; |
|||
export * from './lib/services'; |
|||
export * from './lib/states'; |
|||
|
|||
@ -0,0 +1,5 @@ |
|||
export const enum eThemeBasicComponents { |
|||
ApplicationLayout = 'Theme.ApplicationLayoutComponent', |
|||
AccountLayout = 'Theme.AccountLayoutComponent', |
|||
EmptyLayout = 'Theme.EmptyLayoutComponent', |
|||
} |
|||
@ -1,12 +1,12 @@ |
|||
{ |
|||
"version": "2.4.1", |
|||
"version": "2.5.0", |
|||
"name": "@abp/anchor-js", |
|||
"publishConfig": { |
|||
"access": "public" |
|||
}, |
|||
"dependencies": { |
|||
"@abp/core": "^2.4.1", |
|||
"@abp/core": "^2.5.0", |
|||
"anchor-js": "^4.2.2" |
|||
}, |
|||
"gitHead": "0c72682d406026760da2f832a493145beae3a5c0" |
|||
"gitHead": "a9a36b8a4681df04377497addf94b15e6c521664" |
|||
} |
|||
|
|||
@ -1,11 +1,11 @@ |
|||
{ |
|||
"version": "2.4.1", |
|||
"version": "2.5.0", |
|||
"name": "@abp/aspnetcore.mvc.ui.theme.basic", |
|||
"publishConfig": { |
|||
"access": "public" |
|||
}, |
|||
"dependencies": { |
|||
"@abp/aspnetcore.mvc.ui.theme.shared": "^2.4.1" |
|||
"@abp/aspnetcore.mvc.ui.theme.shared": "^2.5.0" |
|||
}, |
|||
"gitHead": "0c72682d406026760da2f832a493145beae3a5c0" |
|||
"gitHead": "a9a36b8a4681df04377497addf94b15e6c521664" |
|||
} |
|||
|
|||
@ -1,24 +1,24 @@ |
|||
{ |
|||
"version": "2.4.1", |
|||
"version": "2.5.0", |
|||
"name": "@abp/aspnetcore.mvc.ui.theme.shared", |
|||
"publishConfig": { |
|||
"access": "public" |
|||
}, |
|||
"dependencies": { |
|||
"@abp/aspnetcore.mvc.ui": "^2.4.1", |
|||
"@abp/bootstrap": "^2.4.1", |
|||
"@abp/bootstrap-datepicker": "^2.4.1", |
|||
"@abp/datatables.net-bs4": "^2.4.1", |
|||
"@abp/font-awesome": "^2.4.1", |
|||
"@abp/jquery-form": "^2.4.1", |
|||
"@abp/jquery-validation-unobtrusive": "^2.4.1", |
|||
"@abp/lodash": "^2.4.1", |
|||
"@abp/luxon": "^2.4.1", |
|||
"@abp/malihu-custom-scrollbar-plugin": "^2.4.1", |
|||
"@abp/select2": "^2.4.1", |
|||
"@abp/sweetalert": "^2.4.1", |
|||
"@abp/timeago": "^2.4.1", |
|||
"@abp/toastr": "^2.4.1" |
|||
"@abp/aspnetcore.mvc.ui": "^2.5.0", |
|||
"@abp/bootstrap": "^2.5.0", |
|||
"@abp/bootstrap-datepicker": "^2.5.0", |
|||
"@abp/datatables.net-bs4": "^2.5.0", |
|||
"@abp/font-awesome": "^2.5.0", |
|||
"@abp/jquery-form": "^2.5.0", |
|||
"@abp/jquery-validation-unobtrusive": "^2.5.0", |
|||
"@abp/lodash": "^2.5.0", |
|||
"@abp/luxon": "^2.5.0", |
|||
"@abp/malihu-custom-scrollbar-plugin": "^2.5.0", |
|||
"@abp/select2": "^2.5.0", |
|||
"@abp/sweetalert": "^2.5.0", |
|||
"@abp/timeago": "^2.5.0", |
|||
"@abp/toastr": "^2.5.0" |
|||
}, |
|||
"gitHead": "0c72682d406026760da2f832a493145beae3a5c0" |
|||
"gitHead": "a9a36b8a4681df04377497addf94b15e6c521664" |
|||
} |
|||
|
|||
@ -1,13 +1,13 @@ |
|||
{ |
|||
"version": "2.4.1", |
|||
"version": "2.5.0", |
|||
"name": "@abp/blogging", |
|||
"publishConfig": { |
|||
"access": "public" |
|||
}, |
|||
"dependencies": { |
|||
"@abp/aspnetcore.mvc.ui.theme.shared": "^2.4.1", |
|||
"@abp/owl.carousel": "^2.4.1", |
|||
"@abp/tui-editor": "^2.4.1" |
|||
"@abp/aspnetcore.mvc.ui.theme.shared": "^2.5.0", |
|||
"@abp/owl.carousel": "^2.5.0", |
|||
"@abp/tui-editor": "^2.5.0" |
|||
}, |
|||
"gitHead": "0c72682d406026760da2f832a493145beae3a5c0" |
|||
"gitHead": "a9a36b8a4681df04377497addf94b15e6c521664" |
|||
} |
|||
|
|||
@ -1,12 +1,12 @@ |
|||
{ |
|||
"version": "2.4.1", |
|||
"version": "2.5.0", |
|||
"name": "@abp/bootstrap", |
|||
"publishConfig": { |
|||
"access": "public" |
|||
}, |
|||
"dependencies": { |
|||
"@abp/core": "^2.4.1", |
|||
"@abp/core": "^2.5.0", |
|||
"bootstrap": "^4.3.1" |
|||
}, |
|||
"gitHead": "0c72682d406026760da2f832a493145beae3a5c0" |
|||
"gitHead": "a9a36b8a4681df04377497addf94b15e6c521664" |
|||
} |
|||
|
|||
@ -1,12 +1,12 @@ |
|||
{ |
|||
"version": "2.4.1", |
|||
"version": "2.5.0", |
|||
"name": "@abp/clipboard", |
|||
"publishConfig": { |
|||
"access": "public" |
|||
}, |
|||
"dependencies": { |
|||
"@abp/core": "^2.4.1", |
|||
"@abp/core": "^2.5.0", |
|||
"clipboard": "^2.0.4" |
|||
}, |
|||
"gitHead": "0c72682d406026760da2f832a493145beae3a5c0" |
|||
"gitHead": "a9a36b8a4681df04377497addf94b15e6c521664" |
|||
} |
|||
|
|||
@ -1,12 +1,12 @@ |
|||
{ |
|||
"version": "2.4.1", |
|||
"version": "2.5.0", |
|||
"name": "@abp/codemirror", |
|||
"publishConfig": { |
|||
"access": "public" |
|||
}, |
|||
"dependencies": { |
|||
"@abp/core": "^2.4.1", |
|||
"@abp/core": "^2.5.0", |
|||
"codemirror": "^5.49.2" |
|||
}, |
|||
"gitHead": "0c72682d406026760da2f832a493145beae3a5c0" |
|||
"gitHead": "a9a36b8a4681df04377497addf94b15e6c521664" |
|||
} |
|||
|
|||
@ -1,8 +1,8 @@ |
|||
{ |
|||
"version": "2.4.1", |
|||
"version": "2.5.0", |
|||
"name": "@abp/core", |
|||
"publishConfig": { |
|||
"access": "public" |
|||
}, |
|||
"gitHead": "0c72682d406026760da2f832a493145beae3a5c0" |
|||
"gitHead": "a9a36b8a4681df04377497addf94b15e6c521664" |
|||
} |
|||
|
|||
@ -1,12 +1,12 @@ |
|||
{ |
|||
"version": "2.4.1", |
|||
"version": "2.5.0", |
|||
"name": "@abp/datatables.net-bs4", |
|||
"publishConfig": { |
|||
"access": "public" |
|||
}, |
|||
"dependencies": { |
|||
"@abp/datatables.net": "^2.4.1", |
|||
"@abp/datatables.net": "^2.5.0", |
|||
"datatables.net-bs4": "^1.10.20" |
|||
}, |
|||
"gitHead": "0c72682d406026760da2f832a493145beae3a5c0" |
|||
"gitHead": "a9a36b8a4681df04377497addf94b15e6c521664" |
|||
} |
|||
|
|||
@ -1,12 +1,12 @@ |
|||
{ |
|||
"version": "2.4.1", |
|||
"version": "2.5.0", |
|||
"name": "@abp/datatables.net", |
|||
"publishConfig": { |
|||
"access": "public" |
|||
}, |
|||
"dependencies": { |
|||
"@abp/core": "^2.4.1", |
|||
"@abp/core": "^2.5.0", |
|||
"datatables.net": "^1.10.20" |
|||
}, |
|||
"gitHead": "0c72682d406026760da2f832a493145beae3a5c0" |
|||
"gitHead": "a9a36b8a4681df04377497addf94b15e6c521664" |
|||
} |
|||
|
|||
@ -1,15 +1,15 @@ |
|||
{ |
|||
"version": "2.4.1", |
|||
"version": "2.5.0", |
|||
"name": "@abp/docs", |
|||
"publishConfig": { |
|||
"access": "public" |
|||
}, |
|||
"dependencies": { |
|||
"@abp/anchor-js": "^2.4.1", |
|||
"@abp/clipboard": "^2.4.1", |
|||
"@abp/malihu-custom-scrollbar-plugin": "^2.4.1", |
|||
"@abp/popper.js": "^2.4.1", |
|||
"@abp/prismjs": "^2.4.1" |
|||
"@abp/anchor-js": "^2.5.0", |
|||
"@abp/clipboard": "^2.5.0", |
|||
"@abp/malihu-custom-scrollbar-plugin": "^2.5.0", |
|||
"@abp/popper.js": "^2.5.0", |
|||
"@abp/prismjs": "^2.5.0" |
|||
}, |
|||
"gitHead": "0c72682d406026760da2f832a493145beae3a5c0" |
|||
"gitHead": "a9a36b8a4681df04377497addf94b15e6c521664" |
|||
} |
|||
|
|||
@ -1,12 +1,12 @@ |
|||
{ |
|||
"version": "2.4.1", |
|||
"version": "2.5.0", |
|||
"name": "@abp/font-awesome", |
|||
"publishConfig": { |
|||
"access": "public" |
|||
}, |
|||
"dependencies": { |
|||
"@abp/core": "^2.4.1", |
|||
"@abp/core": "^2.5.0", |
|||
"@fortawesome/fontawesome-free": "^5.11.2" |
|||
}, |
|||
"gitHead": "0c72682d406026760da2f832a493145beae3a5c0" |
|||
"gitHead": "a9a36b8a4681df04377497addf94b15e6c521664" |
|||
} |
|||
|
|||
@ -1,11 +1,11 @@ |
|||
{ |
|||
"version": "2.4.1", |
|||
"version": "2.5.0", |
|||
"name": "@abp/highlight.js", |
|||
"publishConfig": { |
|||
"access": "public" |
|||
}, |
|||
"dependencies": { |
|||
"@abp/core": "^2.4.1" |
|||
"@abp/core": "^2.5.0" |
|||
}, |
|||
"gitHead": "0c72682d406026760da2f832a493145beae3a5c0" |
|||
"gitHead": "a9a36b8a4681df04377497addf94b15e6c521664" |
|||
} |
|||
|
|||
@ -1,12 +1,12 @@ |
|||
{ |
|||
"version": "2.4.1", |
|||
"version": "2.5.0", |
|||
"name": "@abp/jquery-form", |
|||
"publishConfig": { |
|||
"access": "public" |
|||
}, |
|||
"dependencies": { |
|||
"@abp/jquery": "^2.4.1", |
|||
"@abp/jquery": "^2.5.0", |
|||
"jquery-form": "^4.2.2" |
|||
}, |
|||
"gitHead": "0c72682d406026760da2f832a493145beae3a5c0" |
|||
"gitHead": "a9a36b8a4681df04377497addf94b15e6c521664" |
|||
} |
|||
|
|||
@ -1,12 +1,12 @@ |
|||
{ |
|||
"version": "2.4.1", |
|||
"version": "2.5.0", |
|||
"name": "@abp/jquery-validation-unobtrusive", |
|||
"publishConfig": { |
|||
"access": "public" |
|||
}, |
|||
"dependencies": { |
|||
"@abp/jquery-validation": "^2.4.1", |
|||
"@abp/jquery-validation": "^2.5.0", |
|||
"jquery-validation-unobtrusive": "^3.2.11" |
|||
}, |
|||
"gitHead": "0c72682d406026760da2f832a493145beae3a5c0" |
|||
"gitHead": "a9a36b8a4681df04377497addf94b15e6c521664" |
|||
} |
|||
|
|||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue