Interfaces

Project

Pages Html

NuGet packageOwin.Framework.Pages.Html
GitHub sourceOwinFramework.Pages.Html

Home |  Readme

Allows you to build a website with pages of Html content using templates, regions, layouts and 3rd party packages

This package contains a Build Engine for modules, pages, layouts, regions and components. If you want the fluent builder to use this build engine you need to add a couple of lines to your startup code similar to:

    var fluentBuilder = ninject.Get<IFluentBuilder>();
    ninject.Get<OwinFramework.Pages.Html.BuildEngine>().Install(fluentBuilder);
The assumes that you are using Ninject as your IoC container, and followed the getting started walkthrough. If this is not the case then you will need to adjust the code to work in your application.

After adding this build engine to the fluent builder you can add web pages to your application by defining modules, pages, layouts, regions and components using classes that are decorated with attributes. The fluent builder will discover these classes and use the attributes to configure these elements.

This package also contains a templating system. You can choose from a variety of template loaders and template parsers to add templates to the name manager, or you can programatically construct templates using a fluent syntax. These templates can be referenced by pages, layouts and regions to define their content. See template overview for more details.

Sample Page

The code below is taken from the getting started walkthrough. It creates a very simple web page that only contains "Hello, world".

    [IsPage]
    [Route("/", Method.Get)]
    [PageTitle("Getting started with Owin Framework Pages")]
    [UsesLayout("homePageLayout")]
    internal class HomePage { }

    [IsLayout("homePageLayout", "region1")]
    [RegionHtml("region1", "hello-world", "Hello, world")]
    internal class HomePageLayout { }

Attributes

The attributes that you can attach to your classes are defined in OwinFramework.Pages.Core.Attributes.

Page Attributes

The attributes that can be applied to pages are:

  • [IsPage] identifies this class as a web page
  • [CacheOutput] configures output caching for the page
  • [DataScope] defines a scope name used in resolving data providers
  • [DeployCss] css rules to include on this page
  • [DeployFunction] a JavaScript function to include on this page
  • [DeployedAs] specifies how JavaScript and CSS should be deployed for this page
  • [Description] a description to output into the page documentation
  • [Example] an example usage to output into the page documentation
  • [NeedsComponent] identifies a dependent component
  • [NeedsData] identifies data that is required for this page
  • [Option] documents an optional part of the request for this page
  • [PageTitle] specifies the title to output into the page head
  • [PartOf] defines the package (namespace) for CSS and JavaScript on this page
  • [RegionTemplate] defines a template to render into a region of the page layout
  • [RequiresPermission] specifies a permission that users must have to see this page
  • [Route] defines which requests will be routed to this page
  • [Style] adds CSS style to the body element of the page
  • [UsesComponent] adds a non-visual component to the page
  • [UsesLayout] specifies the layout for the page

Layout Attributes

The attributes that can be applied to layouts are:

  • [IsLayout] identifies this class as a layout
  • [ChildContainer] configures the html element to use when grouping layout regions
  • [Container] configures the html element to use as a container for this layout
  • [DeployCss] css rules to include on any page that includes this layout
  • [DeployFunction] a JavaScript function to include on any page that has this layout on it
  • [DeployedAs] specifies how JavaScript and CSS should be deployed for this layout
  • [LayoutRegion] defines the region element to place into a region of the layout
  • [NeedsComponent] identifies a dependent component
  • [NeedsData] identifies data that is required for this layout
  • [PartOf] defines the package (namespace) for CSS and JavaScript in this layout
  • [RegionComponent] fills a region of the layout with a component
  • [RegionHtml] fills a region of the layout with localized HTML
  • [RegionLayout] fills a region of the layout with a layout
  • [RegionTemplate] fills a region of the layout with a template
  • [Style] adds CSS style to layout container

Region Attributes

The attributes that can be applied to regions are:

  • [IsRegion] identifies this class as a region
  • [ChildContainer] configures the html element to use when repeating the contents of the region
  • [ChildStyle] a css style to apply to the elements that repeat within the region
  • [Container] configures the html element to use as a container for this region
  • [DataScope] defines a scope name used in resolving data providers
  • [DeployCss] css rules to include on any page that includes this region
  • [DeployFunction] a JavaScript function to include on any page that has this region on it
  • [DeployedAs] specifies how JavaScript and CSS should be deployed for this region
  • [NeedsComponent] identifies a dependent component
  • [NeedsData] identifies data that is required for this region
  • [PartOf] defines the package (namespace) for CSS and JavaScript in this region
  • [Repeat] makes the contents of this region repeat for each object in a list
  • [RenderTemplate] specifies a template to render into an area of the page
  • [Style] adds CSS style to region container
  • [UsesComponent] specifies that this region contains a component
  • [UsesLayout] specifies that this region contains a layout

Component Attributes

The attributes that can be applied to components are:

  • [IsComponent] identifies this class as a component
  • [DeployCss] css rules to include on any page that includes this component
  • [DeployFunction] a JavaScript function to include on any page that has this component on it
  • [DeployedAs] specifies how JavaScript and CSS should be deployed for this component
  • [NeedsComponent] identifies a dependent component
  • [NeedsData] identifies data that is required for this component
  • [PartOf] defines the package (namespace) for CSS and JavaScript in this component
  • [RenderHtml] makes this component render localized HTML

Templates

Templates are a really important concept in building a website. Writing classes and decorating them with attributes to define pages, layouts, regions etc is very powerful and flexible, especially when those classes inherit from the standard implementation and override some of the virtual methods, but this technique has two important drawbacks: All of the website content is compiled into the asemblies which means you need to recompile and redepoly to change anything; Writing html and JavaScript as strings that are passed to attribute constructors is ugly, and you don't get any help from the development tool (syntax highlighting and intellisense).

The templating system addresses these two issues by allowing you to place snippets of html, css and JavaScript into separate resources then referencing them from region and layout elements.

The template system overview contains a lot more information about how to use templates. Note that all of the content of this website is defined in templates.

Semi-Custom Elements

As well as defining classes and decorating them with attributes as described above, you can also make these classes inherit from the base class and override virtual methods to customize the behavior.

You can use this technique for all types of element. The example below is for a page:

using System;
using System.Threading;
using System.Threading.Tasks;
using OwinFramework.Pages.Core.Attributes;
using OwinFramework.Pages.Core.Interfaces.Builder;
using OwinFramework.Pages.Core.Interfaces.Runtime;
using OwinFramework.Pages.Html.Runtime;
namespace Sample1.SamplePages
{
    [Description("<p>This is an example of how to add a semi custom page that inherits from the base Page base class</p>")]
    [Example("<a href='/pages/semiCustom.html'>/pages/semiCustom.html</a>")]
    internal class SemiCustomPage : Page
    {
        public SemiCustomPage(IPageDependenciesFactory dependenciesFactory)
            : base(dependenciesFactory)
        {
            TitleFunc = context => "Page title";
        }

        public override IWriteResult WriteBodyArea(IRenderContext context)
        {
            var html = context.Html;

            // Save this location in the output buffer
            var begining = html.CreateInsertionPoint();

            // Write a paragraph of text
            html.WriteElementLine("p", "This is a semi custom page", "class", "normal");

            // Use the saved buffer location to write the heading before the paragraph
            // and do this in a separate thread
            var task = Task.Factory.StartNew(() =>
                {
                    // Simulate a call to a service or database here
                    Thread.Sleep(10);
                    begining.WriteElementLine("h1", "Semi Custom", "class", "page-heading");
                });

            // Write a second paragraph of text
            html.WriteElementLine("p", "My second paragraph of text", "class", "normal");

            return WriteResult.WaitFor(task);
        }

        public override IWriteResult WriteInPageStyles(
            ICssWriter writer, 
            Func<ICssWriter, IWriteResult, IWriteResult> childrenWriter)
        {
            writer.WriteRule(".normal", "background-color: linen; font-size: 12px;");
            writer.WriteRule(".page-heading", "font-size: 16px;");
            return base.WriteInPageStyles(writer, childrenWriter);
        }

        public override IWriteResult WriteHeadArea(IRenderContext context)
        {
            var result = base.WriteHeadArea(context);
            context.Html.WriteUnclosedElement(
                "link", "rel",
                "stylesheet", "href", "https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css");
            context.Html.WriteLine();
            return result;
        }
    }
}

Fully Custom Elements

As well as defining classes and decorating them with attributes as described above, you can also make these classes implement an interface to fully define the behavior.

You can use this technique for all types of element. The example below is for a page:

using System;
using System.Threading.Tasks;
using Microsoft.Owin;
using OwinFramework.InterfacesV1.Middleware;
using OwinFramework.Pages.Core.Attributes;
using OwinFramework.Pages.Core.Debug;
using OwinFramework.Pages.Core.Enums;
using OwinFramework.Pages.Core.Interfaces;
using OwinFramework.Pages.Core.Interfaces.Runtime;
namespace Sample1.SamplePages
{
    [Description("<p>This is an example of how to add a full custom page that directly implements IPage</p>")]
    [Option(OptionType.Method, "GET", "<p>Returns the html for this custom page</p>")]
    [Option(OptionType.Header, "Accept", "text/html")]
    [Example("<a href='/pages/anything.html'>/pages/anything.html</a>")]
    internal class FullCustomPage : IPage
    {
        public ElementType ElementType { get { return ElementType.Page; } }
        public string Name { get; set; }
        public IPackage Package { get; set; }
        string IRunable.RequiredPermission { get { return null; } set { } }
        string IRunable.SecureResource { get { return null; } set { } }
        bool IRunable.AllowAnonymous { get { return true; } set { } }
        Func<IOwinContext, bool> IRunable.AuthenticationFunc { get { return null; } }
        Func<IRenderContext, string> IPage.CanonicalUrlFunc { get; set; }
        string IRunable.CacheCategory { get; set; }
        CachePriority IRunable.CachePriority { get; set; }
        public void Initialize()
        {
        }
        public DebugInfo GetDebugInfo(int parentDepth, int childDepth)
        {
            return new DebugPage
            {
                Name = Name,
                Instance = this
            };
        }
        Task IRunable.Run(IOwinContext context, Action<IOwinContext, Func<string>> trace)
        {
            context.Response.ContentType = "text/html";
            return context.Response.WriteAsync("<html><head><title>Full custom</title></head><body>This is a fully custom page</body></html>");
        }
    }
}