Repository navigation
feat(core): introduce EnvironmentInjector.runInContext API
#46653
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
EnvironmentInjector.runInContext API
This commit introduces a new API on `EnvironmentInjector` to run a function
in the context of the injector. This makes `inject()` available within the
body of the function to inject dependencies. We expect this functionality to
be very useful for designing ergonomic APIs both in and out of Angular, as
it should allow for a smaller and more functional API style.
Note that it's possible to implement nearly identical functionality in user
code already today:
```typescript
function runInContext<T>(injector: EnvironmentInjector, fn: () => T): T {
const token = new InjectionToken<T>('TOKEN');
const tmpInjector = createEnvironmentInjector([
{provide: token, useFactory: () => fn()},
], injector);
return tmpInjector.get(token);
}
```
The factory provider for `token` in this example runs in the context of the
`tmpInjector`, giving it access to `inject` that can retrieve values from
`injector` as well. This is nearly identical, although because of the child
injector `self` and `skipSelf` injection options don't function correctly.- Loading branch information
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -89,6 +89,18 @@ export abstract class EnvironmentInjector implements Injector { | |
| */ | ||
| abstract get(token: any, notFoundValue?: any): any; | ||
|
|
||
| /** | ||
| * Runs the given function in the context of this `EnvironmentInjector`. | ||
| * | ||
| * Within the function's stack frame, `inject` can be used to inject dependencies from this | ||
| * injector. Note that `inject` is only usable synchronously, and cannot be used in any | ||
| * asynchronous callbacks or after any `await` points. | ||
| * | ||
| * @param fn the closure to be run in the context of this injector | ||
| * @returns the return value of the function, if any | ||
| */ | ||
| abstract runInContext<ReturnT>(fn: () => ReturnT): ReturnT; | ||
|
|
||
| abstract destroy(): void; | ||
|
|
||
| /** | ||
|
|
@@ -180,6 +192,19 @@ export class R3Injector extends EnvironmentInjector { | |
| this._onDestroyHooks.push(callback); | ||
| } | ||
|
|
||
| override runInContext<ReturnT>(fn: () => ReturnT): ReturnT { | ||
| this.assertNotDestroyed(); | ||
|
|
||
| const previousInjector = setCurrentInjector(this); | ||
| const previousInjectImplementation = setInjectImplementation(undefined); | ||
| try { | ||
| return fn(); | ||
| } finally { | ||
| setCurrentInjector(previousInjector); | ||
| setInjectImplementation(previousInjectImplementation); | ||
| } | ||
| } | ||
|
|
||
| override get<T>( | ||
| token: ProviderToken<T>, notFoundValue: any = THROW_IF_NOT_FOUND, | ||
| flags = InjectFlags.Default): T { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hahaha :) I don't think we want the additional stack frame, but that'd be clever. |
||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not that it matters but only T or T as a prefix like TReturn/TRet/TRes is more canonical