I have Different data from different Schema. I want to compare data from both schema to find out if they are same then map through them.
allStaff = [
{
role: "admin",
name: "jamal",
branch: {
name: "kansas",
},
},
{
role: "legal",
name: "keith",
branch: {
name: "arizona",
},
},
{
role: "admin",
name: "dwyane",
branch: {
name: "kansas",
},
},
...
];
contributor = {
fullName: 'Aaraf',
branch: {
name: "kansas",
},
};
I want the form option for this contributor to contain only staff in same branch as he is(kansas).
<Form.Group controlId="branch">
<Form.Label>Investment Officer:</Form.Label>
<Form.Control
as="select"
value={investmentOfficer}
onChange={(e) => setInvestmentOfficer(e.target.value)}
>
<option>Investment Officer</option>
{(allStaff.branch?.name === contributor.branch?.name).map(
staff) => (
<option key={staff._id} value={staff._id}>
{staff.name}
</option>
)
)}
</Form.Control>
</Form.Group>
You want to filter your data first before you map it so that allStaff is filtered down where the branch names match. I have provided the code to do so below.
allStaff.filter((staff) => staff.branch.name === contributor.branch.name)
.map((filteredStaff) => (
<option key={staff._id} value={staff._id}>
{staff.name}
</option>
));
the problem is your code trying to map a boolean... .map() is built in attribute for array, also your code not explaining what is you trying to iterate, so the first step figure out which array you want to iterate first, after that follow this example,
for this example, im going to assume that you want to iterate allStaff.branch
{allStaff.branch.map(
(branch,i) => {
if(branch?.name === contributor.branch[i].name)
return (<option key={branch._id} value={branch._id}>
{branch.firstName}
</option>)
})}
Related
The following code provided in React-Admin docs lets me pick a record and enter only ONE field into the database:
const choices = [
{ id: 123, first_name: 'Leo', last_name: 'Tolstoi' },
{ id: 456, first_name: 'Jane', last_name: 'Austen' },
];
const optionRenderer = choice => `${choice.first_name} ${choice.last_name}`;
<AutocompleteInput source="author_id" choices={choices} optionText={optionRenderer} optionValue = "first_name" />
So in this case, a field called "first_name" would be inserted into my database table.
Is there a way to enter all three fields as an input? For example, I would want 3 separate fields
id: 456,
first_name: 'Jane',
last_name: 'Austen'
to be inserted into the database(not as a dictionary of 3 fields but 3 independent fields).
<AutocompleteInput> allows to select an existing record related to the current one (e.g. choosing the author for a post).
I understand that you want to create a new record instead. You can do so via the onCreate prop, as explained in the doc:
import { AutocompleteInput, Create, SimpleForm, TextInput } from 'react-admin';
const PostCreate = () => {
const categories = [
{ name: 'Tech', id: 'tech' },
{ name: 'Lifestyle', id: 'lifestyle' },
];
return (
<Create>
<SimpleForm>
<TextInput source="title" />
<AutocompleteInput
onCreate={(filter) => {
const newCategoryName = window.prompt('Enter a new category', filter);
const newCategory = { id: categories.length + 1, name: newCategoryName };
categories.push(newCategory);
return newCategory;
}}
source="category"
choices={categories}
/>
</SimpleForm>
</Create>
);
}
I get from the API a json, with a list of each staff member:
const MOCK_STAFF = [{
id: 1,
name: "Jhon Doe",
department: "HR"
}, {
id: 2,
name: "Jane Doe",
department: "Research"
}, etc
Then they get mapped in a datalist <option>, inside a Form.Control component:
<Form.Group className="mb-3">
<Form.Label>Name</Form.Label>
<Form.Control
name='staffName'
value={0}
list="namesList"
onChange={(e) => onChangeHandler(e)}/>
<Form.Label>Department</Form.Label>
<Form.Control disabled
name=department
value={}
/>
<datalist id="namesList">
{MOCK_DATA.map( (data) => (
<option key={data.id} value={data.department}>{data.name}</option>
))}
</datalist>
</Form.Group>
sandbox link: https://codesandbox.io/s/modal-t59e7z?file=/src/App.tsx
I would like the onChange={(e) => onChangeHandler(e)} to get the data-value of <option key={data.id} on form submit, and to make the department Form.Control to reference the value of <option value={data.department} in the datalist. The 'key' id must not show to the user, it is used as a primary key on the database.
I have tried:
function onChangeHandler(e:React.SyntheticEvent) {
console.log(e.target.key);
}
but "property key does not exist on event.target". Nor I can use document.getElementById(); with react. How can I get the values 'key', 'value' and/or 'default-value` from a Form.Control with a datalist?
Thank you
I could not achieve this with data-list, but did so with react-select:
type StaffOption = {
label: string, value: number
}
const MOCK_DATA= [{
id: 1,
name: "Jhon Doe",
department: "HR"
}, {
id: 2,
name: "Jane Doe",
department: "Research"
}, {
id: 3,
name: "Elizabeth meyer",
department: "Operations"
}]
type NameOption = {value: number, label: string, department: string}
type NameOptions = Array<NameOption>
function AddNewRowModal(props:AddNewRowProps) {
const [selectedStaffID, setSelectedStaffID] = useState(0);
function onChangeHandler(option: OnChangeValue<StaffOption, false>,
actionMeta: ActionMeta<StaffOption>) {
console.log(option); //this returns all 3 values defined on type StaffOption
if (option?.value !== undefined) {
setSelectedStaffID(option.value!);
}
}
function BuildOptions (data:any[]) {
var options:NameOptions = []
data.forEach(element => {
options.push({
value: element.id!,
label: (element.name),
department: element.department});
});
return options;
var nameOptions = BuildOptions(MOCK_DATA);
return (
<Modal
show={props.showModal}
backdrop="static"
keyboard={false}
onHide={() => props.closeModal()} >
<Modal.Header closeButton>
<Modal.Title>Add new Entry</Modal.Title>
</Modal.Header>
<Modal.Body>
<Select
options={nameOptions}
onChange={onChangeHandler} />
</Modal.Body>
<ModalFooter>
<Button variant='primary'>Create Entry</Button>
<Button variant='danger' onClick={() => props.closeModal()}>Cancel</Button>
</ModalFooter>
</Modal>
);
}
And the codesandbox
I'm working on a project right now that makes use of plenty of information on selects.
Is there any way to group this information using native Angular (5/6?) functions?
I tried the ng-select component but did not perform well.
My :
<div class="form-group">
<label for="disbursementType" class="ng-required">Typ wypłaty</label>
<select id="disbursementType" class="form-control"
name="disbursementType" required [(ngModel)]="hero.power" #power="ngModel" >
<option *ngFor="let disbursementTypeOption of disbursementTypeOptions"
[value]="disbursementTypeOption.key">{{disbursementTypeOption.title}}</option>
</select>
</div>
And this is my class variable
disbursementTypeOptions = [
{
"key": 1,
"title": "Przelew na konto",
"group": "first",
},
{
"key": 2,
"title": "Czek Giro",
"group": "second",
},
];
You could slice and dice and array to grouped object inside your component, and then use optgroup with option tag inside select
HTML
<select id="disbursementType" class="form-control"
name="disbursementType" required [(ngModel)]="hero.power" #power="ngModel" >
<optgroup [attr.label]="group.name" *ngFor="let group of groupedArray">
<option *ngFor="let disbursementTypeOption of group.values"
[value]="disbursementTypeOption.key">
{{disbursementTypeOption.title}}
</option>
</optgroup>
</select>
Code
unique: string[];
groupedArray: any[]
formatArray() {
this.unique = [...new Set(this.disbursementTypeOptions.map(item => item.group))];
this.groupedArray = unique.map(i => ({
name: i,
values: disbursementTypeOptions.filter(d => d.group === i)
}))
}
If you don't want to add lodash to use groupBy, you can use rxjs (since it already comes with angular) like below. Not really useful if you have static data (why not group it in the 1st place), but can be useful with async data.
const disbursementTypeOptions: Item[] = [
{
'key': 1,
'title': 'Przelew na konto',
'group': 'first',
},
{
'key': 2,
'title': 'Czek Giro',
'group': 'second',
},
];
let groups: { group: string, items: Item[] }[];
from(disbursementTypeOptions)
.pipe(
groupBy(v => v.group),
mergeMap(group => group.pipe(toArray(), map(items => ({ group: group.key, items })))),
toArray()
)
.subscribe(grouped => groups = grouped);
How can I iterate through this object using .map():
state = {
contacts: [
{ "id":1,
"name":"Leanne Graham",
"email":"Sincere#april.biz",
"address":{
"street":"Kulas Light",
"city":"Gwenborough",
"geo":{
"lat":"-37.3159",
"lng":"81.1496"
}
},
"phone":"1-770-736-8031",
},
{ "id":2,
"name":"Ervin Howell",
"email":"Shanna#melissa.tv",
"address":{
"street":"Victor Plains",
"city":"Wisokyburgh",
"geo":{
"lat":"-43.9509",
"lng":"-34.4618"
}
},
"phone":"010-692-6593",
}
]
}
so map over the contacts will work because is an array and all data like id, name, email and phone is accessible but if I want to iterate over the address, is crashing. I have used some example like:
render(){
const {contacts} = this.state
return(
<>
{Object.keys(contacts.address).map((address, index) => (
<span className="d-block" key={index}>{contacts.address[address]}</span>
))}
</>
);
}
which should work with address but is crashin on geo{} and at this point I have lost the signal.
Anyone can give me an ideea ?
This should help:
const address = contacts[0].address;
<>
{Object.keys().map((addressKey, index) => (
<span className="d-block" key={index}>
{typeof address[addressKey] === "object"
? Object.keys(address[addressKey]).map(e => (
<span>{address[addressKey][e]}</span>
))
: contacts[0].address[address]}
</span>
))}
</>;
I don't think is a matter as long as it displays them
After you mapped the contacts you can excess the addresses properties as you like:
const contacts = [
{
id: 1,
name: "Leanne Graham",
email: "Sincere#april.biz",
address: {
street: "Kulas Light",
city: "Gwenborough",
geo: {
lat: "-37.3159",
lng: "81.1496",
},
},
phone: "1-770-736-8031",
},
{
id: 2,
name: "Ervin Howell",
email: "Shanna#melissa.tv",
address: {
street: "Victor Plains",
city: "Wisokyburgh",
geo: {
lat: "-43.9509",
lng: "-34.4618",
},
},
phone: "010-692-6593",
},
];
const addresses = contacts.map(({ address, id }) => ({
id,
...address,
}));
console.log(addresses);
Like rendering them:
addresses.map(({ street, city, id }) => (
<span className="d-block" key={id}>
{street}:{city}
</span>
))
You can map over an array because you expect it to have consistent values through each element, but that is not really the case for object. All the values are different and have different meaning. Also, your span will not be able to display objects, it will only primitive values such as strings or numbers
You can do what you want to achieve manually.
const { contacts } = this.state;
return (
<>
{contacts.map(({ address }, id) => {
return (
<React.Fragment key={id}>
<span>Street: {address.street}</span>
<span>City: {address.city}</span>
<span>Lat: {address.geo.lat}</span>
<span>Lng: {address.geo.lng}</span>
</React.Fragment>
);
})}
If you really want to do it using a loop or some form of iteration you can look into Object.entries. But it would be really difficult to do that with nested objects if you do not know what you are dealing with.
contacts.map(({ address }) => {
for (let [key, value] of Object.entries(address)) {
console.log(`${key}: ${value}`); // logs "street: Kulas Light"
}
})
but note for geo it will give "geo: [Object object]" if you put this directly into a span.
P.S I would recommend finding a better key than the index of array for the Fragment.
so I have the following component that is a dropdown list created using react-select.
import React from 'react'
import Select from 'react-select';
const options = [
{ value: 'chocolate', label: 'Chocolate' },
{ value: 'strawberry', label: 'Strawberry' },
{ value: 'vanilla', label: 'Vanilla' }
];
class MealsFilters extends React.Component {
constructor(props) {
super(props);
this.state = {
selectedOption: null,
};
}
handleChange = (selectedOption) => {
this.setState({ selectedOption });
console.log(`Option selected:`, selectedOption);
}
render() {
const { selectedOption } = this.state;
return (
<div className="container my-3">
<div className="row">
<div className="col-lg-4 col-md-6 col-sm-8">
<Select
isMulti
isSearchable
placeholder={"catégories"}
value={selectedOption}
onChange={this.handleChange}
options={options}
/>
</div>
</div>
</div>
)
}
}
export default MealsFilters;
the options variable is the default one from the docs. I actually need to replace its values by each meal category available.
To do so, as you can see, I need to create an array of objects with a value and a label.
this component accesses meal categories through props called meals that are like so:
console.log(this.props.meals);
=> [{
id: 0,
name: spaghettis,
category: italian,
price: 5.99},
{
id: 1,
name: hamburger,
category: american,
price: 7.99},
{
etc.
}, {}]
How can I take advantage of this.props.meals to get my options array of objects ?
EDIT: multiple meals can have the same category, and I need each category to only appear once in the options.
Map over your this.props.meals array, and create the needed options array,
<Select
isMulti
isSearchable
placeholder={"catégories"}
value={selectedOption}
onChange={this.handleChange}
options={this.props.meal.map(item=>({value: item.id, label: item.name}))}
/>
You could do something like this:
options={this.props.meals.map(
({id, name})=>({value:id,label:name})
)}
You could also use redux connect to create a container that will map the data to dropdown values for you
You can merge the data by category in the following way:
var items = [
{
id: 0,
name: 'spaghettis',
category: 'italian',
price: 5.99,
},
{
id: 1,
name: 'hamburger',
category: 'american',
price: 7.99,
},
{
id: 2,
name: 'other hamburger',
category: 'american',
price: 7.99,
},
];
console.log(
[
...items.reduce(
(result, item) => (
result.get(item.category)
? result.get(item.category).push(item.id)
: result.set(item.category, [item.id]),
result
),
new Map(),
),
].map(([label, value]) => ({ label, value })),
);
In the component it'll look like this:
options={[
...this.props.meals.reduce(
(result, item) => (
result.get(item.category)
? result.get(item.category).push(item.id)
: result.set(item.category, [item.id]),
result
),
new Map(),
),
].map(([label, value]) => ({ label, value }))}
You only need the "name" property so when you map through meals, simply retrieve it. Then upper case the first letter.
const meals = [{
id: 0,
name: "spaghettis",
category: "italian",
price: 5.99
},
{
id: 1,
name: "hamburger",
category: "american",
price: 7.99
}
]
const result = meals.map(({name}) => ({
label: `${name[0].toUpperCase()}${name.slice(1)}`,
value: name
}))
console.log(result);
You can use getOptionLabel and getOptionValue props.
<Select
options={this.props.meals},
getOptionLabel={m => m.name}
getOptionValue={m => m.id} />
https://react-select.com/props
getOptionLabel generic = (option) => string
Resolves option data to a string to be displayed as the label by components
getOptionValue generic = (option) => string
Resolves option data to a string to compare options and specify value attributes