generate many components from an array - html

I have an array and I need to generate many components as the array lengh:
I tried this but it didn't worked:
let items = ["Item 1", "Item 2", "Item 3", "Item 4", "Item 5"];
let itemList = [];
items.forEach((item, index) => {
itemList.push(<li key={index}>{item}</li>);
});
export default function TaskList(props) {
return (
<div className="task-list-container">
<Task id="" nombre={itemList} />
</div>
);
}
So I need one component from TaskList that contains Item1 and other Item2 and so one

That's not very clear what you're trying to do...
But I think you should try to loop in the render part instead of stacking components in variables ...
let items=['Item 1','Item 2','Item 3','Item 4','Item 5'];
export default function TaskList(props) {
return (
<div className="task-list-container">
{
items.map((item, index) => <Task key={index} value={item} />)
}
</div>
)
}
It will render as many <Task> components as string in the items array

Related

Datalist input showing options from another Vue component

I'm quite new with Vue, and webdev in general. Am creating a simple hiking pack list as a school project. Have a component 'PackItem' that consists of input fields for brand, model and weight. In the backend I have a database with some items that the user can pick from. When selecting a brand, the model input will show a list of models for that brand, and when a model is chosen the weight input gets filled automatically.
<template>
<div class="inputRow">
<h1> Brand: {{ this.brand }}</h1>
<input
type="text"
list="brands"
placeholder="Brand"
v-model="this.brand"
/>
<datalist id="brands">
<option
v-for="(suggested_brand, index) in getProductBrands"
:key="index"
:value="suggested_brand"/>
</datalist>
<input
type="text"
list="models"
v-model="this.model"
/>
<datalist id="models"> -->
<option
v-for="(suggested_model, index) in brandModels"
:key="index"
:value="suggested_model"/>
</datalist>
<input
class="product-inputs"
type="number"
name="Weight"
placeholder="Weight"
v-model="this.getProductWeight"
#change="this.updateItemOnStore($event.target.value);"/>
</div>
</template>
This is the component. The datalists and input gets their values from computed properties, that talk to vuex.
export default defineComponent({
name: 'PackItem',
props: {
},
data() {
return {
model: '',
brand: '',
weight: 0,
}
},
...
...
...
computed: {
getProductBrands(){
return this.$store.getters.productsBrandList
},
getProductWeight(){
let weight = this.$store.getters.productWeight(this.brand, this.model)
return weight
},
brandModels(){
if(this.brand === ''){
return []
}
console.debug('Item %d getting models for %s', this.uuid, this.brand)
let models = this.$store.getters.brandModels(this.brand)
return models
},
},
}
This all works great for the first PackItem, but when i spawn a second PackItem from the parent, the models list for the new PackItem will be the same as the first PackItem, regardless of choosing a different brand.
Image: One item spawned
Image: Two items spawned
If I change computed property brandModels() to a method, and save the returning array from the store in local data and print it, I can see that the model list looks like it should, but those values are not showing up in the list for the user?
Image: Two items spawned with printouts
I can also see in the logs that I'm getting the correct models for the selected brand.
Item 0 getting models for Hyperlite Mountain Gear
index.js:209 Hyperlite Mountain Gear models: Windrider 3400,Junction 3400
index.js:221 Got weight 907g for product Hyperlite Mountain Gear Windrider 3400
index.js:64 Creating item 1 in category 0
index.js:199 Brands list: ZPacks,Hyperlite Mountain Gear,The OMM
index.js:225 Did not find weight for product in database!
index.js:199 Brands list: ZPacks,Hyperlite Mountain Gear,The OMM
PackItem.vue:119 Item 1 getting models for ZPacks
index.js:209 ZPacks models: Arc Blast
index.js:225 Did not find weight for product ZPacks Windrider 3400 in database!
So seems to me like I'm fetching all the correct data, and as far as I can see it should be displayed in the browser, but for some reason is not. I have no idea what is going on here...
EDIT:
As suggested by #yoduh I've fetched the whole products table into the component, and do computed values there instead of the supposedly broken computed values from getters in vuex. This has unfortunately not fixed it. Can still see in logs that brandModels() creates a correct list of models, but the datalist still shows the wrong ones.
computed: {
getProductBrands(){
let brands = []
this.dbProducts.forEach(product => {
//var json = JSON.parse(product);
var brand = product.brand;
if(!brands.includes(brand)){
console.debug('Adding brand %s to brands list', brand)
brands.push(brand)
}
} )
console.log('Brands list: %s', brands)
return brands
},
brandModels(){
if(this.brand === '') {return }
let models = []
this.dbProducts.filter(
product => product.brand === this.brand)
.forEach(product => models.push(product.model)
)
console.debug('%s models: %s', this.brand, models)
return models
},
getProductWeight(){
if(this.brand === '' || this.model === ''){ return }
let product = this.dbProducts.find(
product => (
product.brand === this.brand && product.model == this.model
))
if(product){
let weight = product.weightGrams
console.debug('Got weight %dg for product %s %s', weight, this.brand, this.model)
return weight
}
console.debug('Did not find weight for product %s %s in database!', this.brand, this.model)
return 0
},
},
Turns out the actual issue was with the pair of elements <InputText> and <datalist> on every item having the same id and list values. The value for list and id connect the two elements together, but once a second item is added, another pair of <InputText> and <datalist> with the same id and list values is created. Because no two elements should ever share the same id, the connection between inputs and datalists becomes confused and broken.
The solution then is to bind unique values to the id/list attributes. I used the uuid of each item since it's a unique number:
PackItem.vue
<InputText
type="text"
:list="`models${uuid}`"
placeholder="Model"
class="p-inputtext"
v-model="this.model"
/>
<datalist :id="`models${uuid}`">
<option
v-for="(suggested_model, index) in brandModels"
:key="index"
:value="suggested_model"
/>
</datalist>
updated codesandbox

Accessing the data from Json in React Native

I am trying to access the data from Nested JSON, but it is showing partial data. JSON Extract and access code are provided below:
JSON
let tableData = [
{ "id":"1",
"title":"Joe's Gelato",
'tagline':"Desert, Ice cream, £££",
'eta':"10-30",
'imgUri':require('./assets/nasi-briyani-mutton.jpg'),
'items':[
{"title":"Gelato",
"contents":[{
"title":"Vanilla" ,
"title":"Chocolate",
"title":"Mint"
}]},
{"title":"Coffee",
"contents":[{
"title":"Flat white" ,
"title":"Latte",
"title":"Caffe Americano"
}]}
]
},
{
"id":"2",
"title":"Joe's Dinner",
'tagline':"Pizza, burgers, £££",
'eta':"50+",
'imgUri':require('./assets/pizza-restaurant.jpg'),
'items':[{
"title":"Pizza",
"contents":[{
"title":"Roco Pizza" ,
"title":"Veggi",
"title":"Chicken Supreme"
}]},
{"title":"Burger",
"contents":[{
"title":"Cubby Burger" ,
"title":"Rock Burger",
"title":"Chicken Lover"
}]
}]
},
];
React Code
<ScrollView>
<TableView>
<Text>---Menu---</Text>
{/* {tableData.map((restaurants,i) => ( */}
{tableData[0].items.map ((menuSection,i) => (
<Section header = {menuSection.title}>
{menuSection.contents.map((dishes,j) => (
<Cell title = {dishes.title}/>
))}
</Section>
))}
</TableView>
</ScrollView>
I am trying to access the last item (contents.title) in nested JSON, but somehow it only shows a partial result instead of the complete list of items. Screen Shot of result is provided below.
It is showing me the last items in the menu instead of showing complete title list as:
- Gelato
Vanilla
Chocolate
Mint
- Coffe
Flat White
Latte
Caffe Americano
Your contents data should be an array then it can map correctly. Currently, it's inside an object, so when you map contents array it has only one object inside. Also, title key inside an object will replace the title with the last value.
Solution - Alter data remove the object and then map.
contents: ['Roco Pizza', 'Veggi', 'Chicken Supreme']
{menuSection.contents.map((dishes, j) => (
<Cell title={dishes} />
))}
I hope this will help you out.

Saving and displaying "nested" JSON data from API

I have the following format of JSON data fetched from an API and stored in IntentList
{
"id": 22,
"name": "IntentName",
"fk_app": 3,
"fk_intent": null,
"nlu_models": [],
"sentences": [
{
"text": "text1",
"id": 2308
},
{
"text": "text2",
"id": 2309
},......
So there are these levels : the first having "name" and "sentences", and the second which is inside sentences, having "text".
My goal is to be able to search the API data By text and to display on each row the name and text found inside the sentences related to that text.
Therefore if I search "text" this would appear => IntentName text1 IntentName text2
if I search text1 this would appear => IntentName text1
-----What I have done/tried so far-----
I searched for corresponding text and stored its intent object and was only able to display its intent name
Typescript:
fetchIntentsTxt(event: any) {
if (event.target.value === '') {
return this.searchResultINTxt = [];
}
this.searchResultINTxt = this.IntentList.filter(
i => i.sentences.some(
s => s.text.toLowerCase().includes(event.target.value.toLowerCase())));
}
Html:
<input type="text" autocomplete="off" [(ngModel)]="searchInputINTxt" (keyup)="fetchIntentsTxt($event)"
class="form-control" id="search0" placeholder="Search for Intents by Text">
<a #myIntentTxt *ngFor="let intentTxt of searchResultINTxt" >{{intentTxt.sentences.text(this doesn't work obviously)}}<span
class="float-right">{{intentTxt.name}}</span> <br></a>
Any recommendation is appreciated, even if it meant changing the search function. Thanks in advance.
Example: https://stackblitz.com/edit/angular-ivy-pyaq7a
I would just create a new array in the fetchIntentsTxt method and add the data I want to display by the user input/search. After "filtering" all the data I need I set it to the variable thats iterated over in the View/Template.
public fetchIntentsTxt(searchValue: string): void {
const searchResults = [];
for (const entry of this.intentList) {
for (const sentence of entry.sentences) {
if (sentence.text.toLowerCase().includes(searchValue)) {
searchResults.push({
name: entry.name,
text: sentence.text,
});
}
}
}
this.searchResultINTxt = searchResults;
}
View:
<input type="text" autocomplete="off" [(ngModel)]="searchInputINTxt" (ngModelChange)="fetchIntentsTxt($event)" class="form-control" id="search0" name="search0" placeholder="Search for Intents by Text"/><br />
<a #myIntentTxt *ngFor="let intentTxt of searchResultINTxt">
{{ intentTxt.text }}<span class="float-right">{{ intentTxt.name }}</span>
<br/>
</a>
Also note here, I used the (ngModelChange) instead of the (keyup) on the search input so I do not need to hassle around with the events and just get the value I need for filtering.

Is there a method to display data from api where the id key is taken from another key?

JSON Data:
"abcd":[
{
"id":"1",
"cityId":"2",
},
{
"id":"2",
"cityId":"3",
}
],
"city":[
{
"id":"2",
"cityName":"california"
},
{
"id":"3",
"cityName":"vicecity"
}
]
Angular:
<div *ngFor="let clg of abcd$">
<p>
{{clg.cityId}}
<!-- Here i need to print the cityname from city by using the cityId we have got from abcd -->
</p>
</div>
app.component.ts:
ngOnInit() {
this.data.getColleges().subscribe(
data => this.abcd$ = data
)
}
fetching data from "abcd" is perfectly working....and no problem in fetching the datas from "city" too. But is it possible to fetch the cityName from "city" by using the cityId key from the "abcd" section.
You can use a method to get city by ID:
app.component.ts:
getCityByID = (cityID) => {
return this.city$.find(item => item.id === cityID);
}
Template:
<div *ngFor="let clg of abcd$">
<p>
{{ clg.cityId }}
{{ getCityByID(clg.cityId).cityName }}
</p>
</div>
Update
As far as I understand, you are fetching colleges and cities with 2 separate observables. Because of this, when you are trying to get city by ID, it may (or may not) throw an error if second observable has not been resolved yet. So, you need to combine/join these two streams together. I prefer combineLatest to do this but forkJoin will work as well.
In app.component.ts:
import { combineLatest } from 'rxjs';
......
ngOnInit() {
combineLatest(
this.data.getColleges(),
this.data.getCity()
).subscribe(([colleges, cities]) => {
this.abcd$ = colleges;
this.city$ = cities;
});
}
This way, you make sure that both abcd$ and city$ are inited. Hope this helps.
For further reading:
combineLatest: https://www.learnrxjs.io/operators/combination/combinelatest.html
forkJoin: https://www.learnrxjs.io/operators/combination/forkjoin.html

Order Json object

I'm creating a simple movie listing app with Angular 4. I'm making an HTTP GET request to fetch all the movies stored in a json file. They have some fields like "Id", "Title", "Genre", "Duration", etc. When i'm listing all the movies, how can i order them by ID descending, so that the last one appear first?
Here's the code that i am using to get this json data:
On my data service file:
getMovies(){
return this.http.get('assets/data/movies.json')
.map(res => res.json());
}
On my component.ts file:
export class MainComponent implements OnInit {
movies: Movies[];
username:string;
userimg:string;
constructor(private userService:UserService, private dataService:DataService) { }
ngOnInit() {
this.dataService.getMovies().subscribe((movies) =>{
this.movies = movies;
});
}
}
interface Movies {
id:number,
title:string,
year:number,
rating:number,
cover:string,
genre:string,
duration:string,
description:string,
favourite:number
}
On my component.html file:
<div *ngFor="let movie of movies" class="row row-movies">
<a [routerLink]="['/movies', {'id': movie.id}]">
<div class="col-md-9">
<h3> {{movie.title}}</h3>
<h4> {{movie.year}}</h4>
<h4> {{movie.rating}}</h4>
<p>{{movie.description}}</p>
<h5> {{movie.genre}}</h5>
<h5> {{movie.duration}}</h5>
</div>
<div class="col-md-3">
<img src="../assets/img/capas/movies/{{movie.capa}}" class="img-responsive capa-filme" width="350px" />
</div>
</a>
</div>
Can you help me please? I'm still very noob with Angular..
This question has nothing to do with angular. You need to do, after loading the movies, something like this:
this.movies.sort((a,b) => (b.id - a.id));
As a note: this works using any Array in vanilla javascript.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort
If you don't need the sort to change, you can do it as you retrieve the data.
this.dataService.getMovies().subscribe((movies) =>{
movies.sort((a, b) => {
return a.id < b.id ? 1 : -1;
});
this.movies = movies;
});
NOTE: I did not syntax check this.