George Edwards
George Edwards

Reputation: 9229

Referencing individual generated components from ngFor

I have an angular2 component which I have included below. I generate a list of chapters which I then display with an *ngFor= tag, but I want to be able to individually target these in my ng2 component (so I can highlight the selected chapter). I would of thought the below code would generate something like this:

<p class="chapter 1" #1>1. My First Chapter</p>

However, I don't get the #1, hence my selector doesn't work and I can't by default set the first chapter in the list to be selected.

import { Component, ViewChild, ElementRef, AfterViewInit } from '@angular/core';

@Component({
  selector: 'tutorial',
  template: `
<div class="content row">
    <div class="chapters col s3">
        <h3>Chapters:</h3>
        <p *ngFor="let chapter of chapters" class="chapter" #{{chapter.number}}>{{chapter.number}}. {{chapter.title}}</p>
    </div>
</div>
`
})
export class bookComponent implements AfterViewInit {
    public chapters = _chapters;
    @ViewChild('2') el:ElementRef;

    ngAfterViewInit() {
      this.el.nativeElement.className += " clicked";
    }
 }

What should I do to be able to individually select my generated <p> tags?

Upvotes: 1

Views: 150

Answers (3)

G&#252;nter Z&#246;chbauer
G&#252;nter Z&#246;chbauer

Reputation: 657666

For you use case this might be a more angulary way

<p *ngFor="let chapter of chapters; let i=index" (click)="clickedItem = i" [class.clicked]="i == clickedItem" class="chapter" #{{chapter.number}}>{{chapter.number}}. {{chapter.title}}</p>
export class bookComponent implements AfterViewInit {
  public chapters = _chapters;
  clickedItem: number;
}

Updating the model and binding the view to make Angular reflect the model to the view is the preferred way instead of imperatively modifying the DOM.

Upvotes: 1

micronyks
micronyks

Reputation: 55443

You can use directive with HostListener to select an element as shown below.

Working Demo : http://plnkr.co/edit/mtmCKg7kPgZoerqT0UIO?p=preview

import {Directive, Attribute,ElementRef,Renderer,Input,HostListener} from '@angular/core';

@Directive({
  selector: '[myAttr]'
})

export class myDir {
    constructor(private el:ElementRef,private rd: Renderer){
      console.log('fired');
      console.log(el.nativeElement); 
    }

    @HostListener('click', ['$event.target'])
    onClick(btn) {
      if(this.el.nativeElement.className=="selected"){
          this.el.nativeElement.className ="";  
      }else{
          this.el.nativeElement.className ="selected";
      }
   }
}

//our root app component
import {Component} from '@angular/core';

@Component({
  selector: 'my-app',
  directives:[myDir],
  template: 
  `
  <style>
    .selected{
      color:red;
      background:yellow;
    }
  </style>
    <div class="content row">
    <div class="chapters col s3">
        <h3>Chapters:</h3>
        <p myAttr *ngFor="let chapter of chapters" class="chapter" #{{chapter.number}}>{{chapter.number}}. {{chapter.title}}</p>
    </div>
</div>
  `
})
export class App {
  chapters=[{number:1,title:"chapter1"},{number:2,title:"chapter2"},{number:3,title:"chapter3"}]


}

Upvotes: 0

Mark Rajcok
Mark Rajcok

Reputation: 364707

I would let the NgFor loop control adding or removing the clicked class:

<p *ngFor="let chapter of chapters" class="chapter"
  [class.clicked]="chapter.number === selectedChapterNumber">
  {{chapter.number}}. {{chapter.title}}
</p>

Then just set selectedChapterNumber appropriately in your component logic.

export class bookComponent {
    public chapters = _chapters;
    private selectedChapterNumber = 1;
}

Upvotes: 0

Related Questions