Save an array state on a Custom Field

I'm trying to integrate a Google map drawing field that can draw polylines. However, I can't figure out a way to save the data once the polylinecomplete event is triggered. I can now draw the polylines (like the image attached). My state consist of:
[
polylines: [
[
coordinates: [
[ lat, long ],
[ lat, long ],
[ lat, long ],
[ lat, long ],
],
marker: [
title: 'Some Map Title'
]
]
]
]
[
polylines: [
[
coordinates: [
[ lat, long ],
[ lat, long ],
[ lat, long ],
[ lat, long ],
],
marker: [
title: 'Some Map Title'
]
]
]
]
This is the code of my field view
<x-dynamic-component
:component="$getFieldWrapperView()"
:field="$field"
>
<div
x-data="{ state: $wire.{{ $applyStateBindingModifiers("\$entangle('{$getStatePath()}')") }} }"
x-init="async function() {
const map = new google.maps.Map($refs.googleMap, {
center: {lat: -34.397, lng: 150.644},
zoom: 8
});

const drawingManager = new google.maps.drawing.DrawingManager({});

drawingManager.setMap(map);

google.maps.event.addListener(drawingManager, 'polylinecomplete', (polyline) => {
const path = polyline.getPath();
const coordinates = path.getArray().map(coord => {
return { lat: coord.lat(), lng: coord.lng() };
});

const center = computeCentroid(coordinates);
const marker = {
position: center,
title: 'Center of Polyline'
};
new google.maps.Marker({
position: center,
map: map,
title: 'Center of Polyline'
});
var entry = [ coordinates, marker ];
state.polylines.push(entry);
});

function computeCentroid(coords) {}
}"
>
<div
wire:ignore
x-ref="googleMap"
class="w-full" style="height: 500px; min-height: 30vh; z-index: 1 !important;">
</div>
</div>
</x-dynamic-component>
<x-dynamic-component
:component="$getFieldWrapperView()"
:field="$field"
>
<div
x-data="{ state: $wire.{{ $applyStateBindingModifiers("\$entangle('{$getStatePath()}')") }} }"
x-init="async function() {
const map = new google.maps.Map($refs.googleMap, {
center: {lat: -34.397, lng: 150.644},
zoom: 8
});

const drawingManager = new google.maps.drawing.DrawingManager({});

drawingManager.setMap(map);

google.maps.event.addListener(drawingManager, 'polylinecomplete', (polyline) => {
const path = polyline.getPath();
const coordinates = path.getArray().map(coord => {
return { lat: coord.lat(), lng: coord.lng() };
});

const center = computeCentroid(coordinates);
const marker = {
position: center,
title: 'Center of Polyline'
};
new google.maps.Marker({
position: center,
map: map,
title: 'Center of Polyline'
});
var entry = [ coordinates, marker ];
state.polylines.push(entry);
});

function computeCentroid(coords) {}
}"
>
<div
wire:ignore
x-ref="googleMap"
class="w-full" style="height: 500px; min-height: 30vh; z-index: 1 !important;">
</div>
</div>
</x-dynamic-component>
Can you please shed some light?
No description
8 Replies
toeknee
toeknee9mo ago
I would say to look at how #google-maps-location-picker is configured, essentially you'll need to trigger back a setter method
binaryruel
binaryruel9mo ago
Tried it but no luck. There is no reactivity when an array is passed.
toeknee
toeknee9mo ago
try: @this.set('field', value);
binaryruel
binaryruel9mo ago
here's the update
<script>
function {{$map_id}}InitMap(config) {
return {
value: config?.value ?? {},
readableCoordinates: {},
{{$map_id}}init: function () {
console.log('initialize map');
const map = new google.maps.Map(this.$refs.{{$map_id}}map, {
center: {lat: -34.397, lng: 150.644},
zoom: 8
});

this.drawings = this.value;

const drawingManager = new google.maps.drawing.DrawingManager({
drawingMode: google.maps.drawing.OverlayType.POLYLINE,
drawingControl: true,
drawingControlOptions: {
position: google.maps.ControlPosition.TOP_CENTER,
drawingModes: ['polyline']
},
polylineOptions: {
strokeColor: '#FF0000',
strokeOpacity: 1.0,
strokeWeight: 2
}
});
drawingManager.setMap(map);

const alpineThis = this;

google.maps.event.addListener(drawingManager, 'overlaycomplete', function(event) {
const that = this;
if (event.type === 'polyline') {
const completedPolyline = event.overlay;
const path = event.overlay.getPath();
const coordinates = path.getArray();
alpineThis.readableCoordinates = coordinates.map(latLng => {
return {lat: latLng.lat(), lng: latLng.lng()};
});
console.log('polyline completed');
}
});

this.$watch('readableCoordinates', () => {
this.value.push({
coordinates: this.readableCoordinates,
centerMarker: {
lat: 0,
lng: 0
},
type: 'type1',
color: '#FF0000'
})

console.log('coordinates changed');
});

}
}
}
</script>

<x-dynamic-component
:component="$getFieldWrapperView()"
:field="$field"
>
<div
wire:ignore
x-data="{{$map_id}}InitMap({
value: $wire.entangle('{{ $getStatePath() }}'),
})"
x-init="{{$map_id}}init()"
>
<div
x-ref="{{$map_id}}map"
class="w-full" style="height: 500px; min-height: 30vh; z-index: 1 !important;">

</div>
</div>
</x-dynamic-component>
<script>
function {{$map_id}}InitMap(config) {
return {
value: config?.value ?? {},
readableCoordinates: {},
{{$map_id}}init: function () {
console.log('initialize map');
const map = new google.maps.Map(this.$refs.{{$map_id}}map, {
center: {lat: -34.397, lng: 150.644},
zoom: 8
});

this.drawings = this.value;

const drawingManager = new google.maps.drawing.DrawingManager({
drawingMode: google.maps.drawing.OverlayType.POLYLINE,
drawingControl: true,
drawingControlOptions: {
position: google.maps.ControlPosition.TOP_CENTER,
drawingModes: ['polyline']
},
polylineOptions: {
strokeColor: '#FF0000',
strokeOpacity: 1.0,
strokeWeight: 2
}
});
drawingManager.setMap(map);

const alpineThis = this;

google.maps.event.addListener(drawingManager, 'overlaycomplete', function(event) {
const that = this;
if (event.type === 'polyline') {
const completedPolyline = event.overlay;
const path = event.overlay.getPath();
const coordinates = path.getArray();
alpineThis.readableCoordinates = coordinates.map(latLng => {
return {lat: latLng.lat(), lng: latLng.lng()};
});
console.log('polyline completed');
}
});

this.$watch('readableCoordinates', () => {
this.value.push({
coordinates: this.readableCoordinates,
centerMarker: {
lat: 0,
lng: 0
},
type: 'type1',
color: '#FF0000'
})

console.log('coordinates changed');
});

}
}
}
</script>

<x-dynamic-component
:component="$getFieldWrapperView()"
:field="$field"
>
<div
wire:ignore
x-data="{{$map_id}}InitMap({
value: $wire.entangle('{{ $getStatePath() }}'),
})"
x-init="{{$map_id}}init()"
>
<div
x-ref="{{$map_id}}map"
class="w-full" style="height: 500px; min-height: 30vh; z-index: 1 !important;">

</div>
</div>
</x-dynamic-component>
weird, so submitting the form carry out the additional polylines, but the json_encode didn't update while I draw them
Saade
Saade9mo ago
I think the issue here is that you're updating key inside state that's an array, i don't think alpine deeply watches the state (when entragled) (but i could be wrong) anyhow, you can use the same aproach i've used in #saade-adjacency-list, https://github.com/search?q=repo%3Asaade%2Ffilament-adjacency-list%20builder%3A%3Asort&type=code
cheesegrits
cheesegrits9mo ago
I don't know if it'll help, but I do similar things in my #google-maps plugin, saving drawing layer JSON to a field on the form. This gets done in drawingModified(), which is called from the various addOverlayEvents() cases. https://github.com/cheesegrits/filament-google-maps/blob/main/resources/js/filament-google-maps.js#L714
GitHub
filament-google-maps/resources/js/filament-google-maps.js at main ·...
Google Maps package for Filament PHP. Contribute to cheesegrits/filament-google-maps development by creating an account on GitHub.
binaryruel
binaryruel9mo ago
@Hugh Messenger , I've implemented your way. Question though, if I need just the coordinates, I just need to update the statePath? Or the whole config array gets updated? Sorry, totally newb.
cheesegrits
cheesegrits9mo ago
I dunno, I'd have to see all your code and understand exactly what you are trying to do.