I want to call the method inside the same class. For example, when I click a button, it will trigger the method handleLoginBtnClicked(). I expect it will call the method checkInputValidation() in the same class. What is the proper way to do this?
export default class LoginCard extends React.Component {
//If I click a button, this method will be called.
handleLoginBtnClicked() {
this.checkInputValidation();
}
checkInputValidation() {
alert("clicked");
}
...
...
...
render() {
...
<LoginBtn onClick={this.handleLoginBtnClicked}/>
...
}
}
Error Message:
Uncaught TypeError: this.checkInputValidation is not a function
You will need to bind those functions to the context of the component. Inside constructor you will need to do this:
export default class LoginCard extends React.Component {
constructor(props) {
super(props);
this.handleLoginBtnClicked = this.handleLoginBtnClicked.bind(this);
this.checkInputValidation = this.checkInputValidation.bind(this);
}
//This is the method handleLoginBtnClicked
handleLoginBtnClicked() {
...
}
//This is the method checkInputValidation
checkInputValidation() {
...
}
...
..
.
}
Where are you binding the handleLoginBtnClicked? You may be losing the functions context and losing the meaning of the special variable this. React will handle and trigger the onClick event, calling the function from a different context which is why it has been lost.
You should use the following syntax to create a new bound function to add as the event listener for the onClick event. This will ensure that handleLoginBtnClicked's context is not lost.
<element onClick={this.handleLoginBtnClicked.bind(this)}>
Related
I am trying to organise my code into components/modules and I'm struggling to understand how to deal with event listeners and/or communicate between JS files. I will explain with a simplified example:
In this hypothetical, app.js imports all the other js file components. In app.js I call a function in nav.js to render my nav to the DOM and I add event listeners to a single button.
When the button is clicked my event handler inside nav.js picks it up and I want to somehow communicate back to app.js and tell it to call a function inside pageA.js to render the page.
I'm not sure if this is the right approach or if I'm missing a key concept to get this working. Any tips would be appreciated.
index.js
import Nav from './nav.mjs';
import PageA from './pageA.mjs';
export default class App {
constructor() {
this.nav = new Nav();
this.pageA = new PageA();
this.nav.renderNav();
}
}
nav.js
export default class Nav {
constructor() {
}
renderNav(){
let button = document.createElement("button");
button.innerHTML = "Page A";
document.body.appendChild(button);
button.addEventListener ("click", function() {
// How do I call renderPageA() in pageA.js?
});
}
}
pageA.js
export default class PageA {
constructor() {
}
renderPageA(){
let pageDiv = document.createElement('div');
document.body.appendChild(pageDiv);
}
}
You can pass pageA instance as a parameter in the Navs constructor.
//Nav constructor
constructor(pageA){
this.pageA = pageA
}
//Add this to the event listener's callback:
this.pageA.renderPageA()
//index.js
this.nav = new Nav(pageA)
Note that you have to instantiate pageA before nav for this to work.
I need to remove the wheel eventlistener immediately after it fired. I tried the following but its not removing the eventlistener.
export class HomeComponent implements OnInit {
constructor() {}
ngOnInit() {
document.querySelector("#section-one").addEventListener("wheel", () => this.myFunction1(), true);
}
myFunction1() {
alert();
document.querySelector("#section-one").removeEventListener("wheel", this.myFunction1, true);
console.log("Done!");
}
}
Any suggestions?
According to the docs:
Calling removeEventListener() with arguments that do not identify any
currently registered EventListener on the EventTarget has no effect.
your code shouldn't work.
Possible fix can be as follows:
wheelHandler: any;
ngOnInit() {
this.wheelHandler = this.myFunction1.bind(this);
document.querySelector("#section-one").addEventListener("wheel", this.wheelHandler, true);
}
myFunction1() {
alert();
document.querySelector("#section-one").removeEventListener("wheel", this.wheelHandler, true);
console.log("Done!");
}
where wheelHandler is a function referring to the same instance of handler
For more angular way solution see
How to listen for mousemove event on Document object in Angular
But AFAIK useCapture parameter is not supported yet. So it's always false
You could use the HostListener decorator to bind a event listener, but this only works for the host element. If you want to add and remove a listener for a child element you have to use the Renderer2.listen method. Which returns a function to remove the event listener.
#Component( {
template: '<div #sectionOne></div>'
})
export class myComponent {
private _listeners = [];
#ViewChild('sectionOne')
public section: ElementRef<any>;
constructor(private _renderer: Renderer2) {}
ngAfterViewInit() {
this._listeners.push(
this._renderer.listen(this.section.nativeElement, 'click', this.handler.bind(this))
);
}
ngOnDestroy() {
this._listeners.forEach(fn => fn());
}
public handler() {
}
}
The useCapture parameter isn't supported by angular by now. For more information, see this issue.
The problem is probably that when you use a class method as a callback function, this no longer points to the class in the callback function.
Use this code instead to add your event listener:
document.querySelector("#section-one")
.addEventListener("wheel", () => this.myFunction1(), true);
Note that this.myFunction1 has become () => this.myFunction1(). In other words, I wrapped the name of the callback function inside a lambda.
The code to remove the listener stays the same:
document.querySelector("#section-one").removeEventListener("wheel", this. myFunction1, true);
Finally, and most importantly, why are you using event listeners like that? This is definitely NOT the Angular way. 😅
I am in an event function and I would like to create a new alert popup (I am using the react-portal library):
onNewAlert: function(username) {
var divModal = (
<Portal ref={'Portal'+username}>
<div id={'div'+username}>
<br /><br/ >Alert for {username}
</div>
</Portal>);
...
}
But then I would have to call a function that is inside a Portal. I could normally do this with references if I was in the render() function, but I am in an event.
this.refs['Portal'+username].openPortal(); // openPortal is a function of the Portal component
Is there a way to call a component function for a component created on the fly in a javascript function?
Even if you could call portal.openPortal() it wouldn't do anything since the component created in the event handler wouldn't be attached to the DOM.
Instead of trying to render the Portal in the event handler function, the event handler should change the component state which will trigger render().
onNewAlert: function(username) {
this.setState({ showAlert: true });
}
The render() function would then use the state variable for the Portal component's isOpened property:
render: function () {
return (
<div>
...
<Portal isOpened={this.state.showAlert}>
...
</Portal>
</div>
);
}
jQuery seems to be working fine in react component however, when I try to apply styling using jquery in react component its not working. In the below code console.log(eachVisitedTopic) within each loop is returning proper result as expected.
topicsVisited(arr){
$(function() {
$.each(arr, function(key, eachVisitedTopic) {
console.log(eachVisitedTopic);
$('.single-topic[data-topic-id="' + eachVisitedTopic + '"]').css({
'background-color': 'red'
});
});
});
};
Markup
import {React, ReactDOM} from '../../../../build/react';
export default class SingleTopicBox extends React.Component {
render() {
return (
<div>
<div className="col-sm-2">
<div className="single-topic" data-topic-id={this.props.topicID} onClick={() => this.props.onClick(this.props.topicID)}>
{this.props.label}
{this.props.topicID}
</div>
</div>
</div>
);
}
};
React should handle all the render, it checks the dirty dom and render only things that changed.
You can achieve what you want, just use a react state.
When you trigger a setState change react will look into the DOM and find what has changed and then render it.
Ref: https://facebook.github.io/react/docs/component-api.html#setstate
constructor() {
super();
this.state = {
bgDisplayColor: "blue"
};
}
Then you can do something like this in yout component:
$('.single-topic[data-topic-id="' + eachVisitedTopic + '"]').css({
'background-color': this.state.bgDisplayColor
});
And to update it you simply use:
this.setState({bgDisplayColor: "red"});
EDIT
To workaround the undefined variable error, you have to store the scope of "this" inside your function and use instead of "this", because inside the jquery .css "this" refers to Jquery and not the "this" scope of your actual class.
Example:
topicsVisited(arr){
var self = this;
$(function(){
$.each(arr, function(key, eachVisitedTopic){
console.log(eachVisitedTopic);
//self here is the global scope of your class
//Inside jQuery.css this refers to Jquery and not to your class.
$('.single-topic[data-topic-id="' + eachVisitedTopic + '"]').css({
'background-color': self.state.bgDisplayColor
});
});
});
});
};
Try it to by putting all jQuery code inside the componentDidMount
E.g :
componentDidMount() {
//Your jQuery function goes here
}
I have a link in a React component:
<a href="#goals-tab" className={ this.setTabStyle()}>Goals</a>
Now, inside setTabStyle method, can I access attributes of the a element, like href without explicitly passing it to the method as a parameter?
If you use a ref, then your component renders DOM without the styles, and then applies the new styles. So the user will notice the change of styles.
I would advise to pass link as a parameter to setTabStyle(link), or make the link another prop of your component:
var Component = React.createClass({
handleClick: function (e) {
console.log(e.currentTarget.getAttribute('href'));
},
setTabStyle: function () {
if (this.props.link == this.props.activelink) {
return myActiveLinkStyle
} else {
return myInactiveLinkStyle
}
},
render: function() {
return <a href={this.props.link} style={this.setTabStyle()} onClick={this.handleClick}>Click</a>;
}
});
That way, you get the right style from the initial load..