Blazor WASM repeatable RenderFragment for similar EditForm properties - c#

I am creating a large html form (using the EditForm tag) in my Blazor WASM app, containing many properties. Mostly all of which follow an identical pattern of a label, an input, and a validation section. I would like to create a render fragment that would let me cut down on my mark up. I am struggling with how to reference the "bind-Value" for the correct property.
Here is the start of the edit form, which has a model called registerModel with many properties, including string "Company" representing "Company Name"
<EditForm autocomplete="off" Model="#registerModel" OnValidSubmit="#HandleValidRegisterSubmit">
This is an example of a single section for the company name part, of which there are many in the form for all the individual properties:
<div class="row mb-3 align-items-center">
<div class="col-sm-2">
<label>
Company Name
</label>
</div>
<div class="col-sm-6 p-0">
<InputText #bind-Value="registerModel.Company" type="text" placeholder="Enter Company Name"
class="rounded-input form-control"
id="Last"
autocomplete="off"/>
</div>
<div class="col-sm-4">
<ValidationMessage For="() => registerModel.Company"/>
</div>
</div>
I want a render fragment function to automate creating each form section so I don't have to repeat so much html, and I can make my component more reusable. This is what I have tried so far
RenderFragment<(
EventCallback<string> eventCallback,
Expression<Func<string>> func,
string id,
string label,
string placeholder)> RenderFieldSection = (item) => __builder =>
{
<div class="row mb-3 align-items-center">
<div class="col-sm-2">
<label>#item.label</label>
</div>
<div class="col-sm-6 p-0">
<InputText #bind-Value="#item.eventCallback" type="text" placeholder="#item.placeholder"
class="rounded-input form-control"
id="#item.id"
autocomplete="off"/>
</div>
<div class="col-sm-4">
<ValidationMessage For="#item.func"/>
</div>
</div>
};
Which I am trying to call in the markup with:
#RenderFieldSection((
eventCallback: EventCallback.Factory.Create<string>(this, s => { registerModel.Company = s;}),
func:()=> registerModel.Company,
id: "Company",
label:Translate("CompanyNameLabel"),
placeholder:Translate("CompanyNamePlaceholder")
)
)
Everything works except the input #bind-Value="#item.eventCallback" part, which is supposed to take an EventCallback<string>. This gives the following error.
Cannot convert lambda expression to intended delegate type because some of the return types in the block are not implicitly convertible to the delegate return type
I guess I am constructing the event callback wrongly, but I don't know how to do it. Any ideas? I expect I could do it by finding the property via reflection, but I (naively?) wonder if I can avoid the overhead of that.

Related

Make #Html.TextBox("datepicker") readonly (so user cannot type)

Please see the View code below:
<div class="row">
<div class="col-md-4">
<form asp-action="MemberView">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="Name" class="control-label"></label>
<input asp-for="Name" class="form-control" />
<span asp-validation-for="Name" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="DOB" class="control-label"></label>
#Html.TextBox("datepicker")
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</form>
</div>
</div>
How do I make #Html.TextBox("datepicker") readonly (so only the datepicker can populate it) i.e. a user cannot type in it. I have spent one hour Googling this and have already looked here: Using DatePicker with .NET
Add a readonly property by passing an anonymous object as the third argument.
The null (second argument) is there to keep the input value empty. Of course you could fill it if necessary.
#Html.TextBox("datepicker", null, new { #readonly = "readonly" })
Note: The # symbol is required, because 'readonly' is a reserved keyword.
Please keep in mind that the user is able to manipulate the HTML by removing the readonly attribute. Proper value checking in the back-end is required to handle the value safely.

How to transfer data to the controller from view outside the form or dont create new input

I'm writing an application in asp.net core 2.0.
I have some data that I send to the controller from the view but I also have data that I want to pass to the same controller but they are not in the form.
It is possible to pass this data to the same controller and I do not have to create new inputs.
How to pass data to the controller from outside the form or in form but without creating new inputs.
I have two variables #Model.MinUppercase ,#Model.MinLowercase but I do not use them in the form, how can I pass them to the controller together with variables from the form?
#model Project2.Models.UserModel
<h1>Step2: Register</h1>
<div>
#if (#ViewData["Message"] != null)
{
<div class="alert alert-danger space text-center">
#ViewData["Message"]
</div>
}
#Model.MinUppercase
#Model.MinLowercase
<center>
<h2>Register form:</h2>
</center>
<div>
<form asp-action="Middle" asp-controller="Home" method="POST" class="form-wrapper">
<div class="input-group">
<label>Login:</label>
<input id="Login" asp-for="Login" type="text" class="input" size="35">
</div>
<div class="input-group">
<label>Password:</label>
<input id="Password" asp-for="Password" type="Password" class="input" size="35">
</div>
<div class="input-group">
<label>Your Regex Description:</label>
<input id="Description" asp-for="Description" type="text" class="input" size="35" value=#Model.Description>
</div>
<div class="input-group">
<label>Your Regex:</label>
<input id="Reg" asp-for="Reg" type="text" class="input" size="35" value=#Model.Reg>
</div>
<div class="input-group">
<a asp-controller="Home" asp-action="CreateRegex">
<button type="button" class="btn btn-danger">Back</button>
</a>
<button class="btn btn-success">Create</button>
</div>
</form>
</div>
Inside your form html content add hidden fields (using razor helper #Html.Hidden/For):
#Html.Hidden("MyName", "Carlos")
or
#Html.HiddenFor(i => i.PropertyOfYourModel) - if is some property of your model.
As carlosfcmendes already answered you can add data with hidden fields.
Or if you want to be more flexible you can intercept the form submit and add data.
There are plenty of answers here on how to do that:
Intercept form, add data, ajax, then submit.
How can I listen to the form submit event in javascript?

Cannot get my bootstrap rows on the same line

I read the other questions that were similar but my issue is more basic. I'm rather new to bootstrap but I'm testing it out to see if this will work for a simple form that I need to be opened by phones, tablets, and any other device.
I'm trying to get my textbox on the same line as the text that describes it. Instead, the textbox is under the text.
Here is what is happening:
Here is the cshtml page of the above:
#model MvcBootstrap.Models.HomeModels.VisitingCustomer
#{
ViewBag.Title = "Home Page";
}
<div class="">
<p class="lead">Please enter your branch number, account number, and at least the first three characters of your last name or at least the first three characters of your company name below so we can locate your account.</p>
</div>
<div class="container">
#using (Html.BeginForm("TypeOfPayment", "Home", FormMethod.Post))
{
<div class="row">
<h2>Account Lookup</h2>
<div class=".col-md-4">
Branch Number
</div>
<div class=".col-md-8">
#Html.TextBoxFor(m => m.Branch, new {#class = "", #maxlength = "2"})
</div>
</div>
<div class="row">
<input id="submitpayment" class="typicalbutton" type="submit" value="Continue" />
</div>
}
</div>
I have no additional css code nor have I modified any of the existing css.
This should be pretty straight forward but I'm just not grasping the concept I guess. What am I doing wrong?
You could group the Branch Number and the text box in one column like so
<div class="col-md-4">
<label for="branch-number">Branch Number</label>
# add text box here and give it an id="branch-number"
</div>
Also, you don't need to have a . before the class name in your classes.
You can try this format. form-inline class makes the form inline. you don't need to use the responsive column classes. also you should use class="col-md-*" not class=".col-md-*"
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
<form class="form-inline">
<div class="form-group">
<label for="branch"> Branch Number</label>
<input type="text" class="form-control" id="branch no">
</div>
<button type="submit" class="btn btn-default">Submit</button>
</form>
You can specify the class for different screen
i.e
col-lg-1 to col-lg-12 for large screen
col-md-1 to col-md-12 for desktop / laptop screen
col-sm-1 to col-sm-12 for tablet screen
col-xs-1 to col-xs-12 for mobile screen
Example
<input type="text" class="col-lg-6 col-md-6 col-sm-6 col-xs-12" >
What you are looking for is an inline form.
#using (Html.BeginForm("TypeOfPayment", "Home", FormMethod.Post, new { #class="form-inline" }))
{
<div class="row">
<div class="col-md-12">
<h2>Account Lookup</h2>
<div class="form-group">
#Html.LabelFor(m => m.Branch)
#Html.TextBoxFor(m => m.Branch, new {#class = "form-control", #maxlength = "2"})
</div>
<input id="submitpayment" class="typicalbutton" type="submit" value="Continue" />
</div>
</div>
}
In your model for this form you will want to add a [DisplayName] attribute for your Branch property so that you can use the Html.LabelFor() helper I included above:
[DisplayName("Branch Number")]
public string Branch { get; set; }
You will also want to read more about the grid system. Your original code did not use the proper classes for the columns.
Like #Asif Raza said, you should specify what size of screen are targeting. Keep in mind that the sizing will work for the size you specify and UP. So if you specify a medium size screen, it will affect medium size screens and larger, not smaller.
I dont think thats your issue though, I think whats happening with you is there are extra margins you are not seeing that is causing the textbox to be placed below. The max width of the container is going to be 12 columns, which you are using, but if there are any other margins in between it's going to cause the textbox to fall below. I recommend inspecting the element with F12 to zone in and see if there is anything extra being added.

How to increase the width of ASP.NET MVC Select tag helper inside form

The width of the Select tag helper in the following veiw remains fixed no matter what value I change in its style attribute style="width:10000px;". Whether it's set to 10px or 10000px the width remains the same. On the other hand, in the same example below, the input tag ProjectNumber along with the Instructions message (correctly) occupies the entire width of the computer screen. Note 1: It's a multi select dropdown (but I think that should not mater). 2. I'm using VS2015-Update3 that comes with built-in Bootstrap functionality:
<div class="row">
<div class="col-sm-3">
#await Component.InvokeAsync("MainLeftMenu")
</div>
<div class="col-md-9">
<form id="target" asp-controller="myTest" asp-action="myAction" method="post" class="form-horizontal">
<div class="form-group">
<label asp-for="SelectedIDs" class="col-md-2 control-label"></label>
<div class="col-md-10">
<select asp-for="SelectedIDs" asp-items="Model.myList" style="width:10000px;"></select>
</div>
</div>
<div class="form-group">
<label asp-for="ProjectNumber" class="col-md-2 control-label"></label>
<div class="col-md-2">
<input asp-for="ProjectNumber" class="form-control" />
<span asp-validation-for="ProjectNumber" class="text-danger"></span>
</div>
<div class="col-md-5">Instructions: Add number in the format such as: ABC-123-xyz-000</div>
</div>
<button type="submit" class="btn btn-default">Submit Test</button>
</form>
</div>
</div>
In bootstrap for the select in form you can add class="form-control" like input type text.
For reference: http://getbootstrap.com/css/#forms
Issue resolved by adding style="min-width:700px;overflow:auto;" to the select tag helper. You can change 700px to whatever width you need.

How to check in razor view if field is valid?

I use bootstrap with ASP.NET Core and to indicate form field validation errors i want to add has-errors class to form-group div when given field has an error. The view looks like that:
<div class="form-group">
<label asp-for="Fragment.Content" class="col-lg-2 control-label "></label>
<div class="col-lg-10">
<textarea asp-for="Fragment.Content" class="form-control content-editor"></textarea>
<span class="help-block">A longer block of help text that breaks onto a new line and may extend beyond one line.</span>
<span asp-validation-for="Fragment.Content"></span>
</div>
</div>
I would like to do something like:
<div class="form-group" asp-add-class-if-error="has-errors" for-field="Fragment.Content"/>
I know i can write my own tag helper, however i am curious if there is a built-in solution.
I found that you can use:
#using Microsoft.AspNetCore.Mvc.ModelBinding
#if(ViewData.ModelState.GetFieldValidationState("Fragment.Content") == Microsoft.AspNetCore.Mvc.ModelBinding.ModelValidationState.Invalid)
{
// something here
}

Categories

Resources