1

I use Angular 4. I have a directive which is a button, inserted into the parent's template like this:

<button (emitPdfUrl)="getInvoicePdfUrl($event);" pdfButton [pdfEl]="pdf">
    <fa name="file-pdf-o"></fa> Download PDF
</button>

How can I run the method getPdf() from the directive when the parent has fully rendered? I need this because this function will then get the innerHtml from some other parts of her parent's template. What happens right now is, the directive's function (getPdf()) gets the half displayed template, ie. the template without the loaded variables of the parent, and thus, the innerHTML is not complete with loaded data inside, so the function takes the "empty" template. Thanks for any advice guys! :)

Vladimir Despotovic
  • 3,200
  • 2
  • 30
  • 58

1 Answers1

3

It is parent component's responsibility to provide a method that will be able to follow the desired execution order.

This can be achieved with a pair of RxJS subjects:

private componentInitSubject = new AsyncSubject().ignoreElements();
public pdfUrlSubject = new ReplaySubject(1);

constructor() {
  this.pdfUrlSubscription = this.componentInitSubject
  .concat(this.pdfUrlSubject)
  .subscribe(url => this.getInvoicePdfUrl(url));
});

ngOnAfterViewInit() {
  this.componentInitSubject.complete();
}

getInvoicePdfUrl(url) {
  // starts to receive urls only after completion of componentInitSubject
}

And values can be emitted to the subject instead of calling getInvoicePdfUrl directly:

<button (emitPdfUrl)="pdfUrlSubject.emit($event)" pdfButton [pdfEl]="pdf">

pdfUrlSubscription should be unsubscribed on component destroy.

Estus Flask
  • 206,104
  • 70
  • 425
  • 565
  • 1
    I believe the question asks regarding a Directive rather than a Component. And `ngAfterViewInit` is a component-only hook. (https://angular.io/guide/lifecycle-hooks) – Muhammad Ahsan Ayaz Jan 19 '18 at 18:58
  • 1
    @AhsanAyaz The question says *inserted into the parent's template*, this implies that the parent is a component. – Estus Flask Jan 19 '18 at 19:00
  • Can you elaborate on your code? I don't understand anything you wrote, because you didn't put my naming of the variables? Pleas advise? Thanks for the help thought! I appreaciate it, I am struggling ith this for hours and hours now. – Vladimir Despotovic Jan 19 '18 at 19:06
  • 1
    Hope this helps. – Estus Flask Jan 19 '18 at 19:08
  • What is ReplaySubject and how to import it? And AsyncObject? – Vladimir Despotovic Jan 19 '18 at 19:10
  • 1
    They are RxJS imports. Usually it is like `import { ReplaySubject } from 'rxjs/ReplaySubject'`. See https://stackoverflow.com/questions/42376972/best-way-to-import-observable-from-rxjs – Estus Flask Jan 19 '18 at 19:15
  • I could import ReplaySubject, but not AsyncSubject. Do you know how to import this second one? – Vladimir Despotovic Jan 19 '18 at 19:21
  • 1
    It should be `import { AsyncSubject } from 'rxjs/AsyncSubject'`, accordingly. – Estus Flask Jan 19 '18 at 19:26
  • I imported AsyncSubject, and I have an error now during the build: Property 'ignoreElements' does not exist on type 'AsyncSubject<{}>'. – Vladimir Despotovic Jan 19 '18 at 19:26
  • It is RxJS operator and should be imported as described in [this question](https://stackoverflow.com/questions/42376972/best-way-to-import-observable-from-rxjs). Like `import 'rxjs/add/operator/ignoreElements'`. This is essential Angular+RxJS knowledge that has been discussed on SO before, so there is nothing really specific there. – Estus Flask Jan 19 '18 at 19:31