не работает ymaps.templateLayoutFactory.createClass внутри меток Placemarker и Clusterer
Я нашел пример кода который отображает содержимое балунов с помощью компонентов. Захотел интегрировать в свой код, но я получаю ошибку. Я заметил, что если я не использую метки Placemarker и Clusterer и пишу ymaps.templateLayoutFactory.createClass где вызываю метод не из этих меток, то ошибка пропадает. Буду рад любой помощи. Спасибо.
Cannot read properties of undefined (reading 'createClass')
TypeError: Cannot read properties of undefined (reading 'createClass')
Пример кода
import React, { useState,useEffect } from 'react';
import { YMaps, Map, Clusterer , Button,Placemark } from 'react-yandex-maps';
import "../css/ballon.css"
import Header from "./Header";
import {Modal} from "react-bootstrap";
import InnerLayout from "./InnerLayout";
import ReactDOM from 'react-dom';
import ReactDOMServer from 'react-dom/server';
const mapState = {
center: [55.7924886417031, 49.12233672582469],
zoom: 9
};
import POINTS from './points';
const YMap = () => {
const placemarkRef = React.useRef(null);
const mapRef = React.useRef(null);
const [address, SetAddress] = React.useState("");
const [count, setValue] = useState(0);
const [canAddPlacemark, checkAddPlacemark] = useState(false);
const [oldRegionName, addOldRegionName] = useState();
const [oldRegion, addOldRegion] = useState();
const [ymaps, setYmaps] = useState(React.useRef(null));
const [openedDescription, openDescription] = useState(null);
const [clusterBalloon, setClusterBalloon] = useState(null);
const [canAddMArker, checkAddMarker] = useState(false);
const getLayout = (Component, props) => {
if (ymaps) {
const html = ReactDOMServer.renderToString(<Component {...props} />);
const Layout = ymaps.templateLayoutFactory.createClass(`<div id="balloon">${html}</div>`,
{
build: function() {
Layout.superclass.build.call(this);
ReactDOM.hydrate(
<Component {...props} />,
document.getElementById('balloon'),
);
},
},
);
return Layout;
}
return null;
};
const animatedLayout = () => {
if (ymaps) {
const animatedLayout = ymaps.templateLayoutFactory.createClass(
'<div class="placemark"></div>',
{
build() {
animatedLayout.superclass.build.call(this);
const element = this.getParentElement().getElementsByClassName(
'placemark',
)[0];
const size = this.isActive ? 60 : 34;
const smallShape = {
type: 'Circle',
coordinates: [0, 0],
radius: size / 2,
};
const bigShape = {
type: 'Circle',
coordinates: [0, -30],
radius: size / 2,
};
this.getData().options.set(
'shape',
this.isActive ? bigShape : smallShape,
);
if (this.isActive) {
element.classList.add('active');
element.style.animation = '.35s show-big-placemark';
} else if (this.inited) {
element.classList.remove('active');
element.style.animation = '.35s show-small-placemark';
}
if (!this.inited) {
this.inited = true;
this.isActive = false;
this.getData().geoObject.events.add(
'click',
function() {
this.isActive = !this.isActive;
this.rebuild();
},
this,
);
}
},
},
);
return animatedLayout;
}
return null;
};
useEffect(() => {
if (document.getElementById('balloon') && clusterBalloon) {
ReactDOM.hydrate(
<InnerLayout
openDescription={openDescription}
openedDescription={openedDescription}
{...clusterBalloon}
/>,
document.getElementById('balloon'),
);
}
}, [clusterBalloon, openedDescription]);
const openCluster = e => {
const cluster = e.get('cluster');
if (cluster) {
if (!clusterBalloon) {
setClusterBalloon(
cluster.state.get('activeObject').properties.getAll(),
);
}
cluster.state.events.add('change', () => {
const activeObject = cluster.state.get('activeObject');
console.log(activeObject.properties.getAll().index)
console.log(clusterBalloon.index)
if (
!clusterBalloon ||
(clusterBalloon &&
activeObject.properties.getAll().index !== clusterBalloon.index)
) {
setClusterBalloon(activeObject.properties.getAll());
}
});
}
};
const closeDescription = () => {
setClusterBalloon(null);
openDescription(null);
};
const createPlacemark = (coords) => {
return new ymaps.current.Placemark(
coords,
{
iconCaption: "loading.."
},
{
preset: "islands#violetDotIconWithCaption",
draggable: true
}
);
};
let regionName = "";
const getAddress = async (coords) => {
placemarkRef.current.properties.set("iconCaption", "loading..");
await ymaps.current.geocode(coords).then((res) => {
const firstGeoObject = res.geoObjects.get(0);
const newAddress = [
firstGeoObject.getLocalities().length
? firstGeoObject.getLocalities()
: firstGeoObject.getAdministrativeAreas(),
firstGeoObject.getThoroughfare() || firstGeoObject.getPremise()
]
.filter(Boolean)
.join(", ");
SetAddress(newAddress);
placemarkRef.current.properties.set({
iconCaption: newAddress,
// balloonContentHeader: "одержимое заголовка балуна геообъекта",
// balloonContentBody : "содержимое основой части балуна геообъекта",
// balloonContentFooter : "содержимое нижней части балуна геообъекта.",
// balloonContent: firstGeoObject.getAddressLine()
balloonContent: ' <div> <input type="button" onClick={() => { openDescription(index);}} value="Подробнее"/> </div>'
});
regionName =firstGeoObject.getAdministrativeAreas()[0];
})
};
const addMarkers = () =>{
checkAddMarker(canAddMArker => !canAddMArker);
}
const onMapClick = (e) => {
var objectId = e.get('id')
console.log(objectId)
if (canAddPlacemark){
let objectManager = new ymaps.current.ObjectManager();
objectManager.options.set('geoObjectInteractivityModel', 'default#transparent');
const coords = e.get("coords");
if (placemarkRef.current) {
placemarkRef.current.geometry.setCoordinates(coords);
} else {
placemarkRef.current = createPlacemark(coords);
mapRef.current.geoObjects.add(placemarkRef.current);
placemarkRef.current.events.add("dragend", function () {
getAddress(placemarkRef.current.geometry.getCoordinates());
});
}
getAddress(coords);
}
};
function sayHello() {
setValue(count+1);
if(count % 2 != 0){
checkAddPlacemark(canAddPlacemark => !canAddPlacemark)
}
else{
checkAddPlacemark(canAddPlacemark => !canAddPlacemark)
}
}
const handleClose2 = () => {
setShow2(false);
}
const handleShow2 = () => {
setShow2(true);
}
return (
<div>
<Header/>
<YMaps query={{
// load: "package.full" ,
apikey: "" }}>
<Map defaultState={{
center: [55.751574, 37.573856],
zoom: 9
}}
modules={["Clusterer","Polygon","GeoObject","geoQuery","control.ZoomControl", "control.FullscreenControl","Placemark", "geocode",
"geoObject.addon.balloon","borders", "ObjectManager",'geoObject.addon.balloon','clusterer.addon.balloon',
'templateLayoutFactory']}
style={{
flex: 2,
height: "calc(100vh - 57px)"
}}
onClick={onMapClick}
instanceRef={mapRef}
onLoad={ymaps => setYmaps(ymaps)}
state={mapState}
width={"100%"} height={"100%"}
>
<Clusterer
options={{
preset: 'islands#invertedVioletClusterIcons',
groupByCoordinates: false,
balloonPanelMaxMapArea: Infinity,
clusterBalloonItemContentLayout: getLayout(InnerLayout, {
openedDescription,
openDescription,
...clusterBalloon,
}),
}}
onBalloonOpen={openCluster}
onBalloonClose={closeDescription}
>
{POINTS.map((point, index) => (
<Placemark
key={index}
geometry={point.coords}
properties={{
balloonContentHeader: point.title,
point,
index,
}}
options={{
iconLayout: animatedLayout(),
balloonContentLayout: getLayout(InnerLayout, {
point,
index,
openedDescription,
openDescription,
}),
balloonPanelMaxMapArea: Infinity,
}}
/>
))}
</Clusterer>
<Button onClick={sayHello} data={{ content: 'Button' }} options={{ float: 'right' }} />
<Button onClick={addMarkers} data={{ content: 'Вывод маркеров' }} options={{ float: 'right' }} />
{/*{canAddMArker && <InnerLayout/>}*/}
</Map>
</YMaps>
</div>
)
}
export default YMap;