Nativescript - plugin nativescript-ui-pro pie chart refresh - nativescript

I'm a new dev on this platform and I have a problem with graphics, specifically pie chart, im trying leave dynamic, but the data in the screen continue static, you have any solution for this?
devs this is my code
The pie chart
<chart:RadPieChart id="pieChart" height="300" allowAnimation="true" row="1" allowAnimation="true">
<chart:RadPieChart.series>
<chart:PieSeries selectionMode="DataPoint" expandRadius="0.4" outerRadiusFactor="0.7" items="{{ pieSource }}" seriesName="pie" valueProperty="Amount" legendLabel="Shift" showLabels="true">
<chart:PieSeries.labelStyle>
<chart:PointLabelStyle margin="15"/>
</chart:PieSeries.labelStyle>
</chart:PieSeries>
</chart:RadPieChart.series>
<chart:RadPieChart.legend>
<chart:RadLegendView position="Right" title="Turnos - Partic." offsetOrigin="TopRight" width="110" enableSelection="true"/>
</chart:RadPieChart.legend>
</chart:RadPieChart>
and below is the code for updating the graph
loadData(val1: number, val2: number, val3: number) {
const v1 = val1;
const v2 = val2;
const v3 = val3;
this.set("pieSource",
[
{ Shift: "T1", Amount: v1 },
{ Shift: "T2", Amount: v2 },
{ Shift: "T3", Amount: v3 }
]);
console.log(this.get("pieSource"));
}

Related

What is the proper way of sorting an array of objects in Nativescript Vue?

I'm currently trying to do a very simple sort on an array of items within a Nativescript Vue app, however it is pulling back some very weird results. I have a playground link that recreates the issue.
Basically, when my page loads, the items are ordered based on a data property for sort direction. This is ordered correctly. However, if I then try to change this direction (ascending or descending) then it just orders really strange.
On load, my items are ordered ascending like this:
When I then try to sort them descending, the order changes to this:
When I then try to sort ascending again, the order changes to this:
Here is basically the code from my playground entry - I know some of it is over-engineered but this is just a result of me testing numerous things:
<template>
<Page>
<ActionBar title="Sort Test" icon="">
</ActionBar>
<StackLayout>
<StackLayout>
<Label :text="item.title" textWrap="true"
v-for="item in orderedItems" :key="item.id" />
</StackLayout>
<Label :text="`Sort Ascending: ${sortAscending}`"
textWrap="true" />
<Label :text="`Sort Descending: ${sortDescending}`"
textWrap="true" />
<Button text="Sort Ascending" #tap="sortAsc" />
<Button text="Sort Descending" #tap="sortDesc" />
</StackLayout>
</Page>
</template>
<script>
export default {
methods: {
sortAsc() {
this.sortAscending = true;
this.sortDescending = false;
},
sortDesc() {
this.sortAscending = false;
this.sortDescending = true;
}
},
computed: {
orderedItems() {
//return (b.date > a.date) ? 1 : (b.date < a.date) ? -1 : 0;
return this.items.sort((a, b) => {
if (this.sortAscending) {
// return (a.id > b.id) ? 1 : (a.id < b.id) ? -1 : 0;
return a.id - b.id;
} else {
// return (b.id > a.id) ? 1 : (b.id < a.id) ? -1 : 0;
return b.id - a.id;
}
});
}
},
data() {
return {
sortAscending: true,
sortDescending: false,
items: [{
id: 1,
title: "Label 1"
},
{
id: 2,
title: "Label 2"
},
{
id: 4,
title: "Label 4"
},
{
id: 5,
title: "Label 5"
},
{
id: 3,
title: "Label 3"
}
]
};
}
};
</script>
<style lang="postcss" scoped></style>
Now, I've found a similar issue that seems to match my exact use case, however changing the behaviour of my orderedItems property doesn't seem to make a difference.
Is there something I'm doing wrong? Is my v-for in the wrong place, or is it a side-effect of using a computed property to display it - should I be using a watch on the sort directions instead to re-order the items data property?
Any help would be appreciated - I haven't tried this on iOS yet but this behaviour is happening in Android.
OK, for anyone interested, the above sort solution works fine - I don't know enough to be sure but the problem seems to be when Nativescript is rendering out the items.
The solution I have in place is to add a loading state in between the sorts. So, when someone clicks the sort ascending / descending button, a loading message appears, the sort happens, then the loading state disappears. Below is a pretty rudimentary version:
<template>
<Page>
<Label v-if="loadingItems">Loading Items</Label>
<StackLayout v-else>
<Label v-for="item in items" :key="item.id" :text="item.title" />
<Button #tap="sort('asc')" text="Sort Ascending" />
<Button #tap="sort('desc')" text="Sort Descending" />
</StackLayout>
</template>
<script>
export default {
data() {
return {
loadingItems: false,
items: [
{
id: 1,
title: "Item 1"
},
{
id: 3,
title: "Item 3"
},
{
id: 3,
title: "Item 3"
},
{
id: 4,
title: "Item 4"
},
{
id: 5,
title: "Item 5"
}
]
},
methods: {
sort(dir) {
this.items = this.items.sort((a,b) => {
return dir == 'asc' ? a.id - b.id : b.id - a.id
});
}
}
}
</script>
Hope this helps anyone with the same issue

Image is overflowing outside the size of FlatList single grid

I am currently working on making a grid using FlatList component of the react-native library. I am trying to insert images inside the grids formed inside the FlatList component, but when I am trying to insert the images, the images do not completely fill the particular grid, ther is always the down part of the image which is not displayed in the grid.
Code for the component:
import React, { Component } from 'react';
import { View , StyleSheet, FlatList, Text, Dimensions, Image } from 'react-native';
export const HomeGrid = () => {
return (
<FlatList
data={data}
renderItem={({item}) => (
<View style={styles.itemContainer}>
<Image
style={{flex: 1, width: size, height: size }}
source = {item.value}
/>
</View>
)}
keyExtractor={item => item.id}
numColumns={numColumns} />
);
}
const data = [
{id: 'a', value: require('../photos/photo3.jpg')},
{id: 'b', value: require('../photos/photo2.jpeg')},
{id: 'c', value: require('../photos/photo4.jpg')},
{id: 'd', value: require('../photos/photo3.jpg')},
{id: 'e', value: require('../photos/photo2.jpeg')},
{id: 'f', value: require('../photos/photo4.jpg')}
];
const numColumns = 2;
const size = Dimensions.get('window').width/numColumns;
const styles = StyleSheet.create({
itemContainer: {
width: size,
height: size,
},
item: {
flex: 1,
backgroundColor: 'lightblue',
}
});
What could be the workaround for this?

Recharts - Tooltip for each Bar in a Bar Chart composed of three bars

I have in my bar chart three different bars.
I would like to have a tooltip for each bar in the bar chart and not just one for the three.
import React from 'react';
import {
BarChart,
Bar,
XAxis,
YAxis,
CartesianGrid,
Tooltip,
Legend,
} from 'Recharts';
const data = [
{ name: 'Page A', uv: 4000, pv: 1982, amt: 2400 },
{ name: 'Page B', uv: 3000, pv: 1398, amt: 4739 },
{ name: 'Page C', uv: 2000, pv: 9800, amt: 9056 },
{ name: 'Page D', uv: 2780, pv: 3908, amt: 2000 },
{ name: 'Page E', uv: 1890, pv: 4678, amt: 2181 },
{ name: 'Page F', uv: 2390, pv: 3800, amt: 2873 },
{ name: 'Page G', uv: 3490, pv: 1987, amt: 2100 },
];
class SimpleBarChart extends React.Component {
render() {
return (
<BarChart
width={600}
height={300}
data={data}
margin={{ top: 5, right: 30, left: 20, bottom: 5 }}
>
<CartesianGrid strokeDasharray="3 3" />
<XAxis dataKey="name" />
<YAxis />
<Tooltip />
<Legend />
<Bar dataKey="pv" barSize={20} fill="#8884d8" />
<Bar dataKey="amt" barSize={20} fill="#82ca9d" />
<Bar dataKey="uv" barSize={20} fill="#ffc658" />
</BarChart>
);
}
}
export default SimpleBarChart;
Following Natasha's answer, first create a custom tooltip:
<Tooltip content={<CustomTooltip/>} />
Add a function/component to render the custom contents. The variable tooltip is used to identify which Bar is being hovered over. The contents can be customized using the payload which contains all the bars in the selection.
var tooltip
const CustomTooltip = ({ active, payload }) => {
if (!active || !tooltip) return null
for (const bar of payload)
if (bar.dataKey === tooltip)
return <div>{ bar.name }<br/>{ bar.value.toFixed(2) }</div>
return null
}
Finally, add name and onMouseOver props to each <Bar> element:
<Bar dataKey="pv" barSize={20} fill="#8884d8"
name="Name" onMouseOver={ () => tooltip="pv" } />
When the mouse hovers over that <Bar> it will set the tooltip variable to the value "pv". Then the CustomTooltip will find that entry in the payload parameter and display the name and value.
You can create a custom tooltip like in the example here:
http://recharts.org/en-US/examples/CustomContentOfTooltip <---- Customized tooltip example (from recharts documentation)
After you create a customized tooltip, you can call it in the Bar component's OnMouseOver property which is in the documentation here:
http://recharts.org/en-US/api/Bar#onMouseOver <---- OnMouseOver
You could also use OnMouseEnter and OnMouseLeave but I know that doesn't work for everyone.
You might want to create a function that shows the tooltip when the mouse hovers over a bar and hides the tooltip when the mouse stops hovering over the bar.
You can add shared={false} props into Tooltip like this:
<Tooltip shared={false} />

nativescript-vue dataform does not update the source data

anyone can help?
i have this one page app to show the problem:
if you modify a field and then press 'save' at the top the changed field is not shown on the console...
<template>
<Page>
<ActionBar>
<Label text="SAVE" #tap="saveScreen()" />
</ActionBar>
<StackLayout>
<RadDataForm :source="person"/>
</StackLayout>
</Page>
</template>
<script>
export default {
data () {
return {
person: {
name: 'John',
age: 23,
email: 'john#company.com',
city: 'New York',
street: '5th Avenue',
streetNumber: 11,
},
};
},
methods: {
saveScreen() {
console.log('=======personName: ' + JSON.stringify(this.person))
}
}
}
</script>
<style>
</style>
i realize this is pretty much a basic question, i searched the internet for an answer however could not find it...
thanks in advance for your help.
Regards,
Hans
RadDataForm is a little bit tricky to work with since it doesn't bind data automatically so you'll have to add some events to get changed data.
Initial data is used to create form and on change data is saved to another object so you can listen to propertyCommitted event and get editedObject.
<RadDataForm :source="person" #propertyCommitted="onPropertyCommitted" />
data() {
return {
person: {
name: "John",
age: 23,
email: "john#company.com",
city: "New York",
street: "5th Avenue",
streetNumber: 11
},
committedPerson: {}
};
},
methods: {
onPropertyCommitted (data) {
this.committedPerson = data.object.editedObject
},
saveScreen () {
console.log(this.committedPerson);
}
}
Don't know if this is the best way to do it in Vue but I see that there are open issues on github regarding this and some workarounds are posted but none for Vue.
It should be explained better in official docs.
Here is working example https://play.nativescript.org/?template=play-vue&id=98Xyjv&v=5
And here you can find some examples on validation, grouping etc.https://github.com/telerik/nativescript-ui-samples-vue/tree/master/dataform/app/examples
Here is my answer for this. Look at the save function.
<StackLayout>
<StackLayout orientation="vertical" backgroundColor="lightgray">
<RadDataForm
id="myDataForm"
:source="record"
v-on:propertyCommitted="save"/>
</StackLayout>
<script>
import { mapState } from 'vuex'
import { getViewById } from "tns-core-modules/ui/core/view";
export default {
data() {}
},
methods:{
save(){
console.log('save')
let data = args.object
var dataform = getViewById(data, "myDataForm");
console.log(dataform.editedObject)//<--updated Data Here
},
}
computed: mapState({
record(state){
return record = this.$store.getters.record;
}
};

Custom legend labels in my rechart chart

I have a pretty straight forward data array for my recharts component :
{name: '12.1.2011', series1: 4000, series2: 2400, series3: 2400},
{name: '12.2.2011', series1: 3000, series2: 1398, series3: 2210},
{name: '12.3.2011', series1: 2000, series2: 9800, series3: 2290}
I would like to have labels for the series values in my Legend. Instead of the chart showing me the different colors for "series1", "series2", and "series3".
Of course I could set the actual values I want to use for my legend in the JSON already but this just doesn't look right. Eg :
{name: '12.1.2011', 'My nice long descriptive text': 4000, 'Some other text': 2400, 'Some other descriptive text': 2400},
{name: '12.2.2011', 'My nice long descriptive text': 3000, 'Some other text': 1398, 'Some other descriptive text: 2210},
{name: '12.3.2011', 'My nice long descriptive text': 2000, 'Some other text': 9800, 'Some other descriptive text: 2290}
I need to map my series level to a descriptive label.
I have looked at content in Legend : http://recharts.org/#/en-US/api/Legend, but that seems more concerned with completely rewriting the Legend Component.
In your Line, Bar and Area add a name attribute.
Example:
<Line name="# Apples" type="monotone" dataKey="series1" stroke="#FF0000" />
<Line name="# Bananas" type="monotone" dataKey="series2" stroke="#FFFF00" />
<Line name="# Grapes" type="monotone" dataKey="series3" stroke="#FF00FF" />
The legend will pick this up automatically:
http://recharts.org/en-US/api/Legend
By default, the content of legend is generated by the name of Line,
Bar, Area, etc. When no name has been setted, dataKey will be used to
generate legend content alternatively.
If you're looking to get this working on a <Pie /> you can override the <Legend /> payload. Please see the following example;
<Legend
payload={
data.map(
(item, index) => ({
id: item.name,
type: "square",
value: `${item.name} (${item.value}%)`,
color: colors[index % colors.length]
})
)
}
/>
http://recharts.org/en-US/api/Legend#payload
For custom legend, use content props, Ref: https://recharts.org/en-US/api/Legend#content
const renderLegend = (props) => {
const { payload } = props;
return (
<ul>
{
payload.map((entry, index) => (
<li key={`item-${index}`}>{entry.value}</li>
))
}
</ul>
);
}
<Legend content={renderLegend} />

Resources