React Nodejs - Custom #font-face in jsx for PDF generation - node.js

I have a React project in which part of the functionality involves sending a request to a Nodejs server that runs PhantomJS and the html-pdf plugin in order to create a pdf that gets build via a react component from my node server. The problem I'm having is actually embedding custom fonts into my react component. I have a base64 encoded font set that would be easier to include versus a bunch of files, but due to how React loads in CSS, I keep getting errors.
Here is the function that gets called to generate the PDF on my NodeJS server:
exports.order = (req, res, next) => {
phantom.create().then(function(ph) {
ph.createPage().then(function(page) {
// page.viewportSize = { width: 600, height: 600 };
page.open(`http://localhost:3005/print/work/${req.params.id}`).then(function(status) {
page.property('content').then(function(content) {
pdf.create(content, options).toBuffer(function(err, buffer){
res.writeHead(200, {
'Content-Type': 'application/pdf',
'Content-Disposition': `attachment; filename=order-${req.params.id}.pdf`,
'Content-Length': buffer.length
});
res.end(buffer);
});
page.close();
ph.exit();
});
});
});
});
}
Here's the actual React component that contains the HTML for the PDF:
var React = require('react');
var moment = require('moment');
class PrintWorkOrder extends React.Component {
render() {
var order = this.props;
var s = {
body: {
width: '100%',
float: 'none',
fontFamily: 'Helvetica',
color: '#000',
display: 'block',
fontSize: 10,
}
}
return (
<div style={s.body}>
I shortened the content in her for brevity
</div>
)
}
}
module.exports = PrintWorkOrder;
The functionality currently works, it's just now I need to add some custom fonts that aren't Helvetica and I'm having trouble solving that with this current implementation. Does anyone have any insight into this?

I also have the same issue, what I did is to encoded front into base64 and put it as
font.css
#font-face {
font-family: 'fontName';
src: url(data:application/x-font-woff;charset=utf-8;base64,<base64>) format('woff'),
font-weight: normal;
font-style: normal;
}
The trick is to read CSS file content and inject it into the DOM markup as style tag
React.createElement('style', { dangerouslySetInnerHTML: { __html: this.props.children } });
So to do it:
For the second line of code, what it does it to create style element. You could do is to write a component that read content from the .css file(s) and then put the content inside style element like this:
Style.js
var React = require('react');
export default class Style extends React.Component {
static propTypes = {
children: React.PropTypes.string.isRequired
};
render() {
return React.createElement('style', { dangerouslySetInnerHTML: { __html: this.props.children } });
}
}
Modify PrintWorkOrder.js
var React = require('react');
var moment = require('moment');
var fs = require('fs');
var path = require('path');
var Style = require('./Style');
const readFile = (filePath) => fs.readFileSync(path.resolve(__dirname, filePath)).toString();
class PrintWorkOrder extends React.Component {
constructor(props) {
super(props);
this.state = {
styles: []
}
}
componentWillMount() {
this.setState({
styles: [
readFile('font.css') // Assuming putting font.css same location with PrintWorkOrder component
]
})
}
render() {
var order = this.props;
var s = {
body: {
width: '100%',
float: 'none',
fontFamily: 'Helvetica',
color: '#000',
display: 'block',
fontSize: 10,
}
}
return (
<html>
<head>
{/* Here to render all the styles */}
{this.state.styles.map(s => (<Style>{s}</Style>))}
</head>
<body>
<div style={s.body}>
I shortened the content in her for brevity
</div>
</body>
</html>
);
}
}
module.exports = PrintWorkOrder;

Related

How to run mediapipe facemesh on a ES6 node.js environment alike react

I am trying to run this HTML example https://codepen.io/mediapipe/details/KKgVaPJ from https://google.github.io/mediapipe/solutions/face_mesh#javascript-solution-api in a create react application. I have already done:
npm install of all the facemesh mediapipe packages.
Already replaced the jsdelivr tags with node imports and I got the definitions and functions.
Replaced the video element with react-cam
I don't know how to replace this jsdelivr, maybe is affecting:
const faceMesh = new FaceMesh({
locateFile: (file) => {
return `https://cdn.jsdelivr.net/npm/#mediapipe/face_mesh/${file}`;
}
});
So the question is:
Why the facemesh is not showing? Is there any example of what I am trying to do?
This is my App.js code (sorry for the debugugging scaffolding):
import './App.css';
import React, { useState, useEffect } from "react";
import Webcam from "react-webcam";
import { Camera, CameraOptions } from '#mediapipe/camera_utils'
import {
FaceMesh,
FACEMESH_TESSELATION,
FACEMESH_RIGHT_EYE,
FACEMESH_LEFT_EYE,
FACEMESH_RIGHT_EYEBROW,
FACEMESH_LEFT_EYEBROW,
FACEMESH_FACE_OVAL,
FACEMESH_LIPS
} from '#mediapipe/face_mesh'
import { drawConnectors } from '#mediapipe/drawing_utils'
const videoConstraints = {
width: 1280,
height: 720,
facingMode: "user"
};
function App() {
const webcamRef = React.useRef(null);
const canvasReference = React.useRef(null);
const [cameraReady, setCameraReady] = useState(false);
let canvasCtx
let camera
const videoElement = document.getElementsByClassName('input_video')[0];
// const canvasElement = document.getElementsByClassName('output_canvas')[0];
const canvasElement = document.createElement('canvas');
console.log('canvasElement', canvasElement)
console.log('canvasCtx', canvasCtx)
useEffect(() => {
camera = new Camera(webcamRef.current, {
onFrame: async () => {
console.log('{send}',await faceMesh.send({ image: webcamRef.current.video }));
},
width: 1280,
height: 720
});
canvasCtx = canvasReference.current.getContext('2d');
camera.start();
console.log('canvasReference', canvasReference)
}, [cameraReady]);
function onResults(results) {
console.log('results')
canvasCtx.save();
canvasCtx.clearRect(0, 0, canvasElement.width, canvasElement.height);
canvasCtx.drawImage(
results.image, 0, 0, canvasElement.width, canvasElement.height);
if (results.multiFaceLandmarks) {
for (const landmarks of results.multiFaceLandmarks) {
drawConnectors(canvasCtx, landmarks, FACEMESH_TESSELATION, { color: '#C0C0C070', lineWidth: 1 });
drawConnectors(canvasCtx, landmarks, FACEMESH_RIGHT_EYE, { color: '#FF3030' });
drawConnectors(canvasCtx, landmarks, FACEMESH_RIGHT_EYEBROW, { color: '#FF3030' });
drawConnectors(canvasCtx, landmarks, FACEMESH_LEFT_EYE, { color: '#30FF30' });
drawConnectors(canvasCtx, landmarks, FACEMESH_LEFT_EYEBROW, { color: '#30FF30' });
drawConnectors(canvasCtx, landmarks, FACEMESH_FACE_OVAL, { color: '#E0E0E0' });
drawConnectors(canvasCtx, landmarks, FACEMESH_LIPS, { color: '#E0E0E0' });
}
}
canvasCtx.restore();
}
const faceMesh = new FaceMesh({
locateFile: (file) => {
return `https://cdn.jsdelivr.net/npm/#mediapipe/face_mesh/${file}`;
}
});
faceMesh.setOptions({
selfieMode: true,
maxNumFaces: 1,
minDetectionConfidence: 0.5,
minTrackingConfidence: 0.5
});
faceMesh.onResults(onResults);
// const camera = new Camera(webcamRef.current, {
// onFrame: async () => {
// await faceMesh.send({ image: videoElement });
// },
// width: 1280,
// height: 720
// });
// camera.start();
return (
<div className="App">
<Webcam
audio={false}
height={720}
ref={webcamRef}
screenshotFormat="image/jpeg"
width={1280}
videoConstraints={videoConstraints}
onUserMedia={() => {
console.log('webcamRef.current', webcamRef.current);
// navigator.mediaDevices
// .getUserMedia({ video: true })
// .then(stream => webcamRef.current.srcObject = stream)
// .catch(console.log);
setCameraReady(true)
}}
/>
<canvas
ref={canvasReference}
style={{
position: "absolute",
marginLeft: "auto",
marginRight: "auto",
left: 0,
right: 0,
textAlign: "center",
zindex: 9,
width: 1280,
height: 720,
}}
/>
</div >
);
}
export default App;
You don't have to replace the jsdelivr, that piece of code is fine; also I think you need to reorder your code a little bit:
You should put the faceMesh initialization inside the useEffect, with [] as parameter; therefore, the algorithm will start when the page is rendered for the first time
Also, you don't need to get videoElement and canvasElement with doc.*, because you already have some refs defined
An example of code:
useEffect(() => {
const faceMesh = new FaceDetection({
locateFile: (file) => {
return `https://cdn.jsdelivr.net/npm/#mediapipe/face_detection/${file}`;
},
});
faceMesh.setOptions({
maxNumFaces: 1,
minDetectionConfidence: 0.5,
minTrackingConfidence: 0.5,
});
faceMesh.onResults(onResults);
if (
typeof webcamRef.current !== "undefined" &&
webcamRef.current !== null
) {
camera = new Camera(webcamRef.current.video, {
onFrame: async () => {
await faceMesh.send({ image: webcamRef.current.video });
},
width: 1280,
height: 720,
});
camera.start();
}
}, []);
Finally, in the onResults callback I would suggest printing first the results, just to check if the Mediapipe implementation is working fine. And don't forget to set the canvas size before drawing something.
function onResults(results){
console.log(results)
canvasCtx = canvasReference.current.getContext('2d')
canvas.width = webcamRef.current.video.videoWidth;
canvas.height = webcamRef.current.video.videoHeight;;
...
}
Good luck! :)

Create image NextJS + NodeJS - 404 This page could not be found

I'm creating images using this library https://github.com/frinyvonnick/node-html-to-image
The output folder is public (https://nextjs.org/docs/basic-features/static-file-serving)
Everything works well on Dev, but when I build the app and run in production mode, I can't have access to the image (the image is created) but I receive the message 404 This page could not be found.
const nodeHtmlToImage = require('node-html-to-image')
async function generateImage({ htmlContent }) {
const htmlFile = `<html>
<head>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700|Roboto+Slab:400,700|Material+Icons"/>
<style>
body {
width: 600px;
height: auto;
}
</style>
</head>
${htmlContent}
</html>`
const image_name = crypto.randomBytes(20).toString('hex')
const relativePath = `output/${image_name}.png`
const output = `${process.env.SERVER_STATIC_URL}/${relativePath}`
return await nodeHtmlToImage({
output: `./public/${relativePath}`,
html: htmlFile,
}).then(() => {
return { image_src: output }
})
.catch((err) => {
console.log('err')
return err
})
}
export { generateImage }
How can I solve it?
Thanks!

How to use OpenLayers 5 with Node (express)?

I've been trying to visualize Vector data. I'm using Tegola as vector tile server. My first choice of visualizing library was Leaflet, but Leaflet's Vector.Grid library seems to have some problems with point data;
Issue on tegola's Github page.
Issue on Leaflet.VectorGrid project on Github.
So I've switched to OpenLayers. OpenLayers has a very inclusive library and there are plenty of examples and tutorials.
It took half a day for me to understand new version and complete the workshop on the link above. OpenLayers seems to be the solution for my needs. But I don't know how to rewrite code the code that I've prepared in workshop which is written to run on web.pack server. I want to render the layers and add a few nodes that will complete my needs on the map.
My map runs on web.pack server ;
But I want to render this map in Node. So I've write a server file;
// Get dependencies
const express = require('express');
const http = require('http');
const bodyParser = require('body-parser');
const dotenv = require('dotenv')
const logger = require('./services/logger');
const cors = require('cors');
const { Pool, Client } = require('pg')
const pool = new Pool()
dotenv.config();
// Get our API routes
const api = require('./routes/api.router');
const client = new Client()
const app = express();
// Parsers for POST data
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cors());
app.set('view engine', 'ejs');
app.use(express.static(__dirname + '/script'));
app.get('/', function (req, res) {
// res.sendFile(__dirname + "/views/index.html");
res.render('index', { title: 'Map' });
});
// Set our api routes
app.use('/api', api);
app.use((req, res, next) => {
const err = new Error('Not Found');
err.status = 404;
logger.error(`Request Error ${req.url} - ${err.status}`)
next(err);
});
app.use((err, req, res, next) => {
res.status(err.status || 500);
res.json({
error: {
message: err.message
}
});
});
/**
* Get port from environment and store in Express.
*/
const port = process.env.PORT || '3000';
app.set('port', port);
const server = http.createServer(app);
server.listen(port, () => console.log(`API running on localhost:${port}`));
Confusing part starts from here, I'm using the same HTML file in workshop server(web.pack). But I don't know how to reference main.js file to this HTML file. As you can see below there is no reference to main.js file inside this file.
How does web.pack combines these two files ? And is it possible to the same using express in NodeJS ?
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>OpenLayers</title>
<style>
html,
body,
#map-container {
margin: 0;
height: 100%;
width: 100%;
font-family: sans-serif;
}
.arrow_box {
position: relative;
background: #000;
border: 1px solid #003c88;
}
.arrow_box:after,
.arrow_box:before {
top: 100%;
left: 50%;
border: solid transparent;
content: " ";
height: 0;
width: 0;
position: absolute;
pointer-events: none;
}
.arrow_box:after {
border-color: rgba(0, 0, 0, 0);
border-top-color: #000;
border-width: 10px;
margin-left: -10px;
}
.arrow_box:before {
border-color: rgba(0, 60, 136, 0);
border-top-color: #003c88;
border-width: 11px;
margin-left: -11px;
}
.arrow_box {
border-radius: 5px;
padding: 10px;
opacity: 0.8;
background-color: black;
color: white;
}
#popup-content {
max-height: 200px;
overflow: auto;
}
#popup-content th {
text-align: left;
width: 125px;
}
</style>
</head>
<body>
<div id="map-container"></div>
<div class="arrow_box" id="popup-container">
<div id="popup-content"></div>
</div>
</body>
</html>
main.js
import 'ol/ol.css';
import Map from 'ol/Map';
import View from 'ol/View';
import MVT from 'ol/format/MVT';
import VectorTileLayer from 'ol/layer/VectorTile';
import VectorTileSource from 'ol/source/VectorTile';
import TileLayer from 'ol/layer/Tile';
import XYZSource from 'ol/source/XYZ';
import Overlay from 'ol/Overlay';
import { Style, Fill, Stroke, Circle, Text } from 'ol/style';
import { fromLonLat } from 'ol/proj';
const map = new Map({
target: 'map-container',
view: new View({
center: fromLonLat([34.633623, 39.818770]),
zoom: 7
})
});
const layer = new VectorTileLayer({
source: new VectorTileSource({
attributions: [
'© OpenMapTiles',
'© OpenStreetMap contributors'
],
format: new MVT(),
url: `http://localhost:8090/maps/observation/{z}/{x}/{y}.vector.pbf`,
maxZoom: 24,
type: 'base'
})
});
const baseLayer = new TileLayer({
source: new XYZSource({
url: 'http://tile.stamen.com/terrain/{z}/{x}/{y}.jpg'
}),
type: 'base'
});
map.addLayer(baseLayer);
const overlay = new Overlay({
element: document.getElementById('popup-container'),
positioning: 'bottom-center',
offset: [0, -10],
autoPan: true
});
map.addOverlay(overlay);
overlay.getElement().addEventListener('click', function () {
overlay.setPosition();
});
map.addLayer(layer);
layer.setStyle(function (feature, resolution) {
const properties = feature.getProperties();
if (properties.layer == 'temperature_stations' || properties.layer == 'temperature_stations_simple') {
const point = new Style({
image: new Circle({
radius: 5,
fill: new Fill({
color: 'red'
}),
stroke: new Stroke({
color: 'grey'
})
})
})
return point
}
if (properties.layer == 'aws_stations') {
const point = new Style({
image: new Circle({
radius: 5,
fill: new Fill({
color: 'blue'
}),
stroke: new Stroke({
color: 'grey'
})
})
})
return point
}
if (properties.layer == 'spa_stations') {
const point = new Style({
image: new Circle({
radius: 10,
fill: new Fill({
color: 'green'
}),
stroke: new Stroke({
color: 'grey'
})
})
})
return point
}
if (properties.layer == 'syn_stations') {
const point = new Style({
image: new Circle({
radius: 10,
fill: new Fill({
color: 'purple'
}),
stroke: new Stroke({
color: 'grey'
})
})
})
return point
}
});
map.on('pointermove', function (e) {
let markup = '';
map.forEachFeatureAtPixel(e.pixel, function (feature) {
markup += `${markup && '<hr>'}<table>`;
const properties = feature.getProperties();
for (const property in properties) {
markup += `<tr><th>${property}</th><td>${properties[property]}</td></tr>`;
}
markup += '</table>';
}, { hitTolerance: 0 });
if (markup) {
document.getElementById('popup-content').innerHTML = markup;
overlay.setPosition(e.coordinate);
} else {
overlay.setPosition();
}
});
The workshop uses the webpack dev server to serve the application for development. The last chapter explains how to build the application for production: https://openlayers.org/workshop/en/deploying/. The steps explained there explain how to use webpack to give you a static web application, You'll be using the resulting artifacts in your Express application as static files: https://expressjs.com/en/starter/static-files.html.

Fetch a variable from node_modules

I am trying to fetch a variable from node_modules to my application. I have exported it with module.exports, but when I use require('path/of/required/file/in/node_modules') it is showing 'unexpected token import'. I am trying to get the user info from node_modules since there is no official strategy for keystone to fetch the user data.
My local file
var keystone = require('keystone');
Teaching = keystone.list('teaching');
UserInfo = require('../../node_modules/keystone/admin/client/App/components/Footer/index.js').footer;
User = keystone.list(keystone.get('user model'));
console.log(User);
exports = module.exports =function(req,res){
var view = new keystone.View(req,res);
var locals = res.locals;
locals.section = 'teaching';
locals.data = {
teachingData : []
};
view.on('init',function(next){
var teaching = Teaching.model.find();
teaching.exec(function(err,results){
locals.data.teachingData = results;
//console.log(locals.data.teachingData);
next(err);
});
});
view.render('teaching');
}
Node_module file
import React from 'react';
import { css, StyleSheet } from 'aphrodite/no-important';
import { Container } from '../../elemental';
import theme from '../../../theme';
var Footer = React.createClass({
displayName: 'Footer',
propTypes: {
appversion: React.PropTypes.string,
backUrl: React.PropTypes.string,
brand: React.PropTypes.string,
user: React.PropTypes.object,
User: React.PropTypes.object, // eslint-disable-line react/sort-prop-types
version: React.PropTypes.string,
},
// Render the user
renderUser () {
const { User, user } = this.props;
if (!user) return null;
return (
<span>
<span> Signed in as </span>
<a href={`${Keystone.adminPath}/${User.path}/${user.id}`} tabIndex="-1" className={css(classes.link)}>
{user.name}
</a>
<span>.</span>
</span>
);
},
render () {
const { backUrl, brand, appversion, version } = this.props;
return (
<footer className={css(classes.footer)} data-keystone-footer>
<Container>
<a
href={backUrl}
tabIndex="-1"
className={css(classes.link)}
>
{brand + (appversion ? (' ' + appversion) : '')}
</a>
<span> powered by </span>
<a
href=""
target="_blank"
className={css(classes.link)}
tabIndex="-1"
>
VK
</a>
<span> version {version}.</span>
{this.renderUser()}
</Container>
</footer>
);
},
});
/* eslint quote-props: ["error", "as-needed"] */
const linkHoverAndFocus = {
color: theme.color.gray60,
outline: 'none',
};
const classes = StyleSheet.create({
footer: {
boxShadow: '0 -1px 0 rgba(0, 0, 0, 0.1)',
color: theme.color.gray40,
fontSize: theme.font.size.small,
paddingBottom: 30,
paddingTop: 40,
textAlign: 'center',
},
link: {
color: theme.color.gray60,
':hover': linkHoverAndFocus,
':focus': linkHoverAndFocus,
},
});
exports.footer = Footer.renderUser();
1) The "Unexpected token import" error is probably occuring because the current JavaScript environment does not support the import statement; the code you are trying to import from the Keystone node module is transpiled before it gets used normally. When Keystone uses that component, it's not an issue. But if you're importing a React component into a non-React file, you're bound to have problems. Which leads to my next question:
2) UserInfo isn't used in your code at all. You can't just use Keystone's React component for showing user information, unless you are working in a React environment (which it does not look like you are.) What is your use case for using this component?

How can I use Esri Arcgis Map in ReactJs Project?

I'm trying to use Esri map. To include map in my project, here is what I found:
require([
"esri/map",
"esri/dijit/Search",
"esri/dijit/LocateButton",
"esri/geometry/Point",
"esri/symbols/SimpleFillSymbol",
"esri/symbols/SimpleMarkerSymbol",
"esri/symbols/SimpleLineSymbol",
But there isn't any esri folder or npm package. Therefore, I'm confused here. How esri is imported in project?
Use esri-loader to load the required esri modules. This is a component rendering basemap.
import React, { Component } from 'react';
import { loadModules } from 'esri-loader';
const options = {
url: 'https://js.arcgis.com/4.6/'
};
const styles = {
container: {
height: '100vh',
width: '100vw'
},
mapDiv: {
padding: 0,
margin: 0,
height: '100%',
width: '100%'
},
}
class BaseMap extends Component {
constructor(props) {
super(props);
this.state = {
status: 'loading'
}
}
componentDidMount() {
loadModules(['esri/Map', 'esri/views/MapView'], options)
.then(([Map, MapView]) => {
const map = new Map({ basemap: "streets" });
const view = new MapView({
container: "viewDiv",
map,
zoom: 15,
center: [78.4867, 17.3850]
});
view.then(() => {
this.setState({
map,
view,
status: 'loaded'
});
});
})
}
renderMap() {
if(this.state.status === 'loading') {
return <div>loading</div>;
}
}
render() {
return(
<div style={styles.container}>
<div id='viewDiv' style={ styles.mapDiv } >
{this.renderMap()}
</div>
</div>
)
}
}
export default BaseMap;
This renders a base map but this is not responsive. If I remove the div around the view div or if I give the height and width of the outer div (surrounding viewDiv) as relative ({ height: '100%', width: '100%'}), the map does not render. No idea why. Any suggestions to make it responsive would be appreciated.
An alternative method to the above is the one demonstrated in esri-react-router-example. That application uses a library called esri-loader to lazy load the ArcGIS API only in components/routes where it is needed. Example:
First, install the esri-loader libary:
npm install esri-loader --save
Then import the esri-loader functions in any react module:
import * as esriLoader from 'esri-loader'
Then lazy load the ArcGIS API:
componentDidMount () {
if (!esriLoader.isLoaded()) {
// lazy load the arcgis api
const options = {
// use a specific version instead of latest 4.x
url: '//js.arcgis.com/3.18compact/'
}
esriLoader.bootstrap((err) => {
if (err) {
console.error(err)
}
// now that the arcgis api has loaded, we can create the map
this._createMap()
}, options)
} else {
// arcgis api is already loaded, just create the map
this._createMap()
}
},
Then load and the ArcGIS API's (Dojo) modules that are needed to create a map:
_createMap () {
// get item id from route params or use default
const itemId = this.props.params.itemId || '8e42e164d4174da09f61fe0d3f206641'
// require the map class
esriLoader.dojoRequire(['esri/arcgis/utils'], (arcgisUtils) => {
// create a map at a DOM node in this component
arcgisUtils.createMap(itemId, this.refs.map)
.then((response) => {
// hide the loading indicator
// and show the map title
// NOTE: this will trigger a rerender
this.setState({
mapLoaded: true,
item: response.itemInfo.item
})
})
})
}
The benefit of using esri-loader over the approach shown above is that you don't have to use the Dojo loader and toolchain to load and build your entire application. You can use the React toolchain of your choice (webpack, etc).
This blog post explains how this approach works and compares it to other (similar) approaches used in applications like esri-redux.
You don't need to import esri api like you do for ReactJS. As the react file will finally compile into a js file you need to write the esri parts as it is and mix the ReactJS part for handling the dom node, which is the main purpose of ReactJS.
A sample from the links below is here
define([
'react',
'esri/toolbars/draw',
'esri/geometry/geometryEngine',
'dojo/topic',
'dojo/on',
'helpers/NumFormatter'
], function(
React,
Draw, geomEngine,
topic, on,
format
) {
var fixed = format(3);
var DrawToolWidget = React.createClass({
getInitialState: function() {
return {
startPoint: null,
btnText: 'Draw Line',
distance: 0,
x: 0,
y: 0
};
},
componentDidMount: function() {
this.draw = new Draw(this.props.map);
this.handler = this.draw.on('draw-end', this.onDrawEnd);
this.subscriber = topic.subscribe(
'map-mouse-move', this.mapCoordsUpdate
);
},
componentWillUnMount: function() {
this.handler.remove();
this.subscriber.remove();
},
onDrawEnd: function(e) {
this.draw.deactivate();
this.setState({
startPoint: null,
btnText: 'Draw Line'
});
},
mapCoordsUpdate: function(data) {
this.setState(data);
// not sure I like this conditional check
if (this.state.startPoint) {
this.updateDistance(data);
}
},
updateDistance: function(endPoint) {
var distance = geomEngine.distance(this.state.startPoint, endPoint);
this.setState({ distance: distance });
},
drawLine: function() {
this.setState({ btnText: 'Drawing...' });
this.draw.activate(Draw.POLYLINE);
on.once(this.props.map, 'click', function(e) {
this.setState({ startPoint: e.mapPoint });
// soo hacky, but Draw.LINE interaction is odd to use
on.once(this.props.map, 'click', function() {
this.onDrawEnd();
}.bind(this));
}.bind(this))
},
render: function() {
return (
<div className='well'>
<button className='btn btn-primary' onClick={this.drawLine}>
{this.state.btnText}
</button>
<hr />
<p>
<label>Distance: {fixed(this.state.distance)}</label>
</p>
</div>
);
}
});
return DrawToolWidget;
});
Below are the links where you can find information in detail.
http://odoe.net/blog/esrijs-reactjs/
https://geonet.esri.com/people/odoe/blog/2015/04/01/esrijs-with-reactjs-updated

Resources