When adding a css class attribute using new { #class="form-control"} to a textbox generated using html helpers, the attribute is added as a value while the same approach works fine with the textarea control.
#{
ViewBag.Title = "New";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>New</h2>
<div>
<div class="col-md-4">
#using (Html.BeginForm())
{
<div class="form-group">
#Html.Label("Title");
#Html.TextBox("Title", new { #class = "form-control" })
</div>
<div class="form-group">
#Html.Label("Body")
#Html.TextArea("Body", new{ #class="form-control"} )
</div>
}
<div class="form-group">
<input class="btn btn-default" name="edit" value="edit"/>
<input class="btn btn-default" name="save" value="Save" />
</div>
</div>
</div>
Try doing this instead #Html.TextBox("Title","", new { #class = "form-control" })
The over load you're currently using is
HtmlHelper.TextBox(string name, object value)
You are using this overload of Html.TextBox for method
public static MvcHtmlString TextBox(
this HtmlHelper htmlHelper,
string name,
object value
)
and the second parameter sets the value of the input, if model state dictionary does not contain one for the input. So your code will render something like this
<input id="Title" name="Title" type="text" value="{ class = form-control }">
You should use this overload which takes the value and htmlAttriubutes
public static MvcHtmlString TextBox(
this HtmlHelper htmlHelper,
string name,
object value,
IDictionary<string, object> htmlAttributes
)
Use it like this
#Html.TextBox("Title", null, new { #class = "form-control" })
Interesting thing to note here is, the helper method will look for the value in model state dictionary and view state dictionary as well. So if your action method is setting the ViewBag.Title to some value, that value will be used as the value of the input if you pass null as the value when calling this method. The default MVC project template uses ViewBag.Title to set the page title. So you probably are going to see that value in the input.
To solve that, you can explicitly pass an empty string instead of null
#Html.TextBox("Title", string.Empty, new { #class = "form-control" })
Related
I have a <textarea /> that has the value of a property in my view model. The property is as follows:
[Display(Name="Description", Description="DescriptionOfDescription", ResourceType=typeof(Resources.Admin))]
public string Description { get; set; }
Notice the Display data attribute. This defines the display name and description of the property. With this, I should be able to set up my markup like so:
<div class="form-horizontal">
<div class="form-group">
#Html.LabelFor(x => x.Description, new { #class="control-label col-md-2" })
<div class="col-md-10">
<textarea data-bind="value: Description" />
#Html.ValidationMessageFor(x => x.Description)
#Html.DescriptionFor(x => x.Description)
</div>
</div>
</div>
The <textarea data-bind="value: Description"/> is using KnockoutJS to bind the value.
In my opinion this should and would work, but for some reason it doesn't. The HTML of #Html.DescriptionFor(x => x.Description) is being rendered within the value of the textarea, which I find very peculiar. I have no idea what's going on, and why it's behaving like this.
Here's an image of the web page:
EDIT
Html.DescriptionFor()
public static class MvcHtmlHelpers
{
public static MvcHtmlString DescriptionFor<TModel, TValue>(this HtmlHelper<TModel> self, Expression<Func<TModel, TValue>> expression)
{
var metadata = ModelMetadata.FromLambdaExpression(expression, self.ViewData);
var description = metadata.Description;
return MvcHtmlString.Create(string.Format(#"<p class='help-block'>{0}</p>", self.Encode(description)));
}
public static string DescriptionForRaw<TModel, TValue>(this HtmlHelper<TModel> self, Expression<Func<TModel, TValue>> expression)
{
var metadata = ModelMetadata.FromLambdaExpression(expression, self.ViewData);
var description = metadata.Description;
return description;
}
}
The problem is that <textarea> is not a self-closing tag, hence the HTML that comes afterwards is considered part of the element.
You need to close it using:
<textarea data-bind="value: Description"></textarea>
I'm trying to generate a selectList with a default selected item in MVC. I assign a selectlist to the viewbag and try to render it in my view and keep getting the below error.
Does anyone know what I might be doing wrong?
Create the list in my controller:
ViewBag.MasterAccountSelect = new SelectList(masterAccountsList, "MasterAccount", "MasterAccount", userSettingsViewModel.MasterAccountName);
Render the SelectList in my view:
<div class="editor-label">
#Html.LabelFor(model => model.MasterAccountSelect, "MasterAccountSelect")
</div>
<div class="editor-field">
#Html.DropDownList("MasterAccountSelect")
#Html.ValidationMessageFor(model => model.MasterAccountSelect)
</div>
I get this error:
Compiler Error Message: CS1061: 'UserSettingsViewModel' does not
contain a definition for 'MasterAccountSelect' and no extension method
'MasterAccountSelect' accepting a first argument of type
'UserSettingsViewModel' could be found (are you missing a using
directive or an assembly reference?)
I also get this error when I
You view model does not have a property named MasterAccountSelect hence the error. From the code you are assigning to ViewBag, you appear to be wanting to bind to a property in your model named MasterAccountName in which case your model should be
public class UserSettingsViewModel
{
[Display(Name = "Master Account Name")]
[Required(ErrorMessage = "Please select a master account name")]
public string MasterAccountName { get; set; }
public SelectList MasterAccountNameList { get; set; }
....
}
Then in the GET method
UserSettingsViewModel model = new UserSettingsViewModel
{
MasterAccountName = ..... ,
MasterAccountNameList = new SelectList(masterAccountsList, "MasterAccount", "MasterAccount")
}
return View(model);
And in the view
<div class="editor-label">
#Html.LabelFor(m => m.MasterAccountName)
</div>
<div class="editor-field">
#Html.DropDownListFor(m => m.MasterAccountName, Model.MasterAccountNameList, "-Please select-")
#Html.ValidationMessageFor(m => m.MasterAccountName)
</div>
Side note: Since you wanting the options value and display text to be the same (using property MasterAccount), you can also use
MasterAccountNameList = new SelectList(masterAccountsList.Select(x => x.MasterAccount))
try Label instead of LabelFor, You can not use LabelFor strictly type html helper if you don't have property in your model.
<div class="editor-label">
#Html.Label("MasterAccountSelect")
</div>
<div class="editor-field">
#Html.DropDownList("MasterAccountSelect")
#Html.ValidationMessageFor(model => model.MasterAccountSelect)
</div>
One good Suggestion
Whenever you are using Viewbag for filling dropdown, Keep your Viewbag and property name different because it will not auto select your selected value.
I have an html helper method for a hidden field. It is bound to a byte[] and I have no problem as it displays the result correctly. But instead of the helper function if I use an html tag, the correct value is not displayed. Instead it displays its type.
following code and image will clarify what I am trying to say.
HTML code:
foreach (var path in Model.PathToImages)
{
<div class="form-group">
<div class="col-sm-6" style="vertical-align:central;">
<input type="button" value="Delete" class="btn btn-primary delete-property" name="#path.ImagePath" />
#Html.HiddenFor(m => path.ConcurrencyCheck)
<input id="#path.ImagePath" name="#path.ImagePath" type="hidden" value="#path.ConcurrencyCheck">
</div>
</div>
}
Property in my model:
public byte[] ConcurrencyCheck { get; set; }
Ignoring the names and id's of the control (this is just to reproduce the problem), following is the html generated:
Now as the image says when i use #Html.HiddenFor(m => path.ConcurrencyCheck) the value is correctly displayed but when I use <input id="#path.ImagePath" name="#path.ImagePath" type="hidden" value="#path.ConcurrencyCheck"> the value is the type System.Byte[].
So why I am not getting the value when I am using the html input tag or the problem is with the way model value should be displayed.
This because byte[] is a a complex array and needs to be converted to Base64String. The Html.HiddenFor() method takes this into account but #path.ConcurrencyCheck does not, and is using the .ToString() method of the properties value to generate the output.
You can view the source code here, but the relevant lines of code are
private static MvcHtmlString HiddenHelper(HtmlHelper htmlHelper, ModelMetadata metadata, object value, bool useViewData, string expression, IDictionary<string, object> htmlAttributes)
{
....
byte[] byteArrayValue = value as byte[];
if (byteArrayValue != null)
{
value = Convert.ToBase64String(byteArrayValue);
}
....
Is it possible to make a HtmlHelper for parts where another htmlherlper is already used.
like in this case:
<div class="control-group">
#Html.LabelFor(model => model.cLinks.Link2Privates)
<div class="controls">
#Html.TextBoxFor(model => model.cLinks.Link2Privates, new { #class = " span7"})
#Html.ValidationMessageFor(model => model.cLinks.Link2Privates)
</div>
</div>
I'm assuming you simply want a helper that will generate all this information for you. While it's technically possible to create a custom helper that will do just that, it's actually better to use editor templates:
Views\Shared\EditorTemplates\BootstrapControlGroup.cshtml
<div class="control-group">
#Html.Label("")
<div class="controls">
#Html.TextBox("", ViewData.TemplateInfo.FormattedModelValue, new { #class = " span7"})
#Html.ValidationMessage("")
</div>
</div>
Form
#Html.EditorFor(m => m.cLinks.Link2Privates, "BootstrapControlGroup")
That basically says to use this template to render the "editor" for this property. If you don't want to have to specify the template name, there's other ways. You can decorate your property with the UIHint attribute:
[UIHint("BoostrapControlGroup")]
public string Link2Privates { get; set; }
Or you can rely on a particular C# type or DataType. For example, if you wanted all strings to be handled this way, you could name that template String.cshtml instead and then just do:
#Html.EditorFor(m => m.cLinks.Link2Privates)
Razor would see that it was a string and use the String.cshtml editor template automatically if it existed. You can also use the DataType attribute, for example:
[DataType(DataType.EmailAddress)]
public string Email { get; set; }
And, then create a Views\Shared\EditorTemplates\EmailAddress.cshtml template. Razor will use this template, then, any time you call Html.EditorFor for this property.
I have created a HtmlHelper extension library that works well with Twitter Bootstrap scenarios.
http://buildmvc.codeplex.com
There's even a HtmlHelper add-on for Twitter Bootstrap Form Groups:
https://www.nuget.org/packages/Build.Mvc.TwitterBootstrap
#using ( Html.BuildForm().Begin(FormRenderStyle.Horizontal) )
{
Html.UpdateFormBuilderContext(ctx =>
{
ctx.RenderValidationMessages = true;
});
<fieldset>
<legend>User Information</legend>
#Html.BuildTextBoxGroupFor(m => m.FirstName, b=> b.TextBox(t=> t.Autofocus()))
#Html.BuildTextBoxGroupFor(m => m.Nickname)
#Html.BuildTextBoxGroupFor(m => m.LastName)
</fieldset>
}
Note: The library expects you to be using TwitterBootstrap > v3. The example above has version 2.3.2 class names.
I'm a little bit confused with Html helpers in MVC3.
I used this syntax when creating my forms before:
#using (Html.BeginForm("action", "controller", FormMethod.Post, new { #class = "auth-form" })) { ... }
this gives me
<form action="/controller/action" class="auth-form" method="post">...</form>
fine, that's what I needed then.
Now I need to pass ReturnUrl parameter to the form, so I can do it like this:
#using (Html.BeginForm("action", "controller", new { ReturnUrl="myurl" } )) { ... }
that would give me
<form action="/controller/action?ReturnUrl=myurl" method="post"></form>
but I still need to pass css class and id to this form and I can't find the way to do it simultaneously passing ReturnUrl parameter.
If I add FormMethod.Post it adds all my parameters as attributes to the form tag, without FormMethod.Post it adds them as query string parameters.
How do I do it?
Thanks.
You can use:
#using (Html.BeginForm("action", "controller", new { ReturnUrl="myurl" }, FormMethod.Post, new { #class = "auth-form" })) { ... }
this will give:
<form action="/controller/action?ReturnUrl=myurl" class="auth-form" method="post">
...
</form>
1-Harder way: define routeValues externally and then use the variable
#{
var routeValues = new RouteValueDictionary();
routeValues.Add("UserId", "5");
// you can read the current QueryString from URL with equest.QueryString["userId"]
}
#using (Html.BeginForm("Login", "Account", routeValues))
{
#Html.TextBox("Name");
#Html.Password("Password");
<input type="submit" value="Sign In">
}
// Produces the following form element
// <form action="/Account/Login?UserId=5" action="post">
2- simpler inline way: Use the Route value internally with Razor
#using (Html.BeginForm("Login", "Account", new { UserId = "5" }, FormMethod.Post, new { Id = "Form1" }))
{
#Html.TextBox("Name");
#Html.Password("Password");
<input type="submit" value="Sign In">
}
// Produces the following form element
// <form Id="Form1" action="/Account/Login?UserId=5" action="post">
Just note that in case you want to add post (FormMethod.Post) or get explicitly it comes after routeValues parameter
official source with good examples