HtmlHelper inside HtmlHelper - html

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.

Related

Adding bootstrap CSS class Attribute to textbox ASP.Net MVC

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" })

MVC using different text for form label

I have just created an MVC project and my view looks like this
{
#model Models.LeadModels
ViewBag.Title = "Add a Lead";
}
<h2>#ViewBag.Title.</h2>
<h3>#ViewBag.Message</h3>
<p>#ViewBag.SaveResult</p>
#using (Html.BeginForm("Add", "Lead"))
{
<p>#Html.LabelFor(m => m.FirstName)
#Html.TextBoxFor(m => m.FirstName)</p>
<p>#Html.LabelFor(m => m.LastName)
#Html.TextBoxFor(m => m.LastName)</p>
<p>#Html.LabelFor(m => m.Company)
#Html.TextBoxFor(m => m.Company)</p>
<p>#Html.LabelFor(m => m.Province)
#Html.TextBoxFor(m => m.Province)</p>
<p>#Html.LabelFor(m => m.Telephone)
#Html.TextBoxFor(m => m.Telephone)</p>
<p>#Html.LabelFor(m => m.EmailAddress)
#Html.TextBoxFor(m => m.EmailAddress)</p>
<p>#Html.LabelFor(m => m.LeadStatus)
#Html.TextBoxFor(m => m.LeadStatus)</p>
<p><input type="submit" value="Add Lead" /></p>
}
When this is rendered the labels are the same as my model properties, as in the text says "FirstName"
Questions.
Is there a way to put in a different label (First Name instead of FirstName) using a HTML Helper method?
Are there any neat stuff/tricks we can do to change the display? Such as set the labels to be a fixed width so they all line up?
Is there a way to use a HTML Helper method to output the submit button? Or do I need to manually write the HTML like I am doing?
Is there a way to put in a different label (First Name instead of
FirstName) using a HTML Helper method?
You can use Display attribute data annotation on your property name
public class LeadModels
{
[Display(Name = "First name")]
public string FirstName { set; get; }
}
LabelFor helper method will render the value you provided for the Name attribute
Are there any neat stuff/tricks we can do to change the display? Such
as set the labels to be a fixed width so they all line up?
You can use your css classes style the content. You may also consider using bootstrap css classes which does the alignment in a neat way
Is there a way to use a HTML Helper method to output the submit
button? Or do I need to manually write the HTML like I am doing?
No. There is no helper methods for that. You should write your html tag for the button.
Also, in the asp.net core version, tag helpers are available which allows us to write more HTML style code instead of calling the C# methods in view.

Html.DescriptionFor rendering inside the previous <textarea/>

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>

Generating SelectList with default selected item in MVC

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.

MVC 5, Razor - How to create Edit Template for one property in model

I have a question that feels like it should be easy to answer, but I am not sure where to go with it.
I have several cshtml pages that take different models. But, each of these models has a common property, called WebSiteSK, and the same razor and Kendo UI code that handles that property in each cshtml file. What I want to do is extract this common razor and Kendo UI into an EditerTemplate.
So, I have one cshtml page that takes a Model, which I'll call ModelA. Then, another that takes another model, called ModelB. Both ModelA and ModelB have an integer property called WebSiteSK, which the code that I want extract into an editor template receives.
Here is the code that I want to centralize in an editor template:
<script type="text/x-kendo-tmpl" id="site-droplist-template">
<span>#: data.WebSiteSK # - </span>
<span><b>#: data.SiteName # </b> - </span>
<span>#: data.EnvironmentNK #</span>
<br />
<span>#: data.SiteUrl #</span>
</script>
<div>
#Html.LabelFor(model => model.WebSiteSK, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#(Html.Kendo().DropDownList()
.Name("WebSiteSK_Target")
.DataTextField("SiteName")
.DataValueField("WebSiteSK")
.DataSource(d => d.Read("GetWebSiteList", "Site"))
.Height(300)
.TemplateId("site-droplist-template")
.Filter("contains")
.OptionLabel("Select a site")
.Events(d =>
{
d.DataBound("onSiteBound");
d.Change("onSiteChange");
})
)
#Html.ValidationMessageFor(model => model.WebSiteSK, string.Empty, new { #class = "text-danger" })
</div>
</div>
Does that make sense? Can anyone help me do this?
You can create a base class that contains only the property 'WebSiteSK'. All models that have have this property then should inherit from this base class. Then you can create a partial view '_WebSiteSK' with the code that you want to reuse.
Your models:
public class MyModel : WebSiteSKBaseClass
The partial view must be typed with the base class
#model MyProject.Models.WebSiteSKBaseClass
Finally you can replace the replicated code in all views with:
#Html.Partial("_WebSiteSK")