How to connect nested map functions inside container? - javascript

I am a little stuck in here, so basically the problem is with the nested map function.
I have main map function which provides me a list of all hotels ever added to the database.
Inside that map function, I must have a nested map of reviews for specified hotel but, the problem is
with the same keys. In main map function, I have keys like, id, name, etc. As for the nested review map, I have id, which is basically different from the id provided in main map so I'm stuck while trying to connect them.
Here's my code:
<div>
<div className='landing-container'>
<nav>
<h2>Hotel Challenge</h2>
<ul>
<li><a className="nav-link" onClick={loadHotels}>List All Hotels</a></li>
<li><Link className='nav-link' to='/dashboard'>Dashboard</Link></li>
<li><Link className='nav-link' to='/favorites'>Favorites</Link></li>
<li><a className="nav-link" onClick={handleLogout}>Logout</a></li>
</ul>
</nav>
</div>
<div className="layout-container">
<div>{hotelItems.map((item) => (
<div className="hotel-container" key={item.id}>
<div className="hotel-name">
{item.name}
<div className="hotel-location">{item.city}, {item.country}</div>
<div className="hotel-stars">
{Array(item.stars).fill(<FontAwesomeIcon className="icon-color-star" icon={faStar} />)}
</div>
<img className="hotel-image" src={item.image} />
<div className="reviews">
<button className="review-button" onClick={showHotelReviews}>Show Reviews</button>
{hotelReviews.map((review) => (
<div>
<div className="review-message">{review.id}</div>
<div className="review-likes">Liked by: <span>{review.likes}</span><sup><FontAwesomeIcon className="icon-color-likes" icon={faHeart} /></sup></div>
</div>
))}
</div>
</div>
</div>
))}</div>
</div>
</div>
I tried providing the main map key to the nested map, but that didn't work either.
All hotels in array:
[
{
"id": 42,
"name": "Courtyard by Marriott Belgrade City Center",
"city": "Belgrade",
"country": "Serbia",
"image": "http://127.0.0.1:8000/media/images/54129602.jpg",
"stars": 4,
"date": "2017-11-15 00:03:32",
"description": "Lorem Ipsum",
"price": 52.0,
"likes": 45,
"dislikes": 2,
"user": [
1,
73
],
"location": "44.8170754,20.4580087"
},
]
Reviews Array:
[
{
"id": 1,
"message": "“Very comfortable.”",
"created_at": "2020-08-13T18:10:56.240421Z",
"likes": 5,
"dislikes": 0,
"positive": true,
"author": {
"id": 1,
"first_name": "First Name",
"last_name": "Last Name"
}
}
]

It would probably be better to split this into a different components (something like Hotel). With that you can have different keys for hotels and reviews - you can keep your current ids for hotels, while using id for reviews in the Hotel component.

Probably some info on ReviewsArray is missing: a "foreign key" to Hotels. Something like:
[
{
"id": 1,
"message": "“Very comfortable.”",
"created_at": "2020-08-13T18:10:56.240421Z",
"likes": 5,
"dislikes": 0,
"positive": true,
"author": {
"id": 1,
"first_name": "First Name",
"last_name": "Last Name"
},
"hotel_id": id <------ "foreign key" id here
}
]
And, in this way, you could filter the list before map:
hotelReviews.filter((review) => review.hotel_id == item.id).map((review) => (code here))
If I understood your problem correctly, is difficult to know the hotel being reviewed given a review without this type of information. Maybe this info exist but is masked inside another information.

Related

VueJS: JSON objects are not showing in my code

I have API that stores JSON data as shown in JSON body below... I wanted to show the data amount stored in installments but it didn't work good because its showing me each amount value two times and I couldn't figure out the problem here.
{
"response": [{
"floors": [{
"flats": [{
"status": "sold",
"price": "150000",
"currency": "USD",
"end_date": "Not Set",
"buyer": "ella",
"buyer_phone_number": "002822128",
"receipt_number_field": "553108012022",
"size_unit": "M",
"_id": "61d9b61397e87e39832a5abb",
"flat_number": 1,
"description": "This is a newly created flat.",
"city": "NY",
"payment": {
"installment_payment": {
"installments": [{
"amount": "1344",
"date": "2022-01-13",
"is_paid": false
},
{
"amount": "444",
"date": "2022-01-24",
"is_paid": false
},
{
"amount": "44444",
"date": "2022-01-17",
"is_paid": false
}
],
"remaining": "150000"
},
"paid_amount": "1234"
},
"floor": "61d9b61397e87e39832a5aba",
"building": "61d9b61397e87e39832a5ab9",
"size": "176.25",
"directions": " south",
"createdAt": "2022-01-08T16:04:43.557Z",
"updatedAt": "2022-01-08T16:22:29.220Z",
"__v": 0
},
my code:
<div v-for="(flat,index) in Flats" :key="index">
<div v-for="(find,indexT) in flat.payment" :key="indexT" >
<div v-if="flat.payment.installment_payment">
<div v-for="(find,indexT) in flat.payment.installment_payment.installments" :key="indexT">
<div v-if="find.amount >0">
<p> {{find.amount}}$ amount </p>
</div>
</div>
</div>
</div>
</div>
p.S: I stored my API data in array Flats
This will probably work, but it's untested.
You generally do not want to use v-if inside of v-for; instead, you should filter the data first and use the result in the v-for loop. [reference]
Also, since each flat has an _id field, you can use that instead of the index for the top level :key attribute.
<div v-for="flat in flatsWithPayments" :key="flat._id">
<div v-for="(installment, index) in getInstallmentsWithPaymentGTZero(flat.payment.installment_payment.installments)" :key="index">
<p> {{installment.amount}}$ amount </p>
</div>
</div>
Obviously, replace Flats with your data, but also note that in order to compare the payment amount, it needs to be converted with either Number(), parseInt() or parseFloat()
// Flats = { ... }
export default {
computed: {
flatsWithPayments() {
return Flats.filter(f => f.payment != undefined)
}
},
methods: {
getInstallmentsWithPaymentGTZero(installments) {
return installments.filter(i => Number(i.amount) > 0)
}
}
}

How to use v-for 2 times to get specific values in check-box action form?

I am new to Vue js. I am trying to do an action form for the rest of API. I don't know how to get the data of name and location only. I was trying to use slice in Array, but it does not work.
My action form:
<div class="form-group">
<label class="required"> Social Media </label>
<b-form-checkbox v-for="event in events" :key="event._id" :value="event" v-model="selected">
{{event.name}}, {{event.location}}
</b-form-checkbox>
<span class="mt-3">Selected: <strong>{{ selected }}</strong></span>
</div>
My Vue instance
export default {
data() {
return {
events: [{
"_id": "d4d81da6-b453-4a31-999f-a2ea04848ee9",
"name": "A",
"location": "US",
"__v": 0
},
{
"_id": "91205d34-4480-4e4e-bdf7-fe66e46922b0",
"name": "B",
"location": "Korea",
"__v": 0
},
{
"_id": "0b168c44-4f38-4f86-8ee6-e077333aca95",
"name": "C",
"location": "Japan",
"__v": 0
}],
selected: ''
};
}
}
The Output when checking the first option of the checkbox:
Selected: ["_id": "d4d81da6-b453-4a31-999f-a2ea04848ee9", "name": "A", "location": "US", "__v": 0]
Expected output when checking the first option of the checkbox:
Selected: [ "name": "A", "location": "US" ]
You can create the necessary structure within the :value="" assignment.
<b-form-checkbox v-for="event in events" :key="event._id" :value="{ name: event.name, location: event.location }" v-model="selected">
{{event.name}}, {{event.location}}
</b-form-checkbox>
Firstly make Selected:false boolean ... then make a button and on click it'll get to a function which will accepts a parameter, iterate your array and select an object which is matching with the parameter
private selectFun(item){this.events.filter(val=>{val._id===item._id})//and then whatever}

Using an array's object's attribute in a v-for with v-bind in vue.js?

So I'm trying to follow what I've found in the API and examples from the vue.js page but it doesn't seem to be working out.
I have this component
Vue.component('project', {
data: function () {
return {
title: "Sketch",
desc: "Zoe Beauty is an online store web application that sells different types of makeup from many brands in " +
"the market. It works with a nodeJs server and Handlebars templating to create the front end. The app is " +
"created under the slogan “Just Shine”, Most feedback in the app is elusive to this slogan and so is it's " +
"graphic style. The user can filter the items by many different things such as a type of product, brand, price, " +
"rating, etc. Also, they can add items to their cart.",
links: [{
"github": {
"link": "https://github.com/booleanaVillegas/juliana-villegas-taller-uno",
"logo": "https://firebasestorage.googleapis.com/v0/b/booleana-s-portafolio.appspot.com/o/github.svg?alt=media&token=83edf83c-cd80-43fa-bedd-a2348ff23b3e"
},
"web": {
"link": "https://enigmatic-shelf-33047.herokuapp.com/",
"logo": "https://firebasestorage.googleapis.com/v0/b/booleana-s-portafolio.appspot.com/o/web.svg?alt=media&token=b5e092a2-beb3-4c72-a329-8e402e82032f"
}
}]
,
img: "https://firebasestorage.googleapis.com/v0/b/booleana-s-portafolio.appspot.com/o/projects%2Fzoe.png?alt=media&token=b0255dda-51f9-4958-8f7b-af978cc9b790"
}},
template: `
<div class="each-project">
<img src="https://firebasestorage.googleapis.com/v0/b/booleana-s-portafolio.appspot.com/o/projects%2Fzoe.png?alt=media&token=b0255dda-51f9-4958-8f7b-af978cc9b790"
alt="" class="pic-project">
<h3 class="purple title-project">{{title}}</h3>
<p class="project-desc">{{desc}}</p>
<div class="links-container" v-for="link in links">
<a :href="link.link" class="link-container"><img
:src='link.logo' alt='link.key' class='link-img'></a>
</div>
</div>
`
});
The :src and :href in the v-for: link in links are not displaying anything, and when I inspect the element it is literally showing 'link.logo' instead of the actual link
how can I mix v-for and v-bind correctly?
first your array just contains 1 element, and that element was an object so just remove the []
links: {
"github": {
"link": "https://...",
"logo": "https://..."
},
"web": {
"link": "https://...",
"logo": "https://..."
}
}
look https://codepen.io/maoma87/pen/JaWQEq?editors=0010
Your links array contains only 1 element?
links: [{
"github": {
"link": "https://github.com/booleanaVillegas/juliana-villegas-taller-uno",
"logo": "https://firebasestorage.googleapis.com/v0/b/booleana-s-portafolio.appspot.com/o/github.svg?alt=media&token=83edf83c-cd80-43fa-bedd-a2348ff23b3e"
},
"web": {
"link": "https://enigmatic-shelf-33047.herokuapp.com/",
"logo": "https://firebasestorage.googleapis.com/v0/b/booleana-s-portafolio.appspot.com/o/web.svg?alt=media&token=b5e092a2-beb3-4c72-a329-8e402e82032f"
}
}]
If it's, you can loop like this:
<div class="links-container" v-for="(linkValue, key) in links[0]">
<a :href="linkValue.link" class="link-container"><img
:src='linkValue.logo' alt='key' class='link-img'></a>
</div>
Your v-for should once read array one element.
Links object a element like this
{
"github": {
"link": "https://github.com/booleanaVillegas/juliana-villegas-taller-uno",
"logo": "https://firebasestorage.googleapis.com/v0/b/booleana-s-portafolio.appspot.com/o/github.svg?alt=media&token=83edf83c-cd80-43fa-bedd-a2348ff23b3e"
},
"web": {
"link": "https://enigmatic-shelf-33047.herokuapp.com/",
"logo": "https://firebasestorage.googleapis.com/v0/b/booleana-s-portafolio.appspot.com/o/web.svg?alt=media&token=b5e092a2-beb3-4c72-a329-8e402e82032f"
}
}
So your v-for like this
<div class="links-container" v-for="link in links">
<a :href="link.github.link" class="link-container">
<img :src='link.github.logo' alt='link.key' class='link-img'>
</a>
<a :href="link.web.link" class="link-container">
<img :src='link.web.logo' alt='link.key' class='link-img'>
</a>
</div>

Fetching information from nested JSON based on unique fields using AngularJS

I have a json as following.
{
"id":14,
"discussion":8,
"parent":0,
"userid":2,
"subject":"communication skill discussion 2",
"message":"<p>hi all to communication discussion 2 </p>",
"children":[
24,
16,
15
]
},
{
"id":15,
"discussion":8,
"parent":14,
"userid":2,
"subject":"Re: communication skill discussion 2",
"message":"<p>hiiiiiiiiii</p>",
"children":[
25,
23
],
},
{
"id":23,
"discussion":8,
"parent":15,
"userid":2,
"created":1461562317,
"modified":1461562317,
"mailed":0,
"subject":"Re: communication skill discussion 2",
"message":"<p>helloooo</p>",
"children":[
],
}
I want first fetch the details whose Ids matches with the elments in children array
such as for id:14 there are 3 children 24,16,15.Then the control should go directly to id:15 and fetch details of id:15.Again id has children eg. consider id:23 which has no children and will directly print the message.
Please guide me how will I achieve this using ng-repeat of angular ?
Refer to the demo.
Please find the code below:
HTML:
<div ng-app="app" ng-controller="test">
<div ng-repeat="(key,value) in data">
{{key + 1}}) --
<span ng-if="value.children.length > 0">
{{value.children}}
</span>
<span ng-if="!(value.children.length > 0)">
No children found!!
</span>
</div>
</div>
JS:
var app = angular.module('app', []);
app.controller('test', function($scope) {
$scope.data = [{
"id": 14,
"discussion": 8,
"parent": 0,
"userid": 2,
"subject": "communication skill discussion 2",
"message": "<p>hi all to communication discussion 2 </p>",
"children": [
24,
16,
15
]
}, {
"id": 15,
"discussion": 8,
"parent": 14,
"userid": 2,
"subject": "Re: communication skill discussion 2",
"message": "<p>hiiiiiiiiii</p>",
"children": [
25,
23
],
}, {
"id": 23,
"discussion": 8,
"parent": 15,
"userid": 2,
"created": 1461562317,
"modified": 1461562317,
"mailed": 0,
"subject": "Re: communication skill discussion 2",
"message": "<p>helloooo</p>",
"children": [
],
}];
});
UPDATE: As per the request
Demo
HTML:
<div ng-app="app" ng-controller="test">
<div ng-repeat="(key,value) in data">
[{{key + 1}}] --
<div ng-if="value.children.length > 0">
<div ng-repeat="item in value.children">
<span>{{item}}</span> <span class="green" ng-bind-html="getMessage(item)"></span>
</div>
</div>
<span ng-if="!(value.children.length > 0)">
No children found!!
</span>
<br />
</div>
</div>
JS:
$scope.getMessage = function(itemId) {
var flag = true;
var msg;
angular.forEach($scope.data, function(value, key) {
if (flag && value.id == itemId) {
flag = false;
msg = value.message;
}
});
return $sce.trustAsHtml(msg);
}
CSS:
.green {
color: green;
}
Use ng-repeat to display the records.
<ul ng:controller="Cntl">
<li ng:repeat="item in data">
{{item.subject}}: Parent
<ul>
<li ng:repeat="child in item.children">{{child}} : Children
</li>
</ul>
</li>
This is one of the way to display in html. Based on your page design ng-repeat will change.
You can use lodash or underscore _.where:
<div ng:controller="Cntl">
<div ng:repeat="item in data">
{{item.subject}}<br/>
Children
<div ng:repeat="child in item.children">{{_.where(data, {id:child});}}
</div>
</div>
First you need to restructure your json into a tree structure. May be you want to have a look at this post. Then you have to recursively add templates

Angular ng-repeat this JSON structure for user to user messaging

Very simple user to user messaging piece that I'm struggling with the interface in an app to use ng-repeat on the items.
Here is the data:
{
"ID": 4118,
"CreatedDate": "2015-08-20T03:12:50.397",
"recipient": [
{
"ID": 13,
"FirstName": "Heather",
"LastName": "Martin",
"ProfileImage": "https://../profilepictures/13"
}
],
"sender": [
{
"ID": 1046,
"FirstName": "Brad",
"LastName": "Martin",
"ProfileImage": "https://../profilepictures/1046"
}
],
"messages": [
{
"ID": 4137,
"ConversationID": 4118,
"UserID": 1046,
"Body": "hey",
"CreatedDate": "2015-08-20T14:34:42.4716233+00:00"
}
]
}
In the controller I get the conversations out of LS, one conversation is one record in LocalStorage, the JSON above will represent one conversation.
$scope.convo = JSON.parse(localStorage.getItem("convo-" + $stateParams.chatId));
Here is the structure I am trying to achieve (again, very simple, nothing fancy).
<ul>
<li class="item-avatar-left" ng-repeat="c in convo track by $index">
<img ng-src="{{c.recipient[0].ProfileImage}}" />
<p class="bubble speech">
{{c.messages[0].Body}}
</p>
</li>
</ul>
I've tried multiple variations on the ng-repeat directive.
Essentially what I'd like to achieve is just showing one <li> per each message.
Current result:
Console output of a conversation from LS:
You can try normally by ng-repeat
In controller like:
$scope.convo = [
{
"ID": 4118,
"CreatedDate": '2015-08-20T03:12:50.397',
//...... as your info
}
];
In HTML:
<ul>
<li class="item-avatar-left" ng-repeat="c in convo">
<img ng-src="{{c.recipient[0].ProfileImage}}" />
<p class="bubble speech" ng-repeat="m in c.messages">
{{m.Body}}
</p>
</li>
</ul>
NB: your $scope.convo need to be an array

Categories

Resources