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

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

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.

export const provideToken = (val: string) => {
  return {
    provide: SOME_TOKEN,
    useFactory: () => {
      const token = inject(SOME_TOKEN)
      return token ? [...token, val] : val
    }
  }
}

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:

@NgModule({
  declarations: [SharedDirective],
  imports: [],
  exports: [SharedDirective],
  providers: [provideToken("shared")]
})
export class SharedModule {}

@NgModule({
  declarations: [SomeComponent],
  imports: [SharedModule],
  exports: [],
  providers: [provideToken("some")]
})
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.

export const provideToken = (val: string) => {
  return {
    provide: SOME_TOKEN,
    useFactory: () => {
      const token = inject(SOME_TOKEN, {skipSelf: true, optional: true})
      return token ? [...token, val] : val
    },
    multi: true
  }
}
// constructor of the extended directive

constructor(
    ...,
    @Optional()
    @Inject(SOME_TOKEN)
    providedToken: MaybeArray<string>,
    ...
) {

   if (Array.isArray(providedToken)) {
            providedToken = [...new Set(providedToken.flat(Infinity))]
   }
   super(..., providedToken, ...)
}
英文:

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.

export const provideToken = (val: string) =&gt; {
  return {
    provide: SOME_TOKEN,
    useFactory: () =&gt; {
      const token = inject(SOME_TOKEN)
      return token ? [...token, val] : val
    }
  }
}

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:

@NgModule({
  declarations: [SharedDirective],
  imports: [],
  exports: [SharedDirective],
  providers: [provideToken(&quot;shared&quot;)]
})
export class SharedModule {}


@NgModule({
  declarations: [SomeComponent],
  imports: [SharedModule],
  exports: [],
  providers: [provideToken(&quot;some&quot;)]
})
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.

export const provideToken = (val: string) =&gt; {
  return {
    provide: SOME_TOKEN,
    useFactory: () =&gt; {
      const token = inject(SOME_TOKEN, {skipSelf: true, optional: true})
      return token ? [...token, val] : val
    },
    multi: true
  }
}
// constructor of the extended directive

constructor(
    ...,
    @Optional()
    @Inject(SOME_TOKEN)
    providedToken: MaybeArray&lt;string&gt;,
    ...
) {

   if (Array.isArray(providedToken)) {
            providedToken = [...new Set(providedToken.flat(Infinity))]
   }
   super(..., providedToken, ...)
}

答案1

得分: 1

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

export const provideToken = (val: string) => {
  return {
    provide: SOME_TOKEN,
    useFactory: () => {
      const token = inject(SOME_TOKEN, { skipSelf: true })
      return token ? [token, val] : val
    }
  }
}
英文:

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

export const provideToken = (val: string) =&gt; {
  return {
    provide: SOME_TOKEN,
    useFactory: () =&gt; {
      const token = inject(SOME_TOKEN, { skipSelf: true })
      return token ? [token, val] : val
    }
  }
}

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:

确定