4

I have a set of data called reports which contain an array of networks (report.networks). I have model.ts to manipulate the networks array before i return it back. I do an ngFor to iterate over the network data to display the details which works fine. However, adding a matToolTips within the ngFor does not get displayed.

component.html

<span matTooltip="Tooltip Works">Tooltip Works</span>

<div *ngFor="let network of report.networks">
   <span matTooltip="Tooltip Does NOT work!">Tooltip Does NOT work</span>
</div>

component.ts

import { Report } from './../../core/models/report/report.model';

@Component({
    selector: 'app-report-detail',
    templateUrl: './report-detail.component.html',
    styleUrls: ['./report-detail.component.scss']
})
export class ReportDetailComponent implements OnInit {

    report: Report;

    constructor(private route: ActivatedRoute) { }

    ngOnInit() {

        this.report = this.route.snapshot.data.report;
        console.log(this.report);

    }

}

report.model.ts

export class Report {

    networks?: any = null;

    constructor(data?: Report) {
        if (data) {
            this.deserialize(data);
        }
    }

    private deserialize(data: Report) {

        const keys = Object.keys(this);

        for (const key of keys) {
            if (data.hasOwnProperty(key)) {
                this[key] = data[key];
            }
        }
    }

    get networks() {

        let n = this.networks;
        //some manipulation
        return n
    }
}
3
  • could you check your console for error about your ngFor cause it seems to work just fine here stackblitz.com/edit/angular-dsm5ks Commented Apr 3, 2018 at 15:13
  • @PierreMallet No errors in console BUT apparently report.networks is a function thats why its not working check github.com/angular/material2/issues/10039 But i dont know away around this because report is coming from my model.ts file which calls the array of data. Commented Apr 3, 2018 at 15:16
  • I do this quite often because i need to manipulate the data before returning it, i do all of this in model files. Commented Apr 3, 2018 at 15:26

2 Answers 2

5

As your networks getter return a new collection every time, the tooltips are always reclosed while ngFor recreate new elements in the DOM.

But you can workaround this if you have an identifier in the returned networks.

The ngFor* directive accepts a trackBy function that will be used to check the identity of an existing rendered item vs item that has to be displayed. If the value return by this function is the same for the existing item and an item to create, angular will keep the existing item, and thus will not destroy it.

here is an exemple in the guide, and the documentation of the internal mechanism and the TrackByFn interface

    in the template : 

    <div *ngFor="let network of networks(); trackBy: trackByFn">
       <span matTooltip="Tooltip Does NOT work!">Tooltip Does NOT work</span>
    </div>
    ...
    In the component: 

    networks () { let n = [{ name: 'toto', id: 1},{ id: 2 } , { id: 3 } ]; return n;  };
    trackByFn = (index: number, item: any) => item.id;

Here is the stackblitz implementing a simple trackByFunction, you can see the tooltips with the usage of trackBy

EDIT: if you dont have any id your can use the index of your array, ergo :

trackByFn = (index: number, item: any) => index;
Sign up to request clarification or add additional context in comments.

1 Comment

I dont have a specific id every time, however if its an array can i not just use the index?
4

As mentioned here https://github.com/angular/material2/issues/10039

The array is instance being created over and over again when using a function in a ngFor which causes multiple issues like matToolTip not displaying as-well as performance issues.

Another way i got around this is to change my model.ts file. So below im assigning a variable to the result of the function. Then i use this variable inside the ngFor isntead of the function directly.

export class Report {

    networks?: any = null;

    //custom

    customNetworks? : any = null;

    constructor(data?: Report) {
        if (data) {
            this.deserialize(data);

            this.customNetworks = this.generateNetworks()
        }

    }

    private deserialize(data: Report) {

        const keys = Object.keys(this);

        for (const key of keys) {
            if (data.hasOwnProperty(key)) {
                this[key] = data[key];
            }
        }
    }

    generateNetworks() {

        let n = this.networks;
        //some manipulation
        return n
    }
}

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.