Angular 2 unique id for every component instance of jQuery

15,331

Solution 1

Suppose you have a checkbox,

<input class="styled-checkbox" id="{{checkboxId}}" type="checkbox">
<label for="{{checkboxId}}">{{checkboxLabel}}</label>

import { Component, Input } from '@angular/core';
@Component({
    selector: 'checkbox',
    templateUrl: './checkbox.component.html'
})

export class CheckboxComponent {
    @Input() checkboxId:string;
    @Input() checkboxLabel:string;
}

in parent -->

<checkbox [checkboxId]="Math.random().toString(36).substring(2)"></checkbox>

Solution 2

Traversing the DOM: bad idea

You should not use jQuery to traverse the DOM and apply some transformations to it, here is why:

Jquery is tightly coupled with the DOM while angular tends to abstract all these interactions: when you modify the DOM in angular, you are supposed to use the Renderer API that will apply transformations on the elements. This is what allows rendering on Workers, building nativescript applications, etc.

Some problem may occur if you want to traverse the DOM from a parent component to apply modifications :

  • Some elements can be added to or removed from the DOM dynamically by the Angular renderer. How to deal with elements that are rendered conditionally ? traversing on every change detection is an anti-pattern.
  • How to be sure the plugin you applied on an element has been properly destroyed when the element has been removed by the angular renderer ?

Sometimes you do need jQuery

If you do need to use jQuery, for example to apply a jQuery plugin on an element, it is a better practice to write a directive or a component and apply the plugin by adding the directive or component inside your template than traversing the DOM from a component.

You can create a directive like this :

NB: I have no idea of the full calendar API, so it is full improvisation.

@Directive({ selector: '[fullCalendar]' })
export class HighlightDirective implements OnInit,OnDestroy,OnChanges {
    private jqElement:any;
    constructor(private el: ElementRef) {
       el.nativeElement.style.backgroundColor = 'yellow';
    }
    @Input()
    options:any={};

    ngOnInit(){
      this.jqElement = $(this.el.nativeElement);
      this.jqElement.fullCalendar(this.options);
    }

    ngOnChanges(changes:{[k:string]:SimpleChange}){
      if(changes['options']&&!changes['options'].firstChange) // do not apply options on first change (jquery plugin not applied)
        this.jqElement.fullCalendar("option",this.options);
    }

    ngOnDestroy(){
      this.jqElement.fullCalendar("destroy");
    }
}

and apply it this way:

<div fullCalendar [options]="someOptions"></div>

Solution 3

A simple and elegant solution (found it here)

let searchBoxId = 0;

@Component({
  selector: 'app-auto-complete',
  templateUrl: './auto-complete.component.html',
  styleUrls: ['./auto-complete.component.less']
})

export class MyComponent implements AfterViewInit {
  @Input() searchBoxId = `search-box-${searchBoxId++}`;

  ngAfterViewInit(): void {
    const searchBox = document.getElementById(`${this.searchBoxId}`);
    //   Get your input unique id
    console.log("searchBox: ")
  }
}
  • Set a global variable
  • Each time the component is being used (loaded) - we counter up the variable
  • Make sure you get the unique id from the DOM, only after it has been loaded (ngAfterViewInit)
Share:
15,331
Amr Ibrahim
Author by

Amr Ibrahim

Making a better world .

Updated on June 14, 2022

Comments

  • Amr Ibrahim
    Amr Ibrahim almost 2 years

    I have Angular 2 component contain jquery component and i want to generate id for every instance of my Angular 2 component because i need different jquery selector for each component

    this is my component

    @Component({
      selector: 'full-calendar',
      templateUrl: 'full-calendar.html'
    })
    export class FullCalendarComponent {
    
    ngOnChanges(changes: any) {
     
        $('angular2-fullcalendar').fullCalendar('option',changes.options.currentValue)
     
        }
      
    }

    i want to use more than one with different selector each time

    i found this solution

    export class GetId {
      protected getId(id: number|string = _.uniqueId()): string {
        return _.lowerFirst(this.constructor.name) + '_' + id;
      }
    }
    
    class MyComponent extends GetId {
      ...    
    }
    

    and id can be used like this

    <input [id]="getId('name')" type="text">

    but i am still searching for built-in solution .

  • Amr Ibrahim
    Amr Ibrahim about 7 years
    thank you for your explanation, i already implemented it this way . but question is not about this . question is about creating many component with different id. check the correct answer above
  • Celso Soares
    Celso Soares almost 5 years
    how can you guarantee that you will not get the same ID if this is a random id? Unless every time you generate a random id you check if this id already exist in the document. If yes, generate again... but dont think that this is the best practice.
  • Caltor
    Caltor over 4 years
    I used a slightly modified version of this to generate a checkboxId value in the typescript file that is then referenced as the id for the checkbox and for in the label.