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

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

Related

How did my ViewBag become populated with data?

I'm working with a Kendo Grid that shows a modal when editing or adding a row. I'm seeking to modify the modal and add another dropdown list to it. The one thing I'm totally confused about at the moment is that the cshtml for the modal refers to the ViewBag to provide the source data for the dropdownlists, but I can't find anywhere in the entire solution where any code (anywhere) populates the ViewBag with the properties the modal uses.
Before I started modifying, the cshtml had:
#Html.Kendo().DropDownListFor(model => model.Status).BindTo(ViewBag.Statuses).DataTextField("Name").DataValueField("Value").OptionLabel("Please Select")
^^^^^^^^^^^^^^^^
The debugger says this is valid; the ViewBag does contain a .Statuses and it is loaded with data, but I've no idea how this thing came to be in the ViewBag. The only place the controller refers to the viewbag is in setting the .Title
Here's cshtml for the modal:
#model ModalModel
#Html.HiddenFor(model => model.Id)
<!-- this is the new one -->
<div class="editor-group">
<div class="editor-label">
#Html.LabelFor(model => model.ProjectId)
</div>
<div class="editor-field">
#Html.Kendo().DropDownListFor(model => model.ProjectId).BindTo(ViewBag.ProjectId_Data).OptionLabel("Please Select")
#Html.ValidationMessageFor(model => model.ProjectId)
</div>
</div>
<!-- existing one. Needs DataTextField and DataValueField because model.Statuses is not an IEnumerable<SelectListItem>, its a custom collection of c# enum name/value representation -->
<div class="editor-group">
<div class="editor-field">
#Html.Kendo().DropDownListFor(model => model.Status).BindTo(ViewBag.Statuses).DataTextField("Name").DataValueField("Value").OptionLabel("Please Select")
#Html.ValidationMessageFor(model => model.Status)
</div>
</div>
Here's a snip of the cshtml for the main grid and some periphery stuff:
#model GridModel
<h3>#ViewBag.Title</h3>
#{
var projectListItems = Model.Projects.Select(e => new SelectListItem { Value = e.Id.ToString(), Text = e.Name });
var activityListItems = Model.Activities.Select(e => new SelectListItem { Value = e.Id.ToString(), Text = e.PrivateName });
}
#(Html.Kendo().Grid<UsageModel>()
.Name("MainGrid")
.Columns(cfg =>
{
cfg.Bound(e => e.DateUsed).ClientTemplate("#= kendo.toString(DateUsed, \"d\") #");
cfg.ForeignKey(e => e.ProjectId, projectListItems, "Value", "Text").Title("Project name").Width(150);
cfg.ForeignKey(e => e.ActivityId, activityListItems, "Value", "Text").Title("Activity name").Width(150);
cfg.ForeignKey(e => e.Status, Model.Statuses, "Value", "Name");
cfg.Command(cmd => { cmd.Edit(); cmd.Destroy().HtmlAttributes(new { style = "visibility:hidden" }); }).Width(80);
})
.Pageable()
...
The 4 items in the ViewBag are:
ProjectId_Data (IEnumerable<SelectListItem>)
ActivityId_Data (IEnumerable<SelectListItem>)
Status_Data (IEnumerable<SelectListItem>)
Statuses (IEnumerable<a custom internal type used for expanding enums into name/value strings>)
Am I correct in assuming that Kendo added these things to the viewbag as part of the data binding process on the main grid? The rendering of the grid to page occurs before the processing of the modal..
Please give (IEnumerable) inside BindTo() for casting and try
#using Kendo.Mvc.UI
#using System.Collections
#Html.Kendo().DropDownListFor(model => model.Status).BindTo((IEnumerable)ViewBag.Statuses).DataTextField("Name").DataValueField("Value").OptionLabel("Please Select")

what are those # methods in MVC html?

All those... comands(?) or methods(?) or something... that start with the # in the html code of a page built using the MVC. What exactly are those? Do they have a specific name so I can search a documentation page of them or something like that?
For example:
#model MyImdb.ViewModels.MovieCreateViewModel
<!--- ... some lines ... --->
#{
ViewBag.Title = "Create";
}
<!--- ... some lines ... --->
#using (Html.BeginForm()) { #Html.AntiForgeryToken()
<!--- ... some lines ... --->
#Html.LabelFor(model => model.Title, htmlAttributes: new { #class = "control-label col-md-2" })
<!--- ... some lines ... --->
#Html.EditorFor(model => model.Title, new { htmlAttributes = new { #class = "form-control" } })
<!--- ... some lines ... --->
<div>
#Html.ActionLink("Back to List", "Index")
</div>
# in View basically means that razor code starts from here which is written in c# in asp.net mvc.
There are multiple purposes of it depending on where it is used in view, for example at the first line of the view it is just specifying the binding of the view to a Model/ViewModel to tell that which kind of data it expects.
While if we move down there, it is uses to write C# statements and calling Helper method which generate the html string the render it in the browser.
As an example when you are writing #Html.ActionLink("Back to List", "Index") it will the ActionLink method which will return Html String that will be rendered as html by the razor view engine so in this case it will be anchor tag like Back To List.
This blogpost might be helpful to you and also have a look at this blogpost.
Following is the link to official documentation of razor (Thanks to #Stephen Muecke for referring to this in comments):
https://learn.microsoft.com/en-us/aspnet/core/mvc/views/razor
Hope it give some idea to you.

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.

HtmlHelper inside HtmlHelper

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.

validationmessagefor missing "data-valmsg-for" in editorfor

I have a collection of objects that I'm trying to display, however the span generated by the ValidationMessageFor does not include all the validation attributes:
<span class="field-validation-error">This field is required</span>
instead of:
<span class="field-validation-error" data-valmsg-replace="true" data-valmsg-for="Questions[0].SingleAnswer"></span>
This is how I'm generating the html:
<fieldset id="dr_profileUpdates">
#Html.EditorFor(model => model.Questions)
</fieldset>
And here is my editor template:
#Html.ValidationMessageFor(model => model.SingleAnswer)
#Html.TextBoxFor(model => model.SingleAnswer, new { #class = "textBoxDefault" })
The validation works, however the span does not dissapear after filling out the textbox and focusing out of it - I would assume this is because the span does not get generated correctly
Any help is appreciated.
EDIT: Actually it appears that after posting back to the server and returning the partial view (assuming the ModelState is invalid), those attributes do not get generated again - it only seems to affect the ValidationMessage. Any ideas?
Thanks,
Try add on top of your razor file
#{
Html.EnableUnobtrusiveJavaScript();
}