Search code examples
reactjsreact-admin

Writing my own GeoJSON input component with a mapgox-gl interface


Hej. I want to create a react admin input component MapInput where I'm given a map with drawing controls github. There I'm able to draw my polygon and get GeoJSON as a result, which I can then send to my backend REST API server (json-server).

My system consists of my react client program, and a json-server with REST API. Example for an area in my REST API:

{
  "id": "4",
  "name": "Area 4",
  "description": "This is area 4.",
  "geometry": {
    "type": "Polygon",
    "coordinates": [
      [
        [-1.8017578124999998, 52.45600939264076],
        [0.406494140625, 52.20760667286523],
        [0.560302734375, 53.48804553605622],
        [-1.8017578124999998, 52.45600939264076]
      ]
    ]
  }
}

At the moment, with my MapInput tag, I can draw a polygon and I can log the GeoJSON, but I can't figure out how to pass it to the react admin dataProvider, for it to be able to send it to my server.

I follow this instructions.

Inside my components/CreateMyArea.tsx component (POST)

import React from 'react';
import { Create, SimpleForm, TextInput } from 'react-admin';
import { MapInput } from '../components/MapboxGl';

export const CreateMyArea = (props: any) => {
  return (
    <Create title="Create my area" {...props}>
      <SimpleForm>
        <TextInput label="Name" source="name" />
        <TextInput multiline label="Description" source="description" />
        <MapInput />
      </SimpleForm>
    </Create>
  );
};

In MapInput tag, how can I pass the "geometry" parameter as I do with "name" and "description" (JSON example) in source tag?

Inside my components/MapboxGl.tsx component

import * as React from 'react';
import '@mapbox/mapbox-gl-draw/dist/mapbox-gl-draw.css';
import ReactMapboxGl from 'react-mapbox-gl';
import DrawControl from 'react-mapbox-gl-draw';
const Map = ReactMapboxGl({
  accessToken:
    'ACCESS-TOKEN'
});
//...
export const MapInput = () => {
return (
    <Map
      style="mapbox://styles/mapbox/streets-v11"
      containerStyle={{
        height: '400px',
        width: '800px'
      }}
    >
      <DrawControl
        displayControlsDefault={false}
        controls={{
          trash: true,
          polygon: true
        }}
        //onDrawCreate={onDrawCreate} // with this method I can console.log created geoJson
        //onDrawUpdate={onDrawUpdate}
      />
    </Map>
  );
};

Solution

  • Your custom input component must display and allow the change of the polygon. This data is in the form state, handled by react-final-form. So you'll need to use react-final-form's useField() hook to grab the input value and change callback:

    import { useField } from 'react-final-form';
    
    export const MapInput = () => {
       const { input, meta } = useField('geometry');
       const { value, onChange } = input;
       // do what you want with value and onChange
    

    In this example, I didn't make the field name ('geometry') customizable. If you want to reuse the same input component for a different source, add support for a source prop - just like for react-admin Input components:

    export const MapInput = ({ source }) => {
       const { input, meta } = useField(source);
       const { value, onChange } = input;
       // do what you want with value and onChange
    

    You can learn more about writing your own input components in the react-admin documentation:

    https://marmelab.com/react-admin/Inputs.html#writing-your-own-input-component