Search code examples
asp.net.netasp.net-corerazorrazor-pages

How to pass js function argument to inline c# in cshtml?


Preface: I have a static formatting function in my backend, which takes an int Id and returns string SerialNumber. I want to keep it in on place. In one of the Views - I am showing a column of Serial Number. This column is sortable (by sql query), so the content of the column should be Id value, so I am modifying the displayed value with js to convert Id to Serial.

So, I got this script in my .cshtml View:

<script>
    function formatToSerial(id) {
        return @MyFormater.GetFormatedSerial(int.Parse(id));
    }
</script>

Problem: argument "id" is not recognized in this context.

Question: is there a nice built-in way to pass the js function argument to inline c# call? Or should I stick with duplication string formatting function separately for js?

What I did tried so far: I was looking into making the formatting JSInvokable and use it that way. But If I understood correctly - this is Blazor functionality, which I am not using in this project.


Solution

  • Is there a nice built-in way to pass the JS function argument to inline C# call?

    The answer to this question is no, not for Razor Pages (or a Razor View). The C# code @MyFormater.GetFormatedSerial(int.Parse(id)); executes on the server when the Razor Page view is requested.

    The id argument in your view is declared as a JavaScript variable that will be processed on the client through your JS function formatToSerial(id).

    The error "argument id is not recognized in this context" is the result of the Razor View engine on the server trying to process the unknown/undeclared id variable the engine sees in the C# code int.Parse(id).

    Should I stick with duplication string formatting function separately for JS?

    If you want to leverage your server's implementation from the client, you can make a post request with the id value using fetch() to a Web API that returns the result from MyFormater.GetFormatedSerial().

    There's the usual trade-off of:

    1. Leveraging your existing client-side function but maintaining two separate functions that do the same thing: one in JavaScript and the other in C#.
    2. Avoiding a HTTP request to the server.

    versus:

    1. Maintaining one function (i.e. formatting on the server only) that formats the value in the id variable.
    2. Set up a HTTP request using fetch().
    3. Declaring and maintaining a Web API.

    Web API sample

    using Microsoft.AspNetCore.Authorization;
    using Microsoft.AspNetCore.Mvc;
    
    namespace WebApplication1.Controllers
    {
        [Route("api/[controller]")]
        [ApiController]
        [ValidateAntiForgeryToken]
        public class SerialFormatController : Controller
        {
            [HttpPost]
            [AllowAnonymous]
            public JsonResult Post([FromBody] int? id)
            {
                if (id == null)
                {
                    return new JsonResult(new
                    {
                        Success = false,
                        Message = "The 'id' parameter is null."
                    });
                }
    
                return new JsonResult(new
                {
                    Success = true,
                    Value = MyFormater.GetFormatedSerial(id)
                });
            }
        }
    
        public static class MyFormater
        {
            public static string? GetFormatedSerial(int? id)
            {
                return "formatted-" + id.ToString();
            }
        }
    }
    

    fetch() sample

    function formatToSerial(id) {
        // Validate 'id' parameter
    
        //const data = {
        //    id: id
        //};
    
        const csrfToken = document.querySelector('input[name="__RequestVerificationToken"]').value;
    
        const url = "api/SerialFormat";
    
        fetch(url, {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
                'RequestVerificationToken': csrfToken
            },
            //body: JSON.stringify(data)
            body: id
        })
            .then(response => response.json())
            .then(responseData => {
                document.querySelector("#result").textContent = responseData.value;
            })
            .catch(error => {
                console.error(error);
            });
    }