I have a list of topics and groups being returned from an API call. Topics belongs to at least 1 or more groups. The topics are currently filtered by the groups that are selected. Each group selected is set or removed in the selectedGroups state. I have an input search box which is used to help the user find a topic, when they start typing in the textbox I want a dropdown just below showing if any topic titles match their search input. When they click that topic it should only show that topic in the topics state.
Example.. if I type..
"Jo"
We get a dropdown of topics just below as suggestions and should render as in the dropdown:-
John..
Johnny..
Joan..
etc
Then when we click one of these topics in the dropdown, the state for topics update. So yes it will just show one topic in this case.
I have the search input and onchange method called handleInputChange
I am getting an error: Property 'search' does not exist on type 'PracticeAreas'. and not sure where I should be heading towards getting this to work correctly. Any help would be really grateful, thanks
I have included example data from the API call
And the react script
Main:
import * as React from 'react';
import './PracticeAreas.css';
import IReportGroup from 'src/model/IReportGroup';
import { Loader } from '../loader/Loader';
import Suggestions from './Suggestions'
export interface IGroupTopics {
id: string
name: string,
groups: string[]
}
interface IOwnProps {
}
interface IOwnState {
groups: IReportGroup[],
topics: IGroupTopics[],
selectedGroups: IReportGroup[],
query: string,
}
class PracticeAreas extends React.Component<IOwnProps, IOwnState> {
constructor(props: IOwnProps) {
super(props);
this.state = {
groups: [],
topics: [],
selectedGroups: [],
query: ""
}
}
public render() {
const { topics } = this.state;
return topics.length > 0 ?
this.renderData(topics) :
<Loader />
}
public renderData(data: any) {
return (
<div className="col-md-12 practiceAreas">
<h1>Practice Areas</h1>
<div className="selection-refinement">
<div className="refinement-search">
<form>
<input
placeholder="Search for..."
ref={input => this.search = input}
onChange={this.handleInputChange}
/>
<Suggestions topics={this.state.topics} />
</form>
</div>
</div>
<ul className="list-inline groupedTags">
{this.state.groups.map((item,i) =>
<li key={i}>
<a className={"navigator-tags " + (this.groupInState(item) ? "active" : "")} onClick={() => this.setSelectedGroups(item)}>
{item.name}
</a>
</li>
)}
</ul>
<div className="row practiceAreasContainer">
{this.state.topics.filter(topic => this.topicInGroupSelection(topic)).map((item,i) =>
<div key={i} className="result">
<div className="col-md-6 result-item">
<div className="item-container default shadowed item-content highlight row">
<div className="col-sm-12 no-padding">
<p>Editor: John Sinclair, Eric Draven, Coco Zames</p>
<p>Beiten Burkhardt</p>
<div className="row no-margin">
<div className="col-12 col-sm-10 text-content">
<h3>
<a href="#" >{item.name}</a>
</h3>
<p className="summary">
Summary
</p>
</div>
<div className="col-10 col-sm-2 links-container rhs">
Compare
<div className="divider" />
View
</div>
</div>
</div>
</div>
</div>
</div>
)}
</div>
<div className="row text-center">
<a className="lex-primary-btn medium-btn">Load more</a>
</div>
</div>
);
}
public handleInputChange = () => {
this.setState({
query: this.search.value
}, () => {
if (this.state.query && this.state.query.length > 1) {
// this.showDropdown()
if (this.state.query.length % 2 === 0) {
this.state.topics
}
} else if (!this.state.query) {
// this.hideDropdown()
}
})
}
public componentDidMount() {
fetch(`.../api/v2/navigator/reports/topics`, {
method: "GET",
headers: {
"Accept": "application/json",
"Content-Type": "application/json"
}})
.then((res) => res.json()
.then((data) => {
this.setState({
groups: data.groups,
topics: data.data
});
}));
}
public setSelectedGroups = (group: IReportGroup) => {
// remove from state
if (this.groupInState(group)) {
this.setState(state => ({
selectedGroups: state.selectedGroups.filter(t => t.id !== group.id)
}));
// set state
} else {
this.setState(previousState => ({
selectedGroups: [...previousState.selectedGroups, group]
}));
}
}
public topicInGroupSelection = (topic: IGroupTopics) => {
return (this.state.selectedGroups.length > 0 ? this.state.selectedGroups.some(item => topic.groups.some(group => group === item.id)) : true)
}
public groupInState = (group: IReportGroup) => {
return this.state.selectedGroups.some(item => group.id === item.id);
}
}
export default PracticeAreas
Suggestions (which should topics in the state):
import * as React from 'react';
const Suggestions = (props) => {
const options = props.topics.map(r => (
<li key={r.id}>
{r.name}
</li>
))
return <ul>{options}</ul>
}
export default Suggestions
Data ex:
<ReportSelectionCriteriaResponse xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/">
<Data xmlns:d2p1="http://schemas.datacontract.org/2004/07/">
<d2p1:NavigatorReportSelection>
<d2p1:About>test title 4</d2p1:About>
<d2p1:Groups xmlns:d4p1="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
<d4p1:guid>d21384b5-27be-4bfc-963d-0d2ad40dbbfb</d4p1:guid>
</d2p1:Groups>
<d2p1:Id>2fb2783c-f48e-4d49-8098-0d39e4a16e7a</d2p1:Id>
<d2p1:Name>Test</d2p1:Name>
<d2p1:ParentId i:nil="true"/>
<d2p1:Selected>false</d2p1:Selected>
<d2p1:Type>Topics</d2p1:Type>
<d2p1:Visible>true</d2p1:Visible>
</d2p1:NavigatorReportSelection>
<d2p1:NavigatorReportSelection>
<d2p1:About i:nil="true"/>
<d2p1:Groups xmlns:d4p1="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
<d4p1:guid>2fb2783c-f48e-4d49-8098-0d39e4a16e7a</d4p1:guid>
</d2p1:Groups>
<d2p1:Id>47cb7f1d-2267-426c-9f7f-0df3b9291fb7</d2p1:Id>
<d2p1:Name>Another test topic</d2p1:Name>
<d2p1:ParentId i:nil="true"/>
<d2p1:Selected>false</d2p1:Selected>
<d2p1:Type>Topics</d2p1:Type>
<d2p1:Visible>true</d2p1:Visible>
</d2p1:NavigatorReportSelection>
</Data>
<Groups xmlns:d2p1="http://schemas.datacontract.org/2004/07/">
<d2p1:NavigatorReportSelectionGroup>
<d2p1:Focused>false</d2p1:Focused>
<d2p1:Id>2fb2783c-f48e-4d49-8098-0d39e4a16e7a</d2p1:Id>
<d2p1:Name>Allan's Test group</d2p1:Name>
<d2p1:Order>0</d2p1:Order>
<d2p1:Type>Topics</d2p1:Type>
</d2p1:NavigatorReportSelectionGroup>
<d2p1:NavigatorReportSelectionGroup>
<d2p1:Focused>false</d2p1:Focused>
<d2p1:Id>47cb7f1d-2267-426c-9f7f-0df3b9291fb7</d2p1:Id>
<d2p1:Name>Another test topic group</d2p1:Name>
<d2p1:Order>1</d2p1:Order>
<d2p1:Type>Topics</d2p1:Type>
</d2p1:NavigatorReportSelectionGroup>
</Groups>
</ReportSelectionCriteriaResponse>
Related
Im trying to load the information from a database to my React App. Everything is working as expected except for the map function I'm running. Im trying to list all the genres of the given movie in the div.
import React, { Component } from 'react';
import axios from 'axios';
import ReactPlayer from 'react-player';
import play from '../../img/play-icon.svg';
import './hero-display.scss';
class HeroDisplay extends Component {
constructor() {
super();
this.state = {
movie: {},
trailer: false
}
}
componentDidMount() {
axios.get('https://petflix.herokuapp.com/movie')
.then((response) => {
this.setState({
movie: response.data
})
})
.catch((error) => {
console.error(error);
});
}
openTrailer = () => {
this.setState({
trailer: true
});
}
closeTrailer = () => {
this.setState({
trailer: false
});
}
render() {
const { movie, trailer } = this.state;
return (
<>
<div className="hero-display__container" style={{backgroundImage: `url(${movie.backdropURL})`}}>
<div className="hero-display__movie-information">
<img className="hero-display__movie-logo" src={movie.logoURL} alt=""/>
<h1 className="hero-display__heading">Watch Now</h1>
<p className="hero-display__movie-description">{movie.description}</p>
<div className="hero-display__control-buttons">
<button onClick={this.openTrailer} className="hero-display__button play">
<img src={play} alt=""/>
Play Trailer
</button>
</div>
</div>
<div className="hero-display__rating">
<p>{movie.rating}</p>
</div>
</div>
<div className={trailer ? "movie__trailer showing" : "movie__trailer hidden"}>
<div className="movie-trailer__container">
<button className="movie-trailer__close-button" onClick={this.closeTrailer}></button>
<ReactPlayer className="video-player" light={movie.backdropURL} volume={0} loop={true} playing={true} image={movie.backdropURL} muted={false} url={movie.trailerURL}/>
<div className="movie__information">
<div className="movie-trailer__title-add">
<h1 className="movie-trailer__title">{movie.title}</h1>
<button className="movie-trailer__button"></button>
</div>
<p className="movie-trailer__rating">{movie.rating}</p>
<p className="movie-trailer__director">Directed By: {movie.director}</p>
<p className="movie-trailer__description">{movie.description}</p>
{movie.genres.map((genre) => {
return (
<p className="movie-trailer__genres">{genre}</p>
)
})}
</div>
</div>
</div>
</>
)
}
}
export default HeroDisplay;
The map function works in a different functional component when the state is passed as a prop, but not in the class component itself. Id appreciate the help.
An example of a movie object that is stored in state.
{
"title": "101 Dalmatians",
"description": "A brave young man is thrust into adulthood as he and his courageous team of sled dogs embark on a grueling and treacherous cross-country marathon.",
"director": "Stephen Herek",
"genres": [
"Adventure",
"Family",
"Comedy"
],
"rating": "G",
"backdropURL": "https://www.themoviedb.org/t/p/original/mz75dVXfen4J1Z0R8DbTenXNywz.jpg",
"posterURL": "https://www.themoviedb.org/t/p/original/8o2ADoAyG796UwTjwBFjPyBz0yG.jpg",
"trailerURL": "https://www.themoviedb.org/video/play?key=K2CTJS12RdI",
"logoURL": "https://www.themoviedb.org/t/p/original/noruOCRUoWndPPDPJYdR7kVsiq5.png"
}
It is happening due to the generes property of movie state is not available before the api call.
So instead of direct access of property.
{movie.genres.map((genre) => {
return (
<p className="movie-trailer__genres">{genre}</p>
)
})}
use optional chaining with ?.
{movie?.genres?.map((genre) => {
return (
<p className="movie-trailer__genres">{genre}</p>
)
})}
This part:
{movie.genres.map((genre) => {
return (
<p className="movie-trailer__genres">{genre}</p>
)
})}
Should be
{movie.genres && movie.genres.map((genre) => {
return (
<p className="movie-trailer__genres">{genre}</p>
)
})}
Alternatively, you could do movie?.genres?.map((genre) => {...
Otherwise movie.genres is undefined on first render.
I've created a MERN with redux application where users can order a meal from a menu. In the admin side, I am providing delete and add functions so the meals on the menu can be changed all in the same page. I have managed to get the delete meal item to work, but I am getting the following error when I try and add a new meal item:
My redux action is as follows:
export const createMeal = (meal) => (dispatch) => {
fetch("api/meals", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(meal),
})
.then((res) => res.json())
.then((data) => {
dispatch({ type: CREATE_MEAL, payload: data });
});
};
In my server file, I have the following endpoint created in Express:
app.post("/api/meals", async (req, res) => {
if (!req.body.title) {
return res.send({ message: "Data is required." });
}
const newMeal = new Meal(req.body);
const savedMeal = await newMeal.save();
res.send(savedMeal);
});
My UpdateMenuScreen is as follows:
import React, { Component } from "react";
import { connect } from "react-redux";
import { fetchMeals, deleteMeal, createMeal } from "../actions/mealActions";
class UpdateMenuScreen extends Component {
constructor(props) {
super(props);
this.state = {
meal: null,
showAddMenu: false,
title: "",
};
}
componentDidMount() {
this.props.fetchMeals();
}
componentDidUpdate() {
this.props.fetchMeals();
}
handleInput = (e) => {
this.setState({ [e.target.name]: e.target.value });
};
createMeal = (e) => {
e.preventDefault();
const meal = {
title: this.state.title,
};
this.props.createMeal(meal);
};
deleteMeal(id) {
this.props.deleteMeal(id);
}
render() {
return (
<div>
<h3>Current Menu</h3>
{!this.props.meals ? (
<div>Loading...</div>
) : (
<ul className="meals">
{this.props.meals.map((meal) => (
<li key={meal._id}>
<div className="meal">
<p>{meal.title}</p>
<button
className="button"
onClick={() => this.props.deleteMeal(meal._id)}
>
Delete
</button>
</div>
</li>
))}
</ul>
)}
<button
onClick={() => {
this.setState({ showAddMenu: true });
}}
>
Add New Menu Item
</button>
{this.state.showAddMenu && (
<div className="cart">
<form onSubmit={this.createMeal}>
<ul className="form-container">
<li>
<label>Menu Item Title:</label>
<input
name="title"
type="text"
required
onChange={this.handleInput}
></input>
</li>
<li>
<button className="button primary" type="submit">
Save New Menu Item
</button>
</li>
</ul>
</form>
</div>
)}
</div>
);
}
}
export default connect((state) => ({ meals: state.meals.items }), {
fetchMeals,
deleteMeal,
createMeal,
})(UpdateMenuScreen);
Can anyone see what I'm missing? Or is it not possible to do this all on the same page?
EDIT:
I've console logged this.props.meals in ComponentDidMount and got the following results:
My mealsReducer is as follows:
const { FETCH_MEALS, DELETE_MEAL, CREATE_MEAL } = require("../types");
export const mealsReducer = (state = {}, action) => {
switch (action.type) {
case FETCH_MEALS:
return { items: action.payload };
case DELETE_MEAL:
return { items: action.payload };
case CREATE_MEAL:
return { items: action.payload };
default:
return state;
}
};
I also get this underneath my original error, could it be something in my mealActions that I don't have correct?
Please go to your reducer of meals and define the initial state of meals to []
This should fix your error.
render() {
const { meals = [] } = this.props // default to empty array when it's undefined
return (
<div>
<h3>Current Menu</h3>
{!meals.length ? (
<div>Loading...</div>
) : (
<ul className="meals">
{meals.map((meal) => (
<li key={meal._id}>
<div className="meal">
<p>{meal.title}</p>
<button
className="button"
onClick={() => this.props.deleteMeal(meal._id)}
>
Delete
</button>
</div>
</li>
))}
</ul>
)}
<button
onClick={() => {
this.setState({ showAddMenu: true });
}}
>
Add New Menu Item
</button>
{this.state.showAddMenu && (
<div className="cart">
<form onSubmit={this.createMeal}>
<ul className="form-container">
<li>
<label>Menu Item Title:</label>
<input
name="title"
type="text"
required
onChange={this.handleInput}
></input>
</li>
<li>
<button className="button primary" type="submit">
Save New Menu Item
</button>
</li>
</ul>
</form>
</div>
)}
</div>
);
}
}
I have a list of groups and topics, groups are shown as list items and topics just below. Topics can belong to at least one group. I want to be able to add a click event to the groups so that for each group that's selected is then set in a state, and then the topics state is filtered accordingly.
I should be able to select/deselect groups and the state updates, I know they are immutable so does this mean new state each time?
Each group selected should be set in the selectedGroups state.
I have created a method that checks if a topic belongs to a group
but not sure if this accurate
Groups selected here which should then call the setSelected function to set the state of selected groups... then state of topics is filtered.. hope this is clear.. thanks for any help
<a className="navigator-tags">
-- call selectedGroups to set state..
{item.name}
</a>
Method to set state..
public setSelectedGroups = (topic: IReportGroup) => {
this.setState({
selectedGroups: ...
});
}
Whole react script:
import * as React from 'react';
import './PracticeAreas.css';
import IReportGroup from 'src/model/IReportGroup';
import { IReportTopicSummary, IReportTopic } from 'src/model/IReport';
export interface IReportTopicSummary {
id: string,
name: string
}
export interface IReportTopic {
id: string
name: string
}
interface IOwnProps {
type: 0
}
interface IOwnState {
groups: IReportGroup[],
topics: IReportTopic[],
selectedGroups: IReportGroup[]
}
class PracticeAreas extends React.Component<IOwnProps, IOwnState> {
constructor(props: IOwnProps) {
super(props);
this.state = {
groups: [],
topics: [],
selectedGroups: []
}
}
public render() {
const { topics } = this.state;
return topics ?
this.renderData(topics) :
this.renderLoading();
}
public renderLoading () {
return <div>Loading...</div>;
}
public renderData(data: any) {
// if (data && data.length > 0) {
return (
<div className="col-md-12 practiceAreas">
<h1>Practice Areas</h1>
<div className="item-container plain-bg selection-refinement">
<div className="refinement-search">
<input type="text" value="" placeholder="What are you looking for?" />
</div>
</div>
<ul className="list-inline groupedTags">
{this.state.groups.map((item,i) =>
<li key={i}>
<a className="navigator-tags">
{item.name}
</a>
</li>
)}
</ul>
<div className="row practiceAreasContainer">
{this.state.topics.filter(topic => this.isTopicInCurrentGroup(topic)).map((item,i) =>
<div key={i} className="result">
<div className="col-md-6 result-item">
<div className="item-container default shadowed item-content highlight row">
<div className="col-sm-12 no-padding">
<p>Editor: John Sinclair, Eric Draven, Coco Zames</p>
<p>Beiten Burkhardt</p>
<div className="row no-margin">
<div className="col-12 col-sm-10 text-content">
<h3>
<a href="#" >{item.name}</a>
</h3>
<p className="summary">
Summary
</p>
</div>
<div className="col-10 col-sm-2 links-container rhs">
Compare
<div className="divider" />
View
</div>
</div>
</div>
</div>
</div>
</div>
)}
</div>
<div className="row text-center">
<a className="lex-primary-btn medium-btn">Load more</a>
</div>
</div>
);
// } else {
// return <div>No items found</div>;
// }
}
public componentDidMount() {
fetch(`.../navigator/reports/groups`, {
method: "GET",
headers: {
"Accept": "application/json",
"Content-Type": "application/json"
}})
.then((res) => res.json()
.then((data) => {
this.setState({
groups: data.groups,
topics: data.data
});
}));
}
public setSelectedGroups = (topic: IReportTopic) => {
this.setState({
// selectedGroups: ...
});
}
public isTopicInCurrentGroup = (topic: IReportTopic) => {
return (this.state.selectedGroups.length > 0 ? this.state.selectedGroups.some(item => topic.id === item.id) : true)
}
}
export default PracticeAreas
If I understood correctly, you are trying to do the following:
public setSelectedGroups = (topic: IReportTopic) => {
if (this.isTopicInCurrentGroup(topic)) {
this.setState(state => ({
selectedGroups: state.selectedGroups.filter(t => t.id !== topic.id)
}));
} else {
this.setState(state => ({
selectedGroups: [...state.selectedGroups, topic]
}));
}
}
I'm trying to make simple CRUD example using react.js as frontend.
I already have add/edit functionality done in a component,
but I want to call this component dynamically on click and show it as a popup or modal window on the same page without redirecting to another route.
Does anyone have experience with doing this using react.js?
This is my parent component code where I show a grid of items displaying cities:
import * as React from 'react';
import { RouteComponentProps } from 'react-router';
import { Link, NavLink } from 'react-router-dom';
interface FetchNaseljeDataState {
nasList: NaseljeData[];
loading: boolean;
}
export class FetchNaselje extends React.Component<RouteComponentProps<{}>, FetchNaseljeDataState> {
constructor() {
super();
this.state = { nasList: [], loading: true };
fetch('api/Naselje/Index')
.then(response => response.json() as Promise<NaseljeData[]>)
.then(data => {
this.setState({ nasList: data, loading: false });
});
// This binding is necessary to make "this" work in the callback
this.handleDelete = this.handleDelete.bind(this);
this.handleEdit = this.handleEdit.bind(this);
}
public render() {
let contents = this.state.loading
? <p><em>Loading...</em></p>
: this.renderNaseljeTable(this.state.nasList);
return <div>
<h1>Naselje Data</h1>
<p>This component demonstrates fetching Naselje data from the server.</p>
<p>
<Link to="/addnaselje">Create New</Link>
</p>
{contents}
</div>;
}
// Handle Delete request for an naselje
private handleDelete(id: number) {
if (!confirm("Do you want to delete naselje with Id: " + id))
return;
else {
fetch('api/Naselje/Delete/' + id, {
method: 'delete'
}).then(data => {
this.setState(
{
nasList: this.state.nasList.filter((rec) => {
return (rec.idnaselje != id);
})
});
});
}
}
private handleEdit(id: number) {
this.props.history.push("/naselje/edit/" + id);
}
// Returns the HTML table to the render() method.
private renderNaseljeTable(naseljeList: NaseljeData[]) {
return <table className='table'>
<thead>
<tr>
<th></th>
<th>ID Naselje</th>
<th>Naziv</th>
<th>Postanski Broj</th>
<th>Drzava</th>
</tr>
</thead>
<tbody>
{naseljeList.map(nas =>
<tr key={nas.idnaselje}>
<td></td>
<td>{nas.idnaselje}</td>
<td>{nas.naziv}</td>
<td>{nas.postanskiBroj}</td>
<td>{nas.drzava && nas.drzava.naziv}</td>
<td>
<a className="action" onClick={(id) => this.handleEdit(nas.idnaselje)}>Edit</a> |
<a className="action" onClick={(id) => this.handleDelete(nas.idnaselje)}>Delete</a>
</td>
</tr>
)}
</tbody>
</table>;
}
}
export class NaseljeData {
idnaselje: number = 0;
naziv: string = "";
postanskiBroj: string = "";
drzava: DrzavaData = { iddrzava: 0, naziv: ""};
drzavaid: number = 0;
}
export class DrzavaData {
iddrzava: number = 0;
naziv: string = "";
}
This is my child component that I want to dynamically show on create new link click:
import * as React from 'react';
import { RouteComponentProps } from 'react-router';
import { Link, NavLink } from 'react-router-dom';
import { NaseljeData } from './FetchNaselje';
import { DrzavaData } from './FetchNaselje';
interface AddNaseljeDataState {
title: string;
loading: boolean;
drzavaList: Array<any>;
nasData: NaseljeData;
drzavaId: number;
}
export class AddNaselje extends React.Component<RouteComponentProps<{}>, AddNaseljeDataState> {
constructor(props) {
super(props);
this.state = { title: "", loading: true, drzavaList: [], nasData: new NaseljeData, drzavaId: -1 };
fetch('api/Naselje/GetDrzavaList')
.then(response => response.json() as Promise<Array<any>>)
.then(data => {
this.setState({ drzavaList: data });
});
var nasid = this.props.match.params["nasid"];
// This will set state for Edit naselje
if (nasid > 0) {
fetch('api/Naselje/Details/' + nasid)
.then(response => response.json() as Promise<NaseljeData>)
.then(data => {
this.setState({ title: "Edit", loading: false, nasData: data });
});
}
// This will set state for Add naselje
else {
this.state = { title: "Create", loading: false, drzavaList: [], nasData: new NaseljeData, drzavaId: -1 };
}
// This binding is necessary to make "this" work in the callback
this.handleSave = this.handleSave.bind(this);
this.handleCancel = this.handleCancel.bind(this);
}
public render() {
let contents = this.state.loading
? <p><em>Loading...</em></p>
: this.renderCreateForm(this.state.drzavaList);
return <div>
<h1>{this.state.title}</h1>
<h3>Naselje</h3>
<hr />
{contents}
</div>;
}
// This will handle the submit form event.
private handleSave(event) {
event.preventDefault();
const data = new FormData(event.target);
// PUT request for Edit naselje.
if (this.state.nasData.idnaselje) {
fetch('api/Naselje/Edit', {
method: 'PUT',
body: data,
}).then((response) => response.json())
.then((responseJson) => {
this.props.history.push("/fetchnaselje");
})
}
// POST request for Add naselje.
else {
fetch('api/Naselje/Create', {
method: 'POST',
body: data,
}).then((response) => response.json())
.then((responseJson) => {
this.props.history.push("/fetchnaselje");
})
}
}
// This will handle Cancel button click event.
private handleCancel(e) {
e.preventDefault();
this.props.history.push("/fetchnaselje");
}
// Returns the HTML Form to the render() method.
private renderCreateForm(drzavaList: Array<any>) {
return (
<form onSubmit={this.handleSave} >
<div className="form-group row" >
<input type="hidden" name="idnaselje" value={this.state.nasData.idnaselje} />
</div>
< div className="form-group row" >
<label className=" control-label col-md-12" htmlFor="Naziv">Naziv</label>
<div className="col-md-4">
<input className="form-control" type="text" name="naziv" defaultValue={this.state.nasData.naziv} required />
</div>
</div >
<div className="form-group row">
<label className="control-label col-md-12" htmlFor="PostanskiBroj" >Postanski broj</label>
<div className="col-md-4">
<input className="form-control" name="PostanskiBroj" defaultValue={this.state.nasData.postanskiBroj} required />
</div>
</div>
<div className="form-group row">
<label className="control-label col-md-12" htmlFor="Drzava">Država</label>
<div className="col-md-4">
<select className="form-control" data-val="true" name="drzavaid" defaultValue={this.state.nasData.drzava ? this.state.nasData.drzava.naziv : ""} required>
<option value="">-- Odaberite Državu --</option>
{drzavaList.map(drzava =>
<option key={drzava.iddrzava} value={drzava.iddrzava}>{drzava.naziv}</option>
)}
</select>
</div>
</div >
<div className="form-group">
<button type="submit" className="btn btn-default">Save</button>
<button className="btn" onClick={this.handleCancel}>Cancel</button>
</div >
</form >
)
}
}
I'm assuming I'll have to make css for the create/edit component to make it look like a popup...
EDIT: I would appreciate if someone could make code example using my classes, thanks...
In the parent component set a state on click functionality, say for eg:
this.setState({display: true})
In the parent component render based on condition display child component, say for eg:
<div>{(this.state.display) ? <div><childComponent /></div> : ''}</div>
To display the child component in a modal/popup, put the component inside say a bootstrap or react-responsive-modal. For that, you have to install and import react-responsive-modal and then
In the render method,
return (
<div>
{this.state.toggleModal ? <div className="container">
<Modal open={this.state.toggleModal} onClose={this.onCloseModal} center>
<div className="header">
<h4>{Title}</h4>
</div>
<div className="body">
<div>
{this.state.toggleModal ? <someComponent /> : ''}
</div>
</div>
</Modal>
</div>
: null}
</div>
)
Have your popup component receive a prop from the parent that will tell it if it should be displayed or not, a simple boolean will do the trick. Then, when you want something to show the popup, just change that state in the parent.
I am making my first app with Javascript and React and started with a page which views a shopping list. It gets the items from an api call.
If the user clicks on the button 'done' (or should I use an checkbox?) This product should go to the bottom of the list (and be grayed out with css but thats not the problem).
The problem is, I have no clue how to do this. Can anyone help me out a bit?
This is my code:
import React from 'react';
//import image from '../images/header.png';
//import Collapsible from './Collapsible';
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
isLoading: true,
orders: []
}
}
componentWillMount() {
localStorage.getItem('orders') && this.setState({
orders: JSON.parse(localStorage.getItem('orders')),
isLoading: false
})
}
componentDidMount() {
if (!localStorage.getItem('orders')){
this.fetchData();
} else {
console.log('Using data from localstorage');
}
}
fetchData() {
fetch('http://localhost:54408/api/orders/all/15-03-2018')
.then(response => response.json())
.then(parsedJSON => parsedJSON.map(product => (
{
productname: `${product.ProductName}`,
image: `${product.Image}`,
quantity: `${product.Quantity}`,
isconfirmed: `${product.IsConfirmed}`,
orderid: `${product.OrderId}`
}
)))
.then(orders => this.setState({
orders,
isLoading: false
}))
.catch(error => console.log('parsing failed', error))
}
componentWillUpdate(nextProps, nextState) {
localStorage.setItem('orders', JSON.stringify(nextState.orders));
localStorage.setItem('ordersDate', Date.now());
}
render() {
const {isLoading, orders} = this.state;
return (
<div>
<header>
<img src="/images/header.jpg"/>
<h1>Boodschappenlijstje <button className="btn btn-sm btn-danger">Reload</button></h1>
</header>
<div className={`content ${isLoading ? 'is-loading' : ''}`}>
<div className="panel">
{
!isLoading && orders.length > 0 ? orders.map(order => {
const {productname, image, quantity, orderid} = order;
return<div className="product" key={orderid}>
<div className="plaatjediv">
<img className="plaatje" src={image} />
</div>
<div className="productInfo">
<p>{productname}</p>
<p>Aantal: {quantity}</p>
<p>ID: {orderid}</p>
</div>
<div className="bdone">
<button className="btn btn-sm btn-default btndone">Done</button>
</div>
</div>
}) : null
}
</div>
<div className="loader">
<div className="icon"></div>
</div>
</div>
</div>
);
}
}
export default App;
You can achieve by using this :
this.handleDoneAction = event = > {
let itemIndex = event.target.getAttribute("data-itemIndex");
let prevOrders = [...this.state.orders];
var itemToMoveAtLast = prevOrders.splice(itemIndex, 1);
var updatedOrderList = prevOrders.concat(itemToMoveAtLast);
this.setState({order: updatedOrderList})
}
I have attach an event handler on the button handleDoneAction.
<button className="btn btn-sm btn-default btndone" data-itemIndex={index} onClick={this.handleDoneAction}>Done</button>
the attribute data-itemIndex is the index of the object in orders array.
And your map function will be like this:
orders.map((order, index) => {
//content
})
ANd for the different style effects on the done products, I will suggest you to use different array for all done products.