UPDATE:
More general question what is the way to make a higher-order composition of views? The same way you pass a delegate into a method.
ORIGINAL QUESTION:
I have a page view and a control as a partial view. From the page view I render the control using Html.Partal("MyControl", myControlModel). Now this control has some areas that I wish were customizable from the page view. So that if the control is rendered from a different page these areas are filled with different content. Basically what I am looking for is a way to inject a piece of HTML from the page view into a partial view. Can I do it in MVC? If so, how?
Example:
Page view:
<div class="page">
#Html.Partial("MyControl", myControlModel, #<text>My <b>custom</b> piece of HTML which is different for each page<text>)
</div>
My control view:
<div class="my-control">
<div class="common-part-for-all-pages">
#Model.Value
</div>
<div class="part-that-has-to-be-customized">
#* there must be a piece of HTML provided somehow from the page view *#
</div>
</div>
Expected result:
<div class="page>
<div class="my-control">
<div class="common-part-for-all-pages">
#Model.Value
</div>
<div class="part-that-has-to-be-customized">
My <b>custom</b> piece of HTML which is different for each page
</div>
</div>
</div>
Add new properties to the viewmodel of the partial: "TemplateName" and "TemplateModel". Then do
<div class="my-control">
<div class="common-part-for-all-pages">
#Model.Value
</div>
<div class="part that has to be customized">
#Html.Partial(Model.TemplateName, Model.TemplateModel)
</div>
</div>
Or you could just add a string property "Template" and do
<div class="my-control">
<div class="common-part-for-all-pages">
#Model.Value
</div>
<div class="part that has to be customized">
#Html.Raw(Model.Template)
</div>
</div>
Call it like this
#{
// just set the property
myControlModel.Template = "some html";
myControlModel.Template = Html.TextBox(/*...*/).ToString();
myControlModel.Template = Template("hello").ToString();
}
#Html.Partial("MyControl", myControlModel)
#helper Template(string text)
{
<span>#text</span>
}
ToString() isn't necessary if MvcHtmlString type is used.
You'd need to create controller actions for this, but you can use #Html.Action("MyAction", "MyController", myModelObject) and pass any parameters from the page to partial view in the myModelObject parameter. It can be a bit of overkill but if your control/partial view needs to do any special C# code then this way works pretty well.
Make a class PartialModel, give it two properties string Name and object Model, then use #Html.Partial(pm.Name, pm.Model) in your partial view.
If you want to put different HTML inside every time, the above won't work, so read on.
You can use something similar to Html.BeginForm:
#using (Html.BeginMyContainer())
{
<h3>Hi!</h3>
<p>This is some custom content.</p>
}
(This would be a BeginMyContainer extension method on the HtmlHelper class.)
Your Html.BeginMyContainer method should return a class inheriting from MvcForm, which is IDisposable. In the BeginMyContainer method you'll write whatever HTML comes before the custom content of your container, and in the Dispose method of the returned MvcForm you'll write whatever HTML comes after your custom content.
When Razor processes the code I have above, it will:
Run the BeginMyContainer method at the start of the using statement, writing whatever HTML comes before the custom content
Write the HTML custom content inside of the using statement
Call Dispose on the MvcForm at the end of the using statement, writing whatever HTML comes before the custom content
Related: rolling my own #Html.BeginfBrm()
Related
I am having trouble with a simple Visual Basic Code for MVC using Check Boxes.
I am trying to create a Form where the user could choose which Office Department will have access to a particular Document.
Here is the output in the browser:
The data gets displayed correctly from the Model to Controller to View.
However, I cannot get the clicked values from the View back to the Controller.
Here is the Model:
Public Class Department
<Required>
<Display(Name:="Department ID")>
Public Property DepartmentID As Integer = 1
<Required>
<Display(Name:="Department Name")>
Public Property DepartmentName As String = ""
<Required>
<Display(Name:="Department Image")>
Public Property DeptImage As String = ""
Public Property IsSelected As Boolean = False
End Class
Here is the View
<section class="checkBoxListFor">
<div class="panel panel-primary">
<div class="panel-heading">
<h3 class="panel-title">Access Rights</h3>
</div>
<div class="panel-body">
#For Each oDept As Department In Model.DeptList
#<text>
#Html.CheckBoxFor(Function(m) oDept.IsSelected)
#oDept.DepartmentName
#Html.HiddenFor(Function(m) oDept.DepartmentID)
#Html.HiddenFor(Function(m) oDept.DepartmentName) <br />
</text>
Next
</div>
</div>
</section>
I have read an article here about NOT using a For Each Loop to display the collection of Form Controls since when you look at the generated HTML Code, the "name" will not be correct and hence it won't bind to the Controller when POST Back is called.
The problem is I do not know how to do it.
Any help is appreciated on how to properly implement this simple task.
Thanks!
Ok, I found the problem.
It is because of the
<fieldset>
</fieldset>
tag.
The checkbox collection loop is outside of these tags.
Although all of the fields and the Check Boxes are under the html helper "BeginForm"
the checkboxes loop is outside of the tag.
This is the reason why these checkbox's values were never passed back to the controller during post back.
I put the loop inside the tag and viola, they are now populated on the "POST" controller.
I am using
<div class="row">
<div class="col-lg-12 breadcrumb" >
#Html.MvcSiteMap().SiteMapPath("")
</div>
</div>
to display the sitemap of current page. How do i check if the Sitemap exists for the Current page and then only display it?
like:
#if([Check Exists]){
<div class="row">
<div class="col-lg-12 breadcrumb" >
#Html.MvcSiteMap().SiteMapPath("")
</div>
</div>
}
By default, the SiteMapPath HTML helper already does this check. If there is no node that corresponds to the current request the SiteMap.CurrentNode property will be null. When that happens, no HTML will be output from the SiteMapPath HTML helper.
If that isn't good enough to cover your particular use case, you can use the built-in FilteredSiteMapNodeVisibilityProvider, a custom visibility provider, or security trimming to hide the nodes you don't want visible.
Failing that, you could create a custom partial view template for the SiteMapPath or create a custom HTML helper based on the SiteMapPath if nothing else meets your needs.
How does one conditionally render an HTML element in Razor 2?
For instance, suppose I had the tag
<div class="someclass">
<p>#somevalue</p>
</div>
And I wanted to suppress the <-div-> tag from rendering if the value of #somevalue was equal to 1. Is there a simple way to do this in Razor similar to how I might "hide" the <-div-> tag with Knockout.js in a browser, where I might :
<div class="someclass" data-bind="showWhenTrue: someValue != 1">
<p data-bind="text: someValue"></p>
</div>
At the moment, the best Razor alternative I have is to do this:
#if (someValue != 1) {
<div class="someclass">
<p>#somevalue</p>
</div>
}
There are many ways to do this. First, it should be noted that your knockout code doesn't actually remove the html from output, it just sets its display to hidden.
The razor code you have actually removes the code from the rendered HTML, so that's a very different thing.
To answer your question, we need to know what it is you're trying to achieve. If you just want to hide the display, you can simply do something like this:
<div class="someclass" style="display: #{ somevalue == 1 ? #:"none" : #:"block" };">
<p>#somevalue</p>
</div>
You could also do it with a class:
<div class="someclass #{ somevalue == 1 ? #:"HideMe" : #:"ShowMe" }">
<p>#somevalue</p>
</div>
If you want to remove the code from the output, then you can just do what you've done.. i'm mot sure what you find so objectionable about it... but if you want other alternatives, you could create an Html helper, you could use a razor helper, you could use a Display or EditorTemplate....
The list is actually quite long and i'm just scratching the surface...
An elegant (and re-usable) solution is to write an extension method for Html to do conditional rendering of text ( a bit like IF() in Excel) - e.g.
public static MvcHtmlString ConditionalRender(this HtmlHelper helper, bool condition, string trueString, string falseString = "")
{
return MvcHtmlString.Create((condition) ? trueString : falseString);
}
You can then use it in your code as such:
<div class="someclass" style="display: #Html.ConditionalRender(somevalue == 1, "none","block")">
<p>#somevalue</p>
</div>
I am working with mvc4 and displaying data from my model in to cshtml views.
When setting data in to the markup, I adding it in to div tags.
Is there a way in mvc that if the model property is not set, dont display the div?
Sample of my markup
<div class="myclass"> #Model.Text </div>
You can test for a value being set like so:
#if (!string.IsNullOrEmpty(Model.Text))
{
<div class="myclass"> #Model.Text </div>
}
Update: If you want to incorporate the logic for whether or not to render an element based on its value, you could create a Custom HTML Helper method.
How about wrapping it in a null check
#{
if (#Model.Text != null)
{
<div class="myclass"> #Model.Text </div>
}
}
I have one Asp.net MVC3 page which renders details of a Mobile. This page also hosts a comment form which is rendered using Comment model. One field named Name i.e Mobile.Name and Comment.Name is common in both the models.
In the final output I always see Comment.Name text box filled with the value of Mobile.Name. Not sure why this is happening.
Exact problem can be seen at below link.. check the comments tab
problem url
Edit
Comment form is rendered using a partial view which is something like below :
#using (Ajax.BeginForm("Savecomment", new AjaxOptions() { UpdateTargetId = "FormContainer" , OnSuccess = "$.validator.unobtrusive.parse('form');" }))
{
#Html.HiddenFor(m => m.MobileId)
<div class="row">
<div class="five columns">
<label >Your name:</label>
#Html.TextBox("Name"," ")
#Html.ValidationMessage("Name")
</div>
</div>
Your problem is more than likely related to how you are rendering you Partial View. I am assuming you have a parent model that contains both Mobile and Comment? You are passing the Comment model to the Partial View? Because of this, the partial view is rendering the name of the field as if it were the primary model.
I would suggest using an EditorTemplate to render your Comment and Mobile rather than partial views. Editor Templates are more aware of their context than Partial Views are.