angularionic-frameworkvega-litevega-embed

How to use vega-embed in Ionic/Angular component?


I'm pretty sure it's my fault, but I can't spot it.

I created a new Ionic/Angular app with ionic start oracolo and then I added a new component to it (named Histogram, though that does not really matter). Then I installed vega-embed with npm install vega-embed and tried to add a Vega/Vega-Lite view into my component using this code:

 async ngOnInit() {
    const spec = "https://raw.githubusercontent.com/vega/vega/master/docs/examples/bar-chart.vg.json";
    const result = await embed("figure#vis", spec);
    console.log(result.view);
  }

paired with its HTML template:

<div>
  <figure id="vis"></figure>
</div>

I think what I have done here is just follow the example code on the vega-embed project page, however when I run my app no Vega view appears and I get the following error in my browser console:

Uncaught (in promise): Error: figure#vis does not exist
Error: figure#vis does not exist
    at _callee4$ (vega-embed.module.js:2960:17)
    at tryCatch (vega-embed.module.js:123:15)
    at Generator.invoke [as _invoke] (vega-embed.module.js:319:20)
    at prototype.<computed> [as next] (vega-embed.module.js:172:19)
    at asyncGeneratorStep (vega-embed.module.js:54:24)
    at _next (vega-embed.module.js:73:9)
    at vega-embed.module.js:78:7
    at new ZoneAwarePromise (zone.js:1429:21)
    at vega-embed.module.js:70:12
    at _embed3 (vega-embed.module.js:3227:18)
    at resolvePromise (zone.js:1211:31)
    at zone.js:1118:17
    at zone.js:1134:33
    at rejected (tslib.es6.js:119:89)
    at _ZoneDelegate.invoke (zone.js:372:26)
    at Object.onInvoke (core.mjs:24313:33)
    at _ZoneDelegate.invoke (zone.js:371:52)
    at Zone.run (zone.js:134:43)
    at zone.js:1275:36
    at _ZoneDelegate.invokeTask (zone.js:406:31)

I'm not sure if the app being a Ionic app could make any difference from the vega-embed point of view, so I brought it to you as is. What am I doing wrong?

EDITED after E.Maggini's answer:

I've tried moving the code to ngAfterViewInit, where the DOM is supposedly available, but I'm getting the same error:

export class HistogramComponent implements OnInit, AfterViewInit {

  constructor() { }

  ngOnInit() { }

  async ngAfterViewInit() {
    const spec = "https://raw.githubusercontent.com/vega/vega/master/docs/examples/bar-chart.vg.json";
    const result = await embed("figure#vis", spec);
    console.log(result.view);
  }
}

EDIT 2:

Adding the Vega view to the pre-existing ExploreContainerComponent that was autogenerated by the ionic start command DOES work. Adding the view to the HistogramComponent I generated afterwards does NOT work.

However I can't see any differences between the two components, except their name.


Solution

  • In the end the problem was totally unrelated to vega-embed, but was being caused by a mistake in how I was including my custom component into the tab, and this code wasn't shown in my question:

    tab3.page.html, before I found the mistake:

    <app-explore-container name="Tab 3 page">
      <histogram></histogram>
    </app-explore-container>
    

    tab3.page.html, after the fix:

    <app-explore-container name="Tab 3 page">
    </app-explore-container>
    
    <histogram></histogram>
    

    Problem here is that app-explore-container is a custom component that does not support projected content (it lacks ng-content in its template), so nesting anything into it while in an external template (tab3.page.html) won't work.

    The same goes for any custom component I created so far in my life, but for some weird reason I instinctively nested my histogram into app-explore-container taking for granted it would have behaved just like any other HTML tag.

    With reference to E.Maggini's answer, I even tried moving the code back into ngOnInit and it does work, so while this was actually a lifecycle issue as suggeted, because projected content issues are such, and while it's likely true that the DOM isn't available during the OnInit phase, I guess that does not make any difference, because of the async nature of the embed function, which is not executing its code during ngOnInit, but as soon as it can.