Accept "#" inside form builder text boxes fields

1 Answer 39 Views
Form
Daniel
Top achievements
Rank 1
Daniel asked on 08 Feb 2024, 10:38 AM | edited on 08 Feb 2024, 10:38 AM

Hi!

I use a lot of forms (created using HtmlHelper) inside templates used by various other elements (mainly inside TileLayouts).

e.g.:

public record SettingsViewModel
{
    [DataType(DataType.Password)]
    public string Password { get; set; } = null!;
}
<script id="settings-template" type="text/x-kendo-template">
    @(Html.Kendo().Form<settingsViewModel>()
        .Name("settingsForm")
        .FormData(Model.Settings)
        .Layout("grid")
        .Grid(g => g.Cols(2).Gutter(5))
        .HtmlAttributes(new { method = "POST", style = "width: 100%;" })
        .Validatable(v =>
        {
            v.ValidateOnBlur(false);
            v.ValidationSummary(true);
        })
        .Items(items =>
        {
                
            items.Add().Field(f => f.Password).Label("Password");
        })
        .ToClientTemplate()
        @* .ToString()
        .Replace("#", "\\#") *@
    )
</script>
<div class="k-d-flex-col k-justify-content-center">
    @(Html.Kendo().TileLayout()
        .Name("settings")
        .Columns(5)
        .RowsHeight("auto")
        .Containers(c =>
        {
            ...
            c.Add().BodyTemplateId("settings-template").ColSpan(4).RowSpan(1);
            ...
        })
    )
</div>

Many of these forms requires to accept text boxes (like passwords) which should accept "#"  in their content. 

Saving works perfect but when rendering the Form I always get an exception. Please see below error:

Uncaught Error: Invalid template:'
        <form id="settingsForm" method="POST" name="settingsForm" style="width: 100%;"></form><script>kendo.syncReady(function(){jQuery("\#settingsForm").kendoForm({"validatable":{"validateOnBlur":false,"validationSummary":{}},"formData":{"Password":"bla#bla","Id":0},"layout":"grid","grid":{"cols":2,"gutter":5},"serverErrors":{},"items":[{"field":"Password","label":{"text":"Password"},"editorOptions":{},"validation":{"data-val":"true","data-val-required":"The Password field is required.","type":"password"},"shouldRenderHidden":true}]});});<\/script>
    ' Generated code:'var $kendoOutput, $kendoHtmlEncode = kendo.htmlEncode;with(data){$kendoOutput='\n        <form id="settingsForm" method="POST" name="settingsForm" style="width: 100%;"></form><script>kendo.syncReady(function(){jQuery("#settingsForm").kendoForm({"validatable":{"validateOnBlur":false,"validationSummary":{}},"formData":{"Password":"bla';bla","Id":0},"layout":"grid","grid":{"cols":2,"gutter":5},"serverErrors":{},"items":[{"field":"Password","label":{"text":"Password"},"editorOptions":{},"validation":{"data-val":"true","data-val-required":"The Password field is required.","type":"password"},"shouldRenderHidden":true}]});});<\/script>
    ;$kendoOutput+=;}return $kendoOutput;'
    at Object.compile (kendo.all.js:322401:19)
    at init._initContainers (kendo.all.js:322401:19)
    at new init (kendo.all.js:322401:19)
    at HTMLDivElement.<anonymous> (kendo.all.js:322401:19)
    at Function.each (jquery.min.js:2:2898)
    at n.fn.init.each (jquery.min.js:2:846)
    at e.fn.<computed> [as kendoTileLayout] (kendo.all.js:322401:19)
    .......

Any idea how to fix that on the client-side (and not need to escape each property I want to accept "#" at server-side) ? 

Thanks

1 Answer, 1 is accepted

Sort by
0
Alexander
Telerik team
answered on 13 Feb 2024, 08:17 AM

Hi Daniel,

Thank you for reaching out.

Before proceeding with this discussion, I noticed that there is no active license associated with your account which limits our support service. Hence, I would recommend renewing your license to get a hold of all the latest bug fixes, features, and support services the product provides.

A list of the available support plans can be found here:

Regardless, I have gone more in-depth with the currently employed scenario on your side, and what I would normally suggest is to consider the usage of our newly inducted Template component.

The Template component allows you to declare multiple helper components into the templating options of components in more complex layout templates whilst levering C#'s syntax.

We have an online demo that showcases this approach that I personally think you might find helpful in this regard:

However, I stumbled upon a rather peculiar behavior that prohibits the usage of the Form component. After further investigation, it appears that this is a bug on our side.

I have subsequently logged it within the following resources:

"Both of the items are symbiotically connected, so feel free to find the one you find most suitable."

I am happy to let you know that I have personally embarked on the initiative of providing a fix candidate which currently pends review by our fellow developer subject matter experts.

For the time being, I'm afraid that a more server-side approach would be required instead. I understand your concerns that manually escaping properties would prove a hefty process. Hence, my personal recommendation here would be to define your own helper class which will have a more dynamic nature that:

  1. Traverses through a given model's properties.
  2. Asserts whether it is of type string.
  3. Based on the assertion, escapes the hash symbol.

Here is an example:

public static class HashEscaper
{
    public static void EscapeHash<T>(T model) where T : class
    {
        PropertyInfo[] properties = typeof(T).GetProperties(BindingFlags.Instance | BindingFlags.Public); // Gather the Properties through reflection.

        foreach (PropertyInfo property in properties) // Iterate through each of the properties.
        {
            if(property.PropertyType.Name == "String") // Assert whether it is of type string.
            {
                var value = property.GetValue(model) as string;
                if (value.Contains("#")) // Assert whether the string value contains "#" symbols.
                {
                   property.SetValue(model, value.Replace("#", "\\#"));  // Escape the "#" symbol.
                }
            }
        }
    }
}

This will allow you to call the method in a similar fashion as the following:

public IActionResult Index()
{
    var model = new SettingsViewModel
    {
        Password = "1#2#3#"
    };

    HashEscaper.EscapeHash(model);

    return View(model);
}

Which would produce the following result:

For your convenience, I am also attaching a runnable sample that further depicts the aforementioned approach.

"As a word of caution, introducing more complex types to the model may require a more recursive approach for gathering the properties depending on the level of class nesting. This should be handled by the developer as per his requirements and needs."

For further bringing this to our attention, I have also added some Telerik points as a token of appreciation.

I hope this helps.

Kind Regards,
Alexander
Progress Telerik

Stay tuned by visiting our public roadmap and feedback portal pages. If you're new to the Telerik family, be sure to check out our getting started resources, as well as the only REPL playground for creating, saving, running, and sharing server-side code.
Tags
Form
Asked by
Daniel
Top achievements
Rank 1
Answers by
Alexander
Telerik team
Share this question
or