As I want to create Route from my current location to a particular clicked marker in React. For Implementing this I am using React Google Map library (react-google-maps) and it's direction example (Direction Example) but unfortunately I am unable to call new google.maps.DirectionsService()
method in React component. It is giving me error as google is undefined.There is already open issue for the same scenario (google is undefined)
Add :
/* global google */
at the top your react file.
To get access to google class at window level, I have created plain javascript file .
UserDetails.js
module.exports = {
"Steepless": {
directionsService: new google.maps.DirectionsService(),
directionsRenderer: new google.maps.DirectionsRenderer(),
elevationService: new google.maps.ElevationService(),
travelMode: google.maps.TravelMode.DRIVING,
directionStatus: google.maps.DirectionsStatus.OK,
longestDistance: 0,
highestElevation: 0,
lowestElevation: Infinity,
chartWidth: 400,
chartBarWidth: 2
},
getOrigin: function (lat, lng) {
var origin = new google.maps.LatLng(lat, lng);
return origin;
},
getDestination: function (lat, lng) {
var destination = new google.maps.LatLng(lat, lng);
return destination;
}
};
reactComponent.jsx
import * as React from "react";
var config = require("./Config");
var $ = require('jquery');
var _this;
var userDetails = require("./api/UserDetails");
var _ = require("lodash")
var modelState = undefined;
const { Button, Popover, Modal, Tooltip, OverlayTrigger, Accordion, Panel, Row, Col } = require("../../../../../../node_modules/react-bootstrap");
var GoogleMapLib = require("../../../../../../node_modules/react-google-maps");
var GoogleMap = GoogleMapLib.GoogleMap;
var withGoogleMap = GoogleMapLib.withGoogleMap;
var Marker = GoogleMapLib.Marker;
var InfoWindow = GoogleMapLib.InfoWindow;
var Direction = GoogleMapLib.DirectionsRenderer;
var buyNow;
import "../styles/glyphs-style.scss";
import ReferFriend from "./ReferFriend";
import SaleAlert from "./SaleAlert";
import WishListButton from "./WishListButton";
var ProductDetails = React.createClass({
getInitialState: function () {
return {
appended: '',
showModal: true,
isLoding: 'none',
cordinates: '',
morelikethis: '',
merchantCollection: '',
getParent: false,
acoordianData: '',
markers: [{
position: {
lat: 40.712784, lng: -74.005941,
},
key: `New York`,
defaultAnimation: 2
}],
center: {
lat: parseFloat('40.712784'),
lng: parseFloat('-74.005941'),
},
route: '',
zoom: 7,
showInfo: false,
mywishlistLable: 'ADD TO LIST',
saleAlertLable: 'GET SALE ALERT',
wishListChecked: 'glyphicon-heart-empty',
rawRelated: ''
};
},
contextTypes: {
router: React.PropTypes.object.isRequired
},
/*Model related stuffs */
close() {
userDetails.changeProductDetailsStatus(false);
this.setState({ mywishlistLable: 'ADD TO LIST' });
this.setState({ wishListChecked: 'glyphicon-heart-empty' });
this.setState({ showModal: false });
modelState = true;
},
open() {
this.setState({ showModal: true });
},
/*Map related stuff*/
handleMapLoad: function (map) {
this._mapComponent = map;
if (map) {
console.log(map.getZoom());
}
},
/*
* This is called when you click on the map.
* Go and try click now.
*/
handleMapClick: function (event) {
const nextMarkers = [
...this.state.markers,
{
position: event.latLng,
defaultAnimation: 2
},
];
this.setState({
markers: nextMarkers,
});
if (nextMarkers.length === 3) {
this.props.toast(
`Right click on the marker to remove it`,
`Also check the code!`
);
}
},
// Toggle to 'true' to show InfoWindow and re-renders component
handleMarkerClick: function (targetMarker) {
var showType;
var markers = this.state.markers.map(function (item, i) {
if (item.key === targetMarker.key) {
showType = true;
var geoDetails = userDetails.getUserGeoInfo();
var destination = userDetails.getDestination(targetMarker.position.lat,targetMarker.position.lng);
var origin = userDetails.getOrigin(geoDetails.lat,geoDetails.lng);
console.log(userDetails.Steepless.directionsService);
userDetails.Steepless.directionsService.route({
origin: origin,
destination: destination,
travelMode: userDetails.Steepless.travelMode,
}, (result, status) => {
if (status === userDetails.Steepless.directionStatus) {
this.setState({
route: result,
});
} else {
console.error(`error fetching directions ${result}`);
}
});
} else {
showType = item.showInfo;
}
return {
position: {
lat: parseFloat(item.position.lat),
lng: parseFloat(item.position.lng)
},
key: i,
showInfo: showType,
infoContent: (
item.infoContent)
}
}.bind(this));
this.setState({ markers: markers })
},
handleMarkerClose: function (targetMarker) {
var showType;
var markers = this.state.markers.map(function (item, i) {
if (item.key === targetMarker.key) {
showType = false;
} else {
showType = item.showInfo;
}
return {
position: {
lat: parseFloat(item.position.lat),
lng: parseFloat(item.position.lng)
},
key: i,
showInfo: showType,
infoContent: (
item.infoContent)
}
}.bind(this));
this.setState({ markers: markers })
},
handleMarkerRightClick: function (targetMarker) {
/*
* All you modify is data, and the view is driven by data.
* This is so called data-driven-development. (And yes, it's now in
* web front end and even with google maps API.)
*/
const nextMarkers = this.state.markers.filter(marker => marker !== targetMarker);
this.setState({
markers: nextMarkers,
});
},
getInfoContents: function (item) {
},
GetModelInfo: function () {
this.GetProdInfo();
var geoDetails = userDetails.getUserGeoInfo();
if (geoDetails) {
this.setState({
center: {
lat: parseFloat(geoDetails.lat),
lng: parseFloat(geoDetails.lng)
}
});
var lat = geoDetails.lat;
var lng = geoDetails.lng;
} else {
var lat = null;
var lng = null;
}
this.setState({ cordinates: geoDetails });
$.ajax({
url: config.magentoBaseUrl + config.MORE_LIKE_THIS,
dataType: 'json',
cache: false,
type: 'POST',
data: {
category_name: this.props.productInfo.category_name,
brand: this.props.productInfo.brand,
product_category_ids: this.props.productInfo.product_category_ids,
_id: this.props.productInfo._id,
lat: lat,
lng: lng,
merchant_id: this.props.productInfo.merchant_id,
storename: this.props.productInfo.store_name
},
success: function (data) {
if (data.productData) {
//this.setState({morelikethis:data.productData})
this.setState({ rawRelated: data.productData })
var merchantData = JSON.parse(data.merchantData);
if (merchantData) {
this.setState({ acoordianData: merchantData });
var markers = merchantData.map(function (item, i) {
return {
position: {
lat: parseFloat(item.latitude),
lng: parseFloat(item.longitude)
},
key: item.storename,
showInfo: false,
infoContent: (
this.getInfoContents(item))
}
}.bind(this));
this.setState({ markers: markers })
}
}
}.bind(this),
error: function (xhr, status, err) {
}.bind(this)
});
},
GetProdInfo: function () {
var details = userDetails.getUserInfo();
if (details) {
var prodCheckData = {
cust_id: details.id,
skuNumber: this.props.productInfo.sku_number,
userEmail: details.email
}
}
$.ajax({
url: config.magentoBaseUrl + config.wishlist_action + 'prod_avail/',
dataType: 'json',
cache: false,
type: 'POST',
data: prodCheckData,
success: function (data) {
if (data.wishdata == "exist") {
this.setState({ mywishlistLable: 'ADDED IN LIST' });
this.setState({ wishListChecked: 'glyphicon-heart' });
}
}.bind(this),
error: function (xhr, status, err) {
}.bind(this)
});
},
getHome: function () {
this.setState({ getParent: true });
},
wishListAction: function () {
var details = userDetails.getUserInfo();
//userDetails.changeProductDetailsStatus(false);
if (details) {
if (this.state.wishListChecked === "glyphicon-heart-empty") {
this.wishListTransactcion('add/', details);
this.setState({ wishListChecked: 'glyphicon-heart' });
this.setState({ mywishlistLable: 'ADDED IN LIST' });
}
if (this.state.wishListChecked === "glyphicon-heart") {
this.wishListTransactcion('remove/', details);
this.setState({ wishListChecked: 'glyphicon-heart-empty' });
this.setState({ mywishlistLable: 'ADD TO LIST' });
}
} else {
this.context.router.push('/');
userDetails.setOpenLoginModal('true');
}
},
wishListTransactcion: function (action, details) {
var wishlistData = {
cat_id: this.props.productInfo.product_category_ids[0],
cust_id: details.id,
skuNumber: this.props.productInfo.sku_number,
userEmail: details.email,
merchantName: this.props.productInfo.store_name,
merchantId: this.props.productInfo.merchant_id,
aprice: this.props.productInfo.actual_price,
dprice: this.props.productInfo.discount_price
}, url = config.magentoBaseUrl + config.wishlist_action + action;
$.ajax({
url: url,
dataType: 'json',
cache: false,
type: 'POST',
data: wishlistData,
success: function (data) {
userDetails.setWishlistCount(data);
this.context.router.push('/');
this.setState({ tempState: 'true' });
}.bind(this),
error: function (xhr, status, err) {
}.bind(this)
});
},
getSavedPrice: function (aPrice, dPrice) {
var percentDiff, convertedPrice, diff = (aPrice - dPrice) / aPrice * 100;
percentDiff = diff.toFixed(0);
convertedPrice = (aPrice - dPrice);
convertedPrice = this.addCommas(convertedPrice.toFixed(2));
return "<span class='save-price'>You Save <span>$" + (convertedPrice + " " + "(" + percentDiff + "%)");
},
addCommas: function (nStr) {
nStr += '';
var x = nStr.split('.');
var x1 = x[0];
var x2 = x.length > 1 ? '.' + x[1] : '';
var rgx = /(\d+)(\d{3})/;
while (rgx.test(x1)) {
x1 = x1.replace(rgx, '$1' + ',' + '$2');
}
return x1 + x2;
},
render() {
var containerProps = {
className: 'items-map'
};
var route = this.state.route;
const GettingStartedGoogleMap = withGoogleMap(props => (
<GoogleMap
ref={props.onMapLoad}
zoom={this.state.zoom}
center={this.state.center}
onClick={props.onMapClick}
>
<Marker
icon={config.current_location_marker}
key="100"
position={this.state.center}
>
</Marker>
{this.state.markers.map((marker, index) => (
<Marker
icon={config.marker}
key={index}
position={marker.position}
onClick={() => props.onMarkerClick(marker)}
>
{marker.showInfo && (
<InfoWindow onCloseClick={() => props.onMarkerClose(marker)}>
<div dangerouslySetInnerHTML={{ __html: marker.infoContent }}></div>
</InfoWindow>
)}
</Marker>
))}
{route ?
<Direction
directions={route}
options={{ polylineOptions: { strokeColor: 'green' },suppressMarkers: true}} />
:
null
}
</GoogleMap>
));
var actualPrice = '';
var discountPrice;
var price_html = '';
if (this.props.productInfo.discount_price == null) {
discountPrice = 0;
}
if (parseFloat(this.props.productInfo.discount_price) != 0 && parseFloat(this.props.productInfo.actual_price) != 0) {
if (parseFloat(this.props.productInfo.discount_price) < parseFloat(this.props.productInfo.actual_price)) {
actualPrice = parseFloat(this.props.productInfo.actual_price).toFixed(2)
actualPrice = this.addCommas(actualPrice);
discountPrice = parseFloat(this.props.productInfo.discount_price).toFixed(2)
discountPrice = this.addCommas(discountPrice);
} else {
actualPrice = parseFloat(this.props.productInfo.actual_price).toFixed(2)
discountPrice = this.addCommas(actualPrice);
discountPrice = '';
}
} else if (this.props.productInfo.discount_price != 0) {
discountPrice = parseFloat(this.props.productInfo.discount_price).toFixed(2)
discountPrice = this.addCommas(discountPrice);
//discountPrice = '';
} else if (this.props.productInfo.actual_price != 0) {
actualPrice = parseFloat(this.props.productInfo.actual_price).toFixed(2)
actualPrice = this.addCommas(actualPrice);
discountPrice = '';
}
if (discountPrice != 0 && discountPrice != '') {
var savedPrice = this.getSavedPrice(this.props.productInfo.actual_price, this.props.productInfo.discount_price);
}
return (
<Modal onEnter={this.GetModelInfo} show={this.props.dataFlag === this.state.showModal ? this.props.dataFlag : this.state.showModal} onHide={this.close} bsSize="large" aria-labelledby="contained-modal-title-lg">
<Modal.Header closeButton>
<span onClick={this.close} data-dismiss="modal" aria-label="Close" className="visible-xs glyphicon arrow-left-css glyphicon-arrow-left"></span>
</Modal.Header>
<a onClick={this.close} className="hidden-xs prod-details-close"><img className="prod-details-close-img" src={require('../images/close_24.png')} /></a>
<Modal.Body>
<div>
{!this.state.getParent ?
<div>
<div>
{this.state.acoordianData ?
<div id="map-canvas">
<GettingStartedGoogleMap
containerElement={
<div style={{ height: `100%` }} />
}
mapElement={
<div style={{ height: `100%` }} />
}
center={this.state.center}
markers={this.state.markers}
onMarkerClick={this.handleMarkerClick}
onMarkerClose={this.handleMarkerClose}
/>
</div>
: null}
<div className="container-fluid">
<div className="row">
<div className="hidden-xs col-sm-12 col-md-3 col-lg-3 product-store-locator-div">
<div className="stiky-store-locator-div filter">
<div className="store-locator-section-title">
<span>Store Locator</span>
</div>
<hr className="store-locator-title-hr" />
</div>
</div>
<div className="col-sm-12 col-md-9 col-lg-9 product-details-div">
<div className="product-details-top-section">
<div className="row breadcrum-div">
<div className="col-xs-12 col-sm-12 col-md-12 col-lg-12 breadcrumbs-title">
</div>
</div>
</div>
<div className="row product-details-row-div">
<div className="col-xs-6 padding-0 col-sm-5 col-md-5 col-lg-4">
<div className="product-details-image-display-div center-block">
<div className="dummy-placeholder"></div>
<div className="product-details-display-section-style center-block">
<img src={this.props.productInfo.product_image} className="img-responsive" alt="product image" />
</div>
</div>
</div>
<div className="col-xs-6 col-sm-7 col-md-7 col-lg-5">
<div className="product-details-outer-div" >
<div className="product-details-top-div">
<div className="product-brand-name-div">
<div className="product-details-brand-name">{this.props.productInfo.brand} </div>
<div className="product-details-product-name">{this.props.productInfo.product_name}</div>
</div>
<div className="product-details-store-name">{this.props.productInfo.store_name}</div>
{discountPrice ?
<div className="old-price">
<del className="product-listing-details-old-price-div-less" dangerouslySetInnerHTML={{ __html: "$" + actualPrice }}>
</del>
<span className="product-listing-details-price-div" dangerouslySetInnerHTML={{ __html: "$" + discountPrice }}>
</span>
<div dangerouslySetInnerHTML={{ __html: savedPrice }}></div>
</div>
:
<div className="old-price" dangerouslySetInnerHTML={{ __html: "$" + actualPrice }}></div>
}
<hr className="madal-hr-gray" />
<div className="product-details-description">
<div className="product-details-description-content">
<div className="product-details-description-title">Description</div>
{this.props.productInfo.long_description}
</div>
</div>
</div>
<div className="product-details-bottom-div">
<div className="buy-from-retailer-btn-div">
<a target="_blank" href={this.props.productInfo.retailer_url} >
<button type="button" className="btn buy-from-retailer-btn">
<span className="btn-detail-style">GO TO {this.props.productInfo.store_name}</span>
</button>
</a>
</div>
<div className="add-to-list-btn-div">
<span className="btn add-to-list-btn get-alert-detail-page" ><SaleAlert {...this.props.productInfo} /></span>
</div>
<div className="button-seprator"> </div>
<div className="add-to-list-btn-div">
<button onClick={this.wishListAction} type="button" className="btn add-to-list-btn">
<span className={"add-to-list-btn-span glyphicon " + this.state.wishListChecked}></span>{this.state.mywishlistLable}</button>
</div>
</div>
</div>
</div>
<div className="col-xs-12 col-sm-12 col-md-12 col-lg-3">
</div>
</div>
</div>
</div>
<div className="more-from-brand-div">
<h1 className="more-from-brand-link">More Like This</h1>
</div>
<hr className="madal-hr-gray" />
<div className="row product-listing-row-div" id="relatedProduct-data">
{this.getMoreLikeThisHtml(this.state.rawRelated, this)}
</div>
</div>
</div>
</div>
:
null
}
</div>
</Modal.Body>
</Modal>
);
}
});
export default ProductDetails;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With