My intention was to make a page for modifying a map, where I take the positions of each element (they are images of tables) and make a real-time editor.
However, when trying to move a table from one point to another, despite calling the functions correctly, I get errors that I am not making calls.
I have looked at possible errors such as that the methods should not be static, but they are not.
@page "/mapa-edicion/{id:int}"
@inject NavigationManager Navigation
@inject ILogger<SalonModify> Logger
@inject IJSRuntime JSRuntime
@using System.Text.Json
@using System.Net.Http
@using RMSuit_v2.Models
<script src="https://cdnjs.cloudflare.com/ajax/libs/Sortable/1.14.0/Sortable.min.js"></script>
<style>
.map-container {
position: relative;
width: 100%;
height: 700px;
border: 1px solid #ccc;
overflow: hidden;
background-color: #b8b8b8;
}
.zoom-container {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
transform-origin: top left;
transition: transform 0.3s;
}
.drawing-item {
position: absolute;
width: 100px;
height: 100px;
cursor: pointer;
user-select: none;
}
.drawing-item img {
max-width: 100%;
max-height: 100%;
width: auto;
height: auto;
}
</style>
<h3>Mapa del Salón</h3>
@if (salonResponse == null)
{
<p>Cargando detalles del salón...</p>
}
else
{
<div class="map-container" @onclick="OnMapClick">
<div class="zoom-container" style="transform: scale(1);">
@if (salonResponse.dibujos != null && salonResponse.dibujos.Any())
{
var orderedDrawings = salonResponse.dibujos
.OrderBy(d => d.estado == "D")
.ThenBy(d => d.estado)
.ToList();
var occupiedPositions = new HashSet<(int, int)>();
@foreach (var dibujo in orderedDrawings)
{
var salonDetails = salonResponse.mesas
.Where(m => m.dibujos == dibujo.dibujos)
.ToList();
foreach (var salonDetail in salonDetails)
{
int adjustedLeft = (int)(salonDetail.posicionX);
int adjustedTop = (int)(salonDetail.posicionY);
// Aplicar el ratio de pantalla
int ratioAdjustedTop = (int)(adjustedTop * ratioPantalla);
int ratioAdjustedLeft = (int)(adjustedLeft * ratioPantalla);
var adjustedPosition = (ratioAdjustedTop, ratioAdjustedLeft);
Logger.LogInformation($"ID Dibujo: {dibujo.dibujos}, Estado: {dibujo.estado}, Posición Ajustada: (top: {adjustedTop}, left: {adjustedLeft}), Ratio: {ratioPantalla}");
if (dibujo.estado == "D" || !occupiedPositions.Contains(adjustedPosition))
{
Logger.LogInformation($"Procesando dibujo con ID: {dibujo.dibujos}, Posición: (top: {adjustedPosition.Item1}, left: {adjustedPosition.Item2})");
occupiedPositions.Add(adjustedPosition);
<div class="drawing-item"
style="top:@(adjustedPosition.Item1 + "px"); left:@(adjustedPosition.Item2 + "px");"
@onclick="() => OnImageClick(dibujo.dibujos)"
id="[email protected]">
@if (!string.IsNullOrEmpty(dibujo.grafico))
{
<img src="data:image/jpeg;base64,@dibujo.grafico" alt="@dibujo.estado" />
}
else
{
<p>No hay imagen disponible.</p>
}
</div>
}
}
}
}
else
{
<p>No se encontraron dibujos para este salón.</p>
}
</div>
</div>
}
<button @onclick="GuardarCambios">Guardar Cambios</button>
<button @onclick="Cancelar">Cancelar</button>
@code {
[Parameter] public int id { get; set; }
private SalonResponse? salonResponse;
private double ratioPantalla = 1.0;
private int? hiddenDibujoId = null;
private int maxX;
private int maxY;
private bool isDragging = false;
private void CalculateMaxDimensions()
{
maxX = 0;
maxY = 0;
if (salonResponse?.dibujos != null && salonResponse.mesas != null)
{
foreach (var dibujo in salonResponse.dibujos)
{
var salonDetails = salonResponse.mesas
.Where(m => m.dibujos == dibujo.dibujos)
.ToList();
foreach (var salonDetail in salonDetails)
{
var adjustedLeft = (int)(salonDetail.posicionX);
var adjustedTop = (int)(salonDetail.posicionY);
maxX = Math.Max(maxX, adjustedLeft);
maxY = Math.Max(maxY, adjustedTop);
}
}
}
else
{
Logger.LogWarning("No se pudo calcular las dimensiones máximas: 'salonResponse', 'dibujos' o 'mesas' es nulo.");
}
}
protected override async Task OnInitializedAsync()
{
await LoadSalonDetail();
CalculateMaxDimensions();
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
await Task.Delay(500); // Esperar milisegundos
ratioPantalla = await CalculateScreenRatio();
await JSRuntime.InvokeVoidAsync("initializeSortable");
}
}
private async Task LoadSalonDetail()
{
try
{
using var client = new HttpClient(new HttpClientHandler
{
ServerCertificateCustomValidationCallback = (message, cert, chain, errors) => true
});
var url = $"https://37.59.32.58:1380/Master/Salons/GetSalonDetail/{id}?includeDrawings=true&initialCatalog=ELSIFON";
var response = await client.GetStringAsync(url);
var options = new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
PropertyNameCaseInsensitive = true
};
salonResponse = JsonSerializer.Deserialize<SalonResponse>(response, options);
if (salonResponse == null)
{
Logger.LogWarning("No se pudieron cargar los detalles del salón. salonResponse es null.");
}
}
catch (Exception ex)
{
Logger.LogError(ex, "Error al cargar los detalles del salón.");
}
}
private async Task<double> CalculateScreenRatio()
{
return await JSRuntime.InvokeAsync<double>("calculateScreenRatio");
}
private void Cancelar()
{
Navigation.NavigateTo($"/salonmapa/{id}");
}
private void OnImageClick(int dibujoId)
{
Logger.LogWarning($"Image clicked: {dibujoId}");
hiddenDibujoId = dibujoId;
StateHasChanged();
}
private async Task AddDraggingClass(string elementId)
{
await JSRuntime.InvokeVoidAsync("addDraggingClass", elementId);
}
private async Task OnMapClick(MouseEventArgs e)
{
Logger.LogWarning($"Map clicked: {e.ClientX}, {e.ClientY}");
if (hiddenDibujoId.HasValue && isDragging)
{
var position = await JSRuntime.InvokeAsync<(int x, int y)>("getMousePosition", e.ClientX, e.ClientY);
// Actualizar la posición en la lista de mesas
var dibujoId = hiddenDibujoId.Value;
hiddenDibujoId = null;
await JSRuntime.InvokeVoidAsync("updateElementPosition", $"dibujo-{dibujoId}", position.x, position.y);
Logger.LogInformation($"Dibujo {dibujoId} movido a nueva posición: (x: {position.x}, y: {position.y})");
// Actualizar la posición en el modelo
await UpdateElementPositionAsync(dibujoId, position.x, position.y);
isDragging = false;
}
else
{
Logger.LogWarning("No se encontró un dibujo seleccionado para mover o no se está arrastrando.");
}
}
private async Task GuardarCambios()
{
try
{
await JSRuntime.InvokeVoidAsync("saveNewPositions");
using var client = new HttpClient();
if (salonResponse?.dibujos != null)
{
foreach (var dibujo in salonResponse.dibujos)
{
var detalle = salonResponse.mesas?.FirstOrDefault(m => m.dibujos == dibujo.dibujos);
if (detalle != null)
{
var url = $"https://37.59.32.58:1380/Master/SalonTables/UpdateTablePosition/{id}/{detalle.dibujos}/{detalle.dibujos}?initialCatalog=ELSIFON";
var response = await client.PostAsJsonAsync(url, detalle);
if (response.IsSuccessStatusCode)
{
Logger.LogInformation($"Posición actualizada para dibujo ID: {detalle.dibujos}");
}
else
{
Logger.LogError($"Error al actualizar la posición para dibujo ID: {detalle.dibujos}");
}
}
else
{
Logger.LogWarning($"No se encontró un detalle para el dibujo con ID: {dibujo.dibujos}");
}
}
}
else
{
Logger.LogWarning("No se encontraron dibujos en el salón o la respuesta del salón es nula.");
}
Navigation.NavigateTo($"/salonmapa/{id}");
}
catch (Exception ex)
{
Logger.LogError(ex, "Error al guardar los cambios en las posiciones.");
}
}
public event Action<int, int, int> OnPositionUpdated = delegate { };
// Arrastre
[JSInvokable]
public Task SetDraggingState(bool dragging)
{
// Lógica para actualizar el estado de arrastre
isDragging = dragging;
return Task.CompletedTask;
}
[JSInvokable]
public async Task UpdateElementPositionAsync(int dibujoId, int newX, int newY)
{
if (salonResponse?.mesas != null)
{
var detalle = salonResponse.mesas.FirstOrDefault(m => m.dibujos == dibujoId);
if (detalle != null)
{
Logger.LogInformation($"Actualizando posición para dibujo ID {dibujoId}: (X: {newX}, Y: {newY})");
// Validar y actualizar posición
if (newX >= 0 && newY >= 0 && newX <= maxX && newY <= maxY)
{
detalle.posicionX = (int)(newX / ratioPantalla);
detalle.posicionY = (int)(newY / ratioPantalla);
// Actualizar en la interfaz de usuario
await JSRuntime.InvokeVoidAsync("updateElementPosition", $"dibujo-{dibujoId}", newX, newY);
OnPositionUpdated?.Invoke(dibujoId, newX, newY);
}
else
{
Logger.LogWarning($"Coordenadas fuera de rango para dibujo ID {dibujoId}: (X: {newX}, Y: {newY})");
}
}
else
{
Logger.LogWarning($"No se encontró un detalle para el dibujo con ID: {dibujoId}");
}
}
}
// Para mostrar mensajes de js en logger
[JSInvokable]
public static Task LogMessage(string message)
{
var logger = LoggerFactory.Create(builder => builder.AddConsole()).CreateLogger("JSLogger");
logger.LogInformation($"Mensaje desde JS: {message}");
return Task.CompletedTask;
}
}
<script>
window.addEventListener('load', () => {
initializeSortable();
});
function logMessage(message) {
DotNet.invokeMethodAsync('RMSuit_v2', 'LogMessage', message)
.then(result => {
console.log('Mensaje registrado:', message);
})
.catch(error => {
console.error('Error al registrar el mensaje:', error);
});
}
window.calculateScreenRatio = () => {
let width = window.innerWidth;
let height = window.innerHeight;
let ratio = 1.0;
let baseWidth = 1920;
let baseHeight = 1080;
ratio = Math.min(width / baseWidth, height / baseHeight);
logMessage(`Screen Ratio Calculated: ${ratio}`);
return ratio;
};
window.saveNewPositions = () => {
Object.keys(newPositionData).forEach(dibujoId => {
let position = newPositionData[dibujoId];
DotNet.invokeMethodAsync('RMSuit_v2', 'UpdateElementPositionAsync', parseInt(dibujoId), position.x, position.y)
.then(() => {
logMessage(`Posición guardada para Dibujo ID: ${dibujoId}, X: ${position.x}, Y: ${position.y}`);
})
.catch(error => {
logMessage('Error al guardar la posición:', error);
});
});
newPositionData = {};
};
window.addDraggingClass = (elementId) => {
DotNet.invokeMethodAsync('RMSuit_v2', 'SetDraggingState', true)
.catch(error => console.error('Error al actualizar estado de arrastre:', error));
const element = document.getElementById(elementId);
if (element) {
element.classList.add("dragging");
}
};
window.initializeSortable = () => {
const tryInitialize = () => {
const container = document.querySelector('.zoom-container');
if (container) {
logMessage('Contenedor encontrado:', container);
Sortable.create(container, {
animation: 150,
onStart: () => {
DotNet.invokeMethodAsync('RMSuit_v2', 'SetDraggingState', true)
.catch(error => logMessage(`Error al actualizar estado de arrastre: ${error}`));
},
onEnd: (evt) => {
const elementId = evt.item.id.replace('dibujo-', '');
const newX = evt.item.offsetLeft;
const newY = evt.item.offsetTop;
logMessage(`Elemento ${elementId} movido a X: ${newX}, Y: ${newY}`);
DotNet.invokeMethodAsync('RMSuit_v2', 'UpdateElementPositionAsync', parseInt(elementId), newX, newY)
.catch(error => logMessage(`Error al actualizar la posición: ${error}`));
DotNet.invokeMethodAsync('RMSuit_v2', 'SetDraggingState', false)
.catch(error => logMessage(`Error al actualizar estado de arrastre: ${error}`));
}
});
} else {
logMessage('Sortable: el contenedor no se encontró, reintentando...');
setTimeout(tryInitialize, 100); // Reintento después de 100ms
}
};
tryInitialize();
};
window.updateElementPosition = (elementId, newX, newY) => {
DotNet.invokeMethodAsync('RMSuit_v2', 'UpdateElementPositionAsync', parseInt(elementId), newX, newY)
.catch(error => logMessage(`Error al actualizar la posición: ${error}`));
};
</script>
The error:
Mensaje registrado: Error al actualizar estado de arrastre: Error: System.ArgumentException: The assembly ‘RMSuit_v2’ does not contain a public invokable method with [JSInvokableAttribute(“SetDraggingState”)].
at Microsoft.JSInterop.Infrastructure.DotNetDispatcher.GetCachedMethodInfo(AssemblyKey assemblyKey, String methodIdentifier)
at Microsoft.JSInterop.Infrastructure.DotNetDispatcher.InvokeSynchronously(JSRuntime jsRuntime, DotNetInvocationInfo& callInfo, IDotNetObjectReference objectReference, String argsJson)
at Microsoft.JSInterop.Infrastructure.DotNetDispatcher.BeginInvokeDotNet(JSRuntime jsRuntime, DotNetInvocationInfo invocationInfo, String argsJson)
VM15:11 Mensaje registrado: Elemento 101 movido a X: 303, Y: 164
VM15:11 Mensaje registrado: Error al actualizar la posición: Error: System.ArgumentException: The assembly ‘RMSuit_v2’ does not contain a public invokable method with [JSInvokableAttribute(“UpdateElementPositionAsync”)].
at Microsoft.JSInterop.Infrastructure.DotNetDispatcher.GetCachedMethodInfo(AssemblyKey assemblyKey, String methodIdentifier)
at Microsoft.JSInterop.Infrastructure.DotNetDispatcher.InvokeSynchronously(JSRuntime jsRuntime, DotNetInvocationInfo& callInfo, IDotNetObjectReference objectReference, String argsJson)
at Microsoft.JSInterop.Infrastructure.DotNetDispatcher.BeginInvokeDotNet(JSRuntime jsRuntime, DotNetInvocationInfo invocationInfo, String argsJson)
VM15:11 Mensaje registrado: Error al actualizar estado de arrastre: Error: System.ArgumentException: The assembly ‘RMSuit_v2’ does not contain a public invokable method with [JSInvokableAttribute(“SetDraggingState”)].
at Microsoft.JSInterop.Infrastructure.DotNetDispatcher.GetCachedMethodInfo(AssemblyKey assemblyKey, String methodIdentifier)
at Microsoft.JSInterop.Infrastructure.DotNetDispatcher.InvokeSynchronously(JSRuntime jsRuntime, DotNetInvocationInfo& callInfo, IDotNetObjectReference objectReference, String argsJson)
at Microsoft.JSInterop.Infrastructure.DotNetDispatcher.BeginInvokeDotNet(JSRuntime jsRuntime, DotNetInvocationInfo invocationInfo, String argsJson)
I am trying to get these methods to work so that it works with the objective of being able to drag the elements of the images that I am recreating, to create a real-time editor of the map.