Reputation: 95
I need to use introjs to tour new features on an angular 6 app.
Inside the tour some step target items in ngFor in a child component of the component where I start introjs. The library is able to target child component html elements but seems to not be able to target element inside ngFor. The usage I want is simple I have a li that represent a card item I loop on as :
<div *ngFor="let source of sourcesDisplay; let idx = index" class="col-lg-5ths col-md-4 col-sm-6 col-xs-12">
<!-- item -->
<div class="item blue-light">
<div class="header">
<div class="categories">
<div class="truncat">{{source.categories.length > 0? source.categories : 'NO CATEGORY'}}</div>
</div>
<a routerLink="{{ organization?.name | routes: 'sources' : source.id }}" class="title">
<div class="truncat">{{source.name}}</div>
</a>
<div class="corner-ribbon blue-light" *ngIf="isARecentSource(source.createdAt)">New</div>
<div class="corner-fav" [class.is-active]="isSourceFavorite(source.id)"
(click)="toggleFavorite(source.id)"><em class="icon-heart-o"></em></div>
</div>
<a class="content" routerLink="{{ organization?.name | routes: 'sources' : source.id }}">
<div class="icon">
<em [ngClass]="source.icon? 'icon-'+source.icon : 'icon-cocktail3'"></em>
</div>
</a>
<div class="actions">
<ul class="buttons three">
<li class="icon-font" ><a (click)="openDashboardModal(source)"
[class.disable]="source.powerbi.length === 0" class="btn-effect"><em
class="icon-chart-bar"></em></a></li>
<li class="icon-font"><a
(click)="openDatalakeModal(source)" [class.disable]="source.datalakePath.length === 0"
class="btn-effect"><em class="icon-arrow-to-bottom"></em></a>
</li>
<li class="icon-font"><a routerLink="{{ organization?.name | routes: 'sources' : source.id }}"
class="btn-effect"><em class="icon-info-circle"></em></a></li>
</ul>
</div>
</div>
</div><!-- /item -->
And I want to target part of this card like a button for example as :
<li class="icon-font" id="step4{{idx}}">
<a (click)="openDashboardModal(source)" [class.disable]="source.powerbi.length === 0" class="btn-effect">
<em class="icon-chart-bar"></em>
</a>
</li>
and then in my component :
const intro = IntroJs.introJs();
intro.setOptions({
steps: [
{
element: document.querySelector('#step40'),
intro: 'Welcome to the Data Portal ! Explore and download data accross any division. Let\'s discover the new home page.'
}]})
intro.start();
Is there something I am doing wrong ? is introjs not able to do it at all ? is there another lib that can do it?
Thank you in advance
Upvotes: 1
Views: 828
Reputation: 73761
The sections generated by the *ngFor
loop may not be rendered yet in the AfterViewInit
event. To detect the presence of these elements, you should use @ViewChildren
and monitor the QueryList.changes
event.
In the template, define a template reference variable #step
instead of id
:
<li class="icon-font" #step>
<a (click)="openDashboardModal(source)" [class.disable]="source.powerbi.length === 0" class="btn-effect">
<em class="icon-chart-bar"></em>
</a>
</li>
In the code, retrieve the elements with @ViewChildren
, and subscribe to the QueryList.changes
event. You may have to convert the QueryList
to an array to access an element with a specific index.
@ViewChildren("step") steps: QueryList<ElementRef>;
ngAfterViewInit() {
this.startIntroJs(); // Maybe already present
this.steps.changes.subscribe((list: QueryList<ElementRef>) => {
this.startIntroJs(); // Created later
});
}
private startIntroJs(): void {
if (this.steps.length > 40) {
const intro = IntroJs.introJs();
intro.setOptions({
steps: [
{
element: this.steps.toArray()[40].nativeElement,
intro: 'Welcome to the Data Portal...'
}
]
});
intro.start();
}
}
Upvotes: 3