Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.
MVC
Purée
Codegarde
n
June 2014
Just to introduce myself…
• I’m Andy Butland
• I work for Zone, a digital agency where I head up the
.NET development team...
And what are we talking about… “MVC
Purée”?
• We’ll be discussing best practices with using MVC in
Umbraco:
• “For the ses...
Contents
1
2
3
6
4
MVC best practices 5
7
Using MVC with Umbraco
Strongly typed views and
mapping from Umbraco
data
Cleani...
1. MVC best practices
Journey to Umbraco MVC
“Classic”
ASP
ASP.Net
Webforms
ASP.Net
MVC
Umbraco
(XSLT)
Umbraco
(Razor)
Umbraco
(MVC)
Learnings a...
MVC patterns and practices
MODEL
CONTROLLER
VIEW
VIEW
MODELS
Simple, strongly typed views,
with no domain logic Custom vie...
2. Using MVC with Umbraco
MVC techniques with Umbraco
• Since version 4.10 we’ve been able to use MVC as well
as traditional Web Forms for rendering...
Logic and querying in the views
• This technique is most similar to that used in traditional
Umbraco templating and can be...
Example: using the Umbraco helper in our
view@inherits Umbraco.Web.Mvc.UmbracoTemplatePage
<h1>@Model.Content.GetPropertyV...
Surface controller actions and partials
• We can prepare and pass our own view model by
making an @Html.Action call from w...
Example: using a surface controller and
partial@inherits Umbraco.Web.Mvc.UmbracoTemplatePage
@{
Layout = "_Layout.cshtml";...
Route hijacking
• We can intercept the request with our own controller,
populate a custom view model and pass it to the
st...
Example: route hijacking
using System.Web.Mvc;
using Umbraco.Web.Mvc;
public class EventPageController : RenderMvcControll...
3. Strongly typed views and
mapping from Umbraco data
Custom view model
• We can create a simple POCO class to represent our
view
• In most cases will represent a single docume...
Example: custom view model (definition)
public class NewsLandingPageViewModel
{
public string Heading { get; set; }
public...
Example: custom view model (mapping)
public ActionResult NewsLandingPage()
{
var vm = new NewsLandingPageViewModel
{
Headi...
Using the “Umbraco Mapper” package
• The package has been created to streamline the
mapping of Umbraco content to view mod...
Example: mapping using conventions
public ActionResult NewsLandingPage()
{
var vm = new NewsLandingPageViewModel();
var la...
Example: overriding conventions
mapper.Map(CurrentPage, vm, new Dictionary<string,PropertyMapping>
{
{
"Copy", new Propert...
Example: a custom mapping (1)
public class GeoCoordinate
{
public decimal Longitude { get; set; }
public decimal Latitude ...
Example: a custom mapping (2)
public static object MapGeoCoordinate(IUmbracoMapper mapper,
IPublishedContent contentToMapF...
4. Dependency injection
Dependency injection: what and why?
• By injecting our dependencies to a class, rather than
“newing” them up within one, w...
Example: injecting services to a controller
public class HomePageController : BaseController
{
private readonly IDataServi...
Example: integrating Ninject with Umbraco
PM> Install-Package Ninject.MVC3 Install Ninject from NuGet
Creates a file Ninje...
5. Unit testing
Isolating our unit under test from
dependencies• When unit testing, we aim to confirm the function of a
particular method ...
Example: using mocks (with Moq)
[TestMethod]
public void WebServiceTests_SuccessResponse_ReturnsStatusAndMessage()
{
// Ar...
Example: using mocks (with Moq) (2)
private static IHttpClient MockHttpClient()
{
var mock = new Mock<IHttpClient>();
mock...
Unit testing with Umbraco
• We can use various techniques to control our
dependencies, e.g. mocks and stubs
• Umbraco does...
Example: unit testing a surface controller
[HttpPost]
public ActionResult CreateComment(CommentViewModel model)
{
if (!Mod...
Example: unit testing a surface controller (2)
[TestMethod]
public void CreateComment_WithValidComment_RedirectsWithMessag...
Example: testing a “command handler” class
public class BlogPostSurfaceControllerCommandHandler
{
public ModelStateDiction...
Example: testing a “command handler” class
(2)public class BlogPostSurfaceController : SurfaceController
{
BlogPostSurface...
Example: testing a “command handler” class
(3)[TestMethod]
public void CreateComment_WithValidComment_ReturnsTrueWithMessa...
Using the Umbraco core base test classes
• Allow us to test our surface controller without
modification:
• We need to clon...
Example: base test classes
[TestFixture]
[DatabaseTestBehavior(DatabaseBehavior.NoDatabasePerFixture)]
public class BlogPo...
Working around issues with extension
methods
• Some Umbraco methods – particularly on
IPublishedContent – are implemented ...
Example: can’t mock IPublishedContent
methodprivate static IPublishedContent MockIPublishedContent()
{
var mock = new Mock...
Example: using Microsoft Fakes
[TestMethod]
public void MapFromIPublishedContent_MapsCustomProperties()
{
using (ShimsCont...
6. Cleaning up views
Strongly typed partials
• Often our HTML will contain blocks of similar layout that
are repeated on multiple pages
• We ca...
Example: custom view model for partial
public class HeroSectionViewModel
{
public string Heading { get; set; }
public stri...
Example: view models with inheritance
public abstract class BaseViewModel
{
public string Heading { get; set; }
public str...
Example: view models with interfaces (1)
public interface IHeroSection
{
string Heading { get; }
string Standfirst { get; ...
Example: view models with interfaces (2)
@model CodegardenSamples.Models.HomePageViewModel
@Html.Partial("_HeroSection")
@...
7. Wrap up and Q &A
In summary
• No one true way to build an Umbraco site… pick
what works for you and your team
• If you like the “purist” MV...
Lastly, some thanks…
• Neil, Ali, Rob, Raffaele, Nnamdi, Ollie and the rest of my
colleagues at Zone
• Numerous discussion...
MVC Puree - Approaches to MVC with Umbraco
Upcoming SlideShare
Loading in …5
×

MVC Puree - Approaches to MVC with Umbraco

1,945 views

Published on

Slides from talk given at the Umbraco Codegarden Festival 2014 on using MVC with Umbraco, with a focus on the use of route hijacking, strongly typed models and mapping.

Published in: Technology
  • Be the first to comment

MVC Puree - Approaches to MVC with Umbraco

  1. 1. MVC Purée Codegarde n June 2014
  2. 2. Just to introduce myself… • I’m Andy Butland • I work for Zone, a digital agency where I head up the .NET development team in London • We develop web sites and applications primarily using ASP.Net MVC and Umbraco • Blog (sporadically) at http://web-matters.blogspot.it/ • Find here a copy of slides and links to various resources • Contact: abutland73@gmail.com / @andybutland
  3. 3. And what are we talking about… “MVC Purée”? • We’ll be discussing best practices with using MVC in Umbraco: • “For the session title, how about ‘MVC purist’?” • “Hmm, don’t like the sound of that… makes me sound too much of a pedant, and we all have to be pragmatic too.” • “OK… we’ll go with ‘MVC purée’”
  4. 4. Contents 1 2 3 6 4 MVC best practices 5 7 Using MVC with Umbraco Strongly typed views and mapping from Umbraco data Cleaning up views Dependency injection Unit testing Wrap up and Q & A
  5. 5. 1. MVC best practices
  6. 6. Journey to Umbraco MVC “Classic” ASP ASP.Net Webforms ASP.Net MVC Umbraco (XSLT) Umbraco (Razor) Umbraco (MVC) Learnings and best practices…
  7. 7. MVC patterns and practices MODEL CONTROLLER VIEW VIEW MODELS Simple, strongly typed views, with no domain logic Custom view models for each view Mapping from domain models to view models Application components supported with unit tests and composed via dependency injection COMPONEN T Separation of concerns
  8. 8. 2. Using MVC with Umbraco
  9. 9. MVC techniques with Umbraco • Since version 4.10 we’ve been able to use MVC as well as traditional Web Forms for rendering Umbraco templates. • MVC rendering has been implemented in a flexible way, giving a lot of scope to the developer in building their application. • Logic, querying and data access in the views • Using surface controllers actions with partial views • Hijacking routes
  10. 10. Logic and querying in the views • This technique is most similar to that used in traditional Umbraco templating and can be used be all developers, not just those using Visual Studio. • It’s not ideal though from a more purist MVC perspective. UMBRACO DEFAULT CONTROLLERRequest TEMPLATE/ VIEWPopulates and passes a standard RenderModel Within the view we can use the Umbraco helper to: • Access page properties • Query for other nodes • Run Examine searches
  11. 11. Example: using the Umbraco helper in our view@inherits Umbraco.Web.Mvc.UmbracoTemplatePage <h1>@Model.Content.GetPropertyValue("heading")</h1> @{ var rootNode = Umbraco.TypedContentAtRoot().First(); var categoryNodes = rootNode .Descendants("CategoryFolder") .First() .Children; foreach (var item in categoryNodes) { <div>@item.GetPropertyValue("title")</div> } } Property access not obvious for front-end developers less familiar with Umbraco APIs. The Umbraco helper provides access to functions that arguably should not be the concern of the view.
  12. 12. Surface controller actions and partials • We can prepare and pass our own view model by making an @Html.Action call from within our template, to a surface controller that returns a partial. • It’s downside is a more complex request flow, and the necessity of creating two templates. UMBRACO DEFAULT CONTROLLERRequest TEMPLATE/ VIEW SURFACE CONTROLLER ACTION PARTIAL VIEW
  13. 13. Example: using a surface controller and partial@inherits Umbraco.Web.Mvc.UmbracoTemplatePage @{ Layout = "_Layout.cshtml"; } @Html.Action("Register", "AccountSurface") [ChildActionOnly] public PartialViewResult Register() { var vm = new RegisterViewModel(); PopulateRegisterViewModel(vm); return PartialView("Register", vm); } @model RegisterViewModel <h1>@Model.Heading</h1> Our Umbraco template view calls out to a surface controller action method… … which populates a custom view model and passes this to a partial view… … that can benefit from strong typing.
  14. 14. Route hijacking • We can intercept the request with our own controller, populate a custom view model and pass it to the strongly typed view. • We now have a very clean view with little logic, and proper separation of concerns from an MVC perspective. • There is a little more work to do from a developer perspective, but we can mitigate that… CUSTOM CONTROLLER Request TEMPLATE/ VIEWPopulates and passes a custom view model
  15. 15. Example: route hijacking using System.Web.Mvc; using Umbraco.Web.Mvc; public class EventPageController : RenderMvcController { public ActionResult EventPage() { // Populate view model and // render view } } The controller name must match the document type alias, e.g. EventPage The action method name must match the template name BETTER STILL… see Hybrid Framework method of inheriting from SurfaceController and implementing IRenderMvcController
  16. 16. 3. Strongly typed views and mapping from Umbraco data
  17. 17. Custom view model • We can create a simple POCO class to represent our view • In most cases will represent a single document type • However often we’ll want to pull in data from other nodes, or even other data sources • We’ll map the Umbraco content to our view model • Our view can reference that as it’s @model • It’s now simply displaying properties and collections from the view model, with no business logic or data access • Actually need have no reference to Umbraco at all… • …unless you want it of course. You can still inherit from the Umbraco base page if you need to.
  18. 18. Example: custom view model (definition) public class NewsLandingPageViewModel { public string Heading { get; set; } public IHtmlString BodyText { get; set; } public IList<NewsItem> LatestNewsItems { get; set; } } Directly mapped from fields on the document type Generated from a node query
  19. 19. Example: custom view model (mapping) public ActionResult NewsLandingPage() { var vm = new NewsLandingPageViewModel { Heading = CurrentPage.GetPropertyValue<string>("heading"), BodyText = CurrentPage.GetPropertyValue<IHtmlString>("bodyText"), LatestNewsItems = CurrentPage.Descendants("NewsItem") .OrderByDescending(x => DateTime.Parse( x.GetPropertyValue<string>(“publicationDate"))) .Take(10) .Select(x => new NewsItem { Heading = CurrentPage.GetPropertyValue<string>("heading"), PublicationDate = DateTime.Parse( x.GetPropertyValue<string>("publicationDate"))), }) .ToList(), }; return View("NewsLandingPage", vm); }
  20. 20. Using the “Umbraco Mapper” package • The package has been created to streamline the mapping of Umbraco content to view models • Convention based mapping from single instances and collections of IPublishedContent • Ability to override those conventions for particular properties as required • Mapping from other sources such as XML and JSON • Supply of custom mapping methods for handling custom types
  21. 21. Example: mapping using conventions public ActionResult NewsLandingPage() { var vm = new NewsLandingPageViewModel(); var latestNewsNodes = CurrentPage.Descendants("NewsItem") .OrderByDescending(x => DateTime.Parse( x.GetPropertyValue<string>(“publicationDate"))) .Take(10); var mapper = new UmbracoMapper(); mapper.Map(CurrentPage, vm) .MapCollection(latestNewsNodes, vm.LatestNewsItems); return View("NewsLandingPage", vm); }
  22. 22. Example: overriding conventions mapper.Map(CurrentPage, vm, new Dictionary<string,PropertyMapping> { { "Copy", new PropertyMapping { SourceProperty = "bodyText", } } }) .MapCollection(latestNewsNodes, vm.LatestNewsItems, new Dictionary<string,PropertyMapping> { { "Category", new PropertyMapping { SourceProperty = "Name", LevelsAbove = 1, } } }); Maps from property with different name Maps from node at a higher level in the tree. COMING SOON… attribute based property mappings
  23. 23. Example: a custom mapping (1) public class GeoCoordinate { public decimal Longitude { get; set; } public decimal Latitude { get; set; } public int Zoom { get; set; } } ... var mapper = new UmbracoMapper(); mapper.AddCustomMapping(typeof(GeoCoordinate).FullName, CustomMappings.MapGeoCoordinate);
  24. 24. Example: a custom mapping (2) public static object MapGeoCoordinate(IUmbracoMapper mapper, IPublishedContent contentToMapFrom, string propName, bool isRecursive) { var propertyValueAsCsv = contentToMapFrom .GetPropertyValue<string>(propName, isRecursive, null); if (!string.IsNullOrEmpty(propertyValueAsCsv)) { var parts = propertyValueAsCsv.Split(','); if (parts != null && parts.Length == 3) { return new GeoCoordinate { Latitude = decimal.Parse(parts[0]), Longitude = decimal.Parse(parts[1]), Zoom = int.Parse(parts[2]), }; } } return null; }
  25. 25. 4. Dependency injection
  26. 26. Dependency injection: what and why? • By injecting our dependencies to a class, rather than “newing” them up within one, we: • Program to interfaces – improving the testability of our code • Reduce coupling • Develop components with single responsibilities • An IoC container can then help us with the instantiation of the concrete classes at runtime
  27. 27. Example: injecting services to a controller public class HomePageController : BaseController { private readonly IDataService _dataService; public HomePageController(IDataService dataService) { _dataService = dataService; } public ActionResult HomePage() { // Do something with the data service var data = _dataService.GetData(); ... } } An instance of IDataService - as well as any dependencies it may have - is injected into the controller’s constructor at run-time.
  28. 28. Example: integrating Ninject with Umbraco PM> Install-Package Ninject.MVC3 Install Ninject from NuGet Creates a file NinjectWebCommon.cs in App_Start private static void RegisterServices(IKernel kernel) { kernel.Bind<IDataService>().To<MyDataService>(); } Any time a component “requests” an IDataService, they’ll get a concrete DataService
  29. 29. 5. Unit testing
  30. 30. Isolating our unit under test from dependencies• When unit testing, we aim to confirm the function of a particular method or class, by replacing any dependencies it has with versions that are under the control of our tests • We avoid brittle data or slow running processes • We isolate our tests to just the small piece being examined UNIT UNDER TEST DEPENDENT CLASS DEPENDENT CLASS … which allows us to we replace them with mocks or stubs we control Dependencies are referenced through interfaces…
  31. 31. Example: using mocks (with Moq) [TestMethod] public void WebServiceTests_SuccessResponse_ReturnsStatusAndMessage() { // Arrange var service = new WebServiceWrapper(MockHttpClient(), "http://www.example.com/"); // Act var result = service.Request("exchange-rates", ResponseContentType.JSON).Result; // Assert Assert.IsTrue(result.Success); Assert.IsNotNull(result.Response); Assert.IsNull(result.ErrorMessage); } Within the test, we are avoiding calling the external web service directly by mocking the HTTP call
  32. 32. Example: using mocks (with Moq) (2) private static IHttpClient MockHttpClient() { var mock = new Mock<IHttpClient>(); mock.Setup(x => x.GetAsync(It.IsAny<string>())) .Returns(Task<HttpResponseMessage>.Factory.StartNew(() => { var response = new HttpResponseMessage(HttpStatusCode.OK); response.Content = new StringContent(@"{ 'rates': [{ 'currencyCode': 'EUR', 'rate': 1.24 }]}";); return response; })); mock.Setup(x => x.GetAsync(It.Is<string>(y => y.Contains("invalid")))) .Returns(Task<HttpResponseMessage>.Factory.StartNew(() => { return new HttpResponseMessage(HttpStatusCode.BadRequest); })); return mock.Object; } At runtime we create our own “HttpClient” by mocking the interface and return fixed responses.
  33. 33. Unit testing with Umbraco • We can use various techniques to control our dependencies, e.g. mocks and stubs • Umbraco doesn’t make it particularly easy… • Problems with unit testing surface controllers • Issues with extension and static methods • … but be no means impossible • Avoid the problem - move logic into a separate class, leaving a simple controller of little value to test • Utilise the base test classes • Look into MS Fakes
  34. 34. Example: unit testing a surface controller [HttpPost] public ActionResult CreateComment(CommentViewModel model) { if (!ModelState.IsValid) { return CurrentUmbracoPage(); } TempData.Add("CustomMessage", "Thanks for your comment."); return RedirectToCurrentUmbracoPage(); } If validation fails, should return to view. If successful, should have value in TempData and redirect
  35. 35. Example: unit testing a surface controller (2) [TestMethod] public void CreateComment_WithValidComment_RedirectsWithMessage() { // Arrange var controller = new BlogPostSurfaceController(); var model = new CommentViewModel { Name = "Fred", Email = "fred@freddie.com", Comment = "Can I test this?", }; // Act var result = controller.CreateComment(model); // Assert Assert.IsNotNull(result); } Will fail, with null reference for UmbracoContext
  36. 36. Example: testing a “command handler” class public class BlogPostSurfaceControllerCommandHandler { public ModelStateDictionary ModelState { get; set; } public TempDataDictionary TempData { get; set; } public bool HandleCreateComment(CommentViewModel model) { if (!ModelState.IsValid) { return false; } TempData.Add("CustomMessage", "Thanks for your comment."); return true; } } Logic moved to class with no Umbraco dependency…
  37. 37. Example: testing a “command handler” class (2)public class BlogPostSurfaceController : SurfaceController { BlogPostSurfaceControllerCommandHandler _commandHandler; public BlogPostSurfaceController() { _commandHandler = new BlogPostSurfaceControllerCommandHandler(); _commandHandler.ModelState = ModelState; _commandHandler.TempData = TempData; } [HttpPost] public ActionResult CreateCommentWithHandler(CommentViewModel model) { if (!_commandHandler.HandleCreateComment(model)) { return CurrentUmbracoPage(); } return RedirectToCurrentUmbracoPage(); } } … which is referenced in the surface controller… … leaving a thin controller method, with little value for testing.
  38. 38. Example: testing a “command handler” class (3)[TestMethod] public void CreateComment_WithValidComment_ReturnsTrueWithMessage() { // Arrange var handler = new BlogPostSurfaceControllerCommandHandler(); handler.ModelState = new ModelStateDictionary(); handler.TempData = new TempDataDictionary(); var model = new CommentViewModel { Name = "Fred", Email = "fred@freddie.com", Comment = "Can I test this?", }; // Act var result = handler.HandleCreateComment(model); // Assert Assert.IsTrue(result); Assert.IsNotNull(handler.TempData["CustomMessage"]); } The logic in the handler though, can now be tested.
  39. 39. Using the Umbraco core base test classes • Allow us to test our surface controller without modification: • We need to clone the source code, build and reference the Umbraco.Tests.dll in our project • We have to use NUnit • We then have access to some base classes we can inherit from, to run tests with the appropriate contexts set up • But even then, there are some reflection hoops to jump through • Once done though, we can successfully run tests on Umbraco surface controllers
  40. 40. Example: base test classes [TestFixture] [DatabaseTestBehavior(DatabaseBehavior.NoDatabasePerFixture)] public class BlogPostSurfaceControllerTests : BaseRoutingTest { [Test] public void ExampleTest() { var controller = GetController(); var model = new CommentViewModel { Email = "fred@freddie.com", Comment = "Can I test this?", }; var result = controller.CreateComment(model); var redirectResult = result as RedirectToUmbracoPageResult; Assert.IsNotNull(redirectResult); Assert.AreEqual(1000, redirectResult.PublishedContent.Id); Assert.IsNotNull(controller.TempData["CustomMessage"]); } } We can test for specific Umbraco results and other controller actions. Using base class methods and reflection, the controller can be instantiated with necessary contexts. The test class inherits from the Umbraco base test class.
  41. 41. Working around issues with extension methods • Some Umbraco methods – particularly on IPublishedContent – are implemented as extension methods • These can’t be mocked or stubbed • Using MS Fakes • We can add a fakes assembly for any dll • And at runtime replace a method’s implementation with one of our own • VS.Net Premium or Ultimate only though 
  42. 42. Example: can’t mock IPublishedContent methodprivate static IPublishedContent MockIPublishedContent() { var mock = new Mock<IPublishedContent>(); mock.Setup(x => x.Id).Returns(1000); mock.Setup(x => x.Name).Returns("Test content"); mock.Setup(x => x.CreatorName).Returns("A.N. Editor"); // This won’t work... mock.Setup(x => x.GetPropertyValue(It.IsAny<string>())) .Returns((string alias) => MockIPublishedContentProperty(alias)); return mock.Object; } GetPropertyValue() is an extension method, which can’t be mocked.
  43. 43. Example: using Microsoft Fakes [TestMethod] public void MapFromIPublishedContent_MapsCustomProperties() { using (ShimsContext.Create()) { var model = new SimpleViewModel(); var mapper = GetMapper(); var content = new StubPublishedContent(); Umbraco.Web.Fakes.ShimPublishedContentExtensions .GetPropertyValueIPublishedContentStringBoolean = (doc, alias, recursive) => { switch (alias) { case "bodyText": return "This is the body text"; default: return string.Empty; } }; mapper.Map(content, model); Assert.AreEqual("This is the body text", model.BodyText); } } We replace the method implementation with our own at runtime.
  44. 44. 6. Cleaning up views
  45. 45. Strongly typed partials • Often our HTML will contain blocks of similar layout that are repeated on multiple pages • We can use @Html.Partial to “DRY” up our views • By having our view models implement multiple small interfaces, or by using inheritance, we can strongly type our partial but avoid instantiating objects for each one
  46. 46. Example: custom view model for partial public class HeroSectionViewModel { public string Heading { get; set; } public string Standfirst { get; set; } } @model CodegardenSamples.Models.HeroSectionViewModel <h1>@Model.Heading</h1> <p>@Model.Standfirst</p> ... @model CodegardenSamples.Models.HomePageViewModel @Html.Partial("_HeroSection“, new HeroSectionViewModel { Heading = Model.Heading, Standfirst = Model.Heading }) We can strongly type our partials just as we can full page views BUT… we have to handle instantiating this partial's model in our view
  47. 47. Example: view models with inheritance public abstract class BaseViewModel { public string Heading { get; set; } public string Standfirst { get; set; } } public class HomePageViewModel : BaseViewModel { } public class ContentPageViewModel : BaseViewModel { } ... @model CodegardenSamples.Models.HomePageViewModel @Html.Partial("_HeroSection") ... @model CodegardenSamples.Models.BaseViewModel <h1>@Model.Heading</h1> <p>@Model.Standfirst</p> As our view models inherit from a base class… … a strongly typed partial can be created that can reference the model of the parent page. BUT… using inheritance in this way can be restrictive – we can only inherit from one base.
  48. 48. Example: view models with interfaces (1) public interface IHeroSection { string Heading { get; } string Standfirst { get; } } public interface ISideBar { string SideBarTitle { get; } string SideBarIntro { get; } } public class HomePageViewModel : IHeroSection, ISideBar { public string Heading { get; set; } public string Standfirst { get; set; } public string SideBarTitle { get; set; } public string SideBarIntro { get; set; } } Having our view model implement multiple small interfaces…
  49. 49. Example: view models with interfaces (2) @model CodegardenSamples.Models.HomePageViewModel @Html.Partial("_HeroSection") @Html.Partial("_SideBar") ... @model CodegardenSamples.Models.IHeroSection <h1>@Model.Heading</h1> <p>@Model.Standfirst</p> ... @model CodegardenSamples.Models.ISideBar <h2>@Model.SideBarTitle.</h2> <p>@Model.SideBarIntro</p> … again means we can reference the model in a strongly typed manner in the partials.
  50. 50. 7. Wrap up and Q &A
  51. 51. In summary • No one true way to build an Umbraco site… pick what works for you and your team • If you like the “purist” MVC approach, you can apply these best practices without too much additional effort, and still be “pragmatic” about delivery to your clients
  52. 52. Lastly, some thanks… • Neil, Ali, Rob, Raffaele, Nnamdi, Ollie and the rest of my colleagues at Zone • Numerous discussions, questions and advice as we’ve evolved techniques and technologies over the years • Anthony, Darren, Ismail, Jeavon, Jeroen, Shannon, Warren and many others • Blogs, forum threads and other community contributions that have influenced the thinking behind our work and this presentation

×