3

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 .

15
  • i need different jquery selector for each component . Commented May 3, 2017 at 7:21
  • 3
    why not use a class, and why use jquery? Commented May 3, 2017 at 7:23
  • i am using jquery component in angular 2 application, so i am using jquery inside component Commented May 3, 2017 at 7:24
  • why not use a class? Commented May 3, 2017 at 7:26
  • 1
    no, components are components, directives are directives. But traversing the DOM to apply some transformations is a bad idea. How to deal with conditional rendering of some elements ? Will you traverse the dom on every change detection ? How to be sure the plugin you have applied on an element is really destroyed when the Angular renderer removes the element ? If you want to do things cleanly, you need to apply it using the template, not traversing the dynamically created DOM. Commented May 3, 2017 at 8:33

3 Answers 3

5

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>
Sign up to request clarification or add additional context in comments.

2 Comments

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.
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.
3

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>

1 Comment

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
2

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)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.