Display element only when 2 properties in an object match - javascript

In the first render the object comes like this
{
behindPosition: null,
chipBet: undefined,
chipSelectedId: null,
mainPosition: 4,
sitBehind: (),
submitbetBehind: bound componentDidUpdate(),
timer: false
}
as you see almost every prop is null or undefined
now there is a button in the view
<button id={`playerBehind${position}`}
onClick={this._enclosingFunctions}>Bet Behind</button>
once you click on that button, the object changes by adding more props, so:
{
behindPosition: 4,
mainPosition: 1,
chipSelectedId: null,
timer: true,
nickname: "omega",
refPosition: 4,
balance: 10000,
action: "",
onGame: true,
betAmount: 0,
simpleBetAmount: 0,
message: "",
lastBet: 0,
buttons: [],
position: 1
}
I recorded a video because we are using socket.io so you the error will be reproduced in the right browser within the video.
As you see there is a Bet Behind button in both views, once you click in the button on the left screen, the same button its been rendered in every spot of the right screen, which shouldn't be happening. I need that button to keep displayed only the spot where you saw it at the beginning of the video. The prop behindPosition is the position that you take once you click on the button.
So, all I need is to see that button in the spots where mainPosition is the same as refPosition, which is not happening now, the app is not taking into account anything of that, it is just rendering the buttons in every single spot. By spots I mean the white circles in the view which are 7. So there could be 7 mainPosition, and the button, again, should be displayed only when mainPosition is the matches refPosition
Now, here the way I have my code:
class BetBehindSpot extends Component {
static propTypes = {
'sitBehind' : React.PropTypes.func,
'behindPosition' : React.PropTypes.number,
'mainPosition' : React.PropTypes.number,// position of the spot owner (main player)
'refPosition' : React.PropTypes.number,
}
constructor (props) {
super(props);
}
render () {
console.log('on render', this.props);
const position = this.props.mainPosition;
const behindPosition = this.props.behindPosition;
let displayButton;
if (behindPosition) {
document.getElementById(`betbehind-spot${behindPosition}`).style.display = 'block';
} else if (behindPosition !== position) {
displayButton = (
<button id={`playerBehind${position}`}
className="betbehind-button" onClick={this._enclosingFunctions}>
<span>Bet Behind</span>
</button>
);
} else {
displayButton = null;
}
return (
<div>
{displayButton}
</div>
);
}
_enclosingFunctions = () => {
this._sitBehind();
}
_sitBehind = () => {
if (!this.props.refPosition) {
this.props.sitBehind();
}
}
}
so, what are your recommendations ?

Related

In Intro.js and Intro.js-react, the first and second steps are positioned not properly

The issue is like this.
When reloaded, the first step and pop-up show in the upper left corner instead of the center, this is the first problem for me. I expected it to position center as the Intro.js official document says.
Next, when you press "Next," the pop-up bites into the upper left corner and we can't see it. This is the second problem.
Furthermore, when "Next" is pressed, the pop-up also bites into the upper left corner and we can't see it. This is the second problem as well.
But furthermore, when "Next" is pressed, the pop-up appears in its normal expected position.
When you press "Next" again, the pop-up will still appear in the normal expected position.
The code looks like this.
import dynamic from 'next/dynamic';
import { useState } from 'react';
// Intro.js, see the details here: https://introjs.com/
// Intro.js-react, see the details here: https://github.com/HiDeoo/intro.js-react
// #ts-ignore
const Steps = dynamic(() => import('intro.js-react').then((mod) => mod.Steps), {
ssr: false
});
const Onboarding = () => {
const [stepEnabled, setStepEnabled] = useState(true);
const steps = [
{
title: 'Welcome!!',
intro:
'This is your dashboard. Once you have set up, numbers will be displayed.'
},
{
element: '#user-settings',
title: 'User Settings page',
intro: 'You can jump to the User Settings page from here.',
position: 'right'
},
{
element: '#profile-list',
intro: 'This is your profile list.',
position: 'right'
},
{
element: '#card-list',
intro: 'This is your card list.',
position: 'left'
}
];
const onExit = () => {
setStepEnabled(true);
};
const options = {
showProgress: true,
showBullets: true,
exitOnOverlayClick: true,
exitOnEsc: true,
nextLabel: 'Next',
prevLabel: 'Prev',
// skipLabel: 'Skip',
hidePrev: true,
doneLabel: 'Done',
overlayOpacity: 0.5,
overlayColor: '#000',
showStepNumbers: true,
keyboardNavigation: true,
scrollToElement: true,
helperElementPadding: 10,
showButtons: true
};
// if (!stepEnabled) {
// return null;
// }
return (
<Steps
// #ts-ignore
enabled={stepEnabled}
steps={steps}
initialStep={0}
onExit={onExit}
options={options}
/>
);
};
export default Onboarding;
Does anyone know why and how to fix it?
you can give customized css class to every object in intro.js like this :
()
{
element: ".test",
intro: "",
tooltipClass: "cssClassName1",
tooltipPosition: "bottom-center",
},

How to get onClick Event for a Label in ChartJS and React?

I have a Radar chart with labels, I want to have a click event on the Label of the Radar chart but the element always returns null. I have looked at other Stack over flow questions notedly
1 and this 2. one talks about doing it in vanilla JS approach and other one just is not working for me , Can some one point me to what am i doing wrong ?
End goal -> I want to get the label which is clicked and add a strike through toggle to that label so that i can toggle the data point on and off in the radar chart.
My Implementation
class Chart extends Component {
constructor(props) {
super(props);
this.state = {
chartData: props.chartData
};
}
render() {
return (
<div className="chart">
<Radar
data={this.state.chartData}
options={{
title: {
display: true,
text: "Testing",
fontSize: 25
},
legend: {
display: true,
position: "right"
},
onClick: function(evt, element) {
// onClickNot working element null
console.log(evt, element);
if (element.length > 0) {
console.log(element, element[0]._datasetInde);
// you can also get dataset of your selected element
console.log(data.datasets[element[0]._datasetIndex]);
}
}
}}
/>
</div>
);
}
}
**Link to the sample implementation **
Note: This answer implementation doesn't implement strikethrough. Strikethrough could be implemented by putting unicode character \u0366 between each character of the label string. Here's an example how do this with Javascript. The reason I'm not showcasing this here, is because it didn't really look that great when I tested it on codesandbox.
In a newer version of chart.js radial scale point label positions were exposed. In the example below I'm using chart.js version 3.2.0 and react-chartjs-2 version 3.0.3.
We can use the bounding box of each label and the clicked position to determine if we've clicked on a label.
I've used a ref on the chart to get access to the label data.
I've chosen to set the data value corresponding to a label to 0. I do this, because if you were to remove an element corresponding to a label, the label would disappear along with it. My choice probably makes more sense if you see it in action in the demo below.
const swapPreviousCurrent = (data) => {
const temp = data.currentValue;
data.currentValue = data.previousValue;
data.previousValue = temp;
};
class Chart extends Component {
constructor(props) {
super(props);
this.state = {
chartData: props.chartData
};
this.radarRef = {};
this.labelsStrikeThrough = props.chartData.datasets.map((dataset) => {
return dataset.data.map((d, dataIndex) => {
return {
data: {
previousValue: 0,
currentValue: d
},
label: {
previousValue: `${props.chartData.labels[dataIndex]} (x)`,
currentValue: props.chartData.labels[dataIndex]
}
};
});
});
}
render() {
return (
<div className="chart">
<Radar
ref={(radarRef) => (this.radarRef = radarRef)}
data={this.state.chartData}
options={{
title: {
display: true,
text: "Testing",
fontSize: 25
},
legend: {
display: true,
position: "right"
}
}}
getElementAtEvent={(element, event) => {
const clickX = event.nativeEvent.offsetX;
const clickY = event.nativeEvent.offsetY;
const scale = this.radarRef.scales.r;
const pointLabelItems = scale._pointLabelItems;
pointLabelItems.forEach((pointLabelItem, index) => {
if (
clickX >= pointLabelItem.left &&
clickX <= pointLabelItem.right &&
clickY >= pointLabelItem.top &&
clickY <= pointLabelItem.bottom
) {
// We've clicked inside the bounding box, swap labels and label data for each dataset
this.radarRef.data.datasets.forEach((dataset, datasetIndex) => {
swapPreviousCurrent(
this.labelsStrikeThrough[datasetIndex][index].data
);
swapPreviousCurrent(
this.labelsStrikeThrough[datasetIndex][index].label
);
this.radarRef.data.datasets[datasetIndex].data[
index
] = this.labelsStrikeThrough[datasetIndex][
index
].data.previousValue;
this.radarRef.data.labels[index] = this.labelsStrikeThrough[
datasetIndex
][index].label.previousValue;
});
// labels and data have been changed, update the graph
this.radarRef.update();
}
});
}}
/>
</div>
);
}
}
So I use the ref on the chart to get acces to the label positions and I use the event of getElementAtEvent to get the clicked x and y positions using event.nativeEvent.offsetX and event.nativeEvent.offsetY.
When we've clicked on the label I've chosen to update the value of the ref and swap the label data value between 0 and its actual value. I swap the label itself between itself and itself concatenated with '(x)'.
sandbox example
The reason I'm not using state here is because I don't want to rerender the chart when the label data updates.
You could run a function that modifies your dataset:
You would create the function where you have your data set
chartClick(index) {
console.log(index);
//this.setState({}) Modify your datasets properties
}
Pass the function as props
<Chart chartClick={this.chartClick} chartData={this.state.chartData} />
Execute the function when clicked
onClick: (e, element) => {
if (element.length) {
this.props.chartClick(element[0]._datasetIndex);
}
}

Push object from JSON rest api into empty array not working in React

I am trying to push a object from a JSON response from a rest api into an empty array in React, so far my code is not working, I am fairly new to React so can't see where I am going wrong? Maybe something to do with the function in the state? I am getting error:
Cannot read property 'saved' of undefined
code so far:
import React, { Component } from 'react';
import './news-hero.css';
import Carousel from "react-multi-carousel";
import "react-multi-carousel/lib/styles.css";
const responsive = {
superLargeDesktop: {
breakpoint: { max: 4000, min: 3000 },
items: 1,
},
desktop: {
breakpoint: { max: 3000, min: 1024 },
items: 1,
},
tablet: {
breakpoint: { max: 1024, min: 464 },
items: 1,
},
mobile: {
breakpoint: { max: 464, min: 0 },
items: 1,
},
};
class NewsHero extends Component {
state = {
loading: false,
data: [],
headline: [],
saved: []
}
saved() {
this.saved.push(this.headline);
//alert('this is saved kind of');
}
onError() {
this.setState({
imageUrl: "../assets/img-error.jpg"
})
}
componentDidMount() {
this.setState({ loading: true })
fetch('https://newsapi.org/v2/everything?q=timbaland&domains=rollingstone.com,billboard.com&excludeDomains=townsquare.media&apiKey=8')
.then(headline => headline.json())
.then(headline => this.setState({ headline: headline.articles, loading: false }, () => console.log(headline.articles)))
}
render() {
return (
<div className="hero">
<h2 className="text-left">News</h2>
{this.state.loading
? "loading..."
: <div>
<Carousel
additionalTransfrom={0}
showDots={true}
arrows={true}
autoPlaySpeed={3000}
autoPlay={false}
centerMode={false}
className="carousel-hero"
containerClass="container-with-dots"
dotListClass="dots"
draggable
focusOnSelect={false}
infinite
itemClass="carousel-top"
keyBoardControl
minimumTouchDrag={80}
renderButtonGroupOutside={false}
renderDotsOutside
responsive={responsive}>
{this.state.headline.map((post, indx) => {
return (
<div className="text-left mt-5" key={indx}>
<img className="media-img card-img-top card-img-hero" src={post.urlToImage} alt="Alt text"></img>
<div className="card-body container hero-text-body">
<h1 className="card-title hero-title text-truncate">{post.title}</h1>
<button className="btn-primary btn mt-2 mb-4" onClick={this.saved}>Add this article</button>
<p className="card-text">{post.description}</p>
Read More
</div>
</div>
)
})}
</Carousel>
</div>
}
</div>
)
}
}
export default NewsHero;
Any idea's and insight?
Cannot read property 'saved' of undefined
You are not correctly referencing this.state for saved or headline.
Cannot read property 'state' of undefined
Either add a constructor and bind this to your saved function.
constructor(props) {
super(props);
this.saved = this.saved.bind(this);
}
saved() {
this.saved.push(this.headline);
//alert('this is saved kind of');
}
Or define saved as an arrow function to bind this
saved = () => {
this.saved.push(this.headline);
//alert('this is saved kind of');
}
Should be
saved() {
const { headline, saved } = this.state;
this.setState({ saved: [...saved, headline] });
}
or
saved = () => {
const { headline, saved } = this.state;
this.setState({ saved: [...saved, headline] });
}
UPDATE: Save a specific post/headline
I see now, if you wish to save a specific headline then you need to update the signature of your saved function and how you call it.
saved = headline => {
this.setState(
prevState => ({ saved: [...prevState.saved, headline] })
);
}
and when you invoke it as a callback
<button
className="btn-primary btn mt-2 mb-4"
onClick={() => this.saved(post)} // pass the current post defined in the map callback
>
Add this article
</button>
One minor comment about naming, consistently referencing the same "object" the same way throughout code goes a long way in helping readability. I.E. how you reference headlines, posts, and articles. Pick one and be consistent.
this.state.headlines => this.state.headlines.map(headline => ...
this.state.posts => this.state.posts.map(post => ...
this.state.articles => this.state.articles.map(article => ...
saved() {
this.saved.push(this.headline);
//alert('this is saved kind of');
}
You cannot defined class methods like this without writing a constructor like this:
class NewsHero extends Component {
constructor(props){
super(props)
this.saved = this.saved.bind(this)
}
...the rest of everything you wrote
}
Without doing this, your saved function is not bound to the instance of the class. Its silly, yes, but that's how class methods work in javasript. The other option is to use an arrow function, which preserves the context if this:
saved = () => {
this.saved.push(this.headline);
//alert('this is saved kind of');
}
I think this is faster and cleaner, and its pretty commonly used.
That is problem 1. Problem 2 is that it appears you are trying to modify the state object directly when writing this.saved.push(this.headline). That's not how to do it. To modify your state, you need to use the this.setState function:
saved = () => {
this.setState({
saved: whatever you want this to be
})
}
But it looks like you are already setting your state correctly in your fetch call, so I'm not sure what you're trying to accomplish with your saved function...?

Vue.js: How to rerun your code whenever you change slides in your vue carousel

I am very new to vue.js and fumbling my way though it, forgive me if my terms are incorrect. I am creating a touchscreen application that needs to be ADA compliant (only the bottom part of the screen is accessible, so i have to use buttons for interaction).
I have a parent component with a carousel creating an array of slides, pulling data from my child component.
parent component HTML
<carousel :navigateTo="selectedListIndex" #pageChange="OnPageChange">
<slide v-for="(member, index) in selectedList" :key="index">
<MemberBioPage :member="member"/>
</slide>
</carousel>
parent component SCRIPT:
export default {
data () {
return {
currentPage: 0
}
},
components: {
MemberBioPage,
Carousel,
Slide
},
computed: {
selectedList () {
return this.$store.state.selectedList
},
selectedListIndex () {
return this.$store.state.selectedListIndex
}
},
methods: {
OnPageChange (newPageIndex) {
console.log(newPageIndex)
this.currentPage = newPageIndex
}
}
}
within my child component, i have bio copy being pulled from my data and arrow buttons that allow you to scroll the text. There is an outer container and an inner container to allow the scrolling and based on the height that the content takes up in the container will determine when the arrows disable or not.
child component HTML:
<div class="member-bio-page">
<div class="bio">
<div class="portrait-image">
<img :src="member.imgSrc" />
</div>
<div class="bio-container">
<div class="inner-scroll" v-bind:style="{top: scrollVar + 'px'}">
<h1>{{ member.name }}</h1>
<div class="description-container">
<div class="para">
<p v-html="member.shortBio"></p>
</div>
</div>
</div>
</div>
<div class="scroll-buttons">
<div>
<!-- set the class of active is the scroll variable is less than 0-->
<img class="btn-scroll" v-bind:class="{ 'active': scrollVar < 0 }" #click="scrollUp" src="#/assets/arrow-up.png">
</div>
<div>
<!-- set the class of active is the scroll variable is greater than the height of the scrollable inner container-->
<img class="btn-scroll" v-bind:class="{ 'active': scrollVar > newHeight }" #click="scrollDown" src="#/assets/arrow-down.png">
</div>
</div>
</div>
</div>
child component SCRIPT:
<script>
export default {
props: [
'member', 'currentPage'
],
data () {
return {
scrollVar: 0,
outerHeight: 0,
innerHeight: 0,
newHeight: -10
}
},
mounted () {
this.outerHeight = document.getElementsByClassName('bio-container')[0].clientHeight
this.innerHeight = document.getElementsByClassName('inner-scroll')[0].clientHeight
this.newHeight = this.outerHeight - this.innerHeight
return this.newHeight
},
methods: {
scrollUp () {
console.log(this.scrollVar)
this.scrollVar += 40
},
scrollDown () {
console.log(this.scrollVar)
this.scrollVar -= 40
},
showVideo () {
this.$emit('showContent')
}
}
}
</script>
I am able to get the height of the first bio i look at, but on page change it keeps that set height. I basically want the code in mounted to be able to rerun based on the index of the slide i am on. I need 'newHeight' to update on each page change. I tried grabbing the 'currentPage' from my parent component using props, but it pulls undefined.
here is all a snippet from my data to show you what data i currently have:
{
index: 12,
name: 'Name of Person',
carouselImage: require('#/assets/carousel-images/image.jpg'),
imgSrc: require('#/assets/bio-page-image-placeholder.jpg'),
shortBio: '<p>a bunch of text being pulled</p>',
pin: require('#/assets/image-of-pin.png')
}
this is also my store just in case
const store = new Vuex.Store({
state: {
foundersList: founders,
chairmanList: chairmans,
selectedList: founders,
selectedListIndex: -1
},
mutations: {
setSelectedState (state, list) {
state.selectedList = list
},
setSelectedListIndex (state, idx) {
state.selectedListIndex = idx
}
}
})
Alright, so this is a good start. Here's a few things I would try:
Move the code you currently have in mounted to a new method called calculateHeight or something similar.
Call the method from your scrollUp and scrollDown methods.
So your final code would look something like this:
export default {
props: [
'member', 'currentPage'
],
data () {
return {
scrollVar: 0,
outerHeight: 0,
innerHeight: 0,
newHeight: -10
}
},
mounted () {
this.calculateHeight();
},
methods: {
calculateHeight() {
this.outerHeight = document.getElementsByClassName('bio-container')[0].clientHeight
this.innerHeight = document.getElementsByClassName('inner-scroll')[0].clientHeight
this.newHeight = this.outerHeight - this.innerHeight
},
scrollUp () {
console.log(this.scrollVar)
this.scrollVar += 40
this.calculateHeight()
},
scrollDown () {
console.log(this.scrollVar)
this.scrollVar -= 40
this.calculateHeight()
},
showVideo () {
this.$emit('showContent')
}
}
}

Changes to properties in mounted not triggering computed in VueJS

I have a VueJS component which contains a button whose class and text are computed properties and changes every time the button is clicked. They are changing fine as long as I click on the button once it is loaded. I wanted to store the state in localStorage and if I reload the page set the text and class based on the value stored. The value of ordered is changing but the button text and class are not reflecting that in UI. Does anyone have any suggestion as to what I may be doing wrong? Following is the source
<template>
<div class="main-view">
<button type="button" :class="order_button_style" #click="on_order_button_click()">
{{ order_button_text }}
</button>
</div>
</template>
<script>
export default {
name: "FoodComponent",
props: {
item: Object
},
methods: {
on_order_button_click() {
this.item.ordered = !this.item.ordered;
localStorage.setItem(this.item.id, this.item.ordered);
}
},
mounted() {
var storedState = localStorage.getItem(this.item.id);
if (storedState) {
this.item.ordered = storedState;
}
},
computed: {
order_button_text() {
return this.item.ordered === true ? "Ordered" : "Order";
},
order_button_style() {
return this.item.ordered === true
? "button ordered-button"
: "button unordered-button";
}
}
};
</script>
What you will get from the local storage is a string. In mounted, ordered property will be a string instead of a boolean so you order_button_text computed property condition will never be true. To fix this you can just convert storedState property to a boolean :
mounted() {
const storedState = localStorage.getItem(this.item.id) === 'true';
if (storedState) {
this.item.ordered = storedState;
}
},

Categories

Resources