Use Display attribute as Placeholder

Hello,

I have a model with similar fields:

        [Display(Name = "Megnevezés")]

        [StringLength(100, ErrorMessage = "Maximum {1} karakter hosszú lehet!")]

        [Required(ErrorMessage = "A mező kitöltése kötelező!")]

        public string? Megnevezes { get; set; }

I can use this display attribute as GridColumn text in SfGrid.

Can I also use this attribute as Placeholder text automatically (for example in custom add/edit dialog)?

      <SfTextBox TValue="string" @bind-Value="@AddOrEditDto.Megnevezes" TabIndex="3" Placeholder="Megnevezés" FloatLabelType="FloatLabelType.Always"></SfTextBox>

So this: Placeholder="Megnevezés" should be filled automatically from the model's Display attribute. This is possible?

Thank you!

BR, SZL



12 Replies

SP Sureshkumar P Syncfusion Team May 4, 2022 11:32 AM UTC

You can achieve your requirement by calling a public method in the code section to find the Display Name Attribute of that particular property and returned it to the Placeholder property. Refer to the below code example.


Model class:

[Required(ErrorMessage = "The Employee field is required.")]

        [Display(Name = "Megnevezés")]

        public string FirstName { get; set; }


Code example here:

 

<GridColumn Field="BlockStart10DigitInclusive"

            TextAlign="TextAlign.Left"

            EditType="EditType.NumericEdit">

        <EditTemplate>

            <SfTextBox ID="BlockStart10DigitInclusive" Placeholder="@GetDisplayName("BlockStart10DigitInclusive")" FloatLabelType="FloatLabelType.Always"

                                  @bind-Value="@((context as AABModel).BlockStart10DigitInclusive)">

                </SfTextBox>

        </EditTemplate>

    </GridColumn>

 

public string GetDisplayName(string col)

    {

        var name = "";

        MemberInfo property = typeof(AABModel).GetProperty(col);

        var dd = property.GetCustomAttribute(typeof(DisplayAttribute)) as DisplayAttribute;

        if (dd != null)

        {

            name = dd.Name;

        }

 

        return name;

    }




SZ SZL replied to Sureshkumar P May 7, 2022 06:46 PM UTC

Thank you for the solution, its working, but in my case the funcion runs 4 (2+2) times for 1 TextBox. This is a normal behavior?

img.jpg

(this is one add-dialog loading and one textbox)



SP Sureshkumar P Syncfusion Team May 9, 2022 05:49 AM UTC

Hi SZL,


Please share the below details to proceed further,

  1. Please share the rendered code example by calling the GetDisplayName method calling

  2. Please confirm whether you have rendered the textbox component inside or outside the grid component.

  3. If possible, please share the full code example of add-dialog coding with the textbox component.

These details will help us to provide an exact solution as earlier as possible.


Regards,

Sureshkumar P



SZ SZL May 9, 2022 11:09 AM UTC

Hello,

I create my code based on yours to create a function for all objects trough my base class.

 My code (for example):

<SfDatePicker @bind-Value="@AddOrEditDto.Datum" TValue="DateTime" TabIndex="1" Placeholder="@GetPlaceHolder(nameof(AddOrEditDto.Datum))" ShowClearButton="true" FloatLabelType="FloatLabelType.Always"></SfDatePicker>

The function in my base class:

        public string GetPlaceHolder(string propName)

        {

            return AddOrEditDto?.ToDisplayName(propName) ?? propName;

        }

The extension:

    public static class ObjectExtensions

    {

        public static string ToDisplayName(this object value, string propName)

        {

            try

            {

                Type type = value.GetType();


                MemberInfo[] memInfo = type.GetMember(propName);


                if (memInfo.Length == 0) return propName;


                DisplayAttribute[] attributes = memInfo[0].GetCustomAttributes<DisplayAttribute>(false).ToArray();


                return (attributes.Length == 0) ? propName : (attributes[0].Name ?? propName);

            }

            catch (Exception)

            {

                return propName;

            }

        }

    }


2. I render the textbox (or datepicker) outside of grid in a custom EditForm (the default add/edit grid operations are prevented and cancelled).

3. Unfortunatelly not, my project is huge and have lot of references, I cannot separate it easily.


Thank you!



AR Aravind Ravi Syncfusion Team May 12, 2022 12:55 PM UTC

Hi SZL,

Query: Thank you for the solution, its working, but in my case the funcion runs 4 (2+2) times for 1 TextBox. This is a normal behavior?

This is the default behavior of the blazor application. Because of the _host.cshtml page the rendering mode is represented as ServerPrerendered. So, in this case, the lifecycle of blazor triggers twice (prerender and afterrendering) for each component.


Also, the InputText component also has the same behavior when placed inside the dialog component the placeholder method triggers 4 times.


Find the code example here for input text component:

 

@using Syncfusion.Blazor.Popups

@using Syncfusion.Blazor.Inputs

@using System.ComponentModel.DataAnnotations

@using System.Reflection

 

<div id="target" class="col-lg-12 control-section" style="height:500px">

    <div>

        @if (this.ShowButton)

        {

            <button class="e-btn" @onclick="@OpenDialog">Open Dialog</button>

        }

    </div>

    <SfDialog Height="75%" Width="435px" Target="#target" ShowCloseIcon="true" @bind-Visible="Visibility">

        <DialogTemplates>

            <Content>

                <EditForm EditContext="@myEditContext" OnSubmit="@SubmitPerson">

 

                    <DataAnnotationsValidator />

 

                    <div class="form-group">

 

                        <label for="NameInput">Name</label>

 

                        <InputText class="form-control" id="NameInput" @attributes="@(new Dictionary<string,object>() { { "placeholder", @GetDisplayName("CustomerID") } })" @bind-Value="Person.Name" />

 

                    </div>

 

                    <div>

 

                        <button type="submit" class="btn btn-primary">Submit</button>

 

                    </div>

 

                </EditForm>

                @*<SfTextBox ID="CustomerID" Placeholder="@GetDisplayName("CustomerID")">

                    </SfTextBox>*@

            </Content>

        </DialogTemplates>

        <DialogEvents OnOpen="@BeforeDialogOpen" Closed="@DialogClosed"></DialogEvents>

    </SfDialog>

</div>

 

 

@code {

 

    public class Order

    {

        [Display(Name = "Order ID")]

        public int? OrderID { get; set; }

        [Display(Name = "Customer Name")]

        public string CustomerID { get; set; }

        [Display(Name = "Order Date")]

        public DateTime? OrderDate { get; set; }

        [Display(Name = "Freight")]

        public double? Freight { get; set; }

    }

 

    public string GetDisplayName(string col)

    {

        var name = "";

        MemberInfo property = typeof(Order).GetProperty(col);

        var dd = property.GetCustomAttribute(typeof(DisplayAttribute)) as DisplayAttribute;

        if (dd != null)

        {

            name = dd.Name;

        }

        return name;

    }

 

    // dialog rendering code

 

    SfTextBox TextboxObj;

 

    private string TextBoxValue;

    private bool Visibility { get; set; } = true;

    private bool ShowButton { get; set; } = false;

 

    private void BeforeDialogOpen(Syncfusion.Blazor.Popups.BeforeOpenEventArgs args)

    {

        this.ShowButton = false;

    }

 

    private void DialogClosed(CloseEventArgs args)

    {

        this.ShowButton = true;

    }

 

    private void OpenDialog()

    {

        this.Visibility = true;

    }

 

    // validation

 

    public PersonModel Person = new PersonModel();

 

    EditContext myEditContext { get; set; }

 

    private string ValidMEssage;

 

    protected override void OnInitialized()

    {

        myEditContext = new EditContext(Person);

        base.OnInitialized();

    }

 

    protected void SubmitPerson(EditContext editContext)

    {

        bool isFormValid = editContext.Validate();

        if (isFormValid)

        {

            ValidMEssage = "Save successfully";

        }

        else

        {

            ValidMEssage = "Invalid";

        }

    }

 

    public class PersonModel

    {

 

        public int ID { get; set; }

 

        [Required(AllowEmptyStrings = false)]

 

        [StringLength(255)]

 

        public String Name { get; set; }

 

    }

 

}


We can avoid this by changing the ServerPrerendered into Server in the component tag. To know more about this, please refer to the stack overflow link: https://stackoverflow.com/questions/58075628/why-are-blazor-lifecycle-methods-getting-executed-twice

But the method triggers 2 times initially for each component rendering dialog and textbox component.

Regards,

Sureshkumar p



SZ SZL replied to Aravind Ravi May 13, 2022 07:15 PM UTC

Thank you for the detailed infomations!



PO Prince Oliver Syncfusion Team May 16, 2022 05:51 AM UTC

Most welcome, we are glad to assist you



SZ SZL May 17, 2022 02:12 PM UTC

Sorry, I have only one last question about this topic:

Now I need set the placeholder for every field manually. It's possible to create an extension to your component to fill this field automatically (inside the extension by bind-Value for example)?

So the extension could be:

Placeholder="@GetPlaceHolder(nameof( bind-Value ))"

Or similar?


This is possible to create similar extensions to components?

or

There are any chance to add this function (optionally of course) to your components?

So for example:

PlaceHolder="auto" fill the placeholder by bind-Values object DisplayName attribute?


Thank you!



SP Sureshkumar P Syncfusion Team May 18, 2022 01:27 PM UTC

You can achieve your requirement by rendering textbox as custom component as like below code example

[index.razor]

<label for="NameInput">Name</label>

                        <CustomTextbox TVal="PersonModel" CustomPlaceHolder="@nameof(Person.Name)"  @bind-Value="Person.Name"></CustomTextbox>

 

[customTextbox.razor]

 

 

@typeparam TVal;

 

 

 

<SfTextBox Placeholder='@GetDisplayName(CustomPlaceHolder)' Value="@Value" ValueChanged="ValueChanged" ValueExpression="@ValueExpression"></SfTextBox>

 

@code

{

    [Parameter]

 

    public string Value { get; set; }

 

    [Parameter]

 

    public string CustomPlaceHolder { get; set; }

 

    [Parameter]

    public Expression<Func<string>> ValueExpression { get; set; }

 

    [Parameter]

    public EventCallback<string> ValueChanged { get; set; }

 

    public string PlaceHolder { get; set; }

 

   

 

    public string GetDisplayName(string Name)

    {

        var name = "";

        MemberInfo property = typeof(TVal).GetProperty(Name);

        var dd = property.GetCustomAttribute(typeof(DisplayAttribute)) as DisplayAttribute;

        if (dd != null)

        {

            name = dd.Name;

        }

        return name;

    }

}

 

We have created the sample based on your requirement. Please find the sample in the attachment:


Attachment: BlazorAppTest1_866e6f9d.zip


SZ SZL replied to Sureshkumar P May 19, 2022 10:43 AM UTC

Thank you for the suggestion.

For individual cases this is a perfect solution, but for general cases I should be create custom component for every input type and unfortunatelly the base component parameters are not accessible directly from custom compoenent.

Thanks for help, I think we can close this topic.




RP Ranjani Prabakaran Syncfusion Team May 20, 2022 11:54 AM UTC

Dear Customer,


Please let us know if you need any further assistance.


Regards,


Ranjani





SZ SZL May 22, 2022 07:04 PM UTC

Dear Ranjani,


No, thank you!


Loader.
Up arrow icon