Sending input field to an EndPoint

Boucourt 105 Reputation points
2024-08-29T12:25:46.9766667+00:00

Hello,

I display a table where each line gives a start and end time for the appearance of an object.

User's image

I want to be able to modify the values ​​on a line and transmit the new value to the server to update this record.

I can not transmit the updated value.

I tried several approaches without success

  • I can not put a form for each line because I can not put <form> inside a <tr>
  • I can not index the element because it is an IEnumarable

so I tried to copy the input into a variable and transmit its value. (Unfortunately I am very bad at javascript)

Here is my code

<tbody>
@{
	int i = 0;
	double actualSync = Model.SimulatedResponse.EndSecs;
}
@foreach (TimeLinesEpisodeEntitiesDto ptl in
					@Model.TimelinesEpisodeEntities)
{
	i++;
	double newStart = 0;
	double newEnd = 0;
	<tr>
		<td style="width:15%">
		@if (actualSync > ptl.Start && actualSync < ptl.End)
			{
				<span class="SelectedTimeLine ml">@ptl.EntityType &#58 @ptl.OwnerId</span>
			}
			else
			{
				<span>@ptl.EntityType &#58 @ptl.OwnerId</span>
			}
		</td>
		<td style="width:15%">
			<input type="number" class="form-input flex-1" 
						style="background-color: #B0C4DE;" readonly 
						asp-for="TimelinesEpisodeEntities" 
						name="TimelinesEpisodeEntities[@i].TimeLineId" value=@ptl.TimeLineId>
		</td>
		<td style="width:15%">
			<input type="number" class="form-input flex-1" 
						onchange="copyTo(this, newStart )" 
						id=$"Start{@i}" name=$"Start{@i}" value=@ptl.Start>
		</td>
		<td style="width:15%">
			<input type="number" class="form-input flex-1"
					 onchange="copyTo(this, newEnd )"
					 id=$"End{i}" name=$"End{i}" value=@ptl.End>
		</td>
		<td style="width:10%">
			<a href="@Url.Action(
							nameof(EpisodeController.CorrigeUnTimeLine),
							nameof(EpisodeController).ExtracControllerName(),
				new { TimeLineId = ptl.TimeLineId, 
							Start= newStart,  
							End= newEnd })"
				class="icon" title="Edit">
				<img height="25px" width="25px" float="inline-end"
					  src="~/Icon/Edit.png" />
			</a>
		</td>
	</tr>
}
</tbody>

Here is the javascript I try to write

<script type="text/javascript">
function copyTo(source, dest)
{
	dest = document.getElementById(source).value;
}
</script>
ASP.NET Core
ASP.NET Core
A set of technologies in the .NET Framework for building web applications and XML web services.
4,553 questions
0 comments No comments
{count} votes

Accepted answer
  1. Ping Ni-MSFT 4,335 Reputation points Microsoft Vendor
    2024-08-30T06:30:48.09+00:00

    Hi Boucourt

    I can not put a form for each line because I can not put <form> inside a <tr>

    It can work fine in my side. Here is the whole working demo.

    Model

    public class TimeLinesEpisodeEntitiesDto
    {
        public int TimeLineId { get; set; }
        public string EntityType { get; set; }
        public int OwnerId { get; set; }
        public double Start { get; set; }
        public double End { get; set; }
    }
    public class RequestViewModel
    {
    	public SimulatedResponse SimulatedResponse { get; set; }
    	public IEnumerable<TimeLinesEpisodeEntitiesDto> TimelinesEpisodeEntities { get; set; }
    }
    public class SimulatedResponse
    {
    	public double EndSecs { get; set; }
    	// Other properties related to the simulation can be added here
    }
    

    View

    @model RequestViewModel
    <tbody>
    @{
    	int i = 0;
    	double actualSync = Model.SimulatedResponse.EndSecs;
    }
    @foreach (TimeLinesEpisodeEntitiesDto ptl in
    					@Model.TimelinesEpisodeEntities)
    {
    	i++;
            <form method="post" action="@Url.Action("CorrigeUnTimeLine", "Episode")">
            <tr>
                <td style="width:15%">
                        @if (actualSync > ptl.Start && actualSync < ptl.End)
                        {
                        <span class="SelectedTimeLine ml">@ptl.EntityType &#58; @ptl.OwnerId</span>
                        }
                        else
                        {
                        <span>@ptl.EntityType &#58; @ptl.OwnerId</span>
                        }
                </td>
                <td style="width:15%">
                    <input type="hidden" name="TimeLineId" value="@ptl.TimeLineId" />
                    <input type="number" class="form-input flex-1" style="background-color: #B0C4DE;" readonly value="@ptl.TimeLineId">
                </td>
                <td style="width:15%">
                    <input type="number" class="form-input flex-1" name="Start" value="@ptl.Start">
                </td>
                <td style="width:15%">
                    <input type="number" class="form-input flex-1" name="End" value="@ptl.End">
                </td>
                <td style="width:10%">
                    <button type="submit" class="icon" title="Edit">
                        <img height="25px" width="25px" float="inline-end" src="~/Icon/Edit.png" />
                    </button>
                </td>
            </tr>
            </form>
    }
    </tbody>
    

    Controller

    [HttpPost]
    public IActionResult CorrigeUnTimeLine(int timeLineId, double start, double end)
    {
        // Update the timeline record in the database
    }
    

    If the answer is the right solution, please click "Accept Answer" and kindly upvote it. If you have extra questions about this answer, please click "Comment".

    Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.

    Best regards,
    Rena

    1 person found this answer helpful.
    0 comments No comments

2 additional answers

Sort by: Most helpful
  1. Bruce (SqlWork.com) 65,131 Reputation points
    2024-08-29T16:03:38.3966667+00:00

    your UI does not make sense. if the user can only edit one line at a time, the input fields should be read only. clicking the edit makes them editable, with some cancel/commit action. this is doable with javascript. or just let the user edit all, and have a submit.

    note: just change the model property TimelinesEpisodeEntities to an array or List. this would be required to support postback anyway. the model should be designed to support both render and postback.

    0 comments No comments

  2. Michael Taylor 54,316 Reputation points
    2024-08-29T16:56:55.98+00:00

    You can put a form inside a tr. There is no restriction on this. What you cannot do is put a form outside a td which is likely what you're trying to do.

    <table>
       <tr>
          <td>
             <!-- Allowed -->
             <form id="form"></form>
          </td>
       </tr>
    </table>
    
    <table>
       <tr>
          <!-- Not allowed, only a td/th can be a child -->
          <form id="form">
             <td>
             </td>
          </form>
       </tr>
    </table>
    

    In general, unless you are attempting to POST data back to the server then you don't really need a form. Given your UI I assume that the user will click the edit button and then you will switch the row to be input controls that the user can edit. You will presumably have a Save/Cancel button that the user can click. When they click the save button then you want to send the data back to the server and reset your UX back to read only mode. Hence you are implementing inline editing. Since you don't need to refresh the entire UX, just the one row, then making an API call to update the data makes the most sense.

    When the user clicks the "edit" button swap out the controls in the appropriate row to input controls. Display a "save/cancel" set of buttons (or whatever) to allow the user to save their changes. When the save button is clicked then you need to grab the current values of the controls and insert them into a object that you pass back to the server via an API call to handle the update. Assuming everything is successful then when the API call returns switch the row back to "read only" mode.

    1. Define an API controller on your server. You tagged this question with ASP.NET Core so I assume you're using ASP.NET Core which means it has this built in. If not then you'll need to set up this up as part of your app startup. Here's what it might look like.
    [ApiController]
    [Route("api/apparitions")]
    public class ApparitionsApiController : ControllerBase
    {
       [HttpPost("{id}")]   
       public async Task<ActionResult> Update ( int id, [FromBody] ApparitionUpdateModel model, CancellationToken cancellationToken ) 
       {
           //id is the unique ID of whatever you're updating, use whatever type you need to know which one to update
           //model contains the data from the client
    
           //TODO: You should verify the data is valid first
           //TODO: Update your system with the changed data
           //OPTIONAL: Return information to the client if the save was successful
       }
    }
    
    public class ApparitionUpdateModel
    {
       //TODO: Put the data in here that you want the client to send you to update
       //OPTIONAL: Add data annotations and/or implement IValidatableObject to ensure the model is valid
       public int Start { get; set; }
       public int End { get; set; }
    }
    

    On the client side you need to call a Javascript function that can make an API call. If you're using raw JS then JQuery is probably your best option. The function needs to capture the data from the input controls and send it back to the server.

    save ( int ownerId, int index ) {
       //TODO: Use jQuery to get the input control values based upon their ID/name/whatever - note that the value needs to be unique so you'll need to ensure that, using your example values here as a sample
       var startValue = $(`#Start${index}`).val();
       var endValue = $(`#End${index}`).val();
    
       //Build JSON object, must match model's properties
       var data = { start: startValue, end: endValue };
    
       //Send the request
       $.post(`api/apparitions/${owernId}`, data);
    }
    

    Of course your code is going to need to be more complex as you'll want to handle errors. Refer to the docs on how to handle jQuery errors when posting data.

    In your HTML you won't be using @Url.Action as you are not calling to get an HTML view. Instead your image should be changed to a button or anchor with the image as the contents. Then add a call to the save function you wrote and pass in the data needed for the function to know what row of data to save.

    <a href="#" onclick="save(@ptl.OwnerId, @i); return false;"><img>...</a>
    

    Now when the user clicks the link then it calls your function passed in the data. You can ideally clean this up to pass in the names of the values instead, just depends on how much work you want to do in the view vs JS file.

    0 comments No comments

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.