Hello Orchard 6–Composing view using different content parts
This is the 6th part of my Orchard introduction. As of today I updated my source code to reflect today’s build and also because I need it for certain bug fixes. For more info about my previous posting, check out
In part 5, we talk about composing content type with parts to import commonly used properties such as Owner and Published Date. Now we want to reused UI in our view composition. According to the original walkthrough document on how UI composition works:-
The idea behind UI composition is that the view of a product (in our example, the editor view in the admin panel) should be assembled from the constituent views from all the attached parts. Each part defines its own view templates that expect a specific view model type to be passed to it. The obtain these view models, the Controller action calls the BuildXxxModel methods on ContentManager. The ContentManager delegates to ContentDriver types (one for each part) to retrieve the appropriate view models for each part. The ContentManager then aggregates those into a single view model that is handed back to the Controller. What is actually passed back from the ContentDriver is a ContentPartTemplate object – the view, the model for that view, and the zone and location/position within the zone to render the part (note that the zone/position information will eventually be driven from metadata/templates instead of code).
Let’s get started
1. First of all, add reference to Orchard.Tags and Orchard.Comments
2. Then let’s enable the “Comments” and “Tags” part for our ProductHandler:
using Orchard.Tags.Models;using Orchard.Comments.Models;
public ProductHandler(IRepository<ProductRecord> repository) { Filters.Add(new ActivatingFilter<Product>(ProductHandler.ContentType.Name)); Filters.Add(new ActivatingFilter<Orchard.Core.Common.Models.CommonPart>(ProductHandler.ContentType.Name)); Filters.Add(new ActivatingFilter<CommentsPart>(ProductHandler.ContentType.Name)); Filters.Add(new ActivatingFilter<TagsPart>(ProductHandler.ContentType.Name)); Filters.Add(StorageFilter.For(repository)); }
For now, the association of content parts is done in code. However, eventually this will move to a metadata system that enables association of parts to items on-the-fly, using the Orchard admin panel instead of writing code to do this. This would enable our Product type to be completely independent, with no compile-time dependency on specific parts.
2. Now let’s update the “Create” method of our AdminController to create the ContentItemViewModel for the product:
public ActionResult Create(){ var product = _contentManager.New<Product>("product"); var model = _contentManager.BuildEditorModel(product); return View(model);}
3. We also need to update the “Create.ascx” view to use the “ContentItemViewModel” and dispatch the form composition to the Orchard HtmlHelper.
<%@ Control Language="C#" Inherits="Orchard.Mvc.ViewUserControl<Orchard.Mvc.ViewModels.ContentItemViewModel<Orchard.Commerce.Models.Product>>" %><h1> <%=Html.TitleForPage(T("Create Product").ToString()) %></h1><% using (Html.BeginFormAntiForgeryPost()) { %><%=Html.ValidationSummary() %><%=Html.EditorForModel(Model)%><fieldset> <input type="submit" value="Create" /></fieldset><% } %><div> <p> <%=Html.ActionLink("Back to List", "Index") %></p></div>
4. Next, we will define a driver and template for the Product type that participates in this UI composition process.
Create a new Drivers folder and then add a new ProductDriver class (that derives from ContentItemDriver). This class is responsible for returning the appropriate view model, template, and location for the various views of a Product (in our example, the editor view):
using System;using System.Collections.Generic;using System.Linq;using System.Web;using Orchard.ContentManagement.Drivers;using Orchard.Commerce.Models;using Orchard.ContentManagement;
namespace Orchard.Commerce.Drivers{ public class ProductDriver : ContentItemDriver<Product> {
protected override bool UseDefaultTemplate { get { return true; } }
protected override DriverResult Display(Product product, string displayType) { return ContentPartTemplate(product, "Product.Fields").Location("primary", "1"); }
//GET protected override DriverResult Editor(Product product) { return ContentPartTemplate(product, "Product.Fields").Location("primary", "3"); }
//POST protected override DriverResult Editor(Product part, IUpdateModel updater) { updater.TryUpdateModel(part, Prefix, null, null); return Editor(part); } }}
This code says that the template to use for displaying the product is “Product.Fields”. Notice how we pass the “products” instance as the view model for that template. We also mention it’s going to be displayed in the “primary” zone, at the top of the zone (position=”1”).
5. Let’s now create “Product.Fields.ascx” under the Commerce > Views > EditorTemplates folder. This is essentially the same as our former “Create” view, which displayed the editors for properties for the Product type.
![]()
<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<Orchard.Commerce.Models.Product>" %><fieldset> <legend>Fields</legend> <%=Html.HiddenFor(model=>model.Id) %> <div class="editor-label"> <%= Html.LabelFor(model => model.Sku) %> </div> <div class="editor-field"> <%= Html.TextBoxFor(model => model.Sku)%> <%= Html.ValidationMessageFor(model => model.Sku)%> </div> <div class="editor-label"> <%= Html.LabelFor(model => model.Description)%> </div> <div class="editor-field"> <%= Html.TextBoxFor(model => model.Description)%> <%= Html.ValidationMessageFor(model => model.Description)%> </div> <div class="editor-label"> <%= Html.LabelFor(model => model.Price) %> </div> <div class="editor-field"> <%= Html.TextBoxFor(model => model.Price) %> <%= Html.ValidationMessageFor(model => model.Price) %> </div></fieldset>
6. Type Ctrl-F5 to build and run the site.
Now when we click on “Create new Product” link, we see a form which contains the fields of the products, but also tags, comments and owner fields.
7. The last thing we need to do is re-implement the “Create” “Post” action in AdminController.cs:
[HttpPost] public ActionResult Create(FormCollection input) { var product = _contentManager.New<Product>("product"); var model = _contentManager.UpdateEditorModel(product, this);
if (!ModelState.IsValid) { return View(model); }
_contentManager.Create(product); return RedirectToAction("Index"); }
Note the “this” parameter, passed to UpdateEditorModel. To enable validation of the model, we need an interface between the MVC controller and the content drivers. We are going to implement this interface in the “AdminController” itself, by simply delegating the default MVC base class.
using Orchard.Localization;
namespace Orchard.Commerce.Controllers{ [Admin] [Themed] public class AdminController : Controller, IUpdateModel { bool IUpdateModel.TryUpdateModel<TModel>(TModel model, string prefix, string[] includeProperties, string[] excludeProperties) { return TryUpdateModel(model, prefix, includeProperties, excludeProperties); }
void IUpdateModel.AddModelError(string key, LocalizedString errorMessage) { ModelState.AddModelError(key, errorMessage.ToString()); }
The update/validation of the model is going through the ContentManager, to all the content drivers, and then back to our AdminController. The last thing we need to do is implement the model update/validation for the “Product” part. We do that in the ProductDriver:
protected override DriverResult Editor(Product part, IUpdateModel updater){ updater.TryUpdateModel(part, Prefix, null, null); return Editor(part);}
8. Type Ctrl-F5 to build and run the site.
We can now create a new product, including model validation!
3 Responses to “Hello Orchard 6–Composing view using different content parts”
RSS feed for comments on this post. TrackBack URL

Got through this one, one more to go! Thanks
Comment by Gareth Mark Elms — August 30, 2010 @ 6:11 am
Great blog post series… thanks.
Slight error above for other readers to note… Create.ascx should have Html.EditorForItem(Model) not Html.EditorForModel(Model) as above
Comment by Jeff — September 11, 2010 @ 8:40 pm
3. Views\Admin\Create.ascx
Should be
Comment by wiira — September 19, 2010 @ 2:18 am