0

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>
Trident D'Gao
  • 18,973
  • 19
  • 95
  • 159

3 Answers3

1

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.

Mike Koder
  • 1,898
  • 1
  • 17
  • 27
  • Basically you are suggesting to inject a string with a HTML and the render it raw, aren't you? I don't like the idea of pushing html into a string. How would the page view look like? – Trident D'Gao Sep 18 '13 at 21:13
0

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:

  1. Run the BeginMyContainer method at the start of the using statement, writing whatever HTML comes before the custom content
  2. Write the HTML custom content inside of the using statement
  3. 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()

Community
  • 1
  • 1
Timothy Shields
  • 75,459
  • 18
  • 120
  • 173
  • @bonomo I don't know what you mean. This solution is completely flexible. You shouldn't need to be "defining a file for each little piece". – Timothy Shields Sep 18 '13 at 20:41
  • @bonomo I now understand what you want, there was some confusion. See my updated answer. – Timothy Shields Sep 18 '13 at 20:51
  • @bonomo That's not true. MVC **comes with** the `Html.BeginForm` method which does exactly what I'm suggesting. Take a look: http://msdn.microsoft.com/en-us/library/dd492933(v=vs.108).aspx That answer to the question you linked has only 2 upvotes for a reason. – Timothy Shields Sep 18 '13 at 21:00
  • @bonomo Look, you can either take the 100% solution I gave you or piously reject it as "abuse." The fact is there are examples in many Microsoft made technologies (.NET, MVC, etc) where this pattern is used and accepted. – Timothy Shields Sep 18 '13 at 21:08
  • All abusive stuff apart, I don't see how you solution with the BeginContainer would work. Sorry. – Trident D'Gao Sep 18 '13 at 21:19
0

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.

zaparker
  • 485
  • 3
  • 14