Angular 15: 在作用域中提供相同令牌时附加到提供的值

huangapple go评论90阅读模式
英文:

Angular 15: Appending to provided values when same token is provided in scope

问题

For example, parent scope provides the string "parent" with SOME_TOKEN, then when the child provides the same token (lets say with "child"), it appends to a list of values provided by that same token, making it ["child", "parent"]. I want this because I need to work with a library code that I can not change.

I tried the following function to provide the string but it throws Circular dependency error.

  1. export const provideToken = (val: string) => {
  2. return {
  3. provide: SOME_TOKEN,
  4. useFactory: () => {
  5. const token = inject(SOME_TOKEN)
  6. return token ? [...token, val] : val
  7. }
  8. }
  9. }

Basically I want to get all the values that are provided, but angular only gives the most closest value in the default way.

I can achieve this with a service. Modules or components could append to the list in their constructor and remove the string in their ngOnDestroy, but that seems error prone.

Update

@Anton's answer works but I need to be able to use this concept with shared modules as well. For example:

  1. @NgModule({
  2. declarations: [SharedDirective],
  3. imports: [],
  4. exports: [SharedDirective],
  5. providers: [provideToken("shared")]
  6. })
  7. export class SharedModule {}
  8. @NgModule({
  9. declarations: [SomeComponent],
  10. imports: [SharedModule],
  11. exports: [],
  12. providers: [provideToken("some")]
  13. })
  14. export class SomeModule {}

When skipSelf is used, only "some" token is provided, and the "shared" token is ignored.

Update 2

When multi: true is used in the provider, no token is ignored but the resulting array becomes a nested array, so the injecting service/directive or etc needs to be able to work with it. So, I extended the directive from the library to flatten the array and remove duplicates in its constructor, solving my issue.

  1. export const provideToken = (val: string) => {
  2. return {
  3. provide: SOME_TOKEN,
  4. useFactory: () => {
  5. const token = inject(SOME_TOKEN, {skipSelf: true, optional: true})
  6. return token ? [...token, val] : val
  7. },
  8. multi: true
  9. }
  10. }
  1. // constructor of the extended directive
  2. constructor(
  3. ...,
  4. @Optional()
  5. @Inject(SOME_TOKEN)
  6. providedToken: MaybeArray<string>,
  7. ...
  8. ) {
  9. if (Array.isArray(providedToken)) {
  10. providedToken = [...new Set(providedToken.flat(Infinity))]
  11. }
  12. super(..., providedToken, ...)
  13. }
英文:

For example, parent scope provides the string &quot;parent&quot; with SOME_TOKEN, then when the child provides the same token (lets say with &quot;child&quot;), it appends to a list of values provided by that same token, making it [&quot;child&quot;, &quot;parent&quot;]. I want this because I need to work with a library code that I can not change.

I tried the following function to provide the string but it throws Circular dependency error.

  1. export const provideToken = (val: string) =&gt; {
  2. return {
  3. provide: SOME_TOKEN,
  4. useFactory: () =&gt; {
  5. const token = inject(SOME_TOKEN)
  6. return token ? [...token, val] : val
  7. }
  8. }
  9. }

Basically I want to get all the values that are provided, but angular only gives the most closest value in the default way.

I can achieve this with a service. Modules or components could append to the list in their constructor and remove the string in their ngOnDestroy, but that seems error prone.

Update

@Anton's answer works but I need to be able to use this concept with shared modules as well. For example:

  1. @NgModule({
  2. declarations: [SharedDirective],
  3. imports: [],
  4. exports: [SharedDirective],
  5. providers: [provideToken(&quot;shared&quot;)]
  6. })
  7. export class SharedModule {}
  8. @NgModule({
  9. declarations: [SomeComponent],
  10. imports: [SharedModule],
  11. exports: [],
  12. providers: [provideToken(&quot;some&quot;)]
  13. })
  14. export class SomeModule {}

When skipSelf is used, only &quot;some&quot; token is provided, and the &quot;shared&quot; token is ignored.

Update 2

When multi: true is used in the provider, no token is ignored but the resulting array becomes a nested array, so the injecting service/directive or etc needs to be able to work with it. So, I extended the directive from the library to flatten the array and remove duplicates in its constructor, solving my issue.

  1. export const provideToken = (val: string) =&gt; {
  2. return {
  3. provide: SOME_TOKEN,
  4. useFactory: () =&gt; {
  5. const token = inject(SOME_TOKEN, {skipSelf: true, optional: true})
  6. return token ? [...token, val] : val
  7. },
  8. multi: true
  9. }
  10. }
  1. // constructor of the extended directive
  2. constructor(
  3. ...,
  4. @Optional()
  5. @Inject(SOME_TOKEN)
  6. providedToken: MaybeArray&lt;string&gt;,
  7. ...
  8. ) {
  9. if (Array.isArray(providedToken)) {
  10. providedToken = [...new Set(providedToken.flat(Infinity))]
  11. }
  12. super(..., providedToken, ...)
  13. }

答案1

得分: 1

你可以尝试使用选项 skipSelf 注入令牌。它有效:

  1. export const provideToken = (val: string) => {
  2. return {
  3. provide: SOME_TOKEN,
  4. useFactory: () => {
  5. const token = inject(SOME_TOKEN, { skipSelf: true })
  6. return token ? [token, val] : val
  7. }
  8. }
  9. }
英文:

You can try to inject token with option skipSelf. It works:

  1. export const provideToken = (val: string) =&gt; {
  2. return {
  3. provide: SOME_TOKEN,
  4. useFactory: () =&gt; {
  5. const token = inject(SOME_TOKEN, { skipSelf: true })
  6. return token ? [token, val] : val
  7. }
  8. }
  9. }

huangapple
  • 本文由 发表于 2023年2月9日 03:12:55
  • 转载请务必保留本文链接:https://go.coder-hub.com/75390655.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定