Reputation: 527
I'm pretty new to angular, so I hope this question isn't too basic. I'm trying to run a function in angular, but I seem to have an issue with the scope of my variables. This is what it looks like:
export class Skulder14q1Component {
button1 = document.getElementById('button1');
button2 = document.getElementById('button2');
button3 = document.getElementById('button3');
button4 = document.getElementById('button4');
showallbutton = document.getElementById('showallbutton');
onShowAllOptions() {
this.button1.classList.toggle('hide');
this.button2.classList.toggle('hide');
this.button3.classList.toggle('hide');
this.button4.classList.toggle('hide');
this.showallbutton.classList.add('hide');
}
Now the function is run by the press of a button, but when pressing the button, the console log returns this:
> Skulder14q1Component.html:12 ERROR TypeError: Cannot read property 'classList' of null
at Skulder14q1Component.onShowAllOptions (skulder14q1.component.ts:28)
at Object.eval [as handleEvent] (Skulder14q1Component.html:15)
at handleEvent (core.js:10251)
at callWithDebugContext (core.js:11344)
at Object.debugHandleEvent [as handleEvent] (core.js:11047)
at dispatchEvent (core.js:7710)
at core.js:8154
at HTMLButtonElement.<anonymous> (platform-browser.js:988)
at ZoneDelegate.push../node_modules/zone.js/dist/zone.js.ZoneDelegate.invokeTask (zone.js:421)
at Object.onInvokeTask (core.js:3811)
Changing to this code instead works:
onShowAllOptions() {
const button1 = document.getElementById('button1');
const button2 = document.getElementById('button2');
const button3 = document.getElementById('button3');
const button4 = document.getElementById('button4');
const showallbutton = document.getElementById('showallbutton');
button1.classList.toggle('hide');
button2.classList.toggle('hide');
button3.classList.toggle('hide');
button4.classList.toggle('hide');
showallbutton.classList.add('hide');
}
I feel like I'm missing something basic, and I hope you can help me out. Thanks!
Upvotes: 1
Views: 63
Reputation: 4079
First of all, I would recommend against managing the DOM too much through standard browser methods like document.getElementById
, etc. Using the Angular abstraction is recommended, you can read more here.
This out of the way, we can get to the why.
When the Skulder14q1Component
is initialized, the DOM (the HTML elements / the template) for it has not been created yet. So when you try to find the buttons, they are not there yet, so getElementById
returns null
.
Components in Angular go through a life-cycle, and you can hook into this life-cycle by adding certain methods to your class. you can read more about those in the docs on lifecycle hooks.
In your case, you need the AfterViewInit
hook. To implement it, we need to do two things:
implement the interface AfterViewInit
class Skulder14q1Component implements AfterViewInit {
...
}
Add the method
class Skulder14q1Component implements AfterViewInit {
button1: HTMLElement;
button2: HTMLElement;
button3: HTMLElement;
button4: HTMLElement;
showAllButton: HTMLElement;
ngAfterViewInit() {
this.button1 = document.getElementById('button1');
this.button2 = document.getElementById('button2');
this.button3 = document.getElementById('button3');
this.button4 = document.getElementById('button4');
this.showallbutton = document.getElementById('showallbutton');
}
// when this is called later, the buttons will have been set up
onShowAllOptions() {
this.button1.classList.toggle('hide');
this.button2.classList.toggle('hide');
this.button3.classList.toggle('hide');
this.button4.classList.toggle('hide');
this.showallbutton.classList.add('hide');
}
}
The code in the method will run after Angular has prepared the template for you.
Edit: In my experience, ViewChild
is not used that often, since you want the elements inside your templates (especially if they are other components) to be responsible for their own behaviour, and not control everything from an omnipotent class.
Here is a StackBlitz I made for your use-case precisely.
Upvotes: 1