Skip to content

Commit

Permalink
Release 15.1.0 - DI unchained & NgComponentOutlet Augmentation direct…
Browse files Browse the repository at this point in the history
…ive (#78)

* Add ng component outlet augmentation directive (#76)

* Added ngComponent outlet augmentation directive

* Updated ng component outlet directive and added tests

* Added docs to ngComponentOutlet augmentation directive

* ng component outlet augmentation directive - PR fix

* Feature/35-DI-unchained (#77)

* Added di unchained sub entry and its features

* Added and updated docs for Di unchained decorators

* Added documentation to DI unchained sub-package.

* version 15.1.0

---------

Co-authored-by: Krisztofer Kunc <106075716+krisztofer-kunc@users.noreply.github.com>
Co-authored-by: Krisztofer <krisztofer.kunc@adroitgroup.io>
  • Loading branch information
3 people committed Mar 31, 2023
1 parent 2b51ef0 commit a612bd0
Show file tree
Hide file tree
Showing 22 changed files with 867 additions and 3 deletions.
2 changes: 1 addition & 1 deletion .angulardoc.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{
"repoId": "df053afb-d37e-49ab-8371-559ec36ce6fe",
"lastSync": 0
}
}
124 changes: 124 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ All notable changes to this project are documented in [CHANGELOG.md](https://git
- [Directives](#directives)
- [NgLet Directive](#nglet-directive)
- [NgSubscribe Directive](#ngsubscribe-directive)
- [NgComponentOutlet Augmentation Directive](#ngcomponentoutlet-augmentation-directive)
- [NgFor Augmentation Directive](#ngfor-augmentation-directive)
- [NgIf Augmentation Directive](#ngif-augmentation-directive)
- [NgRenderIn Directive](#ngrenderin-directive)
Expand All @@ -51,6 +52,9 @@ All notable changes to this project are documented in [CHANGELOG.md](https://git
- [Subscription Handler](#subscription-handler)
- [Media Observer](#media-observer)
- [TrackBy Handler](#trackby-handler)
- [DI Unchained](#di-unchained)
- [`WithDiContext`](#withdicontext)
- [`DIContextProvider`](#dicontextprovider)
- [Development environment](#development-environment)

## Installation
Expand Down Expand Up @@ -280,6 +284,57 @@ The directive utilizes the Angular language service's capabilities and gives you

[⬆ Back to top](#table-of-contents)

### NgComponentOutlet Augmentation Directive

Augments the {@link NgComponentOutlet} directive to allow for binding of inputs and outputs.

In your lazy loaded component define the input and outputs you wish to bind to.

```ts
@Component({
selector: 'lazy-loaded-component',
template: `...`,
})
export class LazyLoadedComponent {
@Input() public input1: string;

@Output() public output1 = new EventEmitter<string>();
}
```

In the template where you wish to use the component, use the directive to bind the inputs and outputs.

```html
<ng-container
*ngComponentOutlet="lazyLoadedComp$ | async; inputs: { input1: 'test' }"
></ng-container>
```

If you want to bind to the lazy loaded components outputs' as well, then you have to use the non-micro-syntax version of the directive, as the micro-syntax version does not support binding to outputs.

```html
<ng-container
[ngComponentOutlet]="lazyLoadedComp$ | async"
[ngComponentOutletInputs]="{ input1: 'test' }"
[ngComponentOutletOutputs]="onComponentEvent($event)"
>
</ng-container>
```

Then in your component, you can handle the event like so:

```ts
export class MyComponentThatUsesLazyLoadedComponent {
public onComponentEvent(event: NgComponentOutletEvent): void {
if (event.key === 'output1') {
// do something with the received event data through event.event
}
}
}
```

[⬆ Back to top](#table-of-contents)

### NgFor Augmentation Directive

A utility directive that enhances Angular's `*ngFor` directive with extra functionality.
Expand Down Expand Up @@ -510,6 +565,75 @@ class MyComp extends TrackByHandlerMixin() {

[⬆ Back to top](#table-of-contents)

## DI Unchained

A DI library that allows you to use DI without the need to use the @Injectable decorator. This is useful when you want to use DI in a class that is not injectable (e.g. a mixin).

There are TS decorators exposed by this package:

- `WithDiContext`: A multi purpose decorator that allows you to use DI in any class without the need to use the @Injectable decorator.
- `DIContextProvider`: A class decorator that marks an Angular NgModule as the DI provider for `@Unchained` classes.

### `WithDiContext`

Decorator that frees your classes from the shackles of Angular DI.
Allowing them to fully utilize it without requiring Angular's class decorators: Component, Directive, Injectable, Pipe, Module.

The decorator can be used as a class, method, get/set accessor decorator.

```ts
@Unchained()
class MyClass {
router: Router;

routerFn = () => inject(Router);

static routerFn = () => inject(Router);

constructor() {
this.router = inject(Router);
}

getTitleService() {
const title = inject(Title);

console.log('title: ', title);
}

public get neta() {
return inject(Meta);
}

public static get appRef() {
return inject(ApplicationRef);
}

static getRouter() {
return inject(Router);
}
}
```

### `DIContextProvider`

An Angular ngModule class decorator the marks an Angular NgModule as the DI provider for `@Unchained` classes.

You have to use this decorator on your root module (AppModule) in order to use `@Unchained` decorator on non Angular classes.

The normal DI resolution rules apply to `Unchained` classes and their providers as well.
e.g.: Providers provided in their own lazy-loaded modules' providers array ARE NOT resolvable in other modules contexts.

```ts
@DI()
@NgModule({
declarations: [AppComponent],
bootstrap: [AppComponent],
})
export class AppModule {}
```

[⬆ Back to top](#table-of-contents)

## Development environment

Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files.
Expand Down
137 changes: 137 additions & 0 deletions libs/ng-utils/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ All notable changes to this project are documented in [CHANGELOG.md](https://git
- [Installation](#installation)
- [Usage](#usage)
- [Configuration](#configuration)
- [Package structure](#package-structure)
- [Decorators](#decorators)
- [Auto hooks](#auto-hooks)
- [Quick rundown of usage:](#quick-rundown-of-usage)
Expand All @@ -36,6 +37,7 @@ All notable changes to this project are documented in [CHANGELOG.md](https://git
- [Directives](#directives)
- [NgLet Directive](#nglet-directive)
- [NgSubscribe Directive](#ngsubscribe-directive)
- [NgComponentOutlet Augmentation Directive](#ngcomponentoutlet-augmentation-directive)
- [NgFor Augmentation Directive](#ngfor-augmentation-directive)
- [NgIf Augmentation Directive](#ngif-augmentation-directive)
- [NgRenderIn Directive](#ngrenderin-directive)
Expand All @@ -51,6 +53,9 @@ All notable changes to this project are documented in [CHANGELOG.md](https://git
- [Subscription Handler](#subscription-handler)
- [Media Observer](#media-observer)
- [TrackBy Handler](#trackby-handler)
- [DI Unchained](#di-unchained)
- [`WithDiContext`](#withdicontext)
- [`DIContextProvider`](#dicontextprovider)
- [Development environment](#development-environment)

## Installation
Expand Down Expand Up @@ -87,6 +92,18 @@ import { AdroitNgUtilsModule } from 'ng-utils';

[⬆ Back to top](#table-of-contents)

## Package structure

Ng utils is a monorepo that contains multiple packages. The main package that contains most of the useful stuff we've developed is `@adroit-group/ng-utils`. When you use the library, you should only import from this package. No need to specify any sub-package paths.

However there are some sub-packages that are useful on their own. These are:

- `@adroit-group/ng-utils/di-unchained` - A DI library that allows you to use DI without the need to use the `@Injectable` decorator. This is useful when you want to use DI in a class that is not injectable (e.g. a mixin).

When you want to use these make sure to import from the proper sub-package.

[⬆ Back to top](#table-of-contents)

## Decorators

### Auto hooks
Expand Down Expand Up @@ -280,6 +297,57 @@ The directive utilizes the Angular language service's capabilities and gives you

[⬆ Back to top](#table-of-contents)

### NgComponentOutlet Augmentation Directive

Augments the {@link NgComponentOutlet} directive to allow for binding of inputs and outputs.

In your lazy loaded component define the input and outputs you wish to bind to.

```ts
@Component({
selector: 'lazy-loaded-component',
template: `...`,
})
export class LazyLoadedComponent {
@Input() public input1: string;

@Output() public output1 = new EventEmitter<string>();
}
```

In the template where you wish to use the component, use the directive to bind the inputs and outputs.

```html
<ng-container
*ngComponentOutlet="lazyLoadedComp$ | async; inputs: { input1: 'test' }"
></ng-container>
```

If you want to bind to the lazy loaded components outputs' as well, then you have to use the non-micro-syntax version of the directive, as the micro-syntax version does not support binding to outputs.

```html
<ng-container
[ngComponentOutlet]="lazyLoadedComp$ | async"
[ngComponentOutletInputs]="{ input1: 'test' }"
[ngComponentOutletOutputs]="onComponentEvent($event)"
>
</ng-container>
```

Then in your component, you can handle the event like so:

```ts
export class MyComponentThatUsesLazyLoadedComponent {
public onComponentEvent(event: NgComponentOutletEvent): void {
if (event.key === 'output1') {
// do something with the received event data through event.event
}
}
}
```

[⬆ Back to top](#table-of-contents)

### NgFor Augmentation Directive

A utility directive that enhances Angular's `*ngFor` directive with extra functionality.
Expand Down Expand Up @@ -510,6 +578,75 @@ class MyComp extends TrackByHandlerMixin() {

[⬆ Back to top](#table-of-contents)

## DI Unchained

A DI library that allows you to use DI without the need to use the @Injectable decorator. This is useful when you want to use DI in a class that is not injectable (e.g. a mixin).

There are TS decorators exposed by this package:

- `WithDiContext`: A multi purpose decorator that allows you to use DI in any class without the need to use the @Injectable decorator.
- `DIContextProvider`: A class decorator that marks an Angular NgModule as the DI provider for `@Unchained` classes.

### `WithDiContext`

Decorator that frees your classes from the shackles of Angular DI.
Allowing them to fully utilize it without requiring Angular's class decorators: Component, Directive, Injectable, Pipe, Module.

The decorator can be used as a class, method, get/set accessor decorator.

```ts
@Unchained()
class MyClass {
router: Router;

routerFn = () => inject(Router);

static routerFn = () => inject(Router);

constructor() {
this.router = inject(Router);
}

getTitleService() {
const title = inject(Title);

console.log('title: ', title);
}

public get neta() {
return inject(Meta);
}

public static get appRef() {
return inject(ApplicationRef);
}

static getRouter() {
return inject(Router);
}
}
```

### `DIContextProvider`

An Angular ngModule class decorator the marks an Angular NgModule as the DI provider for `@Unchained` classes.

You have to use this decorator on your root module (AppModule) in order to use `@Unchained` decorator on non Angular classes.

The normal DI resolution rules apply to `Unchained` classes and their providers as well.
e.g.: Providers provided in their own lazy-loaded modules' providers array ARE NOT resolvable in other modules contexts.

```ts
@DI()
@NgModule({
declarations: [AppComponent],
bootstrap: [AppComponent],
})
export class AppModule {}
```

[⬆ Back to top](#table-of-contents)

## Development environment

Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files.
Expand Down
3 changes: 3 additions & 0 deletions libs/ng-utils/di-unchained/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# @adroit-group/ng-utils/di-unchained

Secondary entry point of `@adroit-group/ng-utils`. It can be used by importing from `@adroit-group/ng-utils/di-unchained`.
7 changes: 7 additions & 0 deletions libs/ng-utils/di-unchained/ng-package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"$schema": "../../../node_modules/ng-packagr/ng-package.schema.json",
"lib": {
"entryFile": "src/index.ts"
}
}

6 changes: 6 additions & 0 deletions libs/ng-utils/di-unchained/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export {
DI,
DIContextProvider,
Unchained,
WithDIContext,
} from './lib/decorators';
19 changes: 19 additions & 0 deletions libs/ng-utils/di-unchained/src/lib/bind-class.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { bindDescriptor } from "./bind-descriptor";

/**
* @internal
*/
export function bindCLass(protoOrCtor: object) {
const protoMembers = Object.entries(
Object.getOwnPropertyDescriptors(protoOrCtor)
).filter(
([member, descriptor]) =>
(typeof descriptor.value === 'function' && member !== 'constructor') ||
descriptor.get ||
descriptor.set
);

protoMembers.forEach(([member, descriptor]) =>
bindDescriptor(protoOrCtor, member, descriptor)
);
}

0 comments on commit a612bd0

Please sign in to comment.