What config and options do I need for react-native-highcharts to make a highstock OHLC graph? - react-native

I've been going through HighStock API to try and find which config and options I need to pass to the ChartView component in react-native-highcharts to draw my graph. I'm having a hard time finding what combination of config and options will get my desired result, things like series, dataGrouping, etc... . My data is a 2 dimensional array with 4 values for OHLC. My desired result is the photo at the top of this stackoverflow.
Here is my code so far.
class OHLC extends React.Component {
static navigationOptions = ({ navigation }) => ({
title: "OHLC",
headerLeft: (
<TouchableOpacity
style={NavStyles.headerButton}
onPress={() => navigation.openDrawer()}>
<Icon name="bars" size={20} />
</TouchableOpacity>
),
})
render() {
var Highcharts='Highcharts';
var conf={
title: {
text: 'Stock Name'
},
xAxis: {
type: 'datetime',
tickPixelInterval: 150
},
yAxis: {
title: {
text: 'Price'
},
plotLines: [{
value: 0,
width: 1,
color: '#808080'
}]
},
// tooltip: {
// formatter: function () {
// return '<b>' + this.series.name + '</b><br/>' +
// Highcharts.dateFormat('%Y-%m-%d %H:%M:%S', this.x) + '<br/>' +
// Highcharts.numberFormat(this.y, 2);
// }
// },
legend: {
enabled: false
},
// exporting: {
// enabled: false
// },
series: [{
type: 'ohlc',
name: 'AAPL Stock Price',
data: (function () {
let arrays = aExtractFromJson(data,'data', null,null);
arrays = ohlcExtractor(arrays);
return arrays;
// look at toFixed method for number to limit decimal point
}()),
dataGrouping: {
units: [[
'week', // unit name
[1] // allowed multiples
], [
'month',
[1, 2, 3, 4, 6]
]]
}
}]
};
const options = {
global: {
useUTC: false
},
lang: {
decimalPoint: ',',
thousandsSep: '.'
}
};
return (
<View>
<ChartView style={{height:300}} config={conf} options={options} stock={true} ></ChartView>
//To see if anything gets rendered.
<Text>HELLO DAVID!</Text>
</View>
);
}
}

After further research, I was able to find the config and options needed to create an OHLC Graph using the ChartView component in react-native-highcharts. I encountered some issues with rendering a blank screen so I added javaScriptEnabled={true} domStorageEnabled={true} originWhitelist={['']} to my ChartView.
In the config the essentials:
series with type: 'ohlc' and data: [[1,2,3,4],[2,3,4,5]] inside.
In options, no arguments are required, I left it as options='' in the ChartView.
Don't forget to add stock={true} in ChartView
My code, basic example:
import React, {Component} from 'react';
import {View} from 'react-native';
import ChartView from 'react-native-highcharts';
class OHLC extends React.Component {
constructor(props) {
super(props);
}
render() {
var Highcharts='Highcharts';
var conf={
chart: {
animation: Highcharts.svg, // don't animate in old IE
marginRight: 10,
},
title: {
text: 'Live random data'
},
xAxis: {
type: 'datetime',
tickPixelInterval: 150
},
yAxis: {
title: {
text: 'Value'
},
plotLines: [{
value: 0,
width: 1,
color: '#808080'
}]
},
legend: {
enabled: false
},
exporting: {
enabled: false
},
series: [{
type: 'ohlc',
name: 'Random data',
/*Open, high,low,close values in a two dimensional array(two days)*/
data: [[1,2,3,4],[2,3,4,5]],
}]
};
return (
<View style={{borderRadius: 4, marginTop: 30,}}>
<ChartView style={{height:500}} config={conf} javaScriptEnabled={true} domStorageEnabled={true} originWhitelist={['']} stock={true} options=''></ChartView>
</View>
);
}
}

Related

Display search output using json data in react native

I am at a very primitive stage of learning react-native. And I am trying to solve a simple problem, which may sound silly, but I really want to know the answer.
I have a json file
data.js
export const PRODUCT_DATA = [
{
name: 'abc',
price: 90,
weight: '1 kg',
currency: 'INR',
liked: true,
image: require('../assets/images/carrots/Rectangle238.png')
},
{
name: 'bce',
price: 10,
weight: '1 kg',
currency: 'USD',
liked: false,
image: require('../assets/images/mango/Rectangle234.png')
},
{
AllCategoriesComponent: [
{
icon: "home-outline",
name: "Household",
shape: true,
},
{
icon: "basket-outline",
name: "Grocery",
shape: false,
},
{
icon: "ios-podium",
name: "Milk",
shape: true,
},
{
icon: "ios-rose",
name: "chilled",
shape: false,
},
{
icon: "hardware-chip",
name: "Drinks",
shape: true,
},
{
icon: "cloud",
name: "Pharmacy",
shape: true,
},
{
icon: "fast-food",
name: "Frozen Food",
shape: true,
},
{
icon: "football",
name: "Vegetable",
shape: true,
},
{
icon: "bulb",
name: "Meat",
shape: true,
},
{
icon: "football",
name: "Vegetable",
shape: true,
},
{
icon: "bulb",
name: "Meat",
shape: true,
},
]
},
];
ANd below is screen file
screen.js
import { SearchBar } from 'react-native-elements';
import { Text, View, TextInput } from 'react-native';
import React from 'react';
import { PRODUCT_DATA } from "./data";
export default class App extends React.Component {
constructor() {
super();
this.state = {
search: '',
}
}
updateSearch = (search) => {
this.setState({ search: search });
};
render() {
const { search } = this.state;
return (
<View>
<SearchBar onChangeText={this.updateSearch} value={search} />
{PRODUCT_DATA[2].AllCategoriesComponent.map((item, index) => {
if (item.name === this.state.search) {
return (
<View style={{ backgroundColor: "white" }}>
<Text>{search}</Text>
</View>
);
} else {
return (<Text></Text>);
}
})}
<Text>{this.state.search}</Text>
</View>
);
}
}
As you can see this is not a good solution. I am able to print the output only if I type full name in the SearchBar. Also it seems all the item.name are already on the screen, which comes up when value of search bar matches it. I want to start showing the output as soon as something is typed on the SearchBar
This might help please look into it
import { FlatList, Text, View, TextInput } from "react-native";
export default class Example extends Component {
constructor(props) {
super(props);
this.state = {
text: "",
data: [],
};
this.arrayholder = [];
}
componentDidMount() {
const data = PRODUCT_DATA[2].AllCategoriesComponent.map((item, index) => {
return item;
});
this.setState({ data }, () => {
this.arrayholder = data;
});
}
searchData(text) {
const newData = this.arrayholder.filter((item) => {
const itemData = item.name.toUpperCase();
const textData = text.toUpperCase();
return itemData.indexOf(textData) > -1;
});
this.setState({
data: newData,
text: text,
});
}
render() {
return (
<View style={styles.MainContainer}>
<TextInput
onChangeText={(text) => this.searchData(text)}
value={this.state.text}
placeholder="Search Here"
/>
<FlatList
data={this.state.data}
keyExtractor={(item, index) => index.toString()}
renderItem={({ item }) => <Text style={styles.row}>{item.name}</Text>}
/>
</View>
);
}
}

update vue chart-js y axis max value without re rendering entire chart

I am working on a project where I am implementing some charts from the Vue-Chartjs library. I need the Y-axis max value to change everytime the user changes the filters given. I Import an existing barchart from the vue-chartjs library. In the code there is a javascript file that has some defaults already, to set extra options I can use the extraOptions object as a prop to personalize each chart accordingly. Here is the default component:
import { Bar } from 'vue-chartjs'
import { hexToRGB } from "./utils";
import reactiveChartMixin from "./mixins/reactiveChart";
let defaultOptions = {
tooltips: {
tooltipFillColor: "rgba(0,0,0,0.5)",
tooltipFontFamily: "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",
tooltipFontSize: 14,
tooltipFontStyle: "normal",
tooltipFontColor: "#fff",
tooltipTitleFontFamily: "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",
tooltipTitleFontSize: 14,
tooltipTitleFontStyle: "bold",
tooltipTitleFontColor: "#fff",
tooltipYPadding: 6,
tooltipXPadding: 6,
tooltipCaretSize: 8,
tooltipCornerRadius: 6,
tooltipXOffset: 10,
},
legend: {
display: false
},
scales: {
yAxes: [{
ticks: {
fontColor: "#9f9f9f",
fontStyle: "bold",
beginAtZero: true,
display: false,
min: 0,
max: 100
},
gridLines: {
display: false,
drawBorder: false,
}
}],
xAxes: [{
gridLines: {
display: false,
drawBorder: false,
},
}],
}
};
export default {
name: 'BarChart',
extends: Bar,
mixins: [reactiveChartMixin],
props: {
labels: {
type: [Object, Array],
description: 'Chart labels. This is overridden when `data` is provided'
},
datasets: {
type: [Object, Array],
description: 'Chart datasets. This is overridden when `data` is provided'
},
data: {
type: [Object, Array],
description: 'Chart.js chart data (overrides all default data)'
},
color: {
type: String,
description: 'Chart color. This is overridden when `data` is provided'
},
extraOptions: {
type: Object,
description: 'Chart.js options'
},
title: {
type: String,
description: 'Chart title'
},
},
methods: {
assignChartData() {
let { gradientFill } = this.assignChartOptions(defaultOptions);
let color = this.color || this.fallBackColor;
return {
labels: this.labels || [],
datasets: this.datasets ? this.datasets : [{
label: this.title || '',
backgroundColor: gradientFill,
borderColor: color,
pointBorderColor: "#FFF",
pointBackgroundColor: color,
pointBorderWidth: 2,
pointHoverRadius: 4,
pointHoverBorderWidth: 1,
pointRadius: 4,
fill: true,
borderWidth: 1,
data: this.data || []
}]
}
},
assignChartOptions(initialConfig) {
let color = this.color || this.fallBackColor;
const ctx = document.getElementById(this.chartId).getContext('2d');
const gradientFill = ctx.createLinearGradient(0, 170, 0, 50);
gradientFill.addColorStop(0, "rgba(128, 182, 244, 0)");
gradientFill.addColorStop(1, hexToRGB(color, 0.6));
let extraOptions = this.extraOptions || {}
return {
...initialConfig,
...extraOptions,
gradientFill
};
}
},
mounted() {
this.chartData = this.assignChartData({});
this.options = this.assignChartOptions(defaultOptions);
this.renderChart(this.chartData, this.options, this.extraOptions);
}
}
I use this js file to import the bar chart inside a vue component like you see down below.
everytime the input of the form changes i need to re render the chart. I use the onInputChange() method to turn the boolean loaded to false and call the loadData() method.
Inside the loadData() method I make an axios request that gets me the right data every time. I also get the maximum value for my Y axis.
Then in the response I call on updateChart() so that I can update the data and the max value of the chart. then i turn the boolean loaded to true again so that my chart renders accordingly.
The problem with this approach is that the chart disappears completely for a split of a second. Before deciding to change the max Value of the Y axis I was able to update the data of my chart without having to use the v-if="loaded".
I need to find a solution where the chart re renders without it completely disappearing from the page. I know some suggested to use computed variables but i don't fully understand how it is supposed to work. Here is the component minus the form fields.
I guess in it's essence what I want is to update the Y axis max value without having to re render the entire chart.
<template>
<div>
<BarChart v-if="loaded" :labels="chartLabels"
:datasets="datasets"
:height="100"
:extraOptions="extraOptions"
>
</BarChart>
<br>
</div>
</template>
<script>
import BarChart from '../../components/Library/UIComponents/Charts/BarChart'
import Dropdown from "../../components/Library/UIComponents/Dropdown"
import GroupedMultiSelectWidget from "~/components/widgets/GroupedMultiSelectWidget"
import SelectWidget from "../../components/widgets/SelectWidget";
export default{
name: 'PopularChart',
components: {BarChart, Dropdown, SelectWidget, GroupedMultiSelectWidget},
data(){
return {
loaded:true,
form:{
day: 'Today',
workspace:'',
machine_family: [],
duration: [],
user_group: [],
dt_start:'',
dt_end:''
},
url: `/api/data_app/job_count_by_hour/`,
chart_data: [],
days: [ {day:"Today", id:"Today"},
{day:"Monday", id:"0"},
{day:"Tuesday",id:"1"},
{day:"Wednesday",id:"2"},
{day:"Thursday",id:"3"},
{day:"Friday",id:"4"},
{day:"Saturday",id:"5"},
{day:"sunday",id:"6"} ],
chartLabels: ["00u", "1u", "2u", "3u","4u","5u", "6u", "7u", "8u", "9u", "10u", "11u", "12u", "13u", "14u", "15u","16u", "17", "18u","19u","20u","21u","22u","23u"],
datasets: [],
maximumValue: '',
extraOptions:{}
}
},
methods: {
onInputChange() {
this.loaded = false
this.loadData()
},
async loadData() {
await this.$axios.get(`${this.url}?day=${this.form.day}&date_start=${this.form.dt_start}&date_end=${this.form.dt_end}&workspace=${this.form.workspace}&user_group=${this.form.user_group}&machine_family=${this.form.machine_family}`)
.then(response => {
this.updateChart(response.data.results,response.data.maximum)
this.loaded = true
})
},
updateChart(data,maxValue) {
this.datasets = [{
label: ["jobs %"],
backgroundColor:"#f93232",
data: data
},]
this.maximumValue = maxValue
this.extraOptions = {
tooltips: {
callbacks:{
label: function (tooltipItems,){
if (tooltipItems.value > ((50/100) * maxValue)){
return 'busy';
}else if (tooltipItems.value < ((30/ 100) * maxValue) ){
return ' not busy';
}else if ( tooltipItems.value < ((40/ 100) * maxValue )){
return 'kind of busy'
}
}
}
},
scales: {
yAxes: [{
gridLines: {
zeroLineColor: "transparent",
display: false,
drawBorder: false,
},
ticks: {
max: this.maximumValue,
display: true,
}
}],
xAxes: [{
gridLines: {
zeroLineColor: "transparent",
display: false,
drawBorder: false,
},
}],
},
}
},
},
mounted() {
this.loadData()
},
}
</script>
You can bind the chart to a variable in data(), initialize it with some defaults in mounted() and whenever you want to update the chart data, you use the Chart.js API.
An implementation could look something like this:
export default {
data() {
return {
chart: null
};
},
mounted() {
this.chart = new Chart(/* defaults */);
},
methods: {
updateChart(data) {
this.chart.data.datasets = data;
/* rest of the chart updating */
this.chart.update();
}
}
};

React Native Navigation - bottom tabs and drawer

I'm trying to add bottom tab bar in my jhipster ignite application, which uses react-native-navigation v2.
Screens are registered like:
Navigation.registerComponentWithRedux(LAUNCH_SCREEN, () => LaunchScreen, Provider, store)
Where e.g.:
export const LAUNCH_SCREEN = 'nav.LaunchScreen'
And here is the complete navigation:
export const topBar = {
title: {
text: 'MDNGHT',
color: Colors.snow
},
leftButtons: [
{
id: 'menuButton',
icon: Images.menuIcon,
testID: 'menuButton',
color: Colors.snow
}
]
}
export const launchScreenComponent = {
component: {
id: 'id.launch',
name: LAUNCH_SCREEN,
options: {
topBar: topBar,
bottomTab: {
fontSize: 12,
text: 'HOME'
}
}
}}
export const eventsScreenComponent = {
component: {
id: 'id.events',
name: EVENTS_SCREEN,
options: {
topBar: topBar,
bottomTab: {
fontSize: 12,
text: 'EVENTS'
}
}
}
}
export const bottomTabs = {
id: 'bottomTabs',
children: [
{
stack: {
children: [
launchScreenComponent
]
}
},
{
stack: {
children: [
eventsScreenComponent
]
}
}
],
options: {
bottomTabs: {
activeTintColor: 'red',
inactiveTintColor: 'grey',
backgroundColor: '#121212',
borderTopWidth: 0,
shadowOffset: {width: 5, height: 3},
shadowColor: 'black',
shadowOpacity: 0.5,
elevation: 5
}
}
}
export const appStack = {
root: {
sideMenu: {
left: {
component: {
name: DRAWER_CONTENT
}
},
center: {
bottomTabs: bottomTabs
}
}
}
}
Navigation.events().registerAppLaunchedListener(() => {
Navigation.setDefaultOptions({
topBar: {
topBar: {
title: {
color: Colors.snow
}
},
backButton: {
showTitle: false,
testID: 'backButton',
icon: Images.chevronLeftIcon,
color: Colors.snow,
iconColor: Colors.snow
},
background: {
color: Colors.background
}
},
sideMenu: {
left: {
enabled: false
}
}
})
Navigation.setRoot(appStack)
// handle app state and deep links
AppState.addEventListener('change', handleAppStateChange)
Linking.addEventListener('url', handleOpenURL)
})
I don't get any error message, my application just stops after start.
When I put:
stack: {
id: 'center',
children: [launchScreenComponent]
}
Instead of bottomTabs: bottomTabs in appStack, the application works (but without bottom tab bar)
Following the Layout docs from react-native-navigation, you can replace the appStack with a bottomTabs implementation instead of a drawer like below (only one tab configured as example, add another object in root.bottomTabs.children to add another tab).
export const appStack = {
root: {
bottomTabs: {
children: [
{
stack: {
id: 'firstTabStack',
children: [
{
component: {
name: LAUNCH_SCREEN,
options: {
topBar: {
title: {
text: 'Welcome!',
color: Colors.snow
}
}
}
}
}
],
options: {
bottomTab: {
iconColor: 'gray',
textColor: 'gray',
selectedIconColor: 'black',
selectedTextColor: 'black',
text: 'Launch Screen',
testID: 'LAUNCH_SCREEN',
icon: Images.menuIcon
}
}
}
}
]
}
}
}
It actually turns out that it is required to set an icon for each bottom tab, otherwise the app crashes:
bottomTab: {
fontSize: 12,
text: 'HOME'
icon: require('../shared/images/logo.png')
}
This resolves the issue.

How to show top bar button with bottom tabs and side drawer

Im new to react native and react native navigation (v2) and been struggling with implementing a top nav button i.e. getting the top nav to show.
I wish to add a top bar with a button that can trigger the side drawer but cannot work out how to get the top nav to show.
Here is my working config of the bottom tabs and side draw:
const mainTabs = async () => {
Navigation.setRoot({
root: {
sideMenu: {
left: {
component: {
name: 'foo.SideDrawer',
},
},
center: {
id: 'MY_STACK',
bottomTabs: {
children: [
{
component: {
name: 'foo.HomeScreen',
options: {
bottomTab: {
fontSize: 12,
text: 'Home',
icon: await Icon.getImageSource("home", 30)
}
}
},
},
{
component: {
name: 'foo.ProfileScreen',
options: {
bottomTab: {
text: 'Profile',
fontSize: 12,
icon: await Icon.getImageSource("person", 30)
}
}
},
}
]
}
}
}
}
})
};
Can anyone advise where the top bar and button config should go?
--
EDIT 1 - Home screen component:
import React, { Component } from 'react';
import { View, Text, StyleSheet } from 'react-native';
class HomeScreen extends Component {
static get options() {
return {
topBar: {
title: {
text: 'Home',
},
leftButtons: [
{
icon: require('../../assets/signin.png'),
text: 'Button one',
id: 'homeButton',
},
],
},
};
}
render () {
return (
<View style={styles.container}>
<Text>Home Screen</Text>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F0EFE8'
}
})
export default HomeScreen;
You add this static function in your component
export default class HomeScreen extends Component {
static get options() {
return {
topBar: {
title: {
text: 'Home',
},
leftButtons: [
{
icon: require('../../../assets/icons/icon.png'),
id: 'homeButton',
},
],
},
};
}
}
and here you find all needed configs https://wix.github.io/react-native-navigation/#/docs/topBar-buttons

React native component does not react to mobx observable data change

so I started to build a new app with react native and mobx.
I have a flat list component that gets his state data from the mobx store list. and when i'm adding new item to the mobx list, it won't re render the flat list view.
here is my code:
List Component:
#inject('TravelStore')
#observer
class TripsList extends Component {
constructor(props) {
super(props);
this.state = {
trips_list: props.TravelStore.trips_list
}
};
// set the navigation bar options
static navigationOptions = ({ navigation }) => {
const { params = {} } = navigation.state;
return {
title: 'Your Trips',
headerRight: (
<Button transparent primary onPress={ params.addNewTrip }>
<Icon name='ios-add' />
</Button>
)
};
};
// connect between component functions to header
componentDidMount() {
this.props.navigation.setParams({
addNewTrip: this._addNewTrip.bind(this),
});
}
_addNewTrip() {
this.props.TravelStore.addNewTrip('bla')
}
_renderListItem({ item }) {
return (
<TripsListItem details={item} navigation={this.props.navigation}/>
);
};
render() {
return (
<Container>
<FlatList
data = {this.state.trips_list}
renderItem = {this._renderListItem.bind(this)}
keyExtractor={(item, index) => index}
/>
</Container>
);
};
}
mobx store:
class ObservableTravelListStore {
#observable trips_list = [
{
name: 'to denver',
trip_cost: 400,
buying_list: [
{ name: 'pizza', price: 10 },
{ name: 'burger', price: 40 },
{ name: 'ipad', price: 44 },
{ name: 'bus', price: 45 },
]
},
{
name: 'to yafo',
trip_cost: 30,
buying_list: [
{ name: 'na na na', price: 10 },
{ name: 'burger', price: 40 },
]
},
{
name: 'to tel aviv',
trip_cost: 50,
buying_list: [
{ name: 'na na na', price: 10 },
{ name: 'no no no', price: 40 },
]
},
]
#action addNewTrip (trip_data) {
this.trips_list.push({
name: 'newTrip',
trip_cost: 6060,
buying_list: [
{ name: 'na na na', price: 10 },
{ name: 'burger', price: 40 },
]
})
console.log(this.trips_list[3])
}
}
const TravelStore = new ObservableTravelListStore()
export default TravelStore
any idea why the TripsList component won't rerender when addNewTrip function is called?
the problem is that you are not listening to the real observable but to a copy of it, you save in state in the constructor.
<FlatList
data = {this.state.trips_list}//change this
renderItem = {this._renderListItem.bind(this)}
keyExtractor={(item, index) => index}
/>
<FlatList
data = {this.props.TravelStore.trips_list}//change to this
renderItem = {this._renderListItem.bind(this)}
keyExtractor={(item, index) => index}
/>
the render function is like autobind of mobx and react to changes in the observable if it's a render function of an observer
if you want to react to inner changes in the items of the list,
you should add an observable scheme and this should do the trick,
something like this:
class TripModel {
#observable name = ''
#observable trip_cost = 0
#observable buying_list = []
constructor(name, cost, buy_list){
this.name = name
this.trip_cost = cost
this.buying_list = buy_list
}
/* class functions*/
}
class ObservableTravelListStore {
#observable trips_list = [
new Trip(
'to denver',
400,
[
{ name: 'pizza', price: 10 },
{ name: 'burger', price: 40 },
{ name: 'ipad', price: 44 },
{ name: 'bus', price: 45 },
]
),
new Trip(
'to yafo',
30,
[
{ name: 'na na na', price: 10 },
{ name: 'burger', price: 40 },
]
),
new Trip(
'to tel aviv',
50,
[
{ name: 'na na na', price: 10 },
{ name: 'burger', price: 40 },
]
)
]
#action addNewTrip (trip_data) {
this.trips_list.push(new Trip(
'newTrip',
6060,
[
{ name: 'na na na', price: 10 },
{ name: 'burger', price: 40 },
]
))
}
}
const TravelStore = new ObservableTravelListStore()
export default TravelStore
this is just better planning for reactive apps, so on change to inner content of the items in the list you will react to this change
hope that helps
Its an old post, but I also got stuck with something similar recently. Adding extraData in Flatlist prop list helped me.
<FlatList
data = {this.props.TravelStore.trips_list}
renderItem = {this._renderListItem.bind(this)}
keyExtractor={(item, index) => index}
extraData={this.props.TravelStore.trips_list.length} // list re-renders whenever the array length changes
/>
And as #Omri pointed out, you shouldn't be storing the observable in the Component state but make changes to it directly.