#nativescript-community/ui-mapbox - Can't add markers programatically - nativescript

I am trying add markers dynamically/programatically after map is renderer but nothing is happening. Please see my code below
Service Code:
import { Injectable } from '#angular/core';
import { MapboxMarker, MapboxView, Mapbox } from "#nativescript-community/ui-mapbox";
import { registerElement } from '#nativescript/angular';
import { isIOS } from "#nativescript/core/platform";
#Injectable({
providedIn: 'root'
})
export class MapService {
mapboxView: MapboxView;
mapbox: Mapbox;
/**
* Creates an instance of Mapbox.
* #param config
* #memberof Mapbox
*/
constructor() {
registerElement("Mapbox", () => require("#nativescript-community/ui-mapbox").MapboxView);
}
generateMap(view: any) {
const settings = {
container: view,
accessToken: 'pk.eyJ1IjoiaGV5aXR6c3llZCIsImEiOiJjbDA5aTZlb3gwMGxsM2puejdyeTQyZnNjIn0.aJUDueGHXwKea-kN8lxn2g',
style: 'traffic_day',
margins: {
left: 18,
right: 18,
top: isIOS ? 390 : 454,
bottom: isIOS ? 50 : 8
},
center: {
lat: 52.3702160,
lng: 4.8951680
},
zoomLevel: 18, // 0 (most of the world) to 20, default 0
showUserLocation: false, // default false
hideAttribution: false, // default false
hideLogo: false, // default false
hideCompass: false, // default false
disableRotation: false, // default false
disableScroll: false, // default false
disableZoom: false, // default false
disableTilt: false, // default false
markers: [
{
id: 1,
lat: 52.3732160,
lng: 4.8941680,
title: 'Nice location',
subtitle: 'Really really nice location',
selected: true,
iconPath: '~/assets/Img/map.png',
onTap: () => console.log("'Nice location' marker tapped"),
onCalloutTap: () => console.log("'Nice location' marker callout tapped")
}
]
};
const mapView = new MapboxView();
mapView.setConfig(settings);
view.content = mapView;
}
}
Component.ts code:
import { Component, OnInit } from '#angular/core';
import { MapService } from 'map/map.service';
import { Page } from "#nativescript/core/ui/page";
import { ContentView } from "#nativescript/core/ui/content-view";
#Component({
selector: 'app-map-service-demo',
templateUrl: './map-service-demo.component.html',
styleUrls: ['./map-service-demo.component.css']
})
export class MapServiceDemoComponent implements OnInit {
constructor(public map: MapService, public page: Page) { }
ngOnInit(): void {
const contentView : ContentView = <ContentView>this.page.getViewById( 'mapContainer' );
this.map.generateMap(contentView);
}
}
Component.html code:
<StackLayout>
<ContentView height="100%" width="100%" id="mapContainer">
</ContentView>
</StackLayout>
The above implementation is not displaying markers by default even there is markers: [{}] property available in the settings.
Moreover, In the below code, I dont get what mapbox refers to
mapbox.addMarkers([
firstMarker,
{
// more markers..
}
])
Please guide me on adding markers programatically.

Related

Property 'toBase64String' does not exist on type 'ImageAsset'

I'm trying to save captured camera images to Couchbase with nativescripts & angular.
Followed Nic Raboy's tutorial at https://dzone.com/articles/save-captured-images-in-a-nativescript-angular-app but received the following error:
public capture() {
Camera.takePicture({ width: 300, height: 300, keepAspectRatio: true, saveToGallery: false }).then(picture => {
let base64 = picture.toBase64String("png", 70);
this.database.createDocument({
"type": "image",
"image": base64,
"timestamp": (new Date()).getTime()
});
this.images.push(picture);
}, error => {
console.dump(error);
});
}
error TS2339: Property 'toBase64String' does not exist on type 'ImageAsset'.
Full source
import { Component, OnInit } from "#angular/core";
import { Couchbase } from "nativescript-couchbase";
import * as Camera from "camera";
import * as ImageSource from "image-source";
#Component({
selector: "ns-app",
templateUrl: "app.component.html",
})
export class AppComponent implements OnInit {
public database: any;
public images: Array<any>;
public constructor() {
this.database = new Couchbase("image-database");
this.database.createView("images", "1", function(document, emitter) {
if(document.type && document.type == "image") {
emitter.emit(document._id, document);
}
});
this.images = [];
}
public ngOnInit() {
let rows = this.database.executeQuery("images");
for(let i = 0; i < rows.length; i++) {
this.images.push(ImageSource.fromBase64(rows[i].image));
}
}
public capture() {
Camera.takePicture({ width: 300, height: 300, keepAspectRatio: true, saveToGallery: false }).then(picture => {
let base64 = picture.toBase64String("png", 70);
this.database.createDocument({
"type": "image",
"image": base64,
"timestamp": (new Date()).getTime()
});
this.images.push(picture);
}, error => {
console.dump(error);
});
}
}
picture is type of ImageAsset, you will have to convert it to a ImageSource to use toBase64String(...).
...
ImageSource.fromAsset(picture)
.then(source => {
let base64 = source.toBase64String("png", 70);
...
})
.catch(err => {
console.log(err);
});

Error undefined is not a function when change text

I am trying the increment and decrement tutorial . When I type a number at the textinput, this value is to be reflected to the this text given. But I am getting error. The error says
undefined is not a function(evaluating 'this.props.counterSet(count)')
These are the codes that I have tried. Can anybody tell me where I have done the mistake.
Thank You
- App
-Actions
-ActionTypes.js
-CounterAction.js
-Reducers
-counterReducer.js
app.js
counterReducer.js
export default (state = 0, action) => {
switch (action.type) {
case 'SET':
return action.payload;
default:
return state;
}
}
counterAction.js
export const counterSet = (receivedNumber) => {
return {
type: 'SET',
payload: receivedNumber
}
}
ActionTypes.js
export * from './CounterAction';
app.js
import React, { Component } from 'react';
import { AppRegistry, StyleSheet, Text, TextInput, View, Button } from 'react-native';
import { connect } from 'react-redux';
import { counterSet } from './Actions/ActionTypes';
class App extends Component {
constructor(props) {
super(props);
this.state = {
count: 0
};
this.onChangeText = this.onChangeText.bind(this);
}
onChangeText(number) {
let count = parseInt(number);
// alert("inside", count);
this.props.counterSet(count);
}
render() {
return (
<View style={styles.container}>
<TextInput
style={{ width: 40, height: 40, borderWidth: 1 }}
onChangeText={this.onChangeText}
value={this.props.count.toString()}
/>
<View style={styles.countViewStyle}>
<Text style={styles.welcome}>
{this.props.count}
</Text>
</View>
</View>
);
}
}
function mapStateToProps(state) {
return {
count: state
}
}
export default connect(mapStateToProps, { counterIncrement, counterDecrement, counterClear, counterSet })(App);
Change the import from
export * from './CounterAction';
to
export { counterSet } from './CounterAction;
Hope this will help!

react native navigation - componentDidMount() fired twice

I am new to React Native. I am trying to build an app which has a Splash screen that would later navigate to Login screen if a user has not been authenticated or the Main screen if the user is authenticated. This is done using this.props.navigation.navigate()
The problem is that the Splash component would be mounted twice. I checked this by printing inside componentDidMount() of Splash. Because of this, the Login/Main screen enters twice, which looks very unpleasant. Is there any way to fix this?
Also, I want to add some delay when the screen changes from Splash to Login or Main using setTimeout(). Anyway to go about doing this?
Here's my code:
index.js
import React from 'react';
import { createStore, applyMiddleware, compose } from 'redux';
import { Provider } from 'react-redux';
import { persistStore } from 'redux-persist';
import reduxThunk from 'redux-thunk';
import reducers from './src/reducers';
import { StyleSheet } from 'react-native';
import LoginScreen from './src/components/Login/LoginScreen';
import Splash from './src/components/Login/Splash';
import Navigation from './src/components/Navigation/Navigation';
import { Font } from 'expo';
import {
createStackNavigator
} from 'react-navigation';
const createStoreWithMiddleware = applyMiddleware(reduxThunk)(createStore);
const store = createStoreWithMiddleware(reducers);
const persistor = persistStore(store);
export default class App extends React.Component {
constructor(props){
super(props);
this.state = {
fontLoaded: false,
currentScreen: 'Splash',
};
setTimeout(() => this.setState({currentScreen: 'Login'}), 2000);
}
async componentDidMount() {
await Font.loadAsync({
'Quicksand': require('./assets/fonts/Quicksand-Regular.ttf'),
'Quicksand-Medium': require('./assets/fonts/Quicksand-Medium.ttf'),
'Quicksand-Bold': require('./assets/fonts/Quicksand-Bold.ttf'),
});
this.setState({ fontLoaded: true });
}
render() {
const MainNavigator = createStackNavigator({
Splash: { screen: Splash },
Main: { screen: Navigation },
Login: { screen: LoginScreen },
})
if (this.state.fontLoaded)
return (
<Provider store={store}>
<MainNavigator></MainNavigator>
</Provider>
)
else return null;
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
});
Splash.js
import React from 'react';
import { StyleSheet, Text, View, ImageBackground, Image, Button } from 'react-native';
import bgImage from '../../../assets/images/login-background2.png';
import logo from '../../../assets/images/app-logo.png';
import { connect } from 'react-redux';
import { checkAuth } from '../../actions/auth.actions';
class Splash extends React.Component {
static navigationOptions ={
header: null
}
constructor(props){
super(props);
this.state = {
stillLoading: true,
}
}
componentDidMount() {
this.props.checkAuth();
}
render() {
if (this.props.authState.isLoginPending)
return (
<ImageBackground source={bgImage} style={styles.backgroundContainer}>
<View style={styles.logoContainer}>
<Image source={logo} style={styles.logo}></Image>
<Text style={styles.logoText}> Welcome to HealthScout</Text>
</View>
</ImageBackground>
);
else if (this.props.authState.isLoginSuccess){
setTimeout(() => this.props.navigation.navigate('Main'));
return null;
}
else{
setTimeout(() => this.props.navigation.navigate('Login'));
return null;
}
}
}
const mapStateToProps = state => {
return {
authState: state.authState
}
}
const mapDispatchToProps = dispatch => {
return {
checkAuth: () => dispatch(checkAuth()),
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Splash);
const styles = StyleSheet.create({
backgroundContainer: {
flex: 1,
alignItems: 'center',
width: null,
height: null,
justifyContent: 'center',
},
logoContainer: {
alignItems: 'center',
},
logo: {
width: 110,
height: 149,
},
logoText: {
color: '#fff',
fontSize: 40,
fontFamily: 'Quicksand',
opacity: 0.7,
marginTop: 20,
marginBottom: 10,
textAlign: 'center',
},
});
Solution
Take out the createStackNavigator from render.
It is better way wrapping screens above App class.
const MainNavigator = createStackNavigator({
Splash: { screen: Splash },
Main: { screen: Navigation },
Login: { screen: LoginScreen },
})
export default class App extends React.Component {
...
Why?
render is run repeatedly depends on various conditions as changing state, props and so on.
And your code looks making multiple components with createStackNavigation in render. Take out :)
p.s If you want to wait loading fonts before show home screen, just change to home screen from splash screen after loaded fonts. Thus, the better way is loading fonts in SplashScreen and do what you want.

infinite scrolling using AgGridReact

I'm trying to achieve infinite scrolling using ag grid react component, but it doesn't seems to be working.
here is my implementation :
import { AgGridReact } from 'ag-grid-react';
import 'ag-grid/dist/styles/ag-grid.css';
import 'ag-grid/dist/styles/ag-theme-balham.css';
class TasksGridContainer extends React.Component {
constructor(props) {
super(props);
this.state = {
loading: true,
gridOptions: {
//virtual row model
rowModelType: 'infinite',
paginationPageSize: 100,
cacheOverflowSize: 2,
maxConcurrentDatasourceRequests: 2,
infiniteInitialRowCount: 1,
maxBlocksInCache: 2,
components: {
loadingRenderer: function(params) {
console.log('loadingCellRenderer', params);
if (params.value !== undefined) {
return params.value;
} else {
return '<img src="https://raw.githubusercontent.com/ag-grid/ag-grid-docs/master/src/images/loading.gif">';
}
}
},
defaultColDef: {
editable: false,
enableRowGroup: true,
enablePivot: true,
enableValue: true
}
}
};
}
componentDidMount() {
this.props.actions.getAssignedTasks();
this.props.actions.getTeamTasks();
}
componentWillReceiveProps(newProps) {
if (this.props.taskView.taskGrid.listOfTasks.length > 0) {
this.setState({
loading: false ,
gridOptions: {
datasource: this.props.taskView.taskGrid.listOfTasks
}
});
}
}
render() {
return (
<div id="tasks-grid-container">
<div style={Style.agGrid} id="myGrid" className="ag-theme-balham">
<AgGridReact
columnDefs={this.props.taskView.taskGrid.myTaskColumns}
rowData={this.props.taskView.taskGrid.listOfTasks}
gridOptions={this.state.gridOptions}>
</AgGridReact>
</div>
</div>
);
}
}
TasksGridContainer.propTypes = {
listOfTasks: PropTypes.array,
actions: PropTypes.object
};
const mapStateToProps = ({ taskView }) => {
return {
taskView: {
taskGrid: {
listOfTasks: taskView.taskGrid.listOfTasks,
myTaskColumns: taskView.taskGrid.myTaskColumns,
teamTaskColumns: taskView.taskGrid.teamTaskColumns
}
}
}
};
const mapDispatchToProps = (dispatch) => {
return {
actions: bindActionCreators(taskGridActions, dispatch)
};
}
module.exports = connect(mapStateToProps, mapDispatchToProps)(TasksGridContainer);
columnDefs are being set once props.taskView.taskGrid.myTaskColumns is available.
a sample columndef:
[
{
cellRenderer: "loadingRenderer", checkboxSelection: true, field: "action", headerCheckboxSelection: true, headerCheckboxSelectionFilteredOnly: true, headerName: "Action"
},
{
"activity"headerName: "Activity Name"
}
]
Although grid is loading fine, but when i scroll it should call "loadingRenderer" component. But,I'm not able to see any loading gif when i scroll.
Am i doing something wrong in implementation?
Actual issue was not calling the the props properly and was not having onGridReady function to use gridAPi.
I modified the code and it starts working:
<AgGridReact
components={this.state.components}
enableColResize={true}
rowBuffer={this.state.rowBuffer}
debug={true}
rowSelection={this.state.rowSelection}
rowDeselection={true}
rowModelType={this.state.rowModelType}
paginationPageSize={this.state.paginationPageSize}
cacheOverflowSize={this.state.cacheOverflowSize}
maxConcurrentDatasourceRequests={this.state.maxConcurrentDatasourceRequests}
infiniteInitialRowCount={this.state.infiniteInitialRowCount}
maxBlocksInCache={this.state.maxBlocksInCache}
columnDefs={this.props.columns}
rowData={this.props.rowData}
onGridReady={this.onGridReady}
>
</AgGridReact>
state :
this.state = {
components: {
loadingRenderer: function(params) {
if (params.value !== undefined) {
return params.data.action;
} else {
return '<img src="https://raw.githubusercontent.com/ag-grid/ag-grid-docs/master/src/images/loading.gif">';
}
}
},
rowBuffer: 0,
rowSelection: "multiple",
rowModelType: "infinite",
paginationPageSize: 100,
cacheOverflowSize: 2,
maxConcurrentDatasourceRequests: 2,
infiniteInitialRowCount: 1,
maxBlocksInCache: 2
};
onGridReady function :
onGridReady = (params, data = []) => {
this.gridApi = params;
this.gridColumnApi = params.columnApi;
this.updateData(params,data);
}

TypeScript Visual Studio TS1219 Experimental support for decorators is a feature that is subject to change in a future release.

I get the following error: TS1219 Experimental support for decorators is a feature that is subject to change in a future release. Set the 'experimentalDecorators' option to remove this warning.
I know I can suppress this by setting "experimentalDecorators": true in tsconfig.json.
But I would still like to know why I get the error from this code:
import * as React from "react";
import scriptLoader from 'react-async-script-loader'
#scriptLoader(['https://maps.googleapis.com/maps/api/js?key=your-key'])
export default class Maps extends React.Component<any, any> {
constructor(props: any) {
super(props);
this.map = null;
}
refs: {
[string: string]: any;
map: any;
}
map: any;
componentWillReceiveProps({ isScriptLoaded, isScriptLoadSucceed }) {
if (isScriptLoaded && !this.props.isScriptLoaded) { // load finished
if (isScriptLoadSucceed) {
this.map = new google.maps.Map(this.refs.map, {
center: { lat: 10.794234, lng: 106.706541 },
zoom: 20
});
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition((position) => {
const pos = {
lat: position.coords.latitude,
lng: position.coords.longitude
};
this.map.setCenter(pos);
const marker = new google.maps.Marker({
position: pos,
map: this.map,
title: 'Hello World!'
});
}, () => {
console.log('navigator disabled');
});
} else {
// Browser doesn't support Geolocation
console.log('navigator disabled');
}
}
else this.props.onError()
}
}
render() {
return (
<div>
<div ref="map" style={{ height: '80%', width: '100%' }}></div>
{!this.map && <div className="center-md">Loading...</div>}
</div>
)
}
}
But I would still like to know why I get the error from this code:
Because you are using a decorator in #scriptLoader(['https://maps.googleapis.com/maps/api/js?key=your-key']).

Resources