Problem with querying trough two tables in javascript/react - javascript

I was sitting with my problem for few hours already trying different solutions but nothing seems to work properly. I am displaying cards on my webpage fetched from database. Under each card I want to display Book a ticket button based on if ticket is available (if particular concert have a ticket and if the ticket is not used).
I have two tables: concerts and tickets There is over 2000 concerts but only 68 tickets for around 20 - 30 concerts. So most of concerts don't have a ticket at all, and some of concerts have multiple tickets, and some concerts have one ticket.
What I tried to do was to loop trough concerts then nest loop for tickets inside and get concerts which has a ticket but then I realized I also need to check if ticket is used to be able to properly display a button. It just getting too messy and way too complex in order to display a regular button.
Is there some way around it? Do I need to change my data base structure? What I actually need to do?
So once again: I need to display a button Book a ticket if particular concert has a ticket/tickets and if that ticket/tickets are unused (at least one unused), otherwise button should be gray with another text on it. Any suggestions?
Ticket table:
Concert table
And here is how my page look like:
UPDATE:
I managed to make a function to get all concerts with non booked tickets:
let concertsWithTickets = [];
for (let i = 0; i < resTickets.data.length; i++) {
for (let j = 0; j < filteredData.length; j++) {
if (
resTickets.data[i].concertid == filteredData[j].id &&
resTickets.data[i].booked == 0
) {
concertsWithTickets.push(filteredData[j]);
}
}
}
Then i try to loop again inside the view but i got syntax error.
<div>
{for(let i = 0; concerts.length < i; i++)
{
if(concertsWithTickets.id == concert.id) ? <BookBtn/> :
<BookBtn color="gray"/>}
}
</div>
Here is the whole code without import stuff
useEffect(() => {
let concertsWithTickets = [];
const loadConcerts = async () => {
const resConcerts = await axios.get("/data/concerts");
const resTickets = await axios.get("/data/tickets");
// getting all concerts above today
const filteredData = resConcerts.data.filter((concert) => {
return concert.datum >= currentDate;
});
for (let i = 0; i < resTickets.data.length; i++) {
for (let j = 0; j < filteredData.length; j++) {
if (
resTickets.data[i].concertid == filteredData[j].id &&
resTickets.data[i].booked == 0
) {
concertsWithTickets.push(filteredData[j]);
}
}
}
setConcerts(
filteredData.sort((a, b) =>
a.datum > b.datum ? 1 : a.datum < b.datum ? -1 : 0
)
);
};
loadConcerts();
console.log(concertsWithTickets);
}, []);
if (!concerts.length) {
return <p className="center">Loading...</p>;
}
return (
<div className="center">
<h1 className="textpink">Upcoming concerts </h1>
<hr className="stylez" />
{concerts.slice(0, limit ? limit : concerts.length).map((concert)
=> {
return (
<div className="cards-container " key={concert.id}>
<div className="card">
<h3>
{concert.name.length > 32
? concert.name.slice(0, 32) + "..."
: concert.name}
</h3>
<h2>{concert.id}</h2>
<img
src={concert?.image ? concert.image : defaultpicture}
alt="Band-Image"
/>
<div>
<p className="label">{concert.datum}</p>
<p className="cardAdress">
{concert.venue.length > 30
? concert.venue.slice(0, 27) + "..."
: concert.venue}
</p>
</div>
<div>
{for(let i = 0; concerts.length < i; i++)
{
if(concertsWithTickets.id == concert.id) ? <BookBtn /> : <BookBtn color="gray"/>}
}
</div>
</div>
</div>
);
})}
<div>
<hr className="stylez" />
<button className="btn-singup " onClick={() => setLimit(limit + 5)}>
Show more
</button>
</div>
</div>
);
};

Related

How to change input background color dynamically?

I have a custom Time-picker like this.
I want to change input background color when I click It and If I click another one the previous one bg should be white. But when I click second or etc previous one don't back to normal bg.
const [hours, setHours] = useState('09')
const onClickHours = (e) => {
e.preventDefault();
setHours(e.target.value)
}
const onClickFullTime = (e) => {
e.preventDefault();
setFullTime(e.target.value)
getTime(e.target.value);
changeColor(e);
}
const changeColor = (e) => {
let currentColor = e.target.attributes['data-color'].value;
let newColor = currentColor === "#fff" ? "#40a9ff" : "#fff";
e.target.style.backgroundColor = newColor;
e.target.setAttribute('data-color' , newColor);
}
const getTime= (fullTime) => {
onSelectTime(fullTime)
}
const hoursArray = [];
for (let i = 9; i < 22; i++) {
if (i < 10) {
i = '0' + i;
}
hoursArray.push(
<input key={i} onClick={onClickHours} value={i} readOnly />
)
}
const fullTimeArray = [];
for(let j = 0; j < 60; j = j + 5){
fullTimeArray.push(hours + ":" + (j< 10 ? '0' + j : j))
}
<div className="timepicker">
<div className="hours">
{hoursArray}
</div>
<div className="full-time">
{
fullTimeArray.map((time, index) => (
<input name="fullTime" data-color="#fff" key=
{index} onClick={onClickFullTime} value={time}
readOnly/>
))}
</div>
</div>
after click to input
Try this :
import React, { useState, useEffect } from 'react';
import './style.css';
export default function App() {
const [hours, setHours] = useState('09');
const [currentInput, setCurrentInput] = useState('');
const fullTimeArray = [];
for (let j = 0; j < 60; j = j + 5) {
fullTimeArray.push(hours + ':' + (j < 10 ? '0' + j : j));
}
const onClickFullTime = (e) => {
e.preventDefault();
setCurrentInput(e.target.value);
};
useEffect(() => {
changeColor(currentInput);
}, [currentInput]);
const changeColor = (current) => {
const inputElem = document.querySelectorAll("input[name='fullTime']");
inputElem.forEach((elem) => {
if (elem.value === current) {
elem.style.backgroundColor = '#40a9ff';
} else {
elem.style.backgroundColor = '#fff';
}
});
};
return (
<div className="timepicker">
<div className="full-time">
{fullTimeArray.map((time, index) => (
<input
name="fullTime"
key={index}
onClick={onClickFullTime}
value={time}
readOnly
/>
))}
</div>
</div>
);
}
(No need data-color)
Create a state (currentInput in my example) where you store the current value of the clicked input (see onClickFullTime function)
When the value of currentInput changes, useEffect passes it to the changeColor function
Demo : Stackblitz
If only one should be set at a time just give each button a dynamic html id attribute (just timebutton + i value or something unique) and store that in a variable. When a button is clicked set the stored id (if it exists) to have no background and also set the clicked button to be the stored id, setting its background.
You should only need to keep track of the buttons that are highlighted and update them.
EDIT: I will elaborate further, client side Javascript is needed for the solution I mentioned above example of node.js client side.
Here is an example I have made for simple client side js to highlight a clicked button storing the id and resetting it when clicking another.
var buttonNumId = ""; // variable for saving the currently highlighted element Id
function myFunction(clickedElement) {
// unhighlight the current element
if (buttonNumId !== "") document.getElementById(buttonNumId).style.background = "white";
// set the currently clicked element and change its color
buttonNumId = clickedElement.id;
clickedElement.style.background = "red";
// update the textbox for demo purposes
document.getElementById("demo").innerHTML = "Button Id: " + buttonNumId;
}
<!DOCTYPE html>
<html>
<body>
<h1>Highlight on click</h1>
<button id="Button1" style="background-color: white;" onclick="myFunction(this)">Click me 1</button>
<button id="Button2" style="background-color: white;" onclick="myFunction(this)">Click me 2</button>
<button id="Button3" style="background-color: white;" onclick="myFunction(this)">Click me 3</button>
<button id="Button4" style="background-color: white;" onclick="myFunction(this)">Click me 4</button>
<p id="demo"></p>
</body>
</html>

how to display information texts when dialog open

I have this help dialog and some reason my information texts are not showing as soon as the dialog is open. It only start to show when I click the subsection so just wonder how I can display it as soon as the user open the dialog box. Any suggestion or help will be really appreciated.
HTML
//Clicking this will take the user each subsection
<div *ngFor="let subSection of mappedSecSub | async; let last=last">
<div *ngIf="subSection.parentId == section.id;" (click)="clicked(subSection.id, section.id)">
<a mat-list-item>{{subSection.name}}
<mat-divider *ngIf="!last" class="solid"></mat-divider>
</a>
</div>
</div>
// This display the informations text
<div class="column right">
<div *ngFor="let section of targetSectionGroup">
<h1 *ngIf="section.parentId == null" class="hint">{{section?.name}}</h1>
</div>
<div *ngFor="let section of targetSectionGroup">
<div *ngIf="section.parentId != null">
<h2 id="s{{section.id}}ss{{section.parentId}}" class="hint">{{section?.name}}</h2>
<p class="multi_lines_text" [innerHTML]="section?.text"></p>
</div>
</div>
</div>
TS
mappedSections: BehaviorSubject<any[]> = new BehaviorSubject<any[]>([]);
mappedSecSub = this.mappedSections.asObservable()
targetSection: { id, name, parentId, text };
targetSectionGroup: { id, name, parentId, text }[] = [];
ngOnInit(): void {
this.fetchData();
}
fetchData = () => {
this.HelpService.getHelp().subscribe(res => {
this.mappedSections.next(res['res'])
let newMappedSection = this.mappedSections.getValue()
for (let i = 0; i < newMappedSection.length; i++) {
const element = newMappedSection[i];
if (element.parentId) {
this.targetSection = element;
break
}
}
})
}
clicked(id, parentId) {
this.targetSectionGroup = [];
let data = this.mappedSections.getValue()
for (let i = 0; i < data.length; i++) {
if (data[i].parentId == parentId || data[i].id == parentId) {
this.targetSectionGroup.push(data[i]);
}
if (data[i].id === id) {
this.targetSection = data[i]
}
}
document.querySelector(`#s${id}ss${parentId}`).scrollIntoView({ behavior: 'smooth' })
}
Right now the information text only show up when I click the subsections. I want to show it as soon as the help dialog is open.
I guess this should work
fetchData = () => {
this.HelpService.getHelp().subscribe(res => {
this.mappedSections.next(res['res']);
const response = res['res'];
this.clicked(response.id, response.parentId);
let newMappedSection = this.mappedSections.getValue()
for (let i = 0; i < newMappedSection.length; i++) {
const element = newMappedSection[i];
if (element.parentId) {
this.targetSection = element;
break
}
}
})
}

Setting state in componentDidMount with loops and ifs

I have a provider that provides tags for news articles (list with news). If they are more than three, then the additional tags (>3) will be grouped together (called plusTags in the example code). I can see in my console that I have all the tags, but they are not distributed correctly. Ex.
On the first page I have four news with the distributed tags "a,b,c", b", "ac" "b". On the next page, the news articles are (obviously) different, but the distribution of the tags is the SAME ("a,b,c", b", "ac" "b") as on the first page, even if the tags should be different. So the distribution follows the same pattern. I suspect it's my code in my "componentDidMount" function, as its there where I distribute all the tags. Suspect it might repeat because this function repeats itself?
public componentDidMount(): void {
let tags = [];
let plusTags = [];
if (this.props.tags != null) {
if (this.props.tags.length > 0) {
for (var i = 0; i < 3; i++) {
if (this.props.tags[i] != undefined) {
tags.push(this.props.tags[i] + " ");
}
}
for (var j = this.props.tags.length - 1; j >= 3; j--) {
if (this.props.tags[i] != undefined) {
plusTags.push(this.props.tags[j] + " ");
}
}
} else {
tags = this.props.tags;
}
}
this.setState({
tags: tags,
plusTags: plusTags
});
}
and in my render
public render(): React.ReactElement<INewsTagsProps> {
return <React.Fragment>
<div className={styles.tagContainer}>
{
this.state.tags ?
this.state.tags.map((t) => {
if (this.props.background == BackgroundType.None) {
return (
<a href={this.props.tagPageUrl + "?tag="+ t}>
<span className={styles.tagNewsTiles}>{t}</span>
</a>);
}
else {
return(
<a href={this.props.tagPageUrl + "?tag="+ t}>
<span className={styles.tagFollowedNews}>{t}</span>
</a>);
}
})
: null
}
{this.state.plusTags.length > 0 ?
<span className={`callout-target-${this.state.targetIndex} ${this.props.background == BackgroundType.None ? styles.tagNewsTiles : styles.tagFollowedNews}`}
onClick={(e) => {e.stopPropagation(); this.setState({plusTagsDialogOpen: true});}}>+ {this.state.plusTags.length}</span>
:
null
}
</div>
<Callout
className="ms-CalloutExample-callout"
gapSpace={0}
target={this.state.target}
onDismiss={() => this.closeDialog()}
hidden={!this.state.plusTagsDialogOpen}
isBeakVisible={true}
beakWidth={10}
directionalHint={DirectionalHint.topCenter}
>
<div className={styles.tagPopupWrapper}>
<div className={styles.tagPopupContainer}>
{this.state.plusTags ?
this.state.plusTags.map((t) => {
if (this.props.background == BackgroundType.None) {
return (
<a href={this.props.tagPageUrl+ "?tag="+t}>
<span className={styles.tagNewsTiles}>{t}</span>
</a>);
}
else {
return(
<a href={this.props.tagPageUrl+ "?tag="+t}>
<span className={styles.tagFollowedNews}>{t}</span>
</a>);
}
}):
null}
</div>
</div>
</Callout>
;

Angular 2 - Binding loading variables inaccuratly

I have three variables (before), (current) and (after).
I am getting data from an API constantly (every 1sec).
I am saving my current API info as (before) prior to updating and then I store the update in (after) once I update.
I am checking to see if before is different than (After), if so I am adding a class to the DOM.
Now the problem
When I console.log the values I can clearly see that at some points the two values differ. This is great, but when I bind the exact same data to the DOM, they are always the same. Has anyone seen this before?
DOM
<div class="ticker" *ngFor="let coinresult of coinResults; let beforecoinresult of beforeCoinResults; let aftercoinresult of afterCoinResults; let i = index;">
<div class="wrapper" *ngIf="coinresult.name != step2Selection">
<h1>Before: {{beforecoinresult.amount}} - After: {{aftercoinresult.amount}}</h1>
<div class="pin" (click)="pinTitle(coinresult.amount, coinresult.name)">
<i class="material-icons" *ngIf="pinnedCoinAmount != coinresult.amount">gps_not_fixed</i>
<i class="material-icons selectedCoin" *ngIf="pinnedCoinAmount === coinresult.amount">gps_fixed</i>
</div>
<div class="amount" [ngClass]="{amountpinned: pinnedCoinAmount === coinresult.amount,
amountincrease: beforecoinresult.amount < aftercoinresult.amount,
amountdecrease: beforecoinresult.amount > aftercoinresult.amount}">
{{coinresult.amount}}
</div>
<div class="name" [ngClass]="{ namepinned: pinnedCoinAmount === coinresult.amount,
nameincrease: beforecoinresult.amount < aftercoinresult.amount,
namedecrease: beforecoinresult.amount > aftercoinresult.amount}">
{{coinresult.name}}
</div>
</div>
Component
convert(){
this.beforeCoinResults = this.coinResults;
if(this.holdings){
console.log("before " + this.beforeCoinResults[0].amount);
}
this.coinResults = [];
if(this.cryptoSelected && this.step2Selection){
//convert all the crypto to currencies
for (var i = 0; i<= this.currencies.length -1 ; i++){
var tempName = this.currencies[i] as string;
this.coinResults.push({
name: this.convertName(tempName as string),
amount: Math.round(this.holdings * this.ticker[this.convertName(this.step2Selection)].last * this.ticker['USDT_BTC'].last* this.currencyExchange[tempName]*100)/100}
); // push
} // for
//convert all the crypto to crypto
for(var i = 0 ; i <= this.coins.length - 1; i++){
var tempName = this.coins[i] as string;
this.coinResults.push({
name: this.convertName(tempName as string),
amount: Math.round(this.holdings * this.ticker[this.convertName(this.step2Selection)].last / this.ticker[tempName].last*100000000)/100000000
}) // push
} // for
} // if cryptoselected
if(this.regSelected){
//convert currency to currency
for (var i = 0; i<= this.currencies.length -1 ; i++){
var tempName = this.currencies[i] as string;
this.coinResults.push({
name: this.convertName(tempName as string),
amount: Math.round(this.holdings / this.currencyExchange[this.convertName(this.step2Selection)] * this.currencyExchange[tempName]*100)/100
}) // push
} // for
//convert currency to crypto
for(var i = 0 ; i <= this.coins.length - 1; i++){
var tempName = this.coins[i] as string;
this.coinResults.push({
name: this.convertName(tempName as string),
amount: Math.round(this.holdings / this.currencyExchange[this.convertName(this.step2Selection)] / this.ticker['USDT_BTC'].last / this.ticker[tempName].last*100000000)/100000000
}); //push
} // for
}// if
this.afterCoinResults = this.coinResults;
if(this.holdings){
console.log("after " + this.afterCoinResults[0].amount);
}
}// end convert

Javascript Product Search (working, but need to filter by search term)

I have a little product search code that I've been working on for a while. It works great, although a bit backwards.
The more keywords I type in, ideally, the less products will show up (because it narrows down the results). But as is stands, the more keywords I type in my search system, the MORE products are displayed, because it looks for any product with any of the keywords.
I want to change the script so that it only shows results if they include ALL the searched for keywords, not ANY of them...
Sorry for the long-winded explanation.
Here's the meat and potatoes (jsfiddle):
http://jsfiddle.net/yk0Lhneg/
HTML:
<input type="text" id="edit_search" onkeyup="find_my_div();">
<input type="button" onClick="find_my_div();" value="Find">
<div id="product_0" class="name" style="display:none">Mac
<br/>Apple
<br/>
<br/>
</div>
<div id="product_1" class="name" style="display:none">PC
<br/>Windows
<br/>
<br/>
</div>
<div id="product_2" class="name" style="display:none">Hybrid
<br/>Mac PC Apple Windows
<br/>
<br/>
</div>
JAVASCRIPT:
function gid(a_id) {
return document.getElementById(a_id);
}
function close_all() {
for (i = 0; i < 999; i++) {
var o = gid("product_" + i);
if (o) {
o.style.display = "none";
}
}
}
function find_my_div() {
close_all();
var o_edit = gid("edit_search");
var str_needle = edit_search.value;
str_needle = str_needle.toUpperCase();
var searchStrings = str_needle.split(/\W/);
for (var i = 0, len = searchStrings.length; i < len; i++) {
var currentSearch = searchStrings[i].toUpperCase();
if (currentSearch !== "") {
nameDivs = document.getElementsByClassName("name");
for (var j = 0, divsLen = nameDivs.length; j < divsLen; j++) {
if (nameDivs[j].textContent.toUpperCase().indexOf(currentSearch) !== -1) {
nameDivs[j].style.display = "block";
}
}
}
}
}
So, when you search "mac pc" the only result that should be displayed is the hybrid, because it has both of those keywords. Not all 3 products.
Thank you in advance!
I changed a little bit your code to adjust it better to my solution. I hope you don't mind. You loop first over the terms, and then through the list of products, I do it the other way around.
How this solution works:
Traverse the list of products, for each product:
Create a counter and set it to 0.
Traverse the list of search terms, for each.
If the word is found in the product's name, add 1 to the counter.
If the counter has the same value as the list length, display the product (matched all words)
function gid(a_id) {
return document.getElementById(a_id);
}
function close_all() {
for (i = 0; i < 999; i++) {
var o = gid("product_" + i);
if (o) {
o.style.display = "none";
}
}
}
function find_my_div() {
close_all();
var o_edit = gid("edit_search");
var str_needle = edit_search.value;
str_needle = str_needle.toUpperCase();
var searchStrings = str_needle.split(/\W/);
// I moved this loop outside
var nameDivs = document.getElementsByClassName("name");
for (var j = 0, divsLen = nameDivs.length; j < divsLen; j++) {
// set a counter to zero
var num = 0;
// I moved this loop inside
for (var i = 0, len = searchStrings.length; i < len; i++) {
var currentSearch = searchStrings[i].toUpperCase();
// only run the search if the text input is not empty (to avoid a blank)
if (str_needle !== "") {
// if the term is found, add 1 to the counter
if (nameDivs[j].textContent.toUpperCase().indexOf(currentSearch) !== -1) {
num++;
}
// display only if all the terms where found
if (num == searchStrings.length) {
nameDivs[j].style.display = "block";
}
}
}
}
}
<input type="text" id="edit_search" onkeyup="find_my_div();">
<input type="button" onClick="find_my_div();" value="Find">
<div id="product_0" class="name" style="display:none">Mac
<br/>Apple
<br/>
<br/>
</div>
<div id="product_1" class="name" style="display:none">PC
<br/>Windows
<br/>
<br/>
</div>
<div id="product_2" class="name" style="display:none">Hybrid
<br/>Mac PC Apple Windows
<br/>
<br/>
</div>
You can also see it on this version of your JSFiddle: http://jsfiddle.net/yk0Lhneg/1/

Categories