1

I have create a component that gets an array with table datas. Another component draws the tables. Then I get the innerHTML and put it into a tinyMCE.

This works really well in ng serve, but when I create a ng build --prod, the tables are empty and no data is available.

ngOnInit() {
    this.data = JSON.parse(JSON.stringify(this.data));

    this.tabledata = this.data.protocol;
    this.tinymceModel = "";


        setTimeout(() => {
            let root = document.querySelectorAll('[name="tabledatas"]')

            Object.values(root).forEach(elem => {
                let result = elem.innerHTML;
                    result = result.replace(/<!--.*-->/g,'')
                    result = result.replace(/\t|\n/g,'')
                    result = result.replace(/<tfoot>.*<\/tfoot>/g,'')

                this.tinymceModel = this.tinymceModel + result;
            })
        })
...
}

The code looks like this. I believed that the setTimeout will wait until data are available, but it doesn't seem to work properly. Where is my mistake or how can I ensure to proceed when table is finally drawn?

Lars
  • 920
  • 1
  • 14
  • 34

2 Answers2

1

Instead of a direct answer to your problem, I will offer you some observations and respective advice:

Splitting your data and rendering across two components

You describe two components, one of them handling data retrieval, the other one handling your data rendering. Most likely, you're then passing data via Input() / Output() between the two.

Instead, consider using a service to handle your data interaction and offer this data to your components via dependency injection. Here's the official tutorial on services using the Tour of Heroes, and here is a great introduction to dependency injection in Angular.

Directly accessing the DOM / innerHTML property to pass the data to your template

There are only very few edge cases where accessing the DOM / the innerHTML property of your elements should be considered the first choice. Instead, take a look at the TinyMCE Angular Component, it is very likely a lot easier to apply for your case than directly accessing the DOM.

Using setTimeout() without a milliseconds argument

This will use setTimeout with a time value of 0. There are a couple of use-cases for 'letting the renderer catch-up' or using it to wait for the next micro-task to be completed in nodeJS. Here is a detailed answer on when and how this may be necessary.

ng serve vs ng build --prod

I doubt the reason for your problem lies in serve vs build, it is more than likely that using ng serve --prod will have the same impact on your program. Check out this video on the differences between development and production builds.

Jens Habegger
  • 5,266
  • 41
  • 57
0

We should not use document.querySelectorAll in ngOnInit. We should use @ViewChildren in ngAfterViewInit instead. Somehow like the following code.

import { ViewChildren, ElementRef, QueryList } from '@angular/core';


export class YourComponent {

    @ViewChildren('tabledatasMark') tabledatas: QueryList<ElementRef>;

    ...

    ngAfterViewInit () {
        this.tabledatas.forEach((elem: ElementRef) => {
            elem.nativeElement.innerHTML = 'HTML code you need';
        });
    }

}

Please, notice, that your tabledatas elements should be marked with '#tabledatasMark', i.e.

<div #tabledatas name="tabledatas">
... 
<div #tabledatas name="tabledatas">

P.S. Let me notice, that using InnerHTML is not a good idea.

  • I will try this tomorrow. But my first impression after reading your answer that I was a bit blind :-) Currently, I'm shifting from AngularJS. There it was a good solution. But your absolutely right: why not use the ViewChildren... – Lars Feb 16 '19 at 16:59