-
-
Notifications
You must be signed in to change notification settings - Fork 150
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
afterAttach: HTMLElement (document.getElementById) is null #841
Comments
Strange... Am I the only one having this issue? |
Is your component using shadow dom? If so, none of its view's elements will be visible by querying the document. If you need a reference to an element within your view, the best way is to use a Let me know if this helps. If not, I'll need some more info since I may not be understanding the scenario exactly. |
Hi Rob, No my component is not using shadow dom (as fas as I know, how could I check this to rule this out?). I'll try your Tx 4 the reply! |
I'd say your workaround already ruled that out, because that wouldn't work either if shadow dom was the issue.
That should not be possible. Could you show the rest of the view where that element is located in the html, as well as the view model code where the charting lib is initialized? Another thing you can try is inject |
Hi @fkleuver , The contents of my view (g2.html): <div class="g2">
<div id="g2chart"></div>
</div> My entire code (g2.ts) import { inject } from "aurelia";
import { CovidServiceBackend } from '../../services/backend/covid-service';
import { Chart, registerAnimation } from '@antv/g2';
import * as interfaces from "./interfaces";
import * as utils from "./utils";
import * as animations from "./animations";
@inject(CovidServiceBackend)
export class G2 {
chart: Chart;
data: interfaces.IDataset;
timeline;
index: number = 0;
constructor(private covid: CovidServiceBackend) {
registerAnimation('label-appear', animations.labelAppear);
registerAnimation('label-update', animations.labelUpdate);
}
createChart(index: number) {
const animationDuration = 600;
this.chart = new Chart({
container: 'g2chart',
autoFit: true,
height: 800,
padding: [0, 60, 0, 150], // t r b l
theme: ''
});
this.chart.data(utils.handleData(Object.values(this.data)[index]));
this.chart.coordinate('rect').transpose();
this.chart.legend(false);
this.chart.tooltip(false);
this.chart.axis('country', {
animateOption: { update: { duration: animationDuration, easing: 'easeLinear' } },
label: { style: { fontWeight: 'bold' } }
});
this.chart.annotation().text({
position: ['100%', '90%'],
content: Object.keys(this.data)[index],
style: {
fontSize: 40,
fontWeight: 'bold',
fill: '#aaa',
textAlign: 'end'
},
animate: false,
});
this.chart
.interval()
.position('country*value')
.color('country')
.label('value', (value) => {
return {
animate: {
appear: {
animation: 'label-appear',
delay: 0,
duration: animationDuration,
easing: 'easeLinear'
},
update: {
animation: 'label-update',
duration: animationDuration,
easing: 'easeLinear'
}
},
offset: 5,
style: { fontSize: 12, }
};
})
.animate({
appear: {
duration: animationDuration,
easing: 'easeLinear'
},
update: {
duration: animationDuration,
easing: 'easeLinear'
}
});
this.chart.render();
}
updateChart(index: number) {
if (!this.chart) return this.createChart(index);
this.chart.annotation().clear(true);
this.chart.annotation().text({
position: ['100%', '90%'],
content: Object.keys(this.data)[index],
style: {
fontSize: 40,
fontWeight: 'bold',
fill: '#aaa',
textAlign: 'end'
},
animate: false,
});
this.chart.changeData(utils.handleData(Object.values(this.data)[index]));
}
async afterAttach() {
let element = document.getElementById('g2chart');
while (element === null) {
await utils.sleep(100);
element = document.getElementById('g2chart');
}
this.timeline = await this.covid.get("timeline");
console.log("source", this.timeline);
this.data = utils.transformTimeLine(this.timeline, 40).cases;
console.log("data", Object.keys(this.data));
let interval;
const countUp = () => {
this.updateChart(this.index)
++this.index;
if (this.index === Object.keys(this.data).length) {
clearInterval(interval);
}
}
countUp();
interval = setInterval(countUp, 1500);
}
} |
That confirms what I was suspecting, namely that for some reason your component is not yet attached to the document itself. Without a repro, I can't tell you why that is or how to fix it. That aside, in general I would recommend against directly querying the document itself. Keep things local to your component, by using The injected export class MyVM {
constructor(private el: Element) {}
afterAttach() {
const g2chart = this.el.getElementById('g2chart');
}
} So those would be 2 ways to address this issue, although it would be preferable to get to the bottom of why the component root is not attached to the document. Could you perhaps provide a minimal repro on github? |
Here ya go!
Server will be @PORT 3000 I already found "something"
So.....this may indicate that this issue is router related? |
Thanks for the repro + instructions, that made it super easy to figure out what's wrong :) So I already eluded to this on Discord, but for completeness sake (and future reference), here's the technical side of what happens: During user-invoked navigation
During direct navigation (/ refresh)
In summary, The question is whether we want for example I'll let you know when it's fixed. In the meantime a simple single macroTask should be plenty of time to lift your async afterAttach() {
await new Promise(resolve => setTimeout(resolve));
console.log(this.sander);
let element = document.getElementById('g2chart');
} FYI @jwx (this is part of that larger wireup picture I was talking about) |
Thank you @fkleuver for the perfect explanation. As there is a (dirty) workaround available this isn't blocking at all, so no worries. Looking forward to the next release! V2 is quite usable for me already anyway and would have no issues putting this "in production". |
This is fixed |
🐛 Bug Report
In my afterAttach function Im trying to get an HTMLElement(ById). It is empty so it seems the afterAttach function is fired before the viewModel is (fully) created.
🤔 Expected Behavior
I would expect that if I execute:
as my first statement in the afterAttach function that element is not null
😯 Current Behavior
element is null
💁 Possible Solution
Add following to to beginning of my afterAttach function (which obviously is a very dirty hack)
In my case it's executed once. I could as well wrap my code in a setTimeout function probably
💻 Code Sample
HTML File:
🌍 Your Environment
The text was updated successfully, but these errors were encountered: