Move #Html.Labelfor after the element - asp.net

I have something like this:
#Html.CheckBoxFor(model=>...)
<label>
#Html.DisplayFor(model=>...)
</label>
#Html.HiddenFor(model=>...)
But the final result in browser is:
1)Element
2)Hidden field
3)Label
I need to fix this because a library I use needs the label to be immediately after the Checkbox.

Why are you wrapping a <label> around a DisplayFor? Just use #Html.LabelFor.
Additionally. If you use the #Html.CheckBox.. helpers they will render a hidden field after the checkbox. There is a good reason for this, but requires a lengthy example.
What you probably want is an editor template. This should get you started:
#model Boolean
<input type="checkbox"
id="#ViewData.TemplateInfo.GetFullHtmlFieldId("")"
name="#ViewData.TemplateInfo.GetFullHtmlFieldName("")" />
The file for the code above is called "MyCheckbox" and is in Views/Shared/EditorTemplates
Model:
[UIHint("MyCheckbox")]
public bool MyProp { get; set; }
View:
#Html.EditorFor(x=> x.MyProp)
#Html.LabelFor(x=> x.MyProp)
Output:
<input type="checkbox" id="MyProp" name="MyProp">
<label for="MyProp">MyProp</label>

Related

Tag Helper asp-validation-for is always showing

I tried using the Asp.Net Core TagHelper but it doesn't seem to work. However, when using HtmlHelpers it works as expected. My issue is that it always display the error message although the ModelState is valid. Am I doing something wrong or can someone reproduce this error?
<label class="control-label" asp-for="Firstname">Firstname</label>
<input type="text" class="form-control" asp-for="Firstname">
<span class="form-control-feedback" asp-validation-for="Firstname"> This field has an error. </span>
The property Firstname has a Required attribute in the ViewModel.
It works like this:
<label class="control-label" asp-for="Firstname">Firstname</label>
<input type="text" class="form-control" asp-for="Firstname">
#Html.ValidationMessageFor(x => x.Firstname)
Edit:
It seems to work if I don't add the custom error message to the Html element but instead to the ViewModel DataAnnotation, like this:
<label class="control-label" asp-for="Firstname">Firstname</label>
<input type="text" class="form-control" asp-for="Firstname">
<span class="form-control-feedback" asp-validation-for="Firstname"></span>
Model:
[Required(ErrorMessage = "This field has an error.")]
public string Firstname { get; set; }
TL;DR:
Consider putting text inside the tag helpers in scenarios when you really want something
different from the generated value.
Full answer
You practically find the solution on your own, but I think I can still throw in my two cents here.
Most tag helpers work in a manner of generating content on a condition when its content is empty or contain only whitespace characters. For example, the ValidationMessageTagHelper checks it in this way:
var tagHelperContent = await output.GetChildContentAsync();
// We check for whitespace to detect scenarios such as:
// <span validation-for="Name">
// </span>
if (!tagHelperContent.IsEmptyOrWhiteSpace)
{
message = tagHelperContent.GetContent();
}
It gets tag content and then fills up message variable if the content is null, empty or whitespace. The message variable is then used to generate the validation message:
var tagBuilder = Generator.GenerateValidationMessage(
ViewContext,
For.ModelExplorer,
For.Name,
message: message,
tag: null,
htmlAttributes: htmlAttributes);
If the message is null or empty then the generator will provide the model error (see line 858 of DefaultHtmlGenerator);
if (!string.IsNullOrEmpty(message))
{
tagBuilder.InnerHtml.SetContent(message);
}
else if (modelError != null)
{
modelExplorer = modelExplorer ?? ExpressionMetadataProvider.FromStringExpression(
expression,
viewContext.ViewData,
_metadataProvider);
tagBuilder.InnerHtml.SetContent(
ValidationHelpers.GetModelErrorMessageOrDefault(modelError, entry, modelExplorer));
}
The GetModelErrorMessageOrDefault() of ValidationHelpers:
public static string GetModelErrorMessageOrDefault(
ModelError modelError,
ModelStateEntry containingEntry,
ModelExplorer modelExplorer)
{
Debug.Assert(modelError != null);
Debug.Assert(containingEntry != null);
Debug.Assert(modelExplorer != null);
if (!string.IsNullOrEmpty(modelError.ErrorMessage))
{
return modelError.ErrorMessage;
}
// Default in the ValidationMessage case is a fallback error message.
var attemptedValue = containingEntry.AttemptedValue ?? "null";
return modelExplorer.Metadata.ModelBindingMessageProvider.ValueIsInvalidAccessor(attemptedValue);
}
So yes, if you put any text inside the <span> validation tag, the tag helper will choose your text over validation error from model state. Similar behaviour occurs if you put text inside the <label> tag as you did:
<label class="control-label" asp-for="Firstname">Firstname</label>
The tag helper will not overwrite the Firstname value you put inside the tag. It may not seem as bad behaviour, but if you would like to use display name for the Firstname property:
[Display(Name = "Fancy first name")]
public string Firstname { get; set; }
you would not see it work! Because the tag helper would again choose the text you put in-between <label> tags over the display name for Firstname.
What you should do is leave it as simple as i can be:
<label class="control-label" asp-for="Firstname"></label>
Consider putting text inside the tag helpers in scenarios when you really want something
different from the generated value.
At the begging I said that most tag helpers work that way. Most of them do, but not all of them. For example SelectTagHelper allows you to put any custom text inside the tag and if you provide a select list, it will generate the options by appending them to the existing content. It is extremely handy for adding custom <option> tags. For example I can easily add a selected and disabled option, so the dropdown does not have initial value, therefore the user is forced to manually select an option. These lines of code:
<select asp-for="LevelId" asp-items="#Model.Levels" class="custom-select">
<option selected disabled>Select option</option>
</select>
will result in:
<select class="custom-select" data-val="true" data-val-required="'Level Id' must not be empty." id="LevelId" name="LevelId">
<option selected disabled>Select parking level</option>
<option value="9">-2</option>
<option value="8">-1</option>
<option value="7">0</option>
</select>

Cannot upload an image

I'm trying to update an image to my database, I defined as property model (bounded by database) the following:
public byte[] AvatarImage { get; set; }
then I created another property which store the value in the ViewModel:
public IFormFile AvatarImage { get; set; }
this steps are also described here in the doc.
Iside my form, I added the following html:
<div class="form-group text-center col-lg-12">
<img src="#Model.AvatarImage" class="avatar img-circle" alt="avatar" />
<h6>#Localizer["UploadNewAvatar"] ...</h6>
<input type="file" class="form-control" id="avatarUrl" asp-for="#Model.AvatarImages" />
</div>
when I submit the form the property AvatarImage is even null. But I don't understand why happen this, because all the other form properties are valorized correctly
Sounds like you are missing the form enctype.
Make sure you have:
<form enctype="multipart/form-data">
... inputs
<form>
Your <input type="file"> element assignment below seems to be wrong, because it uses #Model directive which outputs value of AvatarImages property (and the property is not exist in viewmodel class):
<input type="file" class="form-control" id="avatarUrl" asp-for="#Model.AvatarImages" />
The correct way is just using the property name like example below, because asp-for="PropertyName" is equivalent to model => model.PropertyName in HTML helper (assumed you have #model directive set to a viewmodel class):
<input type="file" class="form-control" asp-for="AvatarImage" />
Also don't forget to specify enctype="multipart/form-data" attribute in <form> tag helper:
<form asp-controller="ControllerName" asp-action="ActionName" method="post" enctype="multipart/form-data">
<!-- form contents here -->
</form>
Reference: Tag Helpers in forms in ASP.NET Core
First add enctype="multipart/form-data" to form ;
Then,check your #model, two situations :
1.Use Model directly, since the image is a byte array type, you need to convert the file type to byte[] during the submission process.
2.Or you could use ViewModel, and change the parameter type to viewmodel in the method.

Remove multiple selection from asp.net core application

I have List<SelectListItem> variable with two values. I want to represent it as dropdown box in html so I'm doing like this.
<div class="form-group row">
<label asp-for="Roles" class="col-sm-2 col-sm-offset-2 form-control-label"></label>
<div class="col-md-6">
<select asp-for="Roles" asp-items="#Model.Roles" class="form-control selectpicker bs-select-hidden"></select>
</div>
</div>
and this code shows me the list with those two items, but it also generates
multiple="multiple"
attribute for select tag.
How can I make not to generate multiple attribute?
The Select Tag Helper automatically makes the select element multiple if your asp-for property is an IEnumerable. The way to avoid that is to use your base class (not a collection) as the asp-for property. The asp-items property should still be a collection since these are the items that will become options in the select list.
In your example this is simply changing your asp-for="Roles" to asp-for="Role"
<div class="form-group row">
<label asp-for="Roles" class="col-sm-2 col-sm-offset-2 form-control-label"></label>
<div class="col-md-6">
<select asp-for="Role" asp-items="#Model.Roles" class="form-control selectpicker bs-select-hidden"></select>
</div>
You may need to adjust your view model being passed to the view so that it has access to the base class Role, as well as the collection of Roles to be enumerated
ASP NET Core Select Tag Helper Reference
The select tag helper will automatically generate a multi-select if the property specified in the asp-for attribute is an IEnumerable check this link
So you can easily solve this problem by using string for name and list for items as follow:
public SelectList Roles { get; set; }
public string Role { get; set; }
Then in view
<select asp-for="Role" asp-items="Model.Roles">
</select>
You can remove this attribute multiple with javascript:
Model:
public int RoleId { get; set; }
public SelectList Roles { get; set; }
Add helper css class for example: single-select
<select asp-for="RoleId" asp-items="#Model.Roles" class="single-select">
</select>
Javascript:
$(function(){
$(".single-select").removeAttr("multiple");
});
Note:
If you submit the form you can check the property called RoleId. The property called Roles is probably only for display all items.
Set multiple=false in your select
Try following
<select multiple="multiple" size="1">

Force unobstructive syntax without Html.BeginForm / Ajax.BeginForm in partial view

When I put a part of my form in a partial view, all form parts get unobstructive syntax except the form elements in the partial view.
The only way I found how to "apply" the unobstructive syntax, is by starting another form inside the partial view.
View:
#using (Ajax.BeginForm("SubmitHandler", new DefaultAjaxOptions()))
{
#Html.EditorFor(m => m.Name)
#Html.Partial("MyPartialView", Model)
}
PartialView:
#Html.TextBoxFor(m => m.SomeContent)
Output:
<input class="text-box single-line" data-val="true" data-val-required="This field is required." id="Name" name="Name" type="text" value="">
<input id="SomeContent" name="SomeContent" type="text" value="0">
So only the input element from the View has the unobstructive syntax and the partial view hasn't...
Is there a way to apply unobstructive syntax inside a partial view, wich doesn't require you to begin a new form?
I actually found a better solution! Went digging a bit in the asp.net mvc code and the MvcForm class creates all the unobstructive validation syntax.
so if you use:
#using(new MvcForm(ViewContext))
{
}
instead of
#using(Html.BeginForm())
{
}
it will still apply the unobstructive syntax, but won't create the form html tags :)

jQuery to get the text attribute of a checkbox

I'm adding a check box to a page using the following statement;
<script language="C#" runat="server">
protected void Page_Load ( object src, EventArgs e )
{
if (!IsPostBack)
{
CheckBox XChkBox = new CheckBox(); //instance of System.Web.UI.WebControls.CheckBox
XChkBox.ID = "someId"
XChkBox.Text = "someText"
somePlaceHolder.Controls.Add(XChkBox);
}
}
</script>
I need to get the Text attribute of that check box on click. I tried $(this).attr('Text'); inside $('input[type=checkbox]').click(function(){}); but it returns undefined.
Where am I going wrong? Please suggest.
cheers
ASP .NET renders the Text property of the ASP:CheckBox server control, as a label element just after the <input type="checkbox" /> on the generated markup at the client, it looks something like this:
<input id="someId" type="checkbox" name="someId" />
<label for="someId"> someText </label>
You can get the text of the label by:
$("input:checkbox").click(function() {
var $label = $(this).next('label');
alert($label.text());
});
The CheckBox control renders the Text inside a <label> element. The text is not part of the HTML checkbox. If you want to get the text from jQuery, you have to get it from the <label>.
Also, the <label> it generates doesn't actually have an ID. If your CheckBox is named checkBox1, then the HTML it outputs will be <label for="CheckBox1">, and the text is inside that element. I believe the correct jQuery syntax would be:
$('label[for="checkBox1"]').html()
This depends on how you're implementing what you call "text".
If it's like this:
<input id="chkFoo" type="checkbox" text="Check me, fool!" />
Then you can access the text like this:
$("#chkFoo").attr("text")
If you do it like this
<input id="chkFoo" type="checkbox" />Check me, fool!
I think you're out of luck. Put a span around the text if you have to do it this way and grab it like this:
<input id="chkFoo" type="checkbox" /><span id="spnFoo">Check me, fool!</span>
$("#spnFoo").text()

Resources