1

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, ...)
}
ertucode
  • 560
  • 2
  • 13

1 Answers1

1

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

export const provideToken = (val: string) => {
  return {
    provide: SOME_TOKEN,
    useFactory: () => {
      const token = inject(SOME_TOKEN, { skipSelf: true })
      return token ? [token, val] : val
    }
  }
}
Anton Marinenko
  • 2,749
  • 1
  • 10
  • 16
  • and even this way: `inject(SOME_TOKEN, { skipSelf: true, optional: true })` – Anton Marinenko Feb 08 '23 at 19:47
  • This answers my question, but apparently I needed to not `skipSelf` because when there is a shared module that provides the scope, and some module that uses that shared module also provides scope I want them to push to the array as well. But apparently angular considers them equal. Please check out my updated question – ertucode Feb 09 '23 at 08:52
  • 1
    Check it here https://stackblitz.com/edit/angular-6qq3gk?file=src/main.ts – Anton Marinenko Feb 09 '23 at 09:17
  • It stops working when there is more than one shared module. https://stackblitz.com/edit/angular-et6fc2?file=src/main.ts – ertucode Feb 09 '23 at 10:09
  • I've accepted your answer since although it doesn't solve my problem, it answers the first question – ertucode Feb 09 '23 at 13:01