The Problem
I'm fetching the data from an API and I want to show the options with title, images and a button when the user clicks its redirect to the post page
Fetching Code
const [posts, setPosts] = useState([]);
const { search } = useLocation();
useEffect(() => {
const fetchPosts = async () => {
const res = await axios.get("/posts" + search);
setPosts(res.data);
};
fetchPosts();
}, [search]);
Material UI Autocomplete Input Code
return(
<Stack spacing={2} sx={{ width: 300 }}>
<Autocomplete
freeSolo
id="free-solo-2-demo"
getOptionLabel={(posts) => `${posts.title}`}
disableClearable
options={posts}
noOptionsText={"Not Found"}
sx={{ width: "100%", margin: "10px", background: "#fff" }}
renderInput={(params) => (
<TextField
{...params}
label="Search..."
InputProps={{
...params.InputProps,
type: 'search',
}}
/>
)}
/>
</Stack>
);
I tried
adding ${post.photo} to the code but didn't work, it showed the image path (Name) next to the title only
getOptionLabel={(posts) => `${posts.title} ${posts.photo}`}
My question is: can I view images and clickable buttons to Material UI Autocomplete options? if yes how? if no what other options do I have?
Related
I want to create a frontend for my app using React and TypeScript.
I am gathering data from my backend (simple DRF app) and fetching it using Axios and pushing it into separate arrays.
const myApi = axios.create({
baseURL: 'http://127.0.0.1:8000/app/',
headers: {
"Content-type": "application/json"
}
})
let idArr:number [] = new Array()
let titleArr:string [] = new Array()
let contentArr:string [] = new Array()
myApi.get('/getallarticles').then( res => {
for(let elem in res.data)
{
idArr.push(res.data[elem].id)
titleArr.push(res.data[elem].title)
contentArr.push(res.data[elem].content)
}
})
The app contains a page named feed into which I parse the id Array.
function App() {
console.log(idArr)
return (
<Box>
<Navbar/>
<Grid alignItems={"center"} justifyContent={"center"} marginLeft={'15%'} marginRight={'15%'}>
<Stack spacing={2} margin={2}>
<Box id='res_viewer'>
<Feed content={"CONTENT FROM MAIN"} id={10} title={"Sherlock Holmes"} total={5} idArr={idArr}/>
</Box>
</Stack>
</Grid>
</Box>
);
}
Then in my page Feed I copy the ActionCard (from Mui5) and the problem is that all the Cards show only if I recompile the project. It only works one time, and I have no idea why. Moreover if I put the console.log and alert into the Feed the console.log would show an array every time, while Alert only the first time. Anyone have any idea what the issue might be?
const Feed = ({ id, title, content, total, idArr }: { id: number; title: string; content: string; total: number; idArr:Array<number>}) => {
console.log(idArr)
alert(idArr)
return(
<div>
{idArr.map(customId =>
<Card key={customId} variant="outlined" sx={{ margin: 0 }}>
<CardActionArea>
<CardContent>
<Typography gutterBottom variant="h5" component="div">
{customId}
</Typography>
<Typography variant="body2" color="text.secondary">
{content}
</Typography>
</CardContent>
</CardActionArea>
<CardActions>
<Button style={{
color: "#ff0000",
}} size="large" startIcon={<DeleteForever />}>
DELETE
</Button>
<Button style={{
color: "#68ee06",
}} size="large" startIcon={<Edit />}>
EDIT
</Button>
</CardActions>
</Card>
)}
</div>
)
}
export default Feed
I suspect the asynchronous nature of the call to your API is what's giving this behavior. When making calls to an API for data, it's common to use a useEffect hook that updates a state. This ensures that the UI is in sync with the data. Try rewriting as follows:
const myApi = axios.create({
baseURL: 'http://127.0.0.1:8000/app/',
headers: {
"Content-type": "application/json"
}
})
const [idArr, setIdArr] = useState([] as number[]);
const [titleArr, setTitleArr] = useState([] as string[]);
const [contentArr, setContentArr] = useState([] as string[]);
useEffect(() => {
myApi.get('/getallarticles').then(res => {
const newIds: number[] = [];
const newTitles: string[] = [];
const newContents: string[] = [];
for (let elem in res.data) {
newIds.push(res.data[elem].id);
newTitles.push(res.data[elem].title);
newContents.push(res.data[elem].content);
}
setIdArr(newIds);
setTitleArr(newTitles);
setContentArr(newContents);
});
}, []);
So, I am currently working on a program that is supposed to be a mock-up of a Student Educational Plan for a school. The basic gist of it is that it displays a home page with buttons to a form to submit information (text input has buttons to submit or clear the information entered into the to an AsyncStorage database and a page that displays reports of information collected from the form. All pages display somewhat normally, but for some reason, when I press the submit button, the page freezes and does not submit the info. Here is the code I have as of right now. I am stuck trying to figure out how to use AsyncStorage in the context, which is why I don't have it added here. Another thing to add is that the names in the StyleSheet are the way they are is because I had originally based the code from a previous program (mock-up Music Player) I had worked on. Please explain everything as simply as possible.
Edit: I have tried to do more with the AsyncStorage, but the buttons don't work properly. Below is the edited version of the previous code.
import { Image, ScrollView, StyleSheet, Text, View, Button, TextInput,} from 'react-native';
//import {AsyncStorage} from '#react-native-async-storage/async-storage';
import { NavigationContainer } from '#react-navigation/native';
import { createNativeStackNavigator } from '#react-navigation/native-stack';
const Stack = createNativeStackNavigator();
const MyStack = () => {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen
name="Home"
component={HomeScreen}
options={{ title: 'Welcome' }}
/>
<Stack.Screen name="SEP Form" component={SEPScreen} />
<Stack.Screen name="SEP Database" component={SEPInfo} />
</Stack.Navigator>
</NavigationContainer>
);
};
const HomeScreen = ({ navigation }) => {
return (
<View style={styles.container}>
<Button
title="Your Student Educational Plan"
onPress={() =>
navigation.navigate('SEP Form')
}
/>
<Button
title="All Student Educational Plans"
onPress={() =>
navigation.navigate('SEP Database')
}
/>
</View>
);
};
const SEPScreen = ({ navigation, route }) => {
const[SEPInfo, setForm] = useState({});
const save = async() => {
try{
await AsyncStorage.setItem(SEPInfo)
if (SEPInfo.name == null || SEPInfo.name == '') {
alert("Please Enter your Name");
}
else if (SEPInfo.id == null || SEPInfo.id == '') {
alert("Please Enter your ID");
}
else if (SEPInfo.major == null || SEPInfo.major == '') {
alert("Please Enter your Major");
}
else if (SEPInfo.goal == null || SEPInfo.goal == '') {
alert("Please Enter your Goal");
}
else {
await AsyncStorage.setItem("form", JSON.stringify(SEPInfo));
alert(`Form Saved! Name: ${SEPInfo.name}\nID: ${SEPInfo.id}\nMajor: ${SEPInfo.major}\nGoal: ${SEPInfo.goal}`)
}
}
catch (error) {
alert(error);
};
/* const clear = async() => {
try{
await AsyncStorage.removeItem(SEPInfo);
setForm({});
alert('Form Cleared');
}
catch (error){
alert(error);
}
}; */
const load = async () => {
try {
let form = JSON.parse (await AsyncStorage.getItem(form));
if (form !== null) {
setForm(SEPInfo);
}
}
catch (error) {
alert(error);
}
};
}
function clear () {
setState({name:'', id:'', major:'', goal:''});
alert('Form has been cleared!');
}
return (
<View style={styles.container}>
<Text style={styles.sectionTitle}>Enter your info here:</Text>
<TextInput style={styles.input} name="name" label="StudentName " placeholder="Name" onChangeText={(text) => setForm({...SEPInfo, name: text})} />
<TextInput style={styles.input} name="id" label="StudentID " placeholder="ID" onChangeNumber={(number) => setForm({...SEPInfo, id: number})} />
<TextInput style={styles.input} name="major" label="Major " placeholder="Major" onChangeText={(text) => setForm({...SEPInfo, major: text})} />
<TextInput style={styles.input} name="goal" label="Goal " placeholder="Goal" onChangeText={(text) => setForm({...SEPInfo, goal: text})} />
<Button title="Submit"
onPress={() => save()
}
></Button>
<Button title="Clear"
onPress={clear}
></Button>
</View>
);
};
const SEPInfo = ({navigation, route}) => {
return(
<View style={styles.container}>
<Text style={styles.sectionTitle}>SEP Database</Text>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#CFEAF1'
},
listWrapper: {paddingTop: 80, paddingHorizontal: 20},
sectionTitle: {fontWeight: 'bold', fontSize: 30, margin: 5, color: '#2E2090', alignSelf: 'center'},
sectionTitleTwo: {fontWeight: 'bold', fontSize: 25, margin: 5, color: '#206290', alignSelf: 'center'},
flexContainerWaiting: {backgroundColor: '#20BD57', padding: 15, borderRadius: 10, flexDirection: 'row', alignItems: 'center', marginTop: 5, marginBottom: 5, justifyContent: 'space-evenly'},
flexContainerOne: {backgroundColor: '#0B97B0', fontSize: 16, flex: 0.5, padding: 15, borderRadius: 10, flexDirection: 'row', alignItems: 'center', marginTop: 2.5, marginBottom: 2.5, justifyContent: 'space-evenly'},
flexContainerTwo: {backgroundColor: '#D773EB', fontSize: 16, flex: 0.5, padding: 15, borderRadius: 10, flexDirection: 'row', alignItems: 'center', marginTop: 2.5, marginBottom: 2.5, justifyContent: 'space-evenly'},
input: {
height: 40,
margin: 12,
borderWidth: 1,
padding: 10,
},
});
export default MyStack;
Looking at the AsyncStorage docs (https://reactnative.dev/docs/asyncstorage#setitem), it says that when using setItem(), you need to pass a key: string and a value: string. In your code, you only seem to pass a value: AsyncStorage.setItem(TextInput). Furthermore, you pass the value TextInput into setItem(), but you don't seem to define TextInput anywhere. What you should do is use the value prop and onChangeText prop to store the value of the TextInput in a state variable, as is shown on the docs: https://reactnative.dev/docs/textinput. Then, pass that state variable into setItem() as they value and pass another item as its key.
for saving an item in async storage:-
AsyncStorage.setItem('usertoken', token);
for fetching an item from async storage:-
// need to be called inside an async function
let token = await AsyncStorage.getItem('userToken');
How do I make Materia-UI GridList Face vertically in One Row on Mobile?
My GridList is two in a row which is what I wanted but when I inspect it on mobile it still shows two in a row instead of One. that means it's not responsive, How do I make it responsive?
this is my Events Component.
import React from "react";
import { Link } from "react-router-dom";
import axios from "axios";
import GridList from "#material-ui/core/GridList";
import GridListTile from "#material-ui/core/GridListTile";
import GridListTileBar from "#material-ui/core/GridListTileBar";
import ListSubheader from "#material-ui/core/ListSubheader";
mport IconButton from "#material-ui/core/IconButton";
import Button from "#material-ui/core/Button";
import { withStyles } from "#material-ui/core/styles";
import Typography from "#material-ui/core/Typography";
const useStyles = (theme) => ({
root: {
display: "flex",
flexWrap: "wrap",
justifyContent: "space-around",
overflow: "hidden",
backgroundColor: theme.palette.background.paper,
},
gridList: {
width: 1000,
height: 950,
},
icon: {
color: "rgba(255, 255, 255, 0.54)",
},
});
class EventsList extends React.Component {
constructor(props) {
super(props);
this.state = { events: [] };
}
componentDidMount() {
axios
.get("http://localhost:9000/events/")
.then((response) => {
this.setState({ events: response.data });
})
.catch(function (error) {
console.log(error);
});
}
render() {
const { classes } = this.props;
return (
<div className={classes.root}>
<GridList cellHeight={330} className={classes.gridList} spacing={8}>
<GridListTile key="Subheader" cols={2} style={{ height: "auto" }}>
<ListSubheader component="div">December</ListSubheader>
</GridListTile>
{this.state.events.map((tile) => (
<GridListTile key={tile.eventImage}>
<img src={tile.eventImage} alt={tile.title} />
<GridListTileBar title={tile.title} titlePosition="top" />
<GridListTileBar
paragraph={tile.description}
actionIcon={
<IconButton
aria-label={`info about ${tile.title}`}
className={classes.icon}
></IconButton>
}
/>
</GridListTile>
))}
</GridList>
</div>
);
}
}
export default withStyles(useStyles)(EventsList);
this is the List of my events displayed by GridList on Mobile
but something like this is what I want to see when I am on Mobile.
Since you are using class based component, you can use withWidth to conditionally set cols value to GridList.
See working example here in codesandbox
Example code snippet
class TitlebarGridList extends Component {
render() {
const { classes, width } = this.props; // <---- see here
let columns = width === "xs" || width === "sm" ? 1 : 2;
console.log("=========>", width);
return (
<div className={classes.root}>
<GridList cellHeight={200} className={classes.gridList} cols={columns}> {/* //<---- see here */}
<GridListTile key="Subheader" cols={3} style={{ height: "auto" }}>
<ListSubheader component="div">December</ListSubheader>
</GridListTile>
{tileData.map(tile => (
<GridListTile key={tile.title}>
<img className={classes.image} src={tile.img} alt={tile.title} />
</GridListTile>
))}
</GridList>
</div>
);
}
}
TitlebarGridList.propTypes = {
classes: PropTypes.object.isRequired
};
export default compose(
withStyles(styles, {
name: "TitlebarGridList"
}),
withWidth() //<---- see here
)(TitlebarGridList);
Note: withWidth will be deprecated sometime in the future, so consider to use useMediaQuery hook (in that case you have to switch to functional component).
I'm really new to js and I'm trying to build a full-stack web app. I've built most of the front-end React code and my back-end file has the connection and query to the db.
I'm trying to connect both of those files together, but as soon as i try to import 'express' in any of the files, it breaks. Without importing express it works alright.
Giving me this error on the browser when it runs: "TypeError: Unable to get property 'prototype' of undefined or null reference"
Index.js (trying to import Express here)
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
// import './index.css';
// var express = require('express')
// var app = express()
var Connection = require('tedious').Connection;
var Request = require('tedious').Request;
// const axios = require('axios')
ReactDOM.render(<App />, document.getElementById('root'));
// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers:
serviceWorker.unregister();
App.js (front-end)
import React from 'react';
import TextField from '#material-ui/core/TextField';
import Button from '#material-ui/core/Button';
import Box from '#material-ui/core/Box';
import { createMuiTheme } from '#material-ui/core/styles';
import { ThemeProvider } from '#material-ui/styles';
import { Typography } from '#material-ui/core';
// var express = require('express')
// var app = express()
//change the primary colours of the UI
const theme = createMuiTheme({
palette: {
primary: {
main: '#00a843',
background: 'linear-gradient(45deg, #FE6B8B 30%, #FF8E53 90%)',
},
},
});
export default class App extends React.Component
{
constructor(props) {
super(props);
this.handleEnterClick = this.handleEnterClick.bind(this);
this.handleSaveClick = this.handleSaveClick.bind(this);
this.state = {showAll: false, inputEmail: "", successfullySaved: true, newList: ""};
}
async handleEnterClick() {
//send the entered email to the server
// var xhr = new XMLHttpRequest()
// get a callback when the server responds
// xhr.addEventListener('load', () => {
// update the state of the component with the result here
// console.log(xhr.responseText)
// })
// open the request with the verb and the url
// xhr.open('PUT', '/api/controllers/AppController/enter')
// send the request
// xhr.send(JSON.stringify({ yourEmail: this.state.inputEmail }))
/*
const response = await fetch('/api/controllers/AppController/enter', {
method: 'PUT',
body: JSON.stringify({ email: this.state.inputEmail }),
})
console.log(await response.json())
*/
//update the UI to reflect the button being pressed
this.setState({showAll: true});
}
handleSaveClick() {
//save to the datbase
var xhr = new XMLHttpRequest()
// get a callback when the server responds
xhr.addEventListener('load', () => {
// update the state of the component with the result here
console.log(xhr.responseText)
})
// open the request with the verb and the url
xhr.open('PUT', '/api/controllers/AppController/save')
// send the request
xhr.send(JSON.stringify({ newList: this.state.newList }))
//update the UI to reflect the save
this.setState({showAll: false, inputEmail: "", successfullySaved: true, newList: ""});
//add a successful save message
}
render () {
return (
<form display="flex" noValidate autoComplete="off">
<ThemeProvider theme={theme}>
<div align = 'center'>
<TextField
id="tfInputEmail"
value={this.state.inputEmail}
onKeyPress={(ev) => {
if (ev.key === 'Enter') {
this.setState({showAll: true});
ev.preventDefault();
}
}}
onChange={e => this.setState({ inputEmail: e.target.value })}
style={{ width: 600}}
placeholder="first_last"
autoComplete = "email"
InputLabelProps={{
shrink: true,
style: {fontSize: 17, fontFamily: "Helvetica"}
}}
inputProps={{
style: {fontFamily: "Helvetica"}
}}
variant="outlined"
/>
<br/>
<Button id="btnEnter" variant="contained" color="primary"
style={{ marginTop: 15, marginBottom: 35, width: 600, height: 35}} onClick={this.handleEnterClick}>
Enter
</Button>
<PressedEnter show={this.state.showAll} click={this.handleSaveClick} list={this.state.newList}/>
</div>
</ThemeProvider>
</form>
);
}
}
function PressedEnter(props)
{
if(!props.show) {
return null;
}
return (
<div >
<Box fontSize={18} fontFamily="Helvetica" marginLeft="9px"
marginBottom="20px" marginTop="30px">
Check spelling and formatting before clicking 'Update your list' below. There's no error checking at the moment.
</Box>
<div width="100%" horizontal layout >
{
// REPLACE THE ABOVE DIV WITH A GRID LAYOUT FROM MATERIAL UI
}
{// Vertical layout for the textbox and its label
}
<div>
<Box fontSize={16} fontFamily="Helvetica"
marginBottom="10px" marginTop="20px">
Add/change/delete emails and separate by semicolon (;) here:
</Box>
<TextField
id="tfChangeList"
multiline
style={{ margin: 8, width: 446}}
InputLabelProps={{
shrink: true,
style: {fontSize: 20, fontFamily: "Helvetica"}
}}
inputProps={{
style: {height:300, fontFamily: "Helvetica"}
}}
variant="outlined"
/>
</div>
{// Vertical layout for the textbox and its label
}
<div>
<Box fontSize={16} fontFamily="Helvetica"
marginBottom="10px" marginTop="20px">
View your current Pulse Check list here:
</Box>
<TextField
disabled
id="tfCurrentList"
multiline
style={{ margin: 8, width: 446}}
InputLabelProps={{
shrink: true,
style: {fontSize: 20, fontFamily: "Helvetica"}
}}
inputProps={{
style: {height:300, fontFamily: "Helvetica"}
}}
variant="outlined"
/>
</div>
</div>
<br/>
<Button id="btnSave" variant="contained" color="primary" style={{ marginLeft: 8, marginBottom: 8,
marginTop: -8}} onClick={props.click}>
Save your Updates!
</Button>
</div>
);
}
);
}
Your full-stack web app should run both the client and server individually - i.e. run your Express application on localhost:3000 (the back end) and send HTTP requests to it from your client (React) running in a separate webpack server (the front end).
Because an application is "full-stack", this doesn't mean that the front and back ends are combined. These are separate. Hope this helps.
I am new to React and I load all the data from my database initially on page load but there is info I need to find in an array and apparently it isn't instant. What do I need to do to make sure the render method only renders the objects when the object promises have resolved?
I haven't tried much... I'm really stuck here.
This seems different than the other problems I've read here because I load a bunch on info in the beginning just fine but I need to call some team information every time a function is called so it isn't as simple as loading it once because the object i need is always different.
This code is the main issue. I also included the full file below:
I did some modification to the code in a edit: I realized that I just need to call the opponent team because I have the player team already.
if (team.id === game.team_1) {
var redTeam = team;
// set blueTeam based on game.team_1
// firebase.teams().doc('teams/{game.team_2}')
} else {
var blueTeam = team;
// set redTeam based on game.team_1
// firebase.teams().doc('teams/{game.team_1}')
}
Full file:
import React, { Component } from 'react';
import { Link } from 'react-router-dom';
import Async from 'react-promise'
import { withFirebase } from '../Firebase';
// import * as ROUTES from '../../constants/routes';
import { Container, Image, Spinner, Col, Row, Card, Accordion, Button } from 'react-bootstrap'
class PlayerGameList extends Component {
constructor(props) {
super(props);
this.state = {
loadingTeams: false,
loadingSchedule: false,
teams: [],
schedule: []
};
}
componentDidMount() {
this.setState({
loadingTeams: true,
loadingSchedule: true,
});
this.unsubscribe = this.props.firebase
.teams()
.where('players', 'array-contains', '-LXkkB7GNvYrU4UkUMle')
.onSnapshot(snapshot => {
let teams = [];
snapshot.forEach(doc =>
teams.push({ ...doc.data(), uid: doc.id }),
);
this.setState({
teams,
loadingTeams: false,
});
});
this.unsubscribe2 = this.props.firebase
.schedule()
.onSnapshot(snap => {
let schedule = [];
snap.forEach(doc =>
schedule.push({ ...doc.data(), uid: doc.id }),
);
this.setState({
schedule,
loadingSchedule: false,
});
});
}
componentWillUnmount() {
this.unsubscribe();
this.unsubscribe2();
}
render() {
const { teams, schedule, loadingTeams, loadingSchedule } = this.state;
return (
<div>
<h2>Games</h2>
{loadingTeams && loadingSchedule && <div colSpan="12"><Spinner animation="border" role="status">
<span className="sr-only">Loading...</span>
</Spinner></div>}
{/* CONTENT */}
<Container fluid>
<Row>
{getTeams({ teams, schedule })}
</Row>
</Container>
</div >
);
}
}
function getTeams({ teams, schedule }) {
if (!teams) {
return null;
}
if (!teams.length) {
return null;
} else {
return teams.map(team => getGames({ team, schedule }))
}
}
function getGames({ team, schedule }) {
schedule.sort((a, b) => (a.time > b.time) ? -1 : 1)
if (!schedule) {
return null;
}
if (!schedule.length) {
return null;
} else {
return schedule.map(game => guts({ team, game }));
}
}
function guts({ team, game }) {
const image = {
height: '25px',
width: '25px'
}
if (team.id === game.team_1) {
var redTeam = team;
// set blueTeam based on game.team_1
// firebase.teams().doc('teams/{game.team_2}')
} else {
var blueTeam = team;
// set redTeam based on game.team_1
// firebase.teams().doc('teams/{game.team_1}')
}
if (game.team_1 === team.id || game.team_2 === team.id) {
var time = new Date(game.time.seconds * 1000);
var dateFormat = require('dateformat');
var finalTime = dateFormat(time, 'ddd mmm dd, h:MM tt')
return (
<Col lg='4' md='6' sm='12' key={game.uid} style={{ marginBottom: '15px' }}>
<Card>
<Card.Body>
<Row>
<Image src={team.logo} style={image} roundedCircle />
<p>{team.name}</p>
<div style={{ height: '25px', width: '25px', backgroundColor: 'red' }}></div>
</Row>
<Row>
<Image src={team.logo} style={image} roundedCircle />
<p>{team.name}</p>
<div style={{ height: '25px', width: '25px', backgroundColor: 'blue' }}></div>
</Row>
<Row>
<div>
{finalTime}
</div>
</Row>
</Card.Body>
<Accordion>
<Card style={{ margin: '0', padding: '0' }}>
<Card.Header>
<Accordion.Toggle as={Button} variant="link" eventKey="0">
Show Match IDs
</Accordion.Toggle>
</Card.Header>
<Accordion.Collapse eventKey="0">
<Card.Body>{game.match_id}</Card.Body>
</Accordion.Collapse>
</Card>
</Accordion>
</Card>
</Col>
);
}
}
export default withFirebase(PlayerGameList);
The items all load blank then a few seconds later all the console logs come through with the array objects. When I tell it to await the program just throws an error.