I need to filter AssessmentCards by Year. I made the method.
But I need to call clickAllCards and clickYearCard method in onClick event on other file. How can I do that?
This is my code with the methods, I'm using Pug.JS to render:
import React from 'react';
import { FormattedMessage } from 'react-intl';
import { Link } from 'react-router-dom';
import messages from './messages';
import { getAssessmentsCards } from '../../functions';
import template from './index.pug';
const cardsAssessments = getAssessmentsCards();
export default class CardAssessment extends React.PureComponent { // eslint-disable-line react/prefer-stateless-function
constructor(props){
super(props);
this.state = {
listCards: [],
openCm: false,
}
}
componentWillMount(){
this.setState({listCards: cardsAssessments});
}
hover() {
this.setState({openCm: !this.state.openCm});
}
clickAllCards(e){
e.preventDefault();
this.setState({listCards: cardsAssessments});
}
clickYearCard(e){
e.preventDefault();
var filtered = cardsAssessments.filter((data) => {
return data.yearCard === '2018';
});
this.setState({listCards: filtered});
}
render() {
let cm = ["card-menu"];
if(this.state.openCm) {
cm.push('active');
}
return template.call(this, {
messages,
FormattedMessage,
Link,
cm
});
}
}
This is my pug file:
.card-adjust
div(href="" onClick="{this.clickYearCard.bind(this)}") 2018
div(href="" onClick="{this.clickAllCards.bind(this)}") All
Link.card.add-new(to="/add-assessment")
span
.add-icon
i.ti-plus
|
FormattedMessage(__jsx='{...messages.addAssessment}')
.card.card-materia(#for='data in this.state.listCards', key='{data.id}')
.card-body(id="{data.id}")
div(className="{cm.join(' ')}" onClick="{this.hover.bind(this)}")
i.fas.fa-ellipsis-v
.cm-floating
Link.cmf-agenda(to="/agendamento")
i.ti-agenda
|
FormattedMessage(__jsx='{...messages.scheduled}')
Link.cmf-copy(to="#")
i.pe-7s-copy-file
|
FormattedMessage(__jsx='{...messages.copy}')
Link.cmf-trash(to="#")
i.ti-trash
|
FormattedMessage(__jsx='{...messages.delete}')
.cm-icon
i(className='{data.icon}')
h2.cm-title {data.disciplineAbbreviation}
span.badge.badge-danger {data.status}
p.cm-questions {data.questionNumber}
FormattedMessage(__jsx='{...messages.questions}')
.cm-info
Link(to="#") {data.disciplineName}
Link(to="#") {data.year}
Link(to="#") {data.segment}
.cm-date
//- i.pe-7s-refresh-2
| {data.date}
And this is the file where I need to put the onClick event:
import React from 'react';
import { FormattedMessage } from 'react-intl';
import messages from './messages';
import template from './index.pug';
import '../../assets/scss/main.scss';
export default function (params = {}) {
const { messages, FormattedMessage } = params;
return (
<div>
<ul className="nav nav-tabs">
<li className="nav-item">
<a className="nav-link" href="#">
<FormattedMessage {...messages.all} />
</a>
</li>
<li className="nav-item">
<a className="nav-link" href="#">2018</a>
</li>
<li className="nav-item">
<a className="nav-link" href="#">2017</a>
</li>
</ul>
<div className="navigation-tabs display-none">
<a>
<i className="nt-icon ti-angle-left" />
</a>
1 de 3
<a>
<i className="nt-icon ti-angle-right" />
</a>
</div>
</div>
);
}
Thanks
You can pass any method for onClick event to any component like that:
App.js
class App extends React.Component {
handleClick = () => alert( "Clicked" );
render() {
return (
<div>
<Child click={this.handleClick}/>
</div>
)
}
}
or with a function component if you don't need lifecylce methods or "this" (here we don't need):
const App = () => {
const handleClick = () => alert( "Clicked" );
return (
<div>
<Child click={handleClick}/>
</div>
);
}
Child.js
const Child = ( props ) => (
<div>
<button onClick={props.click}>Click me!</button>
</div>
)
Related
I am trying to add a class and remove it it's available already. I am doing to for each element separately. but it always refers the last element. what is the correct way to do this?
is there any easy and elegant way to do this?
here is my component:
import React from "react";
import "./style.css";
export default class App extends React.Component {
divRef;
constructor(props){
super(props)
this.divRef = React.createRef();
}
toggleView = (e) => {
e.preventDefault();
if(this.divRef.current.classList.contains("active")){
this.divRef.current.classList.remove("active"); //always refer div 2!?
return;
}
this.divRef.current.classList.add("active");
}
render(){
return (
<div class="parent">
<div ref={this.divRef}>
1
<a href="#" onClick={(e) => this.toggleView(e)}>toggle</a>
</div>
<div ref={this.divRef}>2
<a href="#" onClick={(e) => this.toggleView(e)}>toggle</a>
</div>
</div>
);
}
}
Live demo
I would utilize state to achieve this. No need for refs.
import React from "react";
import "./style.css";
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = {
view1: false,
view2: false
};
}
toggleView = (e, view) => {
e.preventDefault();
this.setState({ [view]: !this.state[view] });
};
render() {
return (
<div class="parent">
<div className={this.state.view1 ? "active" : ""}>
1
<a href="#" onClick={e => this.toggleView(e, "view1")}>
toggle
</a>
</div>
<div className={this.state.view2 ? "active" : ""}>
2
<a href="#" onClick={e => this.toggleView(e, "view2")}>
toggle
</a>
</div>
</div>
);
}
}
Live Demo
EDIT:
Here I show how this approach scales when we have lots of items
import React from "react";
import "./style.css";
const NUM_ITEMS = 20;
const items = Array.from({ length: NUM_ITEMS }).map((_, idx) => ({
title: "title" + idx,
active: false
}));
const Item = ({ active, title, onToggle }) => (
<div className={active ? "active" : ""}>
{title}
<a
href="#"
onClick={e => {
e.preventDefault();
onToggle();
}}
>
toggle
</a>
</div>
);
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = {
items
};
}
toggleView = idx => {
const items = [...this.state.items];
items[idx] = { ...items[idx], active: !items[idx].active };
this.setState({ items });
};
render() {
return (
<div class="parent">
{this.state.items.map((item, idx) => (
<Item
key={item.title}
active={item.active}
title={item.title}
onToggle={() => this.toggleView(idx)}
/>
))}
</div>
);
}
}
Demo
May be you can do that by using two separate ref's ,here is the demo:https://stackblitz.com/edit/react-b1wa4f?file=src%2FApp.js
I got with this:
import React from "react";
import "./style.css";
export default class App extends React.Component {
divRef;
constructor(props){
super(props)
this.divRef = React.createRef();
}
toggleView = (e) => {
e.preventDefault();
const element = e.currentTarget.parentElement.classList;
if(element.contains("active")){
element.remove("active");
return;
}
element.add("active");
}
render(){
return (
<div class="parent">
<div ref={this.divRef}>
1
<a href="#" onClick={(e) => this.toggleView(e)}>toggle</a>
</div>
<div ref={this.divRef}>2
<a href="#" onClick={(e) => this.toggleView(e)}>toggle</a>
</div>
</div>
);
}
}
if any one finds, it's not a right way, let me know
I try to call parent function from child component with dynamic value but the value is always showing 80 , Here is the code:
Parent:
filterdata = (child) =>{
alert(child) // this is always showing 80
this.setState({
max:child
})
}
Inside render():(characters is data fetched from axios)
<Pagination pages={Math.ceil(this.state.characters.length/10)} filter={this.filterdata} />
Inside Pagination.js
import React, { Component } from 'react'
class Pagination extends Component {
render() {
var indents = [];
for (var i = 0; i < this.props.pages; i++) {
indents.push( <li key={i} className="page-item"><a class="page-link" onClick={() => this.props.filter((i+1)*10)}>{i+1}</a></li>);
}
return (
<div>
<ul class="pagination justify-content-center">
{indents}
</ul>
</div>
)
}
}
export default Pagination
Like Marc said, I don't think you should do the loop inside render.
import React, { Component } from 'react'
class Pagination extends Component {
constructor(props) {
super(props);
this.state = { indents: [] };
}
componentDidMount() {
vat indents = [];
for (var i = 0; i < this.props.pages; i++) {
indents.push(<li key={i} className="page-item"><a class="page-link" onClick={() => this.props.filter((i+1)*10)}>{i+1}</a></li>);
}
this.setState({ indents: indents });
}
render() {
return (
<div>
<ul className="pagination justify-content-center">
{indents}
</ul>
</div>
)
}
}
export default Pagination
Also, you are using class instead of className on the ul element.
I'd change your component to look like this for the sake of brevity. Like others said though, you could outsource the li generating logic to a method in your class later if the class becomes bigger. This isn't what's causing your problem, though.
I'm not sure what's causing the constant 80, but this code works for me.
in App.js
import Pagination from 'wherever it comes from';
import React from 'react';
class App extends Component {
state = { max: null }
render() {
return (
<div className="App">
<Pagination pages={5} filter={(val) => {
console.log(val);
this.setState({ max:val });
}} />
</div>
)
}
}
Pagination.js
import React, { Component } from 'react';
class Pagination extends Component {
render() {
return (
<div>
<ul className="pagination justify-content-center">
{Array(this.props.pages)
.fill()
.map((el, index) => {
const valueForFilter = (index + 1) * 10;
return (
<li key={index} className="page-item">
<a class="page-link" href="#" onClick={() => this.props.filter(valueForFilter)}>
{index + 1}
</a>
</li>
);
})}
</ul>
</div>
);
}
}
export default Pagination;
I have implemented pagination component which has 5 pages in total. Pagination is child component of home component.
When I click on page 1 it should get page number through getpagenumber() and handleClick() which makes use of page number but in below code getpagenumber() does not work when we click on page number.
pagination.js:
import React, { Component } from 'react';
class Pagination extends Component {
getpagenumber(val){
return val;
}
handleClick(){
this.setState({
end:getpagenumber()*16,
start:end-16
});
const end = getpagenumber()*16;
this.props.onChange(end - 16, end);
}
render() {
return (
<div>
<Content/>
<div className="container">
<ul className="pagination">
<li {this.getpagenumber(1)} onClick={this.handleClick}>1</li>
<li {this.getpagenumber(2)} onClick={this.handleClick}>2</li>
<li {this.getpagenumber(3)} onClick={this.handleClick}>3</li>
<li {this.getpagenumber(4)} onClick={this.handleClick}>4</li>
<li {this.getpagenumber(5)} onClick={this.handleClick}>5</li>
</ul>
</div>
</div>
);
}
}
export default Pagination;
home.js :
import React, { Component } from 'react';
import Pagination from './pagination';
import Content from './content';
class Home extends Component {
constructor(props){
super(props);
this.state = {
start:0,
end:16
};
}
onChangePagination = (start, end) => {
this.setState({
start:start,
end:end
});
};
render() {
return (
<div>
<Content start={start} end={end}/>
<Pagination onChange={this.onChangePagination}/>
</div>
);
}
}
export default Home;
You are exaggerating a bit. All you need is this:
import React, { Component } from 'react';
class Pagination extends Component {
handleClick(value){
this.setState({
end: value*16,
start: end-16
});
const end = value*16;
this.props.onChange(end - 16, end);
}
render() {
return (
<div>
<Content/>
<div className="container">
<ul className="pagination">
<li><a href="#" onClick={this.handleClick.bind(this, 1)}>1</a></li>
<li><a href="#" onClick={this.handleClick.bind(this, 2)}>2</a></li>
<li><a href="#" onClick={this.handleClick.bind(this, 3)}>3</a></li>
<li><a href="#" onClick={this.handleClick.bind(this, 4)}>4</a></li>
<li><a href="#" onClick={this.handleClick.bind(this, 5)}>5</a></li>
</ul>
</div>
</div>
);
}
}
export default Pagination;
Notes:
Maybe consider even passing the number of pages to the Pagination component and then generating an array from 1 to that number and just mapping out the li with the clicks.
Clicks should go on the link tags.
I can declare a method within a constant, I try to implement the onLogout method in the navbar but I get an error in the method by saying 'js [;] expected' or require declaring the class in this way:
export default class Landing extends React.Component
import React from 'react';
import {Link} from 'react-router'
import { Accounts } from 'meteor/accounts-base';
const NavbarLanding = () => {
onLogout() {
Meteor.logout();
};
return (
<div className="navbar-landing">
<nav>
<div>
<ul className="ul-landing">
<img src="/images/fly_paper.svg"></img>
<li className="navbar-title"><a>Landing</a></li>
{/* <img border="0" height="40" hspace="0" src="/images/fly_paper.png" width="80" /> */}
{/* onClick={this.onLogout.bind(this) */}
{/* btn-primary */}
<div className="navbar-menu">
<li><a>acerca</a></li>
<li><a>portafolio</a></li>
<li><a>contacto</a></li>
<button className="btn"onClick={this.onLogout.bind(this)}>Logout</button>
</div>
</ul>
</div>
</nav>
</div>
);
};
export default NavbarLanding;
NavbarLanding.reactProptype = {
title: React.PropTypes.string.isRequired
};
You made an error declaring your component, it should be like this:
export default class NavbarLanding extends React.Component {
onLogout() {
Meteor.logout();
}
render() {
return (
// your html here
);
}
};
NavbarLanding.reactProptype = {
title: React.PropTypes.string.isRequired
};
I'm trying to figure out how to notify another component about a state change. Let's say I have 3 components - App.jsx,Header.jsx,and SidebarPush.jsx and all I'm simply trying to do is toggle a class with an onClick.
So the Header.jsx file will have 2 buttons when clicked will toggle the states to true or false. The other 2 components App.jsx and Header.jsx will need to know about these state changes so they can toggle a class
whenever those states change.
App.jsx
import React from 'react';
import Header from 'Header';
import classNames from "classnames";
import SidebarPush from 'SidebarPush';
import PageWrapper from 'PageWrapper';
var MainWrapper = React.createClass({
render: function() {
return (
<div className={classNames({ 'wrapper': false, 'SidebarPush-collapsed': !this.state.sidbarPushCollapsed })}>
<Header/>
<SidebarPush/>
<PageWrapper>
{this.props.children}
</PageWrapper>
</div>
);
}
});
module.exports = MainWrapper;
Header.jsx
import React from 'react';
import ReactDom from 'react-dom';
class Header extends React.Component {
constructor() {
super();
this.state = {
sidbarPushCollapsed: false,
profileCollapsed: false
};
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState({
sidbarPushCollapsed: !this.state.sidbarPushCollapsed,
profileCollapsed: !this.state.profileCollapsed
});
}
render() {
return (
<header id="header">
<ul>
<li>
<button type="button" id="sidbarPush" onClick={this.handleClick} profile={this.state.profileCollapsed}>
<i className="fa fa-bars"></i>
</button>
</li>
<li>
<button type="button" id="profile" onClick={this.handleClick}>
<i className="icon-user"></i>
</button>
</li>
</ul>
<ul>
<li>
<button id="sidbarOverlay" onClick={this.handleClick}>
<i className="fa fa-indent"></i>
</button>
</li>
</ul>
</header>
);
}
};
module.exports = Header;
SidebarPush.jsx
import React from 'react';
import ReactDom from 'react-dom';
import classNames from "classnames";
class SidebarPush extends React.Component {
render() {
return (
<aside className="sidebarPush">
<div className={classNames({ 'sidebar-profile': true, 'hidden': !this.state.pagesCollapsed })}>
....
</div>
<nav className="sidebarNav">
....
</nav>
</aside>
);
}
}
export default SidebarPush;
Move all of your state and your handleClick function from Header to your MainWrapper component.
Then pass values as props to all components that need to share this functionality.
class MainWrapper extends React.Component {
constructor() {
super();
this.state = {
sidbarPushCollapsed: false,
profileCollapsed: false
};
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState({
sidbarPushCollapsed: !this.state.sidbarPushCollapsed,
profileCollapsed: !this.state.profileCollapsed
});
}
render() {
return (
//...
<Header
handleClick={this.handleClick}
sidbarPushCollapsed={this.state.sidbarPushCollapsed}
profileCollapsed={this.state.profileCollapsed} />
);
Then in your Header's render() method, you'd use this.props:
<button type="button" id="sidbarPush" onClick={this.props.handleClick} profile={this.props.profileCollapsed}>