11

I'm trying to encapsulate my element ids by adding the instance id like this:

<label for="id-{{ unique }}-name"></label>
<input id="id-{{ unique }}-name" type="text" formControlName="name">

I previously worked with this: https://stackoverflow.com/a/40140762/12858538. But after upgrading Angular from 7 to 9 this seems deprecated. I was thinking about a simple helper service, which would generate unique ids for my app.

Something like this:

@Injectable()
export class UniqueIdService {
  private counter = 0

  constructor() {}

  public getUniqueId (prefix: string) {
    const id = ++this.counter
    return prefix + id
  }
}

inspired by lodash uniqueid

But I did rather use angulars build in ids. So my currently solution is to extract the id from the components _nghost attribute.

constructor ( private element: ElementRef, ) {
   const ngHost = 
     Object.values(this.element.nativeElement.attributes as NamedNodeMap)
     .find(attr => attr.name.startsWith('_nghost'))
     .name
   this.unique = ngHost.substr(ngHost.lastIndexOf('-') + 1)
}

But I'm not perfectly happy with this solution and I'm looking for a direct access to the id.

Does anyone know how to access this?

zZeepo
  • 336
  • 1
  • 3
  • 9

3 Answers3

9

The unique ID in Angular 9 can be represented as:

_nghost   -   par    -     2
   |           |           |
 static      APP_ID   componentDef.id

In order to access componentDef.id you can do the following:

export class SomeComponent implements OnInit {
  unique = this.constructor['ɵcmp'].id;

where ɵcmp is a private variable NG_COMP_DEF

Ng-run Example

yurzui
  • 205,937
  • 32
  • 433
  • 399
  • 6
    Since this is private, shouldn't we avoid using it ... otherwise it should have been part of some of the public APIs, right? – Georgi Mar 31 '21 at 16:32
  • 5
    This only works if there's a single instance of the component. Basically, it is the component ID, and not the component instance ID so it won't be unique when multiple components of the same type are rendered. – Canica Dec 07 '21 at 14:53
  • 1
    @Canica - I think that all instances of a component share the same encapsulation ID. At least, that is what I see when I have multiple instances in my Angular page. – ConnorsFan Aug 29 '22 at 15:40
  • 1
    Using your example you can copy and paste the child component and they all have the same id. This isn't unique nor safe. – Kody Sep 08 '22 at 18:09
3

You can use crypto.randomUUID() which is provided by Crypto API in browsers. Support is great.

https://developer.mozilla.org/en-US/docs/Web/API/Crypto/randomUUID

https://caniuse.com/mdn-api_crypto_randomuuid

  • While this link may answer the question, it is better to include the essential parts of the answer here and provide the link for reference. Link-only answers can become invalid if the linked page changes. - [From Review](/review/late-answers/34166977) – Nol4635 Apr 09 '23 at 16:51
  • It's not link only. The function returns unique id which solves the problem. – Petr Pavlov Apr 11 '23 at 07:43
1

Neither _nghost nor ɵcmp are unique across component instances. They should not be used for that purpose.

Your UniqueIdService is the easiest and the safest solution. (The counter property could be static to be sure that every instance of the service returns unique ID).

LukeSw
  • 661
  • 3
  • 13