how I can fix overlap issue in stackbarchart using vue-chartjs - bar-chart

I'm using latest vue-chartjs package with vue3 to create stackbarchart. I've shown the stackbarchart on my app but it's labels are overlapping. I need to know which property can add in options that can fix my issue.
<template>
<Bar
v-if="chartData != null"
:key="id"
:data="chartData"
:options="chartOptions"
/>
</template>
<script>
import { Bar, getElementAtEvent } from "vue-chartjs";
import ChartJSPluginDatalabels from "chartjs-plugin-datalabels";
import uniqueId from "lodash.uniqueid";
import { drilldown } from "#/views/Reports/js/drilldown";
import {
Chart as ChartJS,
Title,
Tooltip,
Legend,
BarElement,
CategoryScale,
LinearScale,
ArcElement
} from "chart.js";
ChartJS.register(
CategoryScale,
LinearScale,
BarElement,
Title,
Tooltip,
Legend,
ArcElement,
ChartJSPluginDatalabels
);
export default {
name: "BarChartStacked",
components: {
Bar,
},
props: ["data", "options", "reportData", "eventInfo", "item", "duringDay"],
data() {
return {
id: null,
};
},
computed:{
chartData() { return this.data; /* mutable chart data */ },
chartOptions() { return this.options; /* mutable chart options */ }
},
mounted() {
this.id = uniqueId();
this.chartOptions.plugins.responsive = true;
if (this.reportData && this.reportData.dataFilter) {
if (this.item.conditions) {
// change cursor to pointer if element is clickable
this.chartOptions.hover = {
onHover: function(e) {
var point =getElementAtEvent(e);
if (point.length) e.target.style.cursor = 'pointer';
else e.target.style.cursor = 'default';
}
}
this.chartOptions.onClick = this.handle;
}
} else {
this.chartOptions.hover = {}
}
},
The stackbarchart should display value for the top most graph only like mention in the picture.

Related

2 V-If Statements in the same div?

I am attempting to put two conditions into one div, but since I am new to coding, I am not sure where to go from here. Basically what my code is doing is displaying a part table constantly, but I only want to display the table when there are results found. When the no results div displays, I would like the table to not display. If any advice could be spared, it would be greatly appreciated. Here is the code....
<keep-alive>
<PartsTable style="grid-area: contentArea" v-if="displayStore.cardView" />
</keep-alive>
<div id="noResultsDiv" v-if="partStore.filterQuery && !computedList.length">No Results Found</div>
script
<script lang="ts">
import { defineComponent } from "vue";
import { mapStores } from 'pinia'
import PartsTable from "../components/PartsTable.vue";
import ActionButton from "../components/ActionButton.vue";
import PartCardHolder from "../components/PartCardHolder.vue";
import { useDisplayStore } from "../stores/display-store";
import { usePartStore } from "../stores/part-store";
import { PartDefinition } from "../types/PartDefinition";
export default defineComponent({
name: "Home",
components: {
PartsTable,
ActionButton,
PartCardHolder,
},
data() {
return {
pageCount: 50 as number,
emptyStore: false as boolean
}
},
computed: {
...mapStores(useDisplayStore, usePartStore),
computedList(): PartDefinition[] {
return this.partStore.filteredList.slice(0, this.pageCount);
},
},
methods: {
addPart(): void {
this.$router.push({
path: `/add`,
});
},
viewPart(id: PartDefinition["id"]): void {
this.$router.push({
path: `/view/${id}`,
});
},
async updateStorage() {
sessionStorage.setItem("cardView", this.displayStore.cardView.toString())
},
},
async mounted() {
if (sessionStorage.getItem("cardView") == "true") {
this.displayStore.cardView = true
sessionStorage.setItem("cardView", "true")
} else {
this.displayStore.cardView = false
sessionStorage.setItem("cardView", "false")
}
console.log(sessionStorage.getItem("cardView"))
},
unmounted() {
this.partStore.filterQuery = ""
},
});
</script>
You can use vue.js Conditional Rendering v-if/v-else to achieve.
<div v-if="computedList.length">
// Your parts table will come here.
</div>
<div v-else>
// No results div will display here
</div>
I just gave you a way to achieve the requirement. You can modify the condition as per your need.

ReactGridLayout.children[0].y must be a number

I get the following error message when I'm trying to run the website in my development environment:
Uncaught Error: ReactGridLayout:
ReactGridLayout.children[0].y must be a number!
at validateLayout (app.js:6171)
at app.js:6132
at forEachSingleChild (app.js:62734)
at traverseAllChildrenImpl (app.js:62638)
at traverseAllChildrenImpl (app.js:62654)
at traverseAllChildren (app.js:62709)
at Object.forEachChildren [as forEach] (app.js:62754)
at synchronizeLayoutWithChildren (app.js:6117)
at ReactGridLayout._initialiseProps (app.js:40638)
at new ReactGridLayout (app.js:40089)
There is also an error telling me this:
app.js:77841 The above error occurred in the component:
in ReactGridLayout (created by ResponsiveReactGridLayout)
in ResponsiveReactGridLayout (created by WidthProvider)
in WidthProvider (created by Grid)
in div (created by Grid)
in Grid (created by Test)
in Test
This is my Test.js file:
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import '../../../public/css/app.css';
import '../../../public/css/all.css';
import Grid from '../components/Grid';
class Test extends Component{
render() {
return (
<Grid/>
)
}
}
export default Test;
if (document.getElementById('example')) {
ReactDOM.render(<Test />, document.getElementById('example'));
}
This is my Grid.jsx file:
import '../../../public/css/all.css';
import React from 'react';
import _ from "lodash";
import {WidthProvider, Responsive} from 'react-grid-layout';
import Select from 'react-select';
import 'react-select/dist/react-select.css';
import Clock from './Clock.jsx';
import Weather from './Weather.jsx';
const ResponsiveReactGridLayout = WidthProvider(Responsive);
const originalLayouts = getFromLS("layouts") || [];
/* This class generates the layout for the web app. It renders the grid
* and it's items, but also button's and a dropdown menu, to control the grid.
*/
class Grid extends React.PureComponent {
static defaultProps = {
className: "layout",
cols: { lg: 12, md: 10, sm: 6, xs: 4, xxs: 2},
rowHeight: 100,
autoSize: true,
};
constructor(props) {
super(props);
this.state = {
items: originalLayouts.map(function(i, key, list) {
return {
i: originalLayouts[key].i,
x: originalLayouts[key].x,
y: originalLayouts[key].y,
w: originalLayouts[key].w,
h: originalLayouts[key].h,
widget: originalLayouts[key].widget,
minW: originalLayouts[key].minW,
minH: originalLayouts[key].minH,
maxH: originalLayouts[key].maxH
};
}),
selectedOption: '',
newCounter: originalLayouts.length
};
this.onAddItem = this.onAddItem.bind(this);
this.onBreakPointChange = this.onBreakPointChange.bind(this);
this.onLayoutChange = this.onLayoutChange.bind(this);
this.onLayoutReset = this.onLayoutReset.bind(this);
}
/* This function renders all grid items in the layout array. It creates a div
* with a remove button, and content. The content managed by a switch statement,
* which output is based on the widget property from the grid items.
*/
createElement(el) {
const removeStyle = {
position: 'absolute',
right: '2px',
top: 0,
cursor: 'pointer'
};
const i = el.i;
const widget = el.widget;
return (
<div key={i} data-grid={el}>
{(() => {
switch(widget) {
case 'Clock':
return <Clock/>;
case 'Photo':
return <div className='photo'></div>;
case 'Weather':
return <Weather/>;
default:
return <span>{widget}</span>;
}
})()}
<span
className='remove'
style={removeStyle}
onClick={this.onRemoveItem.bind(this, i)} >
x
</span>
</div>
);
}
/* The onAddItem() function is called when the user clicks on the 'Add Item' button.
* It adds a new grid item to the state, and takes the selected item in the dropmenu
* into account. This way the correct widget is loaded by the createElement() function.
*/
onAddItem() {
var selection = this.state.selectedOption ? this.state.selectedOption : 0;
var widgetProps = returnProps(selection.value);
if(selection) {
console.log('adding', 'n' + this.state.newCounter + '; ' + selection.value);
} else {
console.log('adding', 'n' + this.state.newCounter + '; empty');
}
this.setState({
items: this.state.items.concat({
i: 'n' + this.state.newCounter,
x: (this.state.items.length * 2) % (this.state.cols || 12),
y: Infinity,
w: widgetProps.w,
h: widgetProps.h,
widget: selection ? selection.value : '',
minW: widgetProps.minW,
minH: widgetProps.minH,
maxH: widgetProps.maxH,
}),
newCounter: this.state.newCounter + 1
});
}
/* onLayoutReset() is called when the user clicks on the 'Reset Layout' button.
* It clears the localStorage and then issues a window refresh.
*/
onLayoutReset() {
localStorage.clear();
window.location.reload();
}
/* Calls back with breakpoint and new # cols */
onBreakPointChange(breakpoint, cols) {
this.setState({
breakpoint: breakpoint,
cols: cols
});
}
/* Is called whenever the layout is changed. The for loop adds widget attribute
* from items array to objects in layout array, so that the widget props
* are also saved to localStorage. This is because objects in the layout array
* do not include a widget property by default.
*/
onLayoutChange(layout) {
this.setState({ layout: layout });
for (var i = 0; i < this.state.items.length; i++) {
layout[i].widget = this.state.items[i].widget;
}
saveToLS('layouts', layout);
}
/* When a user presses the little 'x' in the top right corner of a grid item,
* this function is called. It removes the corresponding grid item.
*/
onRemoveItem(i) {
this.setState({ items: _.reject(this.state.items, {i: i }) });
}
/* handleChange passes the selected dropdown item to the state. */
handleChange = (selectedOption) => {
this.setState({ selectedOption });
if (selectedOption) {
console.log(`Selected: ${selectedOption.label}`);
}
};
/* This render function, renders the grid, dropdown-menu, 'Add Item'-button
* and 'Reset Layout'-button. This is also where the createElement() function
* is called for each grid item.
*/
render() {
const { selectedOption } = this.state;
return (
<div>
<div className='widgetselecter'>
<Select className='dropdown'
name="form-field-name"
value={selectedOption}
onChange={this.handleChange}
options={[
{ value: 'one', label: 'One' },
{ value: 'Clock', label: 'Clock' },
{ value: 'Photo', label: 'Photo' },
{ value: 'Weather', label: 'Weather' },
]}
/>
<button className='addButton' onClick={this.onAddItem}>Add Item</button>
<button className='reset' onClick={this.onLayoutReset}>Reset Layout</button>
<span className='title'>/Dash</span>
</div>
<ResponsiveReactGridLayout
onLayoutChange={this.onLayoutChange}
onBreakPointChange={this.onBreakPointChange}
{...this.props}>
{_.map(this.state.items, el => this.createElement(el))}
</ResponsiveReactGridLayout>
</div>
);
}
}
/* Retrieve layout from local storage. */
function getFromLS(key) {
let ls = {};
if (global.localStorage) {
try {
ls = JSON.parse(global.localStorage.getItem("rgl-8")) || {};
} catch (e) {
/*Ignore*/
}
}
return ls[key];
}
/* Save layout to local storage. */
function saveToLS(key, value) {
if (global.localStorage) {
global.localStorage.setItem(
"rgl-8",
JSON.stringify({
[key]: value
})
);
}
}
/* returnProps function returns widget-specific properties like width, min width,
* heigth, etc.
*/
function returnProps(selection) {
switch(selection) {
case 'Clock':
return {
w: 1.5,
h: 1,
minW: 1.5,
minH: 1,
maxH: 1000
};
case 'Weather':
return {
w: 3,
h: 3,
minW: 3,
minH: 3,
maxH: 3
};
default:
return {
w: 2,
h: 2,
minW: 1,
minH: 1,
maxH: 1000,
};
}
}
export default Grid;
I can't remember that I changed anything in the code and I also can't find anything related to the error message on Google. Can anyone tell me more about it or explain it to me? So i can look for a solution.
Seems I had to change this bit of code:
<ResponsiveReactGridLayout
onLayoutChange={this.onLayoutChange}
onBreakPointChange={this.onBreakPointChange}
{...this.props}>
{_.map(this.state.items, el => this.createElement(el))}
>
</ResponsiveReactGridLayout>
to this:
<ResponsiveReactGridLayout
{...this.props}
onBreakpointChange={this.onBreakpointChange}
onLayoutChange={this.onLayoutChange}>
{_.map(this.state.items, el => this.createElement(el))}
</ResponsiveReactGridLayout>
I think it has something to do with the order of rules of code and then especially this part:
>
{_.map(this.state.items, el => this.createElement(el))}
because this piece is outside the <ResponsiveReactGridLayout> now. I'm not sure if this is the right solution but it works for me. So if anyone has some additional information let me know please.

How to let one component correspond with the other for a specific function

I've got a component where I click a color of a machine, when I change colors, the machine gets loaded with a different color inside a image carousel.
Now I also created a component in the bottom with a image gallery of the same machine. How can I make it that the image gallery also changes color when I click the color button in the top of the page?
Important notice: The two components are not in the same parent component but they do load in the same machine images already, so the methods are not wrong I believe.
this is the clickable color button:
<li
v-for="(color, index) in machine.content[0].machine_colors"
:key="color.color_slug"
v-if="color.inStock"
v-on:click="selectColor(index)"
v-bind:class="{ active: (color.color_slug === selectedColor.color_slug)}">
<img v-bind:src="color.color_dash">
</li>
this is the component that changes color:
<div class="product__carousel">
<Carousel showIcon v-if="selectedColor" :machineColor="selectedColor"/> <!-- Image carousel gets loaded in -->
</div>
and the component that needs to change color but does not:
<div id="tab-two-panel" class="panel">
<footerGallery v-if="selectedColor && machine" :machineColor="selectedColor"/>
</div>
Heres the script of the partent component:
export default {
name: 'aboutMachine',
components: {
Collapse,
footerGallery,
},
data() {
return{
selectedColor: this.getMachineColorContent(),
}
},
props: {
main: {
default () {
return {};
},
},
machine: {
default () {
return {};
},
},
},
methods: {
getMachineColorContent() {
if (this.selectedColor) {
return null;
}
return this.machine.content[0].machine_colors[0];
},
selectColor(index) {
this.selectedColor = this.machine.content[0].machine_colors[index];
},
},
}
and the component itself:
export default {
name: 'footerGallery',
props: {
showIcon: Boolean,
machineColor: {
default () {
return {};
},
},
},
data() {
return {
highLightedThumbIndex: 0,
isActive: undefined,
};
},
created() {
this.highLightedThumbIndex = this.highLightedThumbIndex || 0;
},
methods: {
selectThumb(index) {
this.highLightedThumbIndex = index;
},
},
};
This is my main.js
import Vue from 'vue';
import VueYouTubeEmbed from 'vue-youtube-embed'
import FontAwesome from './libs/fa';
import App from './App';
const eventHub = new Vue();
Vue.use(VueYouTubeEmbed);
Vue.component('font-awesome-icon', FontAwesome);
Vue.config.productionTip = false;
/* eslint-disable no-new */
new Vue({
el: '#app',
components: { App },
template: '<App/>',
});
I would use events to accomplish this. The migration guide to Vue2 has a good short explanation of how to do simple event routing without using a full Vuex solution. In your case, you would declare a global event hub in one of your js files:
var eventHub = new Vue();
In your selectColor method you would emit the index selected:
selectColor(index) {
this.selectedColor = this.machine.content[0].machine_colors[index];
eventHub.$emit("select-color",index);
}
And in the footer, you would register a listener for the select-color event that calls selectThumb with the payload of the event (which is the selected index):
created() {
this.highLightedThumbIndex = this.highLightedThumbIndex || 0;
eventHub.$on("select-color",this.selectThumb);
}

calling function from PhotoGrid render function Library

i am using a PhotoGrid Library in react native to populate the list of photo on my apps. how to call a function from the render function ? it show this error when i call a function called "deva" on my OnPress method in <Button onPress={()=>{this.deva()}}><Text>Bondan</Text></Button> . here is my code...
import React from 'react';
import { StyleSheet, Text, View, WebView, TouchableOpacity, Image, Alert, Dimensions} from 'react-native';
import {DrawerNavigator} from 'react-navigation'
import {Container, Header, Button, Icon, Title, Left, Body, Right, Content} from 'native-base'
import PhotoGrid from 'react-native-photo-grid'
import HomeScreen from './HomeScreen'
export default class Recomended extends React.Component {
constructor() {
super();
this.state = { items: [],
nama : ""
}
}
goToBufetMenu(){
this.props.navigation.navigate("BufetMenu");
}
componentDidMount() {
// Build an array of 60 photos
let items = Array.apply(null, Array(60)).map((v, i) => {
return { id: i, src: 'http://placehold.it/200x200?text='+(i+1) }
});
this.setState({ items });
//this.setState({ nama: "Bondan"});
//this.props.navigation.navigate("BufetMenu");
}
deva() {
Alert.alert('deva');
}
render() {
return (
<Container style={styles.listContainer}>
<PhotoGrid
data = { this.state.items }
itemsPerRow = { 3 }
itemMargin = { 3 }
renderHeader = { this.renderHeader }
renderItem = { this.renderItem }
style={{flex:2}}
/>
</Container>
);
}
renderHeader() {
return(
<Button onPress={()=>{this.deva()}}><Text>Bondan</Text></Button>
);
}
renderItem(item, itemSize) {
return(
<TouchableOpacity
key = { item.id }
style = {{ width: itemSize, height: itemSize }}
onPress = { () => {
this.deva();
}}>
<Image
resizeMode = "cover"
style = {{ flex: 1 }}
source = {{ uri: item.src }}
/>
<Text>{item.src}</Text>
</TouchableOpacity>
)
}
}
const styles = StyleSheet.create({
container: {
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
height: 587,
},
gridText: {
color: '#fff',
textAlign: 'center',
fontStyle: 'normal',
fontSize : 12
},
listContainer: {
height: Dimensions.get('window').height - (Dimensions.get('window').height*53/100),
}
});
You are loosing context of this. You need to either use arrow functions or bind the functions.
Example
constructor() {
super();
this.state = { items: [],
nama : ""
};
this.renderHeader = this.renderHeader.bind(this);
this.renderItem = this.renderItem.bind(this);
}
OR
renderHeader = () => {
// rest of your code
}
renderItem = (item, itemSize) => {
// rest of your code
}
Either change your deva method definition to an arrow function -
deva= () => {
Alert.alert('deva');
}
Or bind the deva method to this inside your constructor
constructor() {
super();
this.state = { items: [],
nama : ""
}
this.deva = this.deva.bind(this)
}
You get the error because when the deva method is invoked using this.deva(), the javascript runtime cannot find the property/function deva on the this it's called with (which is the anonymous callback passed to onPress in this case). But if you bind this to deva beforehand, the correct this is being searched by the javascript runtime.

Custom validator in Angular 5 dynamic forms

I am creating a dynamic form from a configurable json in Angular 5. Everything is working fine,but i am not able to add a custom validator for a particular field.I am getting an error like
TypeError: v is not a function
Json
{
"key": "age",
"label": "Age",
"required": false,
"order": 4,
"controlType": "textbox",
"validations": ['required', 'minlength'],
"custom":['rangeValidator'],//custom validator function name
"type": "email"
}
Component to make dynamic form controls:
toFormGroup(questions) {
let group: any = {};
questions.forEach(question => {
group[question.key] = new FormControl(question.value || '', this.getValidators(question)
);
});
return new FormGroup(group);
}
getValidators(question) {
let vals = [];
question.validations.forEach((v) => {
if (v == 'required') {
vals.push(Validators.required);
}
if (v == 'minlength') {
vals.push(Validators.minLength(4))
}
});
if (question.custom || question.custom.length > 0) {
question.custom.forEach((va) => {
vals.push(va);
});
}
return vals;
}
Main Component file:
import { Component, OnInit, Input } from '#angular/core';
import { FormGroup, AbstractControl ,FormControl} from '#angular/forms';
function rangeValidator(c: FormControl) {
if (c.value !== undefined && (isNaN(c.value) || c.value > 1 || c.value < 10)) {
return { range: true };
}
return null;
}
#Component({
selector: 'app-question',
templateUrl: './dynamic-form-question.component.html',
styleUrls: ['./dynamic-form-question.component.css']
})
export class DynamicFormQuestionComponent implements OnInit {
#Input() question;
#Input() form: FormGroup;
get isValid() { return this.form.controls[this.question.key].valid; }
constructor() { }
ngOnInit() {
console.log("My form", this.form.value)
}
}
Stackblitz Url
Any ideas,Please help
there
if (question.custom || question.custom.length > 0) {
question.custom.forEach((va) => {
vals.push(va);
});
}
you want to add your custom validators, but in fact you just add to the validators array the string "rangeValidator". So yes v is not a function :)
You should should declare you range validators as a static function of your customs validators like that :
export class CustomValidators {
static rangeValidator(c: FormControl) {
if (c.value !== undefined && (isNaN(c.value) || c.value > 1 || c.value < 10)) {
return { range: true };
}
return null;
}
then import it in and use it like that :
getValidators(question) {
....
if (question.custom || question.custom.length > 0) {
question.custom.forEach((va) => {
vals.push(CustomValidators[va]);
});
}
return vals;
}
see the forked stackblitz
NB : this fork only resolve the current matter. You global example form validation still doesnt work