How i can set user Image & avatar in the list, if user upload image then show user image otherwise its show the avatar - material-ui

const ImageDetail = ({ record = {} }) => {
return (
<div>
<Avatar style={{ backgroundColor: colour() }}>
{record.MemberFirstName && record.MemberFirstName.charAt(0)}
{record.MemberLastName && record.MemberLastName.charAt(0)}
</Avatar>
</div>
)
}
This is my Avatar code.
<List>
<Datagrid>
<ImageField label="Image" source="MemberImage.src" />
<ImageDetail source="" label="Image" />
</Datagrid>
</List>
Now I'm using both but I need only one at a time if the user uploads an image show the image otherwise it shows an avatar.

It is enough to pass the image source to the <Avatar/>: src prop and it will do the rest - display the image if available or the default avatar image if not.
<List>
<Datagrid>
<AvatarField label="Image" source="MemberImage.src"/>
</Datagrid>
</List>
...
const AvatarField = ({record, source}) => {
return <Avatar src={record && record[source]}/>
}
Though if you really want to access nested property of the record - e.g. MemberImage["src"] - you might need to swap record[source] with lodash's get utility method -> get(record, source)

const ImageDetail = ({ source, record = {} }) => {
return (
<div>
{(record.MemberImage != undefined && record.MemberImage.src != undefined) ? (
<img width='35' height='35' src={`${record.MemberImage.src}`} />)
: (<Avatar style={{ backgroundColor: colour() }}>
{record.MemberFirstName && record.MemberFirstName.charAt(0)}
{record.MemberLastName && record.MemberLastName.charAt(0)}
</Avatar>)
}
</div >
)
}

Related

React-hook-form + dynamic form: Render element upon dropdown selection

I am working in form using react-hook-form. This form use useFieldArray, it has to be dynamic.
Right now is very simple, it contains a react-select component with a few options and a textfield that get rendered depending on the option that the user select on the select component.
The problem I have is that the textfield component renders when the state updates, which is correct until I add a new group of element to the form. Since the textfield is listening to the same state it doesn't matter which select I use to render the textfield element, it gets rendered in all groups.
I am looking a way to specify which textfield should be rendered when the user change the select.
I the sandbox you can see what I have done. To reproduce the problem click on the "Add"-button and you will see two areas, each one with a select component.
When you choose "Other" in the select component a textfield appears, but not only in the area where the select was changed but in all areas.
How can I avoid that behavior?
https://codesandbox.io/s/vibrant-fast-381q0?file=/src/App.tsx
Extract:
const [isDisabled, setIsDisabled] = useState<boolean>(true);
const { control, handleSubmit, getValues } = useForm<IFormFields>({
defaultValues: {
managerialPositions: [
{
authority: 0,
chiefCategory: 0,
title: 0,
otherTitle: ""
}
]
}
});
useFieldArray implementation:
const {
fields: managerialPositionsFields,
append: managerialPositionsAppend,
remove: managerialPositionsRemove
} = useFieldArray({
name: "managerialPositions",
control
});
Here i update the state when the user select "Other title" in the select component:
const watchChange = (value?: number, i?: number) => {
let values: any = getValues();
if (values.managerialPositions[i].title === 3) {
setIsDisabled(false);
}
};
And here is where I render the button to create a new group of elements and the select component and the textfield that should be rendered if "isDisabled" is false.
{managerialPositionsFields.map((field, index) => {
return (
<Stack className="sectionContainer" key={field.id}>
<Stack horizontal horizontalAlign="space-between">
<StackItem>
<CommandBarButton
iconProps={{ iconName: "AddTo" }}
text="Add"
type="button"
onClick={() => {
managerialPositionsAppend({
authority: 0,
chiefCategory: 0,
title: 0,
otherTitle: ""
});
}}
/>
</StackItem>
</Stack>
<Stack horizontal tokens={{ childrenGap: 20 }}>
<StackItem>
<Label className="select-label requiredIkon">Title</Label>
<Controller
control={control}
name={`managerialPositions.${index}.title`}
render={({ field: { onChange, value, ref } }) => (
<>
<Select
className="react-select-container authoritySelect"
classNamePrefix="react-select"
placeholder="Select title"
options={titelList}
id={`managerialPositions.${index}.title`}
value={
titelList.find((g) => g.value === value)
? titelList.find((g) => g.value === value)
: null
}
onChange={(val) => {
onChange(val.value);
watchChange(val.value, index);
}}
/>
{
// this input is for select validation
<input
tabIndex={-1}
autoComplete="off"
style={{ opacity: 0, height: 0 }}
value={
titelList.find((g) => g.value === value)
? titelList
.find((g) => g.value === value)
.toString()
: ""
}
required={true}
//Without this console will get an error:
onChange={() => {}}
/>
}
</>
)}
/>
</StackItem>
{!isDisabled && (
<StackItem className="">
<Controller
name={`managerialPositions.${index}.otherTitle`}
control={control}
render={({
field: { onChange, name: fieldName, value }
}) => (
<TextField
label="Other title"
name={fieldName}
onChange={(e) => {
onChange(e);
}}
value={value}
/>
)}
/>
</StackItem>
)}
</Stack>
</Stack>
);
})}

How to pass data between pages without URL Parameters

I'm wondering how I can pass non-string data between two pages in Ionic 5 using ReactRouter.
All solutions I could find used Ionic and Angular or just passed one string as URL parameter.
These are my components so far:
App.tsx
const App: React.FC = () => {
return (
<IonApp>
<IonReactRouter>
<IonSplitPane contentId="main">
<Menu />
<IonRouterOutlet id="main">
<Route path="/page/Home" component={Home} exact />
<Route path="/page/DataEntry" component={DataEntry} exact />
<Route path="/page/Request" component={Request} exact />
<Route path="/page/ResultMap" component={ResultMap} exact />
<Redirect from="/" to="/page/Home" exact />
</IonRouterOutlet>
</IonSplitPane>
</IonReactRouter>
</IonApp>
);
};
Page 1 here collects user input data (strings, objects, arrays) and I want to call the route '/page/ResultMap' on Button click and pass the data, so the next page can handle it:
<IonGrid>
<IonRow>
<IonCol class="ion-text-center">
<IonButton text-center="ion-text-center" color="primary" size="default" routerLink='/page/ResultMap'>Erkunden!</IonButton>
</IonCol>
</IonRow>
</IonGrid>
Page 2, which should receive the Data:
const ResultMap: React.FC = () => {
return (
<IonPage>
<IonHeader>
<IonToolbar>
<IonButtons slot="start">
<IonMenuButton />
</IonButtons>
<IonTitle>Map</IonTitle>
</IonToolbar>
</IonHeader>
<IonContent fullscreen>
</IonContent>
</IonPage>
);
};
I understand the React principle about props and state, I just dont know how to combine it with Ionic in this case.
I appreciate your help!
Edit:
As suggested I changed the button onClick like this:
<IonButton text-center="ion-text-center" color="primary" size="default" onClick={e => {
e.preventDefault();
history.push({
pathname: '/page/ResultMap',
state: { time: transportTime }
})}}>
And try to receive the data on the ResultMap page like this:
let time = history.location.state.time;
But I get the error:
Object is of type 'unknown'. TS2571
7 | let history = useHistory();
8 |
> 9 | let time = history.location.state.time;
| ^
How do I access the passed object on the new page?
as for react-router I know you can use this:
history.push({
pathname: '/template',
state: { detail: response.data }
})
in this situation you can pass data without URL Params
can also use history.replace
if you redirect and want the back button work properly to the end user
and for the history do the following
let history = useHistory();
Check this link for a great understand how to implement the useHistory type

ag-Grid Switch goes from checked to unchecked upon down sroll

Getting this weird error where any checked material UI switch becomes unchecked as I scroll down out of that data view.
Below is the Switch return with conditional rendering.
let freeTier = props.params.data.tier;
return freeTier === "FREE" ? (
<FormGroup>
<FormControlLabel
control={
<Switch
disableRipple
focusVisibleClassName={classes.focusVisible}
classes={{
root: classes.root,
switchBase: classes.switchBase,
thumb: classes.thumb,
track: classes.track,
checked: classes.checked
}}
{...props}
/>
}
/>
</FormGroup>
) : null;
And this is where I call the above into cellRendererFramework
cellRendererFramework: params => {
const handleClick = params => {
console.log(params.data);
};
return (
<PaypalSwitch
params={params}
data={params.data}
otherProps={this.props}
onClick={() => handleClick(params)}
/>
);
}
From what it seems like the grid re-renders? I'm getting this error.
ag-Grid: React Component 'cellRendererFramework' not created within
1000ms

How can I have a Dynamic Image uri that is not always defined in React Native?

I am trying to get the filename of my image from a fetch to my rest service. But React tries to require the image before the call has the filename so ofcourse it can't find the image and gives me a 500 error back.
Is there any way to solve or work around this?
I am trying to get the filename from a json which it gets back from the fetch.
The function in which this is supposed to happen
nextPerson = () => {
var numberOfPersons = Object.keys(this.state.peopleList).length;
if (this.state.personIndex < numberOfPersons) {
person = this.state.peopleList[this.state.personIndex]
this.setState({currentPerson:person});
this.setState({image : require('../img/' + Object(this.state.currentPerson.gebruikerFoto).url)});
this.state.personIndex++;
}
}
The render of my component
render() {
var img = this.state.image;
return (
<View style={styles.container}>
<Image style={styles.image} source={img} />
<Text style={styles.persoonNaam}>{this.state.currentPerson.gebruikerNaam}</Text>
<Text style={styles.persoonBio}>{this.state.currentPerson.biografie}</Text>
<TouchableOpacity onPress={this.likePersoon}>
<Text>Like</Text>
</TouchableOpacity>
</View>
);
}
You need to keep your component in loading state, by rendering a shimmer component (react-native-shimmer) or just use ActivityIndicator from react native, until your api call finishes. Once the data is available you can render the Image component.
render() {
if (!this.state.imageUri) return <ActivityIndicator />; // keep showing the loader until the api call is done. Update the state once the call is successful.
return (
<Image /> // your image component
)
see this example:
renderItem = ({item})=>{
return(
<TouchableOpacity onPress={ () => this._openViewPage(item.id, item.cat_id,this.state.token)} style={styles.boxContainer}>
<View style={styles.BoxLeft}>
<H1>{item.title}</H1>
<H2 style={{flexWrap: 'wrap', textAlign: 'right',}}>{item.short_description}</H2>
</View>
<View style={styles.boxRight}>
<Image source={{uri: item.image}} style={{width: 100, height: 100 }} />
</View>
</TouchableOpacity>
)
}
annd in the render of your component you can use it:
<FlatList
data= {this.state.dataSource}
renderItem={this.renderItem}
keyExtractor = { (item, index) => index.toString() }
style={{marginBottom:100}}
/>

Im getting some weird errors with MaterialUI Stepper "Cannot read property stepIndex of undefined"

I'm using MaterialUI and I'm getting the error -
"Uncaught TypeError: Cannot read property 'stepIndex' of undefined"
In my Chrome console
I haven't had this error with any of the other Material-UI React Components. I am pretty new to React so if this is a stupid mistake please bear with me.
I cant seem to solve the problem.
I'm using React 5.3.1.
My code:
class HorizontalLinearStepper extends React.Component {
handleNext() {
const {stepIndex} = this.state;
this.setState({stepIndex: stepIndex + 1,finished: stepIndex >= 2});
};
handlePrev() {
const {stepIndex} = this.state;
if (stepIndex > 0) {
this.setState({
stepIndex: stepIndex - 1
});
}
};
getStepContent(stepIndex) {
switch (stepIndex) {
case 0:
return 'Select campaign settings...';
case 1:
return 'What is an ad group anyways?';
default:
return 'You\'re a long way from home sonny jim!';
}
}
render() {
const {finished, stepIndex} = this.state;
return (
<div>
<Stepper activeStep={stepIndex}>
<Step>
<StepLabel>Select campaign settings</StepLabel>
</Step>
<Step>
<StepLabel>Create an ad group</StepLabel>
</Step>
</Stepper>
<div style={contentStyle}>
{finished ? (
<p>
<a
href="#"
onClick={(event) => {
event.preventDefault();
this.setState({stepIndex: 0, finished: false});
}}
>
Click here
</a> to reset the example.
</p>
) : (
<div>
<p>{this.getStepContent(stepIndex)}</p>
<div style={{marginTop: 12}}>
<FlatButton
label="Back"
disabled={stepIndex === 0}
onTouchTap={this.handlePrev}
style={{marginRight: 12}}
/>
<RaisedButton
label={stepIndex === 2 ? 'Finish' : 'Next'}
primary={true}
onTouchTap={this.handleNext}
/>
</div>
</div>
)}
</div>
</div>
);
}
}
export default HorizontalLinearStepper;
Thanks!
<FlatButton
label="Back"
disabled={stepIndex === 0}
onTouchTap={this.handlePrev.bind(this)}
style={{marginRight: 12}}
/>
<RaisedButton
label={stepIndex === 2 ? 'Finish' : 'Next'}
primary={true}
onTouchTap={this.handleNext.bind(this)}
/>
You need to bind the handlers as they will be run in a different context and will loose the right meaning of this, so we bind the handlers to keep the right context of this.