For my app, I am using an API that contains sales order history.
The returned array looks like this, I take an example of an order with 2 products :
Object {
"amount": 2671.25,
"balance": 0,
"client_id": 1,
"created_at": "2020-05-06T17:42:26Z",
"discount": 0,
"discount_type": 0,
"id": 19,
"items": Array [
Object {
"cost": 2400,
"currency": "EUR",
"description": "blablabla",
"name": "Apple MacBook Air 15'' LED 500 Go SSD 32 Go",
"product_id": 5,
"quantity": 1,
"tax_rate_id": 1,
},
Object {
"cost": 54.25,
"currency": "EUR",
"description": "Blablabla",
"product_id": 2,
"quantity": 5,
"tax_rate_id": 4,
},
],
"po_number": "",
"public_notes": "TEST 6 : Acomptes",
"quote_date": "2020-05-06",
"quote_number": "D1460019",
"quote_status": 40,
"terms": "",
"updated_at": "2020-05-06T18:08:06Z",
},
Currently on my screen, I return the date of the order in 'title' with a drop-down menu where I have the total price of the order. It works, I use this code:
<ScrollView>
{
this.state.displayArray.map((item, i) => (
<List.Section title={item.created_at} key={i.toString()}>
<List.Accordion
title={item.id}
left={props => <List.Icon {...props} icon="cart" />}>
<List.Item title={`${item.amount} €`}/>
</List.Accordion>
</List.Section>
))
}
</ScrollView>
But now, I would like to make this screen a little more complex. I would like to add to the drop-down menu the number of items and the name + price of the items (wholesale, have the detail of the order)
My problem is that I don't know how to extract the products from the array. If I do
<List.Item title = {`$ {item.items.name}} />
it won't work since there are two objects with two different names in the array ... (i tried and it returns 'undefined')
I'm not sure how to do it.
I know my English is not perfect, I don't know if my explanations are clear. Basically there, I only have the price below my order and I will like with the details of the products with their respective prices.
---------------------------- EDIT TO ADD SOLUTION -------------------------------
Thank you #HichamELBSI
<ScrollView>
{
this.state.displayArray.map((item, i) => (
<List.Section title={item.created_at} key={i.toString()}>
<List.Accordion
title={item.id}
left={props => <List.Icon {...props} icon="cart" />}>
<List.Item title={`${item.amount} €`}/>
{
item.items.map((product, i) => (
<List.Item title={product.name} key={i.toString()}/>
))
}
</List.Accordion>
</List.Section>
))
}
</ScrollView>
I think you can map the products like what you do for the display.
<ScrollView>
{
this.state.displayArray.map((item, i) => (
<List.Section title={item.created_at} key={i.toString()}>
<List.Accordion
title={item.id}
left={props => <List.Icon {...props} icon="cart" />}>
<List.Item title={`${item.amount} €`}/>
</List.Accordion>
{item.items.map(product => (
<List.Item title={product.name} />
)}}
</List.Section>
))
}
</ScrollView>
Let me know if that is what you need
Related
I'm trying to essentially achieve the following image which is found here:
In that thread, they talk about the best way to display categories and subcategories and the consensus is an MUI Autocomplete.
I'm not however sure how I would achieve something like that at all and would like some help with how I could achieve it.
What I need is for the user to only be able to select one category, whether it be a "root category" or a sub-category. So in the example above, either the "Boysenberry" or the "Brulee Berry".
I also want to try and have the id of said category so I can apply it on my back end (which I'm sure I can do.
My fetched json structure looks like the below:
[
{
"id": 1,
"name": "Audio Visual Equipment",
"parent": null,
"stockItems": [],
"childCategories": [
{
"id": 2,
"name": "Projectors",
"stockItems": [],
"childCategories": [
{
"id": 3,
"name": "Lenses",
"stockItems": [],
"childCategories": []
}
]
}
]
},
{
"id": 4,
"name": "Lighting Equipment",
"parent": null,
"stockItems": [],
"childCategories": [
{
"id": 5,
"name": "Intelligent",
"stockItems": [],
"childCategories": []
},
{
"id": 6,
"name": "Generic",
"stockItems": [],
"childCategories": []
},
{
"id": 7,
"name": "Control",
"stockItems": [],
"childCategories": []
}
]
},
{
"id": 8,
"name": "Sound Equipment",
"parent": null,
"stockItems": [],
"childCategories": [
{
"id": 9,
"name": "Mixing Desk",
"stockItems": [],
"childCategories": []
}
]
},
{
"id": 10,
"name": "Cables",
"parent": null,
"stockItems": [],
"childCategories": [
{
"id": 11,
"name": "Multicore",
"stockItems": [],
"childCategories": []
},
{
"id": 12,
"name": "Lighting",
"stockItems": [],
"childCategories": []
},
{
"id": 13,
"name": "Audio",
"stockItems": [],
"childCategories": []
},
{
"id": 14,
"name": "Video",
"stockItems": [],
"childCategories": []
},
{
"id": 15,
"name": "Power",
"stockItems": [],
"childCategories": []
}
]
}
]
EDIT:-
I get the following warning when I refresh the page:
MUI: The value provided to Autocomplete is invalid.None of the options match with `-1`.You can use the `isOptionEqualToValue` prop to customize the equality test.
When I then click on the Autocomplete, I get the "root" categories only. When I then click on one, the name is not shown and I get the following error:
MUI: The value provided to Autocomplete is invalid.None of the options match with `1`.You can use the `isOptionEqualToValue` prop to customize the equality test.
1. Flattening the List
My approach is to "flatten" the list of categories into a single array so that MUI can evaluate each sub-category. Each of my flat options has a depth property so that I can display it with the correct level of indentation.
We can use the code from the Checkboxes example and add an indentation with the MUI sx prop:
renderOption={(props, option, { selected }) => (
<li {...props}>
<Checkbox checked={selected} sx={{ ml: 2 * option.depth }} />
{option.name}
</li>
)}
2. Filtering Matches
I'm assuming that we want to display the top-level category above a sub-category which matches on the sub-category term only. Like in your linked "ber" example, if the category was "Fall Gold" and the subcategory was "Fall Gold Berry". This means that we should consider the child terms when deciding if a term is a match.
To achieve this, I am including a matchTerms property on all option objects and using a custom filterOptions function on the Autocomplete which looks at this property. With the createFilterOptions utility, we just need to determine what texts to examine:
filterOptions={(createFilterOptions({
// join with some arbitrary separator to prevent matches across adjacent terms
stringify: (option) => option.matchTerms.join("//")
}))}
3. Highlighting
The last piece of this is the highlighting, which is not included in MUI. The MUI docs recommend the autosuggest-highlight package and include an example of how to use it. We can copy that, changing option.title to option.name.
Complete Code
JavaScript
import {
Autocomplete,
TextField,
Checkbox,
createFilterOptions
} from "#mui/material";
import { data } from "./data";
import parse from "autosuggest-highlight/parse";
import match from "autosuggest-highlight/match";
const toOptions = (category, depth = 0, parentId = null) => {
const { id, name, childCategories = [] } = category;
const children = childCategories.flatMap((child) =>
toOptions(child, depth + 1, id)
);
const option = {
id,
name,
depth,
parentId,
matchTerms: [name].concat(children.map((obj) => obj.name))
};
return [option].concat(children);
};
const optionsList = data.flatMap((category) => toOptions(category));
export default () => {
return (
<Autocomplete
options={optionsList}
getOptionLabel={(option) => option.name}
renderOption={(props, option, { selected, inputValue }) => {
const matches = match(option.name, inputValue);
const parts = parse(option.name, matches);
return (
<li {...props}>
<Checkbox checked={selected} sx={{ ml: 2 * option.depth }} />
<div>
{parts.map((part, index) => (
<span
key={index}
style={{
fontWeight: part.highlight ? 700 : 400
}}
>
{part.text}
</span>
))}
</div>
</li>
);
}}
renderInput={(params) => <TextField {...params} />}
filterOptions={createFilterOptions({
// join with some arbitrary separator to prevent matches across adjacent terms
stringify: (option) => option.matchTerms.join("//")
})}
/>
);
};
TypeScript
import {
Autocomplete,
TextField,
Checkbox,
createFilterOptions
} from "#mui/material";
import { data } from "./data";
import parse from "autosuggest-highlight/parse";
import match from "autosuggest-highlight/match";
// describes the input data
type Category = {
id: number;
name: string;
childCategories?: Category[];
};
// describes the format that we want
interface Option {
id: number;
name: string;
depth: number;
parentId: number | null;
matchTerms: string[];
}
const toOptions = (
category: Category,
depth: number = 0,
parentId: number | null = null
): Option[] => {
const { id, name, childCategories = [] } = category;
const children = childCategories.flatMap((child) =>
toOptions(child, depth + 1, id)
);
const option = {
id,
name,
depth,
parentId,
matchTerms: [name].concat(children.map((obj) => obj.name))
};
return [option].concat(children);
};
const optionsList: Option[] = data.flatMap((category) => toOptions(category));
export default () => {
return (
<Autocomplete
options={optionsList}
getOptionLabel={(option) => option.name}
renderOption={(props, option, { selected, inputValue }) => {
const matches = match(option.name, inputValue);
const parts = parse(option.name, matches);
return (
<li {...props}>
<Checkbox checked={selected} sx={{ ml: 2 * option.depth }} />
<div>
{parts.map((part, index) => (
<span
key={index}
style={{
fontWeight: part.highlight ? 700 : 400
}}
>
{part.text}
</span>
))}
</div>
</li>
);
}}
renderInput={(params) => <TextField {...params} />}
filterOptions={createFilterOptions({
// join with some arbitrary separator to prevent matches across adjacent terms
stringify: (option) => option.matchTerms.join("//")
})}
/>
);
};
CodeSandbox Link
as the title suggests I would like to know how to use the getParam function to access a nested array. This is my json and my code.
Json:
[
{
"name": "Crock Pot Roast",
"ingredients": [
{
"quantity": "1",
"name": " beef roast",
"type": "Meat"
},
{
"quantity": "1 package",
"name": "brown gravy mix",
"type": "Baking"
},
{
"quantity": "1 package",
"name": "dried Italian salad dressing mix",
"type": "Condiments"
},
{
"quantity": "1 package",
"name": "dry ranch dressing mix",
"type": "Condiments"
},
{
"quantity": "1/2 cup",
"name": "water",
"type": "Drinks"
}
],
"steps": [
"Place beef roast in crock pot.",
"Mix the dried mixes together in a bowl and sprinkle over the roast.",
"Pour the water around the roast.",
"Cook on low for 7-9 hours."
],
I capture the data that interest me and print them, accessing the nested array via map (quantity values, name, type) in this screen.
Screen1:
<FlatList
numColumns={2}
data={this.state.breweries}
renderItem={({ item, index }) => (
<View style={styleResult.box}>
<TouchableOpacity onPress={() =>this.props.navigation.navigate('ReviewDetails', item)}>
<Image style={styleResult.logo}
source={{uri: item.imageURL}} />
<Text style={styleResult.titleRecipe}> {item.name}</Text>
{item.ingredients.map((v,i) => (
<Text style={styleResult.titleRecipe}> {v.name}</Text>
))}
</TouchableOpacity>
But when I want to bring every single value to a second page via onpress, in screen 2 if I want to print the name I can with getParam ('name'), but if I wanted to print the names of the ingredients, I don't know how to do it.
Screen2
const link=navigation.getParam('imageURL');
return(
<View style={styleReviewDetails.container}>
<ScrollView>
<Text style={styleReviewDetails.titleRecipe} >{navigation.getParam('name')}</Text>
<Image style={styleReviewDetails.imageRecipe} source={{uri: link}}></Image>
<Text style={styleReviewDetails.titleRecipe} >Ingredients: ???????</Text>
In your details screen you have an object of the json type you posted.
Therefore, if you want to have a string which contains the ingredient names you could obtain it like this:
const ingredients = navigation.getParam('ingredients') // here you have the list
const ingredientNames = ingredients.map((ingredient) => ingredient.name)
// if you want to put there a separator, like "," you can do it like this:
const ingredientNames = ingredients.map((ingredient, index) => `${ingredient.type}` + ( index !== ingredients.length-1 ? " ," : ""))
// in your code it will be used like this
...
<Text>
{ingredientNames}
</Text>
I get an array after console log my state that get data from Firebase. I want to know: can anyone help me to map array and get below details on ui. Thank You.
I tried below way, but app keep getting errors
Array [
Object {
"lists": Array [
Object {
"lists": Array [
Object {
"id": "123",
"imageUrl": "http://www.pngmart.com/files/1/Pizza-Slice-PNG-Transparent-Image.png",
"name": "Chicken Devill pizza",
"price": 700,
"size": "Medium",
},
],
"randomid": "32013408-0f48-4b15-80c4-eba3fc1fe295",
},
Object {
"lists": Array [
Object {
"id": "1234",
"imageUrl": "http://www.pngmart.com/files/1/Cheese-Pizza.png",
"name": "Cheese pork pizza",
"price": 1500,
"size": "Medium",
},
],
"randomid": "12a74805-4932-4397-b838-6773bc7e44b8",
},
],
},
]
In below code it show a error:
TypeError: undefined is not a function (near '...this.state.lists.map...')
{this.state.lists.lists.map((current, i) => (
))}
The first list here is an array not an Object. You can't call lists.lists because of this.
You will need to flatten the list or use nested map operations.
export default function App() {
const state = {
lists: [
{
lists: [
{
id: "123",
imageUrl:
"http://www.pngmart.com/files/1/Pizza-Slice-PNG-Transparent-Image.png",
name: "Chicken Devill pizza",
price: 700,
size: "Medium"
}
],
randomid: "32013408-0f48-4b15-80c4-eba3fc1fe295"
},
{
lists: [
{
id: "1234",
imageUrl: "http://www.pngmart.com/files/1/Cheese-Pizza.png",
name: "Cheese pork pizza",
price: 1500,
size: "Medium"
}
],
randomid: "12a74805-4932-4397-b838-6773bc7e44b8"
}
]
};
return (
<div className="App">
{state.lists.map((list) => {
return list.lists.map((item) => {
return <p>{item.id}</p>;
});
})}
</div>
);
}
You can find this working here: https://codesandbox.io/s/nervous-tu-u2h8v?file=/src/App.js
I also want to add some thing by myself
I done the mapping inside a mapping like this
{this.state.lists.map((current, i) => (
<View>
{current.map((current, i) => (
<Fragment>
<TouchableOpacity style={styles.card} onPress={() => this.details(current.id)}>
<Image style={styles.img} key={i} source={{uri: current.lists[0].imageUrl}}/>
<Text style={styles.txt} key={i}>{current.lists[0].name}</Text>
<Text style={styles.txt} key={i}>{current.lists[0].size}</Text>
<Text style={styles.txt} key={i}>LKR.{current.lists[0].price}</Text>
</TouchableOpacity>
</Fragment>
))}
</View>
))}
I have an array:
[
{
"room": {
"id": "1",
"name": "NameRoom"
},
"users": [
{
"userId": "1",
"userName": "User1",
},
{
"userId": "2",
"userName": "User12",
},
{
"userId": "3",
"userName": "User13",
}
]
},
{
"room": {
"id": "2",
"name": "NameRoom2"
},
"users": [
{
"userId": "4",
"userName": "User14",
},
{
"userId": "5",
"userName": "User15",
},
{
"userId": "6",
"userName": "User16",
}
]
},
]
Here is my rendering code
componentDidMount() {
fetch('https://site.ru/api/rooms')
.then((response) => response.json())
.then((responseJson) => {
this.setState({
dataSource: responseJson,
})
console.log(responseJson[0].users[0].userName) // i get User1
})
.catch((error) => {
console.log(error)
})
}
renderItemRooms = ({ item,index }) => (
<View style = {styles.containerHeader}>
<Text style = {styles.dateTitle}>16.04.2020</Text>
</View>
<TouchableOpacity style = {styles.containerMain}>
<Text style = {styles.nameTitle}>RoomName</Text>
<IconButton
style={styles.menuIcon}
icon="text"
color={'#e7eee6'}
size={40}
onPress={this._toggleBottomNavigationView}
/>
<BottomSheet
visible={this.state.visible}
onBackButtonPress={this._toggleBottomNavigationView}
onBackdropPress={this._toggleBottomNavigationView}
>
<View style={styles.bottomNavigationView}>
<View style={styles.bottomList}>
<FlatList
keyExtractor={item => item.name}
data={this.state.dataSource}
renderItem={this.renderItemUsers}
/>
</View>
<View style={styles.bottomButton}>
<TouchableOpacity style={styles.buttonStyle}>
<Text>Connect</Text>
</TouchableOpacity>
</View>
</View>
</BottomSheet>
</TouchableOpacity>
)
How can I display a list of users in the internal render in my code?
And print the names of rooms.
How to iterate through such an array correctly?
I need to output TouchableOpacity with the name of the room, it has a button on the right, when you click it, a modal menu appears, which contains a list of users in this room, how do I do this correctly?
I've already searched the Internet and didn't find a similar problem.
I notice that you have missed something in FlatList, also there many methods to iterate an array. I will give you an example here and hope this helps.
Example:
<FlatList
keyExtractor={item => item.room.name} {/* item.name is undefined, you need to add room.name */}
data={this.state.dataSource}
renderItem={this.renderItemUsers}
/>
// And renderItemUsers method
renderItemUsers = ({ item }) => {
const Users = () => item.users.map(user => <Text key={user.userId}>{user.userName}</Text>)
return (
<View>
<Text>Room : {item.room.name}</Text>
<Users />
</View>
)
}
I would like to manipulating flatlist index. I would like to convert flat list index to messageID
My main request , I would like to change flatlist item data
I have json array like this;
this.state.messages = [
{
"date": "09:55",
"longDate": "22/10/2018",
"message": "ghjghj",
"messageID": 157,
"senderID": 1,
"showLongDate": 0,
"type": "text",
"uri": ""
},
{
"date": "09:56",
"longDate": "22/10/2018",
"message": "rtyrtyrt",
"messageID": 158,
"senderID": 1,
"showLongDate": 0,
"type": "text",
"uri": ""
}
]
my flat list;
<FlatList
ref={(list) => this.myFlatList = list}
data={this.state.messages}
renderItem={({item, index})=>(
<View><Text>{item.message}</Text></View>
)}
removeClippedSubviews={true}
refreshing={this.state.refreshing}
keyExtractor={(item, index) => item.messageID.toString()}
onRefresh={this.handleRefresh}
extraData={this.state}
/>
<FlatList
ref={(list) => this.myFlatList = list}
data={list}
renderItem={({item, index})=>(
<View /><Text>{item.message}</Text></View>
)}
removeClippedSubviews={true}
refreshing={this.state.refreshing}
keyExtractor={(item, index) => item.message.toString()}
onRefresh={this.handleRefresh}
extraData={this.state}
/>